Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: base/test/launcher/unit_test_launcher.cc

Issue 862133002: Update from https://crrev.com/312398 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/test/launcher/unit_test_launcher.h" 5 #include "base/test/launcher/unit_test_launcher.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 const base::FilePath& output_file) { 93 const base::FilePath& output_file) {
94 CommandLine new_cmd_line(*CommandLine::ForCurrentProcess()); 94 CommandLine new_cmd_line(*CommandLine::ForCurrentProcess());
95 95
96 new_cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file); 96 new_cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file);
97 new_cmd_line.AppendSwitchASCII(kGTestFilterFlag, JoinString(test_names, ":")); 97 new_cmd_line.AppendSwitchASCII(kGTestFilterFlag, JoinString(test_names, ":"));
98 new_cmd_line.AppendSwitch(kSingleProcessTestsFlag); 98 new_cmd_line.AppendSwitch(kSingleProcessTestsFlag);
99 99
100 return new_cmd_line; 100 return new_cmd_line;
101 } 101 }
102 102
103 class UnitTestLauncherDelegate : public TestLauncherDelegate { 103 bool GetSwitchValueAsInt(const std::string& switch_name, int* result) {
104 public: 104 if (!CommandLine::ForCurrentProcess()->HasSwitch(switch_name))
105 explicit UnitTestLauncherDelegate(size_t batch_limit, bool use_job_objects) 105 return true;
106 : batch_limit_(batch_limit), 106
107 use_job_objects_(use_job_objects) { 107 std::string switch_value =
108 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name);
109 if (!StringToInt(switch_value, result) || *result < 1) {
110 LOG(ERROR) << "Invalid value for " << switch_name << ": " << switch_value;
111 return false;
108 } 112 }
109 113
110 ~UnitTestLauncherDelegate() override { 114 return true;
111 DCHECK(thread_checker_.CalledOnValidThread()); 115 }
116
117 int LaunchUnitTestsInternal(const RunTestSuiteCallback& run_test_suite,
118 int default_jobs,
119 bool use_job_objects,
120 const Closure& gtest_init) {
121 #if defined(OS_ANDROID)
122 // We can't easily fork on Android, just run the test suite directly.
123 return run_test_suite.Run();
124 #else
125 bool force_single_process = false;
126 if (CommandLine::ForCurrentProcess()->HasSwitch(
127 switches::kTestLauncherDebugLauncher)) {
128 fprintf(stdout, "Forcing test launcher debugging mode.\n");
129 fflush(stdout);
130 } else {
131 if (base::debug::BeingDebugged()) {
132 fprintf(stdout,
133 "Debugger detected, switching to single process mode.\n"
134 "Pass --test-launcher-debug-launcher to debug the launcher "
135 "itself.\n");
136 fflush(stdout);
137 force_single_process = true;
138 }
112 } 139 }
113 140
114 private: 141 if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
115 struct GTestCallbackState { 142 CommandLine::ForCurrentProcess()->HasSwitch(kGTestListTestsFlag) ||
116 TestLauncher* test_launcher; 143 CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessTestsFlag) ||
117 std::vector<std::string> test_names; 144 force_single_process) {
118 FilePath output_file; 145 return run_test_suite.Run();
119 }; 146 }
147 #endif
120 148
121 bool ShouldRunTest(const std::string& test_case_name, 149 if (CommandLine::ForCurrentProcess()->HasSwitch(kHelpFlag)) {
122 const std::string& test_name) override { 150 PrintUsage();
151 return 0;
152 }
153
154 base::TimeTicks start_time(base::TimeTicks::Now());
155
156 gtest_init.Run();
157 TestTimeouts::Initialize();
158
159 int batch_limit = kDefaultTestBatchLimit;
160 if (!GetSwitchValueAsInt(switches::kTestLauncherBatchLimit, &batch_limit))
161 return 1;
162
163 fprintf(stdout,
164 "IMPORTANT DEBUGGING NOTE: batches of tests are run inside their\n"
165 "own process. For debugging a test inside a debugger, use the\n"
166 "--gtest_filter=<your_test_name> flag along with\n"
167 "--single-process-tests.\n");
168 fflush(stdout);
169
170 MessageLoopForIO message_loop;
171
172 UnitTestLauncherDelegate delegate(batch_limit, use_job_objects);
173 base::TestLauncher launcher(&delegate, default_jobs);
174 bool success = launcher.Run();
175
176 fprintf(stdout, "Tests took %" PRId64 " seconds.\n",
177 (base::TimeTicks::Now() - start_time).InSeconds());
178 fflush(stdout);
179
180 return (success ? 0 : 1);
181 }
182
183 void InitGoogleTestChar(int* argc, char** argv) {
184 testing::InitGoogleTest(argc, argv);
185 }
186
187 #if defined(OS_WIN)
188 void InitGoogleTestWChar(int* argc, wchar_t** argv) {
189 testing::InitGoogleTest(argc, argv);
190 }
191 #endif // defined(OS_WIN)
192
193 } // namespace
194
195 int LaunchUnitTests(int argc,
196 char** argv,
197 const RunTestSuiteCallback& run_test_suite) {
198 CommandLine::Init(argc, argv);
199 return LaunchUnitTestsInternal(run_test_suite, SysInfo::NumberOfProcessors(),
200 true, Bind(&InitGoogleTestChar, &argc, argv));
201 }
202
203 int LaunchUnitTestsSerially(int argc,
204 char** argv,
205 const RunTestSuiteCallback& run_test_suite) {
206 CommandLine::Init(argc, argv);
207 return LaunchUnitTestsInternal(run_test_suite, 1, true,
208 Bind(&InitGoogleTestChar, &argc, argv));
209 }
210
211 #if defined(OS_WIN)
212 int LaunchUnitTests(int argc,
213 wchar_t** argv,
214 bool use_job_objects,
215 const RunTestSuiteCallback& run_test_suite) {
216 // Windows CommandLine::Init ignores argv anyway.
217 CommandLine::Init(argc, NULL);
218 return LaunchUnitTestsInternal(run_test_suite, SysInfo::NumberOfProcessors(),
219 use_job_objects,
220 Bind(&InitGoogleTestWChar, &argc, argv));
221 }
222 #endif // defined(OS_WIN)
223
224 UnitTestLauncherDelegate::UnitTestLauncherDelegate(size_t batch_limit,
225 bool use_job_objects)
226 : batch_limit_(batch_limit), use_job_objects_(use_job_objects) {
227 }
228
229 UnitTestLauncherDelegate::~UnitTestLauncherDelegate() {
230 DCHECK(thread_checker_.CalledOnValidThread());
231 }
232
233 UnitTestLauncherDelegate::GTestCallbackState::GTestCallbackState() {
234 }
235
236 UnitTestLauncherDelegate::GTestCallbackState::~GTestCallbackState() {
237 }
238
239 bool UnitTestLauncherDelegate::GetTests(std::vector<SplitTestName>* output) {
240 DCHECK(thread_checker_.CalledOnValidThread());
241 *output = GetCompiledInTests();
242 return true;
243 }
244
245 bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name,
246 const std::string& test_name) {
123 DCHECK(thread_checker_.CalledOnValidThread()); 247 DCHECK(thread_checker_.CalledOnValidThread());
124 248
125 // There is no additional logic to disable specific tests. 249 // There is no additional logic to disable specific tests.
126 return true; 250 return true;
127 } 251 }
128 252
129 size_t RunTests(TestLauncher* test_launcher, 253 size_t UnitTestLauncherDelegate::RunTests(
130 const std::vector<std::string>& test_names) override { 254 TestLauncher* test_launcher,
255 const std::vector<std::string>& test_names) {
131 DCHECK(thread_checker_.CalledOnValidThread()); 256 DCHECK(thread_checker_.CalledOnValidThread());
132 257
133 std::vector<std::string> batch; 258 std::vector<std::string> batch;
134 for (size_t i = 0; i < test_names.size(); i++) { 259 for (size_t i = 0; i < test_names.size(); i++) {
135 batch.push_back(test_names[i]); 260 batch.push_back(test_names[i]);
136 261
137 if (batch.size() >= batch_limit_) { 262 if (batch.size() >= batch_limit_) {
138 RunBatch(test_launcher, batch); 263 RunBatch(test_launcher, batch);
139 batch.clear(); 264 batch.clear();
140 } 265 }
141 } 266 }
142 267
143 RunBatch(test_launcher, batch); 268 RunBatch(test_launcher, batch);
144 269
145 return test_names.size(); 270 return test_names.size();
146 } 271 }
147 272
148 size_t RetryTests(TestLauncher* test_launcher, 273 size_t UnitTestLauncherDelegate::RetryTests(
149 const std::vector<std::string>& test_names) override { 274 TestLauncher* test_launcher,
275 const std::vector<std::string>& test_names) {
150 MessageLoop::current()->PostTask( 276 MessageLoop::current()->PostTask(
151 FROM_HERE, 277 FROM_HERE,
152 Bind(&UnitTestLauncherDelegate::RunSerially, 278 Bind(&UnitTestLauncherDelegate::RunSerially,
153 Unretained(this), 279 Unretained(this),
154 test_launcher, 280 test_launcher,
155 test_names)); 281 test_names));
156 return test_names.size(); 282 return test_names.size();
157 } 283 }
158 284
159 void RunSerially(TestLauncher* test_launcher, 285 void UnitTestLauncherDelegate::RunSerially(
160 const std::vector<std::string>& test_names) { 286 TestLauncher* test_launcher,
287 const std::vector<std::string>& test_names) {
161 if (test_names.empty()) 288 if (test_names.empty())
162 return; 289 return;
163 290
164 std::vector<std::string> new_test_names(test_names); 291 std::vector<std::string> new_test_names(test_names);
165 std::string test_name(new_test_names.back()); 292 std::string test_name(new_test_names.back());
166 new_test_names.pop_back(); 293 new_test_names.pop_back();
167 294
168 // Create a dedicated temporary directory to store the xml result data 295 // Create a dedicated temporary directory to store the xml result data
169 // per run to ensure clean state and make it possible to launch multiple 296 // per run to ensure clean state and make it possible to launch multiple
170 // processes in parallel. 297 // processes in parallel.
(...skipping 15 matching lines...) Expand all
186 cmd_line, 313 cmd_line,
187 std::string(), 314 std::string(),
188 TestTimeouts::test_launcher_timeout(), 315 TestTimeouts::test_launcher_timeout(),
189 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, 316 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
190 Bind(&UnitTestLauncherDelegate::SerialGTestCallback, 317 Bind(&UnitTestLauncherDelegate::SerialGTestCallback,
191 Unretained(this), 318 Unretained(this),
192 callback_state, 319 callback_state,
193 new_test_names)); 320 new_test_names));
194 } 321 }
195 322
196 void RunBatch(TestLauncher* test_launcher, 323 void UnitTestLauncherDelegate::RunBatch(
197 const std::vector<std::string>& test_names) { 324 TestLauncher* test_launcher,
325 const std::vector<std::string>& test_names) {
198 DCHECK(thread_checker_.CalledOnValidThread()); 326 DCHECK(thread_checker_.CalledOnValidThread());
199 327
200 if (test_names.empty()) 328 if (test_names.empty())
201 return; 329 return;
202 330
203 // Create a dedicated temporary directory to store the xml result data 331 // Create a dedicated temporary directory to store the xml result data
204 // per run to ensure clean state and make it possible to launch multiple 332 // per run to ensure clean state and make it possible to launch multiple
205 // processes in parallel. 333 // processes in parallel.
206 base::FilePath output_file; 334 base::FilePath output_file;
207 CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file)); 335 CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
(...skipping 19 matching lines...) Expand all
227 test_launcher->LaunchChildGTestProcess( 355 test_launcher->LaunchChildGTestProcess(
228 cmd_line, 356 cmd_line,
229 std::string(), 357 std::string(),
230 timeout, 358 timeout,
231 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, 359 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
232 Bind(&UnitTestLauncherDelegate::GTestCallback, 360 Bind(&UnitTestLauncherDelegate::GTestCallback,
233 Unretained(this), 361 Unretained(this),
234 callback_state)); 362 callback_state));
235 } 363 }
236 364
237 void GTestCallback(const GTestCallbackState& callback_state, 365 void UnitTestLauncherDelegate::GTestCallback(
238 int exit_code, 366 const GTestCallbackState& callback_state,
239 const TimeDelta& elapsed_time, 367 int exit_code,
240 bool was_timeout, 368 const TimeDelta& elapsed_time,
241 const std::string& output) { 369 bool was_timeout,
370 const std::string& output) {
242 DCHECK(thread_checker_.CalledOnValidThread()); 371 DCHECK(thread_checker_.CalledOnValidThread());
243 std::vector<std::string> tests_to_relaunch; 372 std::vector<std::string> tests_to_relaunch;
244 ProcessTestResults(callback_state.test_launcher, 373 ProcessTestResults(callback_state.test_launcher,
245 callback_state.test_names, 374 callback_state.test_names,
246 callback_state.output_file, 375 callback_state.output_file,
247 output, 376 output,
248 exit_code, 377 exit_code,
249 was_timeout, 378 was_timeout,
250 &tests_to_relaunch); 379 &tests_to_relaunch);
251 380
252 // Relaunch requested tests in parallel, but only use single 381 // Relaunch requested tests in parallel, but only use single
253 // test per batch for more precise results (crashes, test passes 382 // test per batch for more precise results (crashes, test passes
254 // but non-zero exit codes etc). 383 // but non-zero exit codes etc).
255 for (size_t i = 0; i < tests_to_relaunch.size(); i++) { 384 for (size_t i = 0; i < tests_to_relaunch.size(); i++) {
256 std::vector<std::string> batch; 385 std::vector<std::string> batch;
257 batch.push_back(tests_to_relaunch[i]); 386 batch.push_back(tests_to_relaunch[i]);
258 RunBatch(callback_state.test_launcher, batch); 387 RunBatch(callback_state.test_launcher, batch);
259 } 388 }
260 389
261 // The temporary file's directory is also temporary. 390 // The temporary file's directory is also temporary.
262 DeleteFile(callback_state.output_file.DirName(), true); 391 DeleteFile(callback_state.output_file.DirName(), true);
263 } 392 }
264 393
265 void SerialGTestCallback(const GTestCallbackState& callback_state, 394 void UnitTestLauncherDelegate::SerialGTestCallback(
266 const std::vector<std::string>& test_names, 395 const GTestCallbackState& callback_state,
267 int exit_code, 396 const std::vector<std::string>& test_names,
268 const TimeDelta& elapsed_time, 397 int exit_code,
269 bool was_timeout, 398 const TimeDelta& elapsed_time,
270 const std::string& output) { 399 bool was_timeout,
400 const std::string& output) {
271 DCHECK(thread_checker_.CalledOnValidThread()); 401 DCHECK(thread_checker_.CalledOnValidThread());
272 std::vector<std::string> tests_to_relaunch; 402 std::vector<std::string> tests_to_relaunch;
273 bool called_any_callbacks = 403 bool called_any_callbacks =
274 ProcessTestResults(callback_state.test_launcher, 404 ProcessTestResults(callback_state.test_launcher,
275 callback_state.test_names, 405 callback_state.test_names,
276 callback_state.output_file, 406 callback_state.output_file,
277 output, 407 output,
278 exit_code, 408 exit_code,
279 was_timeout, 409 was_timeout,
280 &tests_to_relaunch); 410 &tests_to_relaunch);
281 411
282 // There is only one test, there cannot be other tests to relaunch 412 // There is only one test, there cannot be other tests to relaunch
283 // due to a crash. 413 // due to a crash.
284 DCHECK(tests_to_relaunch.empty()); 414 DCHECK(tests_to_relaunch.empty());
285 415
286 // There is only one test, we should have called back with its result. 416 // There is only one test, we should have called back with its result.
287 DCHECK(called_any_callbacks); 417 DCHECK(called_any_callbacks);
288 418
289 // The temporary file's directory is also temporary. 419 // The temporary file's directory is also temporary.
290 DeleteFile(callback_state.output_file.DirName(), true); 420 DeleteFile(callback_state.output_file.DirName(), true);
291 421
292 MessageLoop::current()->PostTask( 422 MessageLoop::current()->PostTask(
293 FROM_HERE, 423 FROM_HERE,
294 Bind(&UnitTestLauncherDelegate::RunSerially, 424 Bind(&UnitTestLauncherDelegate::RunSerially,
295 Unretained(this), 425 Unretained(this),
296 callback_state.test_launcher, 426 callback_state.test_launcher,
297 test_names)); 427 test_names));
298 } 428 }
299 429
300 static bool ProcessTestResults( 430 // static
431 bool UnitTestLauncherDelegate::ProcessTestResults(
301 TestLauncher* test_launcher, 432 TestLauncher* test_launcher,
302 const std::vector<std::string>& test_names, 433 const std::vector<std::string>& test_names,
303 const base::FilePath& output_file, 434 const base::FilePath& output_file,
304 const std::string& output, 435 const std::string& output,
305 int exit_code, 436 int exit_code,
306 bool was_timeout, 437 bool was_timeout,
307 std::vector<std::string>* tests_to_relaunch) { 438 std::vector<std::string>* tests_to_relaunch) {
308 std::vector<TestResult> test_results; 439 std::vector<TestResult> test_results;
309 bool crashed = false; 440 bool crashed = false;
310 bool have_test_results = 441 bool have_test_results =
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 test_result.full_name = test_names[i]; 558 test_result.full_name = test_names[i];
428 test_result.status = TestResult::TEST_UNKNOWN; 559 test_result.status = TestResult::TEST_UNKNOWN;
429 test_launcher->OnTestFinished(test_result); 560 test_launcher->OnTestFinished(test_result);
430 called_any_callback = true; 561 called_any_callback = true;
431 } 562 }
432 } 563 }
433 564
434 return called_any_callback; 565 return called_any_callback;
435 } 566 }
436 567
437 ThreadChecker thread_checker_;
438
439 // Maximum number of tests to run in a single batch.
440 size_t batch_limit_;
441
442 // Determines whether we use job objects on Windows.
443 bool use_job_objects_;
444 };
445
446 bool GetSwitchValueAsInt(const std::string& switch_name, int* result) {
447 if (!CommandLine::ForCurrentProcess()->HasSwitch(switch_name))
448 return true;
449
450 std::string switch_value =
451 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name);
452 if (!StringToInt(switch_value, result) || *result < 1) {
453 LOG(ERROR) << "Invalid value for " << switch_name << ": " << switch_value;
454 return false;
455 }
456
457 return true;
458 }
459
460 int LaunchUnitTestsInternal(const RunTestSuiteCallback& run_test_suite,
461 int default_jobs,
462 bool use_job_objects,
463 const Closure& gtest_init) {
464 #if defined(OS_ANDROID)
465 // We can't easily fork on Android, just run the test suite directly.
466 return run_test_suite.Run();
467 #else
468 bool force_single_process = false;
469 if (CommandLine::ForCurrentProcess()->HasSwitch(
470 switches::kTestLauncherDebugLauncher)) {
471 fprintf(stdout, "Forcing test launcher debugging mode.\n");
472 fflush(stdout);
473 } else {
474 if (base::debug::BeingDebugged()) {
475 fprintf(stdout,
476 "Debugger detected, switching to single process mode.\n"
477 "Pass --test-launcher-debug-launcher to debug the launcher "
478 "itself.\n");
479 fflush(stdout);
480 force_single_process = true;
481 }
482 }
483
484 if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
485 CommandLine::ForCurrentProcess()->HasSwitch(kGTestListTestsFlag) ||
486 CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessTestsFlag) ||
487 force_single_process) {
488 return run_test_suite.Run();
489 }
490 #endif
491
492 if (CommandLine::ForCurrentProcess()->HasSwitch(kHelpFlag)) {
493 PrintUsage();
494 return 0;
495 }
496
497 base::TimeTicks start_time(base::TimeTicks::Now());
498
499 gtest_init.Run();
500 TestTimeouts::Initialize();
501
502 int batch_limit = kDefaultTestBatchLimit;
503 if (!GetSwitchValueAsInt(switches::kTestLauncherBatchLimit, &batch_limit))
504 return 1;
505
506 fprintf(stdout,
507 "IMPORTANT DEBUGGING NOTE: batches of tests are run inside their\n"
508 "own process. For debugging a test inside a debugger, use the\n"
509 "--gtest_filter=<your_test_name> flag along with\n"
510 "--single-process-tests.\n");
511 fflush(stdout);
512
513 MessageLoopForIO message_loop;
514
515 UnitTestLauncherDelegate delegate(batch_limit, use_job_objects);
516 base::TestLauncher launcher(&delegate, default_jobs);
517 bool success = launcher.Run();
518
519 fprintf(stdout,
520 "Tests took %" PRId64 " seconds.\n",
521 (base::TimeTicks::Now() - start_time).InSeconds());
522 fflush(stdout);
523
524 return (success ? 0 : 1);
525 }
526
527 void InitGoogleTestChar(int* argc, char** argv) {
528 testing::InitGoogleTest(argc, argv);
529 }
530
531 #if defined(OS_WIN)
532 void InitGoogleTestWChar(int* argc, wchar_t** argv) {
533 testing::InitGoogleTest(argc, argv);
534 }
535 #endif // defined(OS_WIN)
536
537 } // namespace
538
539 int LaunchUnitTests(int argc,
540 char** argv,
541 const RunTestSuiteCallback& run_test_suite) {
542 CommandLine::Init(argc, argv);
543 return LaunchUnitTestsInternal(
544 run_test_suite,
545 SysInfo::NumberOfProcessors(),
546 true,
547 Bind(&InitGoogleTestChar, &argc, argv));
548 }
549
550 int LaunchUnitTestsSerially(int argc,
551 char** argv,
552 const RunTestSuiteCallback& run_test_suite) {
553 CommandLine::Init(argc, argv);
554 return LaunchUnitTestsInternal(
555 run_test_suite,
556 1,
557 true,
558 Bind(&InitGoogleTestChar, &argc, argv));
559 }
560
561 #if defined(OS_WIN)
562 int LaunchUnitTests(int argc,
563 wchar_t** argv,
564 bool use_job_objects,
565 const RunTestSuiteCallback& run_test_suite) {
566 // Windows CommandLine::Init ignores argv anyway.
567 CommandLine::Init(argc, NULL);
568 return LaunchUnitTestsInternal(
569 run_test_suite,
570 SysInfo::NumberOfProcessors(),
571 use_job_objects,
572 Bind(&InitGoogleTestWChar, &argc, argv));
573 }
574 #endif // defined(OS_WIN)
575
576 } // namespace base 568 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698