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/logger/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 : #include "syzygy/trace/service/trace_file_writer_factory.h"
41 :
42 : using namespace trace::client;
43 :
44 : namespace trace {
45 : namespace logger {
46 :
47 : namespace {
48 :
49 : class TestLoggerApp : public LoggerApp {
50 : public:
51 : using LoggerApp::FindActionHandler;
52 : using LoggerApp::OpenOutputFile;
53 : using LoggerApp::kStart;
54 : using LoggerApp::kSpawn;
55 : using LoggerApp::kStatus;
56 : using LoggerApp::kStop;
57 : using LoggerApp::kInstanceId;
58 : using LoggerApp::kUniqueInstanceId;
59 : using LoggerApp::kOutputFile;
60 : using LoggerApp::kMiniDumpDir;
61 : using LoggerApp::kStdOut;
62 : using LoggerApp::kStdErr;
63 : using LoggerApp::logger_command_line_;
64 : using LoggerApp::app_command_line_;
65 : using LoggerApp::instance_id_;
66 : using LoggerApp::action_;
67 : using LoggerApp::action_handler_;
68 : using LoggerApp::output_file_path_;
69 : using LoggerApp::append_;
70 : using LoggerApp::mini_dump_dir_;
71 : };
72 :
73 : typedef common::Application<TestLoggerApp> TestApp;
74 :
75 : class LoggerAppTest : public testing::ApplicationTestBase {
76 : public:
77 : typedef testing::ApplicationTestBase Super;
78 :
79 : LoggerAppTest()
80 : : cmd_line_(base::FilePath(L"logger.exe")),
81 E : test_impl_(test_app_.implementation()) {
82 E : }
83 :
84 E : void SetUp() {
85 E : Super::SetUp();
86 :
87 : // Several of the tests generate progress and (deliberate) error messages
88 : // that would otherwise clutter the unit-test output.
89 E : logging::SetMinLogLevel(logging::LOG_FATAL);
90 :
91 : // Setup the IO streams.
92 E : CreateTemporaryDir(&temp_dir_);
93 E : stdin_path_ = temp_dir_.Append(L"NUL");
94 E : stdout_path_ = temp_dir_.Append(L"stdout.txt");
95 E : stderr_path_ = temp_dir_.Append(L"stderr.txt");
96 E : InitStreams(stdin_path_, stdout_path_, stderr_path_);
97 :
98 : // Initialize the (potential) input and output path values.
99 E : instance_id_ = base::StringPrintf(L"%d", ::GetCurrentProcessId());
100 E : output_file_path_ = temp_dir_.Append(L"output.txt");
101 :
102 : // Point the application at the test's command-line and IO streams.
103 E : test_app_.set_command_line(&cmd_line_);
104 E : test_app_.set_in(in());
105 E : test_app_.set_out(out());
106 E : test_app_.set_err(err());
107 E : }
108 :
109 : protected:
110 : // Stashes the current log-level before each test instance and restores it
111 : // after each test completes.
112 : testing::ScopedLogLevelSaver log_level_saver;
113 :
114 : // @name The application under test.
115 : // @{
116 : TestApp test_app_;
117 : TestApp::Implementation& test_impl_;
118 : base::FilePath temp_dir_;
119 : base::FilePath stdin_path_;
120 : base::FilePath stdout_path_;
121 : base::FilePath stderr_path_;
122 : // @}
123 :
124 : // @name Command-line and parameters.
125 : // @{
126 : CommandLine cmd_line_;
127 : std::wstring instance_id_;
128 : base::FilePath output_file_path_;
129 : // @}
130 : };
131 :
132 : } // namespace
133 :
134 E : TEST_F(LoggerAppTest, GetHelp) {
135 E : cmd_line_.AppendSwitch("help");
136 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
137 E : }
138 :
139 E : TEST_F(LoggerAppTest, EmptyCommandLineFails) {
140 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
141 E : }
142 :
143 E : TEST_F(LoggerAppTest, ParseUnknownActionFails) {
144 E : cmd_line_.AppendArg("unknown");
145 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
146 E : }
147 :
148 E : TEST_F(LoggerAppTest, ParseMispelledActionFails) {
149 E : cmd_line_.AppendArg("star");
150 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
151 E : }
152 :
153 E : TEST_F(LoggerAppTest, ParseUniqueInstanceId) {
154 E : cmd_line_.AppendSwitch(TestLoggerApp::kUniqueInstanceId);
155 E : cmd_line_.AppendArg("start");
156 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
157 : EXPECT_EQ(TestLoggerApp::kMaxInstanceIdLength,
158 E : test_impl_.instance_id_.size());
159 E : }
160 :
161 E : TEST_F(LoggerAppTest, ParseDefaultMiniDumpDir) {
162 E : cmd_line_.AppendArg("start");
163 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
164 :
165 E : base::FilePath cwd;
166 E : ASSERT_TRUE(::PathService::Get(base::DIR_CURRENT, &cwd));
167 :
168 E : EXPECT_EQ(cwd, test_impl_.mini_dump_dir_);
169 E : }
170 :
171 E : TEST_F(LoggerAppTest, ParseMiniDumpDir) {
172 E : base::FilePath new_mini_dump_dir(temp_dir_.Append(L"mini_dumps"));
173 E : ASSERT_FALSE(file_util::DirectoryExists(new_mini_dump_dir));
174 :
175 E : cmd_line_.AppendSwitchPath(TestLoggerApp::kMiniDumpDir, new_mini_dump_dir);
176 E : cmd_line_.AppendArg("start");
177 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
178 :
179 E : EXPECT_EQ(new_mini_dump_dir, test_impl_.mini_dump_dir_);
180 E : EXPECT_TRUE(file_util::DirectoryExists(new_mini_dump_dir));
181 E : }
182 :
183 E : TEST_F(LoggerAppTest, ParseBasicStart) {
184 : cmd_line_.AppendSwitchPath(
185 E : TestLoggerApp::kOutputFile, output_file_path_);
186 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
187 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
188 E : EXPECT_EQ(output_file_path_, test_impl_.output_file_path_);
189 E : EXPECT_TRUE(test_impl_.instance_id_.empty());
190 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStart), test_impl_.action_);
191 E : EXPECT_TRUE(test_impl_.app_command_line_.get() == NULL);
192 E : EXPECT_EQ(&TestLoggerApp::Start, test_impl_.action_handler_);
193 E : }
194 :
195 E : TEST_F(LoggerAppTest, ParseBasicStartWithCommand) {
196 E : const base::FilePath kFooExe(L"foo.exe");
197 : cmd_line_.AppendSwitchPath(
198 E : TestLoggerApp::kOutputFile, output_file_path_);
199 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
200 E : cmd_line_.AppendArgNative(kFooExe.value());
201 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
202 E : EXPECT_EQ(output_file_path_, test_impl_.output_file_path_);
203 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStart), test_impl_.action_);
204 E : EXPECT_TRUE(test_impl_.instance_id_.empty());
205 E : EXPECT_TRUE(test_impl_.app_command_line_.get() != NULL);
206 E : EXPECT_EQ(&TestLoggerApp::Start, test_impl_.action_handler_);
207 E : }
208 :
209 E : TEST_F(LoggerAppTest, ParseFullStartWithCommand) {
210 E : const base::FilePath kFooExe(L"foo.exe");
211 E : const std::wstring kSwitchName(L"switch");
212 E : const std::wstring kSwitchValue(L"value");
213 E : const std::wstring kDash(L"--");
214 E : const std::wstring kArg1 = kDash + kSwitchName + L"=" + kSwitchValue;
215 :
216 : cmd_line_.AppendSwitchPath(
217 E : TestLoggerApp::kOutputFile, output_file_path_);
218 : cmd_line_.AppendSwitchNative(
219 E : TestLoggerApp::kInstanceId, instance_id_);
220 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
221 E : cmd_line_.AppendArgNative(kDash);
222 E : cmd_line_.AppendArgNative(kFooExe.value());
223 E : cmd_line_.AppendArgNative(kArg1);
224 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
225 E : EXPECT_EQ(output_file_path_, test_impl_.output_file_path_);
226 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
227 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStart), test_impl_.action_);
228 E : const CommandLine* app_cmd_line = test_impl_.app_command_line_.get();
229 E : ASSERT_TRUE(app_cmd_line != NULL);
230 E : EXPECT_EQ(kFooExe, app_cmd_line->GetProgram());
231 E : EXPECT_TRUE(app_cmd_line->GetSwitches().empty());
232 E : ASSERT_TRUE(app_cmd_line->GetArgs().size() == 1);
233 E : EXPECT_TRUE(app_cmd_line->GetArgs().at(0) == kArg1);
234 :
235 E : EXPECT_EQ(&TestLoggerApp::Start, test_impl_.action_handler_);
236 E : }
237 :
238 E : TEST_F(LoggerAppTest, ParseSpawn) {
239 : cmd_line_.AppendSwitchNative(
240 E : TestLoggerApp::kInstanceId, instance_id_);
241 E : cmd_line_.AppendArgNative(TestLoggerApp::kSpawn);
242 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
243 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
244 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
245 E : EXPECT_EQ(std::wstring(TestLoggerApp::kSpawn), test_impl_.action_);
246 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
247 E : EXPECT_EQ(&TestLoggerApp::Spawn, test_impl_.action_handler_);
248 E : }
249 :
250 E : TEST_F(LoggerAppTest, ParseStop) {
251 : cmd_line_.AppendSwitchNative(
252 E : TestLoggerApp::kInstanceId, instance_id_);
253 E : cmd_line_.AppendArgNative(TestLoggerApp::kStop);
254 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
255 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
256 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
257 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStop), test_impl_.action_);
258 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
259 E : EXPECT_EQ(&TestLoggerApp::Stop, test_impl_.action_handler_);
260 E : }
261 :
262 E : TEST_F(LoggerAppTest, ParseStatus) {
263 : cmd_line_.AppendSwitchNative(
264 E : TestLoggerApp::kInstanceId, instance_id_);
265 E : cmd_line_.AppendArgNative(TestLoggerApp::kStatus);
266 E : ASSERT_TRUE(test_impl_.ParseCommandLine(&cmd_line_));
267 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
268 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
269 E : EXPECT_EQ(std::wstring(TestLoggerApp::kStatus), test_impl_.action_);
270 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
271 E : EXPECT_EQ(&TestLoggerApp::Status, test_impl_.action_handler_);
272 E : }
273 :
274 E : TEST_F(LoggerAppTest, ParseUnknown) {
275 E : const std::wstring kUnknown(L"unknown");
276 : cmd_line_.AppendSwitchNative(
277 E : TestLoggerApp::kInstanceId, instance_id_);
278 E : cmd_line_.AppendArgNative(kUnknown);
279 E : ASSERT_FALSE(test_impl_.ParseCommandLine(&cmd_line_));
280 E : EXPECT_TRUE(test_impl_.output_file_path_.empty());
281 E : EXPECT_EQ(instance_id_, test_impl_.instance_id_);
282 E : EXPECT_EQ(kUnknown, test_impl_.action_);
283 E : EXPECT_EQ(NULL, test_impl_.app_command_line_.get());
284 E : EXPECT_TRUE(test_impl_.action_handler_ == NULL);
285 E : }
286 :
287 E : TEST_F(LoggerAppTest, StartEndToEnd) {
288 : cmd_line_.AppendSwitchNative(
289 E : TestLoggerApp::kInstanceId, instance_id_);
290 E : cmd_line_.AppendArgNative(TestLoggerApp::kStart);
291 E : cmd_line_.AppendArgNative(L"--");
292 E : cmd_line_.AppendArgNative(L"cmd.exe");
293 E : cmd_line_.AppendArgNative(L"/C");
294 E : cmd_line_.AppendArgNative(L"date /t");
295 :
296 E : ASSERT_EQ(0, test_app_.Run());
297 E : }
298 :
299 E : TEST_F(LoggerAppTest, SpawnStatusAndStop) {
300 : // TODO(rogerm): Write me!
301 E : }
302 :
303 : } // namespace logger
304 : } // namespace trace
|