|  | /* | 
|  | *  Copyright 2010 The WebRTC Project Authors. All rights reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "webrtc/base/gunit.h" | 
|  | #include "webrtc/base/pathutils.h" | 
|  | #include "webrtc/base/scoped_ptr.h" | 
|  | #include "webrtc/base/win32toolhelp.h" | 
|  |  | 
|  | namespace rtc { | 
|  |  | 
|  | typedef struct { | 
|  | // Required to match the toolhelp api struct 'design'. | 
|  | DWORD dwSize; | 
|  | int a; | 
|  | uint32 b; | 
|  | } TestData; | 
|  |  | 
|  | class Win32ToolhelpTest : public testing::Test { | 
|  | public: | 
|  | Win32ToolhelpTest() { | 
|  | } | 
|  |  | 
|  | HANDLE AsHandle() { | 
|  | return reinterpret_cast<HANDLE>(this); | 
|  | } | 
|  |  | 
|  | static Win32ToolhelpTest* AsFixture(HANDLE handle) { | 
|  | return reinterpret_cast<Win32ToolhelpTest*>(handle); | 
|  | } | 
|  |  | 
|  | static bool First(HANDLE handle, TestData* d) { | 
|  | Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); | 
|  | // This method should be called only once for every test. | 
|  | // If it is called more than once it return false which | 
|  | // should break the test. | 
|  | EXPECT_EQ(0, tst->first_called_); // Just to be safe. | 
|  | if (tst->first_called_ > 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *d = kTestData[0]; | 
|  | tst->index_ = 1; | 
|  | ++(tst->first_called_); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool Next(HANDLE handle, TestData* d) { | 
|  | Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); | 
|  | ++(tst->next_called_); | 
|  |  | 
|  | if (tst->index_ >= kTestDataSize) { | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | *d = kTestData[tst->index_]; | 
|  | ++(tst->index_); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool Fail(HANDLE handle, TestData* d) { | 
|  | Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); | 
|  | ++(tst->fail_called_); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool CloseHandle(HANDLE handle) { | 
|  | Win32ToolhelpTest* tst = Win32ToolhelpTest::AsFixture(handle); | 
|  | ++(tst->close_handle_called_); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | virtual void SetUp() { | 
|  | fail_called_ = 0; | 
|  | first_called_ = 0; | 
|  | next_called_ = 0; | 
|  | close_handle_called_ = 0; | 
|  | index_ = 0; | 
|  | } | 
|  |  | 
|  | static bool AllZero(const TestData& data) { | 
|  | return data.dwSize == 0 && data.a == 0 && data.b == 0; | 
|  | } | 
|  |  | 
|  | static bool Equals(const TestData& expected, const TestData& actual) { | 
|  | return expected.dwSize == actual.dwSize | 
|  | && expected.a == actual.a | 
|  | && expected.b == actual.b; | 
|  | } | 
|  |  | 
|  | bool CheckCallCounters(int first, int next, int fail, int close) { | 
|  | bool match = first_called_ == first && next_called_ == next | 
|  | && fail_called_ == fail && close_handle_called_ == close; | 
|  |  | 
|  | if (!match) { | 
|  | LOG(LS_ERROR) << "Expected: (" | 
|  | << first << ", " | 
|  | << next << ", " | 
|  | << fail << ", " | 
|  | << close << ")"; | 
|  |  | 
|  | LOG(LS_ERROR) << "Actual: (" | 
|  | << first_called_ << ", " | 
|  | << next_called_ << ", " | 
|  | << fail_called_ << ", " | 
|  | << close_handle_called_ << ")"; | 
|  | } | 
|  | return match; | 
|  | } | 
|  |  | 
|  | static const int kTestDataSize = 3; | 
|  | static const TestData kTestData[]; | 
|  | int index_; | 
|  | int first_called_; | 
|  | int fail_called_; | 
|  | int next_called_; | 
|  | int close_handle_called_; | 
|  | }; | 
|  |  | 
|  | const TestData Win32ToolhelpTest::kTestData[] = { | 
|  | {1, 1, 1}, {2, 2, 2}, {3, 3, 3} | 
|  | }; | 
|  |  | 
|  |  | 
|  | class TestTraits { | 
|  | public: | 
|  | typedef TestData Type; | 
|  |  | 
|  | static bool First(HANDLE handle, Type* t) { | 
|  | return Win32ToolhelpTest::First(handle, t); | 
|  | } | 
|  |  | 
|  | static bool Next(HANDLE handle, Type* t) { | 
|  | return Win32ToolhelpTest::Next(handle, t); | 
|  | } | 
|  |  | 
|  | static bool CloseHandle(HANDLE handle) { | 
|  | return Win32ToolhelpTest::CloseHandle(handle); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class BadFirstTraits { | 
|  | public: | 
|  | typedef TestData Type; | 
|  |  | 
|  | static bool First(HANDLE handle, Type* t) { | 
|  | return Win32ToolhelpTest::Fail(handle, t); | 
|  | } | 
|  |  | 
|  | static bool Next(HANDLE handle, Type* t) { | 
|  | // This should never be called. | 
|  | ADD_FAILURE(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool CloseHandle(HANDLE handle) { | 
|  | return Win32ToolhelpTest::CloseHandle(handle); | 
|  | } | 
|  | }; | 
|  |  | 
|  | class BadNextTraits { | 
|  | public: | 
|  | typedef TestData Type; | 
|  |  | 
|  | static bool First(HANDLE handle, Type* t) { | 
|  | return Win32ToolhelpTest::First(handle, t); | 
|  | } | 
|  |  | 
|  | static bool Next(HANDLE handle, Type* t) { | 
|  | return Win32ToolhelpTest::Fail(handle, t); | 
|  | } | 
|  |  | 
|  | static bool CloseHandle(HANDLE handle) { | 
|  | return Win32ToolhelpTest::CloseHandle(handle); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // The toolhelp in normally inherited but most of | 
|  | // these tests only excercise the methods from the | 
|  | // traits therefore I use a typedef to make the | 
|  | // test code easier to read. | 
|  | typedef rtc::ToolhelpEnumeratorBase<TestTraits> EnumeratorForTest; | 
|  |  | 
|  | TEST_F(Win32ToolhelpTest, TestNextWithInvalidCtorHandle) { | 
|  | EnumeratorForTest t(INVALID_HANDLE_VALUE); | 
|  |  | 
|  | EXPECT_FALSE(t.Next()); | 
|  | EXPECT_TRUE(CheckCallCounters(0, 0, 0, 0)); | 
|  | } | 
|  |  | 
|  | // Tests that Next() returns false if the first-pointer | 
|  | // function fails. | 
|  | TEST_F(Win32ToolhelpTest, TestNextFirstFails) { | 
|  | typedef rtc::ToolhelpEnumeratorBase<BadFirstTraits> BadEnumerator; | 
|  | rtc::scoped_ptr<BadEnumerator> t(new BadEnumerator(AsHandle())); | 
|  |  | 
|  | // If next ever fails it shall always fail. | 
|  | EXPECT_FALSE(t->Next()); | 
|  | EXPECT_FALSE(t->Next()); | 
|  | EXPECT_FALSE(t->Next()); | 
|  | t.reset(); | 
|  | EXPECT_TRUE(CheckCallCounters(0, 0, 1, 1)); | 
|  | } | 
|  |  | 
|  | // Tests that Next() returns false if the next-pointer | 
|  | // function fails. | 
|  | TEST_F(Win32ToolhelpTest, TestNextNextFails) { | 
|  | typedef rtc::ToolhelpEnumeratorBase<BadNextTraits> BadEnumerator; | 
|  | rtc::scoped_ptr<BadEnumerator> t(new BadEnumerator(AsHandle())); | 
|  |  | 
|  | // If next ever fails it shall always fail. No more calls | 
|  | // shall be dispatched to Next(...). | 
|  | EXPECT_TRUE(t->Next()); | 
|  | EXPECT_FALSE(t->Next()); | 
|  | EXPECT_FALSE(t->Next()); | 
|  | t.reset(); | 
|  | EXPECT_TRUE(CheckCallCounters(1, 0, 1, 1)); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Tests that current returns an object is all zero's | 
|  | // if Next() hasn't been called. | 
|  | TEST_F(Win32ToolhelpTest, TestCurrentNextNotCalled) { | 
|  | rtc::scoped_ptr<EnumeratorForTest> t(new EnumeratorForTest(AsHandle())); | 
|  | EXPECT_TRUE(AllZero(t->current())); | 
|  | t.reset(); | 
|  | EXPECT_TRUE(CheckCallCounters(0, 0, 0, 1)); | 
|  | } | 
|  |  | 
|  | // Tests the simple everything works path through the code. | 
|  | TEST_F(Win32ToolhelpTest, TestCurrentNextCalled) { | 
|  | rtc::scoped_ptr<EnumeratorForTest> t(new EnumeratorForTest(AsHandle())); | 
|  |  | 
|  | EXPECT_TRUE(t->Next()); | 
|  | EXPECT_TRUE(Equals(t->current(), kTestData[0])); | 
|  | EXPECT_TRUE(t->Next()); | 
|  | EXPECT_TRUE(Equals(t->current(), kTestData[1])); | 
|  | EXPECT_TRUE(t->Next()); | 
|  | EXPECT_TRUE(Equals(t->current(), kTestData[2])); | 
|  | EXPECT_FALSE(t->Next()); | 
|  | t.reset(); | 
|  | EXPECT_TRUE(CheckCallCounters(1, 3, 0, 1)); | 
|  | } | 
|  |  | 
|  | TEST_F(Win32ToolhelpTest, TestCurrentProcess) { | 
|  | WCHAR buf[MAX_PATH]; | 
|  | GetModuleFileName(NULL, buf, ARRAY_SIZE(buf)); | 
|  | std::wstring name = ToUtf16(Pathname(ToUtf8(buf)).filename()); | 
|  |  | 
|  | rtc::ProcessEnumerator processes; | 
|  | bool found = false; | 
|  | while (processes.Next()) { | 
|  | if (!name.compare(processes.current().szExeFile)) { | 
|  | found = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | EXPECT_TRUE(found); | 
|  |  | 
|  | rtc::ModuleEnumerator modules(processes.current().th32ProcessID); | 
|  | found = false; | 
|  | while (modules.Next()) { | 
|  | if (!name.compare(modules.current().szModule)) { | 
|  | found = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  | EXPECT_TRUE(found); | 
|  | } | 
|  |  | 
|  | }  // namespace rtc |