1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/windows_services/elevated_tracing_service/session_registry.h"
#include <windows.h>
#include <wrl/client.h>
#include <wrl/implements.h>
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/win/scoped_com_initializer.h"
#include "chrome/windows_services/elevated_tracing_service/with_child_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
namespace elevated_tracing_service {
namespace {
class DummyUnknown
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
IUnknown> {
public:
DummyUnknown() = default;
DummyUnknown(const DummyUnknown&) = delete;
DummyUnknown& operator=(const DummyUnknown&) = delete;
using Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
IUnknown>::CastToUnknown;
};
} // namespace
class SessionRegistryTest : public WithChildTest {
protected:
void SetUp() override { ASSERT_TRUE(scoped_com_initializer_.Succeeded()); }
SessionRegistry& session_registry() { return *session_registry_; }
private:
base::win::ScopedCOMInitializer scoped_com_initializer_;
scoped_refptr<SessionRegistry> session_registry_{
base::MakeRefCounted<SessionRegistry>()};
};
// Tests that only a single session may be active at a time.
TEST_F(SessionRegistryTest, OnlyOneActiveSession) {
auto child_process = SpawnChildWithEventHandles(kExitWhenSignaled);
ASSERT_TRUE(child_process.IsValid());
absl::Cleanup child_stopper = [this] { SignalChildTermination(); };
WaitForChildStart();
auto dummy = Microsoft::WRL::Make<DummyUnknown>();
ASSERT_TRUE(bool(dummy));
auto session = SessionRegistry::RegisterActiveSession(
dummy->CastToUnknown(), child_process.Duplicate());
ASSERT_TRUE(session);
// A second registration should fail.
ASSERT_FALSE(SessionRegistry::RegisterActiveSession(
dummy->CastToUnknown(), child_process.Duplicate()));
// Release the first session.
session.reset();
// And now a new one can be registered.
ASSERT_TRUE(SessionRegistry::RegisterActiveSession(dummy->CastToUnknown(),
std::move(child_process)));
}
// Tests that termination of the client is handled.
TEST_F(SessionRegistryTest, ClientTerminates) {
auto child_process = SpawnChildWithEventHandles(kExitWhenSignaled);
ASSERT_TRUE(child_process.IsValid());
absl::Cleanup child_stopper = [this] { SignalChildTermination(); };
WaitForChildStart();
auto dummy = Microsoft::WRL::Make<DummyUnknown>();
ASSERT_TRUE(bool(dummy));
auto session = SessionRegistry::RegisterActiveSession(
dummy->CastToUnknown(), child_process.Duplicate());
ASSERT_TRUE(session);
base::RunLoop run_loop;
session_registry().SetSessionClearedClosureForTesting(run_loop.QuitClosure());
// Tell the child to terminate.
std::move(child_stopper).Invoke();
// Wait for the child to terminate.
ASSERT_EQ(::WaitForSingleObject(child_process.Handle(), INFINITE),
WAIT_OBJECT_0);
// Wait for the registry to clear the session.
run_loop.Run();
// It should now be possible to register a new session.
child_process = SpawnChildWithEventHandles(kExitWhenSignaled);
ASSERT_TRUE(child_process.IsValid());
absl::Cleanup child_stopper2 = [this] { SignalChildTermination(); };
ASSERT_TRUE(SessionRegistry::RegisterActiveSession(
dummy->CastToUnknown(), child_process.Duplicate()));
}
} // namespace elevated_tracing_service
|