1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/trace/agent_logger/agent_logger_app.h"
16 :
17 : #include <psapi.h>
18 : #include <userenv.h>
19 :
20 : #include "base/command_line.h"
21 : #include "base/environment.h"
22 : #include "base/file_util.h"
23 : #include "base/path_service.h"
24 : #include "base/process_util.h"
25 : #include "base/string_util.h"
26 : #include "base/stringprintf.h"
27 : #include "base/utf_string_conversions.h"
28 : #include "base/files/scoped_temp_dir.h"
29 : #include "base/memory/scoped_ptr.h"
30 : #include "gmock/gmock.h"
31 : #include "gtest/gtest.h"
32 : #include "syzygy/common/align.h"
33 : #include "syzygy/common/unittest_util.h"
34 : #include "syzygy/core/unittest_util.h"
35 : #include "syzygy/trace/client/client_utils.h"
36 : #include "syzygy/trace/parse/parse_utils.h"
37 : #include "syzygy/trace/protocol/call_trace_defs.h"
38 : #include "syzygy/trace/rpc/rpc_helpers.h"
39 : #include "syzygy/trace/service/service_rpc_impl.h"
40 :
41 : using namespace trace::client;
42 :
43 : namespace trace {
44 : namespace agent_logger {
45 :
46 : namespace {
47 :
48 : class TestLoggerApp : public LoggerApp {
49 : public:
50 : using LoggerApp::FindActionHandler;
51 : using LoggerApp::OpenOutputFile;
52 : using LoggerApp::kStart;
53 : using LoggerApp::kSpawn;
54 : using LoggerApp::kStatus;
55 : using LoggerApp::kStop;
56 : using LoggerApp::kInstanceId;
57 : using LoggerApp::kUniqueInstanceId;
58 : using LoggerApp::kOutputFile;
59 : using LoggerApp::kMiniDumpDir;
60 : using LoggerApp::kStdOut;
61 : using LoggerApp::kStdErr;
62 : using LoggerApp::logger_command_line_;
63 : using LoggerApp::app_command_line_;
64 : using LoggerApp::instance_id_;
65 : using LoggerApp::action_;
66 : using LoggerApp::action_handler_;
67 : using LoggerApp::output_file_path_;
68 : using LoggerApp::append_;
69 : using LoggerApp::mini_dump_dir_;
70 : };
71 :
72 : typedef ::common::Application<TestLoggerApp> TestApp;
73 :
74 : class LoggerAppTest : public testing::ApplicationTestBase {
75 : public:
76 : typedef testing::ApplicationTestBase Super;
77 :
78 : LoggerAppTest()
79 : : cmd_line_(base::FilePath(L"agent_logger.exe")),
80 E : test_impl_(test_app_.implementation()) {
81 E : }
82 :
83 E : void SetUp() {
84 E : Super::SetUp();
85 :
86 : // Several of the tests generate progress and (deliberate) error messages
87 : // that would otherwise clutter the unit-test output.
88 E : logging::SetMinLogLevel(logging::LOG_FATAL);
89 :
90 : // Setup the IO streams.
91 E : CreateTemporaryDir(&temp_dir_);
92 E : stdin_path_ = temp_dir_.Append(L"NUL");
93 E : stdout_path_ = temp_dir_.Append(L"stdout.txt");
94 E : stderr_path_ = temp_dir_.Append(L"stderr.txt");
95 E : InitStreams(stdin_path_, stdout_path_, stderr_path_);
96 :
97 : // Initialize the (potential) input and output path values.
98 E : instance_id_ = base::StringPrintf(L"%d", ::GetCurrentProcessId());
99 E : output_file_path_ = temp_dir_.Append(L"output.txt");
100 :
101 : // Point the application at the test's command-line and IO streams.
102 E : test_app_.set_command_line(&cmd_line_);
103 E : test_app_.set_in(in());
104 E : test_app_.set_out(out());
105 E : test_app_.set_err(err());
106 E : }
107 :
108 : protected:
109 : // Stashes the current log-level before each test instance and restores it
110 : // after each test completes.
111 : testing::ScopedLogLevelSaver log_level_saver;
112 :
113 : // @name The application under test.
114 : // @{
115 : TestApp test_app_;
116 : TestApp::Implementation& test_impl_;
117 : base::FilePath temp_dir_;
118 : base::FilePath stdin_path_;
119 : base::FilePath stdout_path_;
120 : base::FilePath stderr_path_;
121 : // @}
122 :
123 : // @name Command-line and parameters.
124 : // @{
125 : CommandLine cmd_line_;
126 : std::wstring instance_id_;
127 : base::FilePath output_file_path_;
128 : // @}
129 : };
130 :
131 : } // namespace
132 :
133 E : TEST_F(LoggerAppTest, GetHelp) {
134 E : cmd_line_.AppendSwitch("help");
135 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
136 E : }
137 :
138 E : TEST_F(LoggerAppTest, EmptyCommandLineFails) {
139 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
140 E : }
141 :
142 E : TEST_F(LoggerAppTest, ParseUnknownActionFails) {
143 E : cmd_line_.AppendArg("unknown");
144 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
145 E : }
146 :
147 E : TEST_F(LoggerAppTest, ParseMispelledActionFails) {
148 E : cmd_line_.AppendArg("star");
149 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
150 E : }
151 :
152 E : TEST_F(LoggerAppTest, ParseUniqueInstanceId) {
153 E : cmd_line_.AppendSwitch(TestLoggerApp::kUniqueInstanceId);
154 E : cmd_line_.AppendArg("start");
155 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
156 : EXPECT_EQ(TestLoggerApp::kMaxInstanceIdLength,
157 E : test_impl_.instance_id_.size());
158 E : }
159 :
160 E : TEST_F(LoggerAppTest, ParseDefaultMiniDumpDir) {
161 E : cmd_line_.AppendArg("start");
162 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
163 :
164 E : base::FilePath cwd;
165 E : ASSERT_TRUE(::PathService::Get(base::DIR_CURRENT, &cwd));
166 :
167 E : EXPECT_EQ(cwd, test_impl_.mini_dump_dir_);
168 E : }
169 :
170 E : TEST_F(LoggerAppTest, ParseMiniDumpDir) {
171 E : base::FilePath new_mini_dump_dir(temp_dir_.Append(L"mini_dumps"));
172 E : ASSERT_FALSE(file_util::DirectoryExists(new_mini_dump_dir));
173 :
174 E : cmd_line_.AppendSwitchPath(TestLoggerApp::kMiniDumpDir, new_mini_dump_dir);
175 E : cmd_line_.AppendArg("start");
176 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
177 :
178 E : EXPECT_EQ(new_mini_dump_dir, test_impl_.mini_dump_dir_);
179 E : EXPECT_TRUE(file_util::DirectoryExists(new_mini_dump_dir));
180 E : }
181 :
182 E : TEST_F(LoggerAppTest, ParseBasicStart) {
183 : cmd_line_.AppendSwitchPath(
184 E : TestLoggerApp::kOutputFile, output_file_path_);
185 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
186 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
187 E : EXPECT_EQ(output_file_path_, test_impl_.output_file_path_);
188 E : EXPECT_TRUE(test_impl_.instance_id_.empty());
189 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStart), test_impl_.action_);
190 E : EXPECT_TRUE(test_impl_.app_command_line_.get() == NULL);
191 E : EXPECT_EQ(&TestLoggerApp::Start, test_impl_.action_handler_);
192 E : }
193 :
194 E : TEST_F(LoggerAppTest, ParseBasicStartWithCommand) {
195 E : const base::FilePath kFooExe(L"foo.exe");
196 : cmd_line_.AppendSwitchPath(
197 E : TestLoggerApp::kOutputFile, output_file_path_);
198 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
199 E : cmd_line_.AppendArgNative(kFooExe.value());
200 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
201 E : EXPECT_EQ(output_file_path_, test_impl_.output_file_path_);
202 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStart), test_impl_.action_);
203 E : EXPECT_TRUE(test_impl_.instance_id_.empty());
204 E : EXPECT_TRUE(test_impl_.app_command_line_.get() != NULL);
205 E : EXPECT_EQ(&TestLoggerApp::Start, test_impl_.action_handler_);
206 E : }
207 :
208 E : TEST_F(LoggerAppTest, ParseFullStartWithCommand) {
209 E : const base::FilePath kFooExe(L"foo.exe");
210 E : const std::wstring kSwitchName(L"switch");
211 E : const std::wstring kSwitchValue(L"value");
212 E : const std::wstring kDash(L"--");
213 E : const std::wstring kArg1 = kDash + kSwitchName + L"=" + kSwitchValue;
214 :
215 : cmd_line_.AppendSwitchPath(
216 E : TestLoggerApp::kOutputFile, output_file_path_);
217 : cmd_line_.AppendSwitchNative(
218 E : TestLoggerApp::kInstanceId, instance_id_);
219 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
220 E : cmd_line_.AppendArgNative(kDash);
221 E : cmd_line_.AppendArgNative(kFooExe.value());
222 E : cmd_line_.AppendArgNative(kArg1);
223 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
224 E : EXPECT_EQ(output_file_path_, test_impl_.output_file_path_);
225 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
226 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStart), test_impl_.action_);
227 E : const CommandLine* app_cmd_line = test_impl_.app_command_line_.get();
228 E : ASSERT_TRUE(app_cmd_line != NULL);
229 E : EXPECT_EQ(kFooExe, app_cmd_line->GetProgram());
230 E : EXPECT_TRUE(app_cmd_line->GetSwitches().empty());
231 E : ASSERT_TRUE(app_cmd_line->GetArgs().size() == 1);
232 E : EXPECT_TRUE(app_cmd_line->GetArgs().at(0) == kArg1);
233 :
234 E : EXPECT_EQ(&TestLoggerApp::Start, test_impl_.action_handler_);
235 E : }
236 :
237 E : TEST_F(LoggerAppTest, ParseSpawn) {
238 : cmd_line_.AppendSwitchNative(
239 E : TestLoggerApp::kInstanceId, instance_id_);
240 E : cmd_line_.AppendArgNative(TestLoggerApp::kSpawn);
241 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
242 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
243 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
244 E : EXPECT_EQ(std::wstring(TestLoggerApp::kSpawn), test_impl_.action_);
245 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
246 E : EXPECT_EQ(&TestLoggerApp::Spawn, test_impl_.action_handler_);
247 E : }
248 :
249 E : TEST_F(LoggerAppTest, ParseStop) {
250 : cmd_line_.AppendSwitchNative(
251 E : TestLoggerApp::kInstanceId, instance_id_);
252 E : cmd_line_.AppendArgNative(TestLoggerApp::kStop);
253 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
254 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
255 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
256 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStop), test_impl_.action_);
257 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
258 E : EXPECT_EQ(&TestLoggerApp::Stop, test_impl_.action_handler_);
259 E : }
260 :
261 E : TEST_F(LoggerAppTest, ParseStatus) {
262 : cmd_line_.AppendSwitchNative(
263 E : TestLoggerApp::kInstanceId, instance_id_);
264 E : cmd_line_.AppendArgNative(TestLoggerApp::kStatus);
265 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
266 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
267 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
268 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStatus), test_impl_.action_);
269 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
270 E : EXPECT_EQ(&TestLoggerApp::Status, test_impl_.action_handler_);
271 E : }
272 :
273 E : TEST_F(LoggerAppTest, ParseUnknown) {
274 E : const std::wstring kUnknown(L"unknown");
275 : cmd_line_.AppendSwitchNative(
276 E : TestLoggerApp::kInstanceId, instance_id_);
277 E : cmd_line_.AppendArgNative(kUnknown);
278 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
279 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
280 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
281 E : EXPECT_EQ(kUnknown, test_impl_.action_);
282 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
283 E : EXPECT_TRUE(test_impl_.action_handler_ == NULL);
284 E : }
285 :
286 E : TEST_F(LoggerAppTest, StartEndToEnd) {
287 : cmd_line_.AppendSwitchNative(
288 E : TestLoggerApp::kInstanceId, instance_id_);
289 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
290 E : cmd_line_.AppendArgNative(L"--");
291 E : cmd_line_.AppendArgNative(L"cmd.exe");
292 E : cmd_line_.AppendArgNative(L"/C");
293 E : cmd_line_.AppendArgNative(L"date /t");
294 :
295 E : ASSERT_EQ(0, test_app_.Run());
296 E : }
297 :
298 E : TEST_F(LoggerAppTest, SpawnStatusAndStop) {
299 : // TODO(rogerm): Write me!
300 E : }
301 :
302 : } // namespace agent_logger
303 : } // namespace trace
|