Index: chrome/browser/image_decoder_browsertest.cc |
diff --git a/chrome/browser/image_decoder_browsertest.cc b/chrome/browser/image_decoder_browsertest.cc |
index f5c81eeb1bbae5eeabdd4a4138ab50d9d5e3a173..7314976722d61edbd5dab82d122d1e005180d7a8 100644 |
--- a/chrome/browser/image_decoder_browsertest.cc |
+++ b/chrome/browser/image_decoder_browsertest.cc |
@@ -4,25 +4,35 @@ |
#include "chrome/browser/image_decoder.h" |
-#if defined(OS_POSIX) |
-#include <sys/types.h> |
-#include <signal.h> |
-#endif |
- |
+#include "chrome/grit/generated_resources.h" |
#include "chrome/test/base/in_process_browser_test.h" |
#include "content/public/browser/browser_child_process_observer.h" |
#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/child_process_data.h" |
#include "content/public/test/test_utils.h" |
+#include "ui/base/l10n/l10n_util.h" |
using content::BrowserThread; |
namespace { |
+std::string GetValidPngString() { |
+ // 1x1 PNG. Does not get much smaller than this. |
+ static const char kPngData[] = |
+ "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" |
+ "\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53" |
+ "\xde\x00\x00\x00\x0c\x49\x44\x41\x54\x08\xd7\x63\xf8\xff\xff\x3f" |
+ "\x00\x05\xfe\x02\xfe\xdc\xcc\x59\xe7\x00\x00\x00\x00\x49\x45\x4e" |
+ "\x44\xae\x42\x60\x82"; |
+ // Need to specify the buffer size because it contains NULs. |
+ return std::string(kPngData, sizeof(kPngData) - 1); |
+} |
+ |
class TestImageRequest : public ImageDecoder::ImageRequest { |
public: |
explicit TestImageRequest(const base::Closure& quit_closure) |
- : quit_closure_(quit_closure), |
+ : decode_succeeded_(false), |
+ quit_closure_(quit_closure), |
quit_called_(false) { |
} |
@@ -32,8 +42,11 @@ class TestImageRequest : public ImageDecoder::ImageRequest { |
} |
} |
+ bool decode_succeeded() const { return decode_succeeded_; } |
+ |
private: |
void OnImageDecoded(const SkBitmap& decoded_image) override { |
+ decode_succeeded_ = true; |
Quit(); |
} |
@@ -47,6 +60,8 @@ class TestImageRequest : public ImageDecoder::ImageRequest { |
quit_closure_.Run(); |
} |
+ bool decode_succeeded_; |
+ |
base::Closure quit_closure_; |
bool quit_called_; |
@@ -55,7 +70,10 @@ class TestImageRequest : public ImageDecoder::ImageRequest { |
class KillProcessObserver : public content::BrowserChildProcessObserver { |
public: |
- KillProcessObserver() { |
+ KillProcessObserver() |
+ : did_kill_(false), |
+ utility_process_name_( |
+ l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME)) { |
Add(this); |
} |
@@ -63,13 +81,19 @@ class KillProcessObserver : public content::BrowserChildProcessObserver { |
Remove(this); |
} |
+ bool did_kill() const { return did_kill_; } |
+ |
private: |
void BrowserChildProcessHostConnected( |
const content::ChildProcessData& data) override { |
- base::ProcessHandle handle = data.handle; |
- |
- if (handle == base::kNullProcessHandle) |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ if (data.handle == base::kNullProcessHandle || |
+ data.name != utility_process_name_) { |
return; |
+ } |
+ |
+ ASSERT_FALSE(did_kill_); |
+ base::ProcessHandle handle = data.handle; |
#if defined(OS_WIN) |
// On windows, duplicate the process handle since base::Process closes it on |
@@ -77,34 +101,22 @@ class KillProcessObserver : public content::BrowserChildProcessObserver { |
base::ProcessHandle out_handle; |
if (!::DuplicateHandle(GetCurrentProcess(), handle, |
GetCurrentProcess(), &out_handle, |
- 0, FALSE, DUPLICATE_SAME_ACCESS)) |
+ 0, FALSE, DUPLICATE_SAME_ACCESS)) { |
return; |
+ } |
handle = out_handle; |
#endif |
- EXPECT_TRUE(base::Process(handle).Terminate(0, true)); |
- } |
-}; |
- |
-#if defined(OS_POSIX) |
-class FreezeProcessObserver : public content::BrowserChildProcessObserver { |
- public: |
- FreezeProcessObserver() { |
- Add(this); |
+ // Use a non-zero exit code so it counts as a crash. |
+ EXPECT_TRUE(base::Process(handle).Terminate(1, true)); |
+ did_kill_ = true; |
} |
- ~FreezeProcessObserver() override { |
- Remove(this); |
- } |
+ bool did_kill_; |
+ const base::string16 utility_process_name_; |
- private: |
- void BrowserChildProcessHostConnected( |
- const content::ChildProcessData& data) override { |
- if (data.handle != base::kNullProcessHandle) |
- EXPECT_EQ(0, kill(data.handle, SIGSTOP)); |
- } |
+ DISALLOW_COPY_AND_ASSIGN(KillProcessObserver); |
}; |
-#endif // defined(OS_POSIX) |
} // namespace |
@@ -117,36 +129,44 @@ IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, Basic) { |
TestImageRequest test_request(runner->QuitClosure()); |
ImageDecoder::Start(&test_request, std::string()); |
runner->Run(); |
+ EXPECT_FALSE(test_request.decode_succeeded()); |
} |
-IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndDestroy) { |
+IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, BasicDecode) { |
scoped_refptr<content::MessageLoopRunner> runner = |
new content::MessageLoopRunner; |
- scoped_ptr<TestImageRequest> test_request( |
- new TestImageRequest(runner->QuitClosure())); |
- ImageDecoder::Start(test_request.get(), std::string()); |
- test_request.reset(); |
+ TestImageRequest test_request(runner->QuitClosure()); |
+ ImageDecoder::Start(&test_request, GetValidPngString()); |
runner->Run(); |
+ EXPECT_TRUE(test_request.decode_succeeded()); |
} |
-IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndKillProcess) { |
- KillProcessObserver observer; |
+IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndDestroy) { |
scoped_refptr<content::MessageLoopRunner> runner = |
new content::MessageLoopRunner; |
scoped_ptr<TestImageRequest> test_request( |
new TestImageRequest(runner->QuitClosure())); |
ImageDecoder::Start(test_request.get(), std::string()); |
+ test_request.reset(); |
runner->Run(); |
} |
-#if defined(OS_POSIX) |
-IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndFreezeProcess) { |
- FreezeProcessObserver observer; |
+// Killing the utility process counts as a crash. Thus the request fails. |
+// If ImageDecoder did not handle the crash properly, the request never finishes |
+// and this test would hang. |
+// Note: This test is inherently racy because KillProcessObserver lives on the |
+// UI thread but ImageDecoder does its work mainly on the IO thread. So the test |
+// checks for both possible valid outcomes. |
+IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndKillProcess) { |
+ KillProcessObserver observer; |
scoped_refptr<content::MessageLoopRunner> runner = |
new content::MessageLoopRunner; |
- scoped_ptr<TestImageRequest> test_request( |
- new TestImageRequest(runner->QuitClosure())); |
- ImageDecoder::Start(test_request.get(), std::string()); |
+ TestImageRequest test_request(runner->QuitClosure()); |
+ ImageDecoder::Start(&test_request, GetValidPngString()); |
runner->Run(); |
+ if (!test_request.decode_succeeded()) { |
+ // The UI thread won the race. Make sure the utility process did get killed. |
+ EXPECT_TRUE(observer.did_kill()); |
+ } |
+ // Else the IO thread won the race and the image got decoded. Oh well. |
} |
-#endif // defined(OS_POSIX) |