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

Side by Side Diff: chrome/browser/safe_browsing/srt_fetcher_browsertest_win.cc

Issue 2846333003: Chrome cleaner: move Chrome Cleaner files to their own directory (Closed)
Patch Set: Created 3 years, 7 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
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
6
7 #include <initializer_list>
8 #include <set>
9 #include <tuple>
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/callback.h"
16 #include "base/callback_helpers.h"
17 #include "base/files/file_path.h"
18 #include "base/macros.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/message_loop/message_loop.h"
21 #include "base/run_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/test/multiprocess_test.h"
24 #include "base/test/scoped_feature_list.h"
25 #include "base/test/test_mock_time_task_runner.h"
26 #include "base/threading/sequenced_task_runner_handle.h"
27 #include "base/threading/thread_task_runner_handle.h"
28 #include "base/time/time.h"
29 #include "base/version.h"
30 #include "chrome/browser/browser_process.h"
31 #include "chrome/browser/lifetime/keep_alive_types.h"
32 #include "chrome/browser/lifetime/scoped_keep_alive.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/safe_browsing/srt_chrome_prompt_impl.h"
35 #include "chrome/browser/safe_browsing/srt_client_info_win.h"
36 #include "chrome/browser/ui/browser.h"
37 #include "chrome/browser/ui/browser_finder.h"
38 #include "chrome/browser/ui/test/test_browser_dialog.h"
39 #include "chrome/common/pref_names.h"
40 #include "chrome/test/base/in_process_browser_test.h"
41 #include "components/chrome_cleaner/public/constants/constants.h"
42 #include "components/component_updater/pref_names.h"
43 #include "components/prefs/pref_service.h"
44 #include "components/safe_browsing_db/safe_browsing_prefs.h"
45 #include "mojo/edk/embedder/embedder.h"
46 #include "mojo/edk/embedder/scoped_ipc_support.h"
47 #include "mojo/edk/system/core.h"
48 #include "testing/multiprocess_func_list.h"
49
50 namespace safe_browsing {
51
52 namespace {
53
54 using chrome_cleaner::mojom::ElevationStatus;
55 using chrome_cleaner::mojom::PromptAcceptance;
56
57 // Special switches passed by the parent process (test case) to the reporter
58 // child process to indicate the behavior that should be mocked.
59 constexpr char kExitCodeToReturnSwitch[] = "exit-code-to-return";
60 constexpr char kReportUwSFoundSwitch[] = "report-uws-found";
61 constexpr char kReportElevationRequiredSwitch[] = "report-elevation-required";
62 constexpr char kExpectedPromptAcceptanceSwitch[] = "expected-prompt-acceptance";
63 constexpr char kExpectedReporterFailureSwitch[] = "expected-reporter-crash";
64
65 // The exit code to be returned in case of failure in the child process.
66 // This should never be passed as the expected exit code to be reported by
67 // tests.
68 constexpr int kFailureExitCode = -1;
69
70 // Pass the |prompt_acceptance| to the mock child process in command line
71 // switch kExpectedPromptAcceptanceSwitch.
72 void AddPromptAcceptanceToCommandLine(PromptAcceptance prompt_acceptance,
73 base::CommandLine* command_line) {
74 command_line->AppendSwitchASCII(
75 kExpectedPromptAcceptanceSwitch,
76 base::IntToString(static_cast<int>(prompt_acceptance)));
77 }
78
79 // Parses and returns the prompt acceptance value passed by the parent process
80 // in command line switch kExpectedPromptAcceptanceSwitch. Returns
81 // PromptAcceptance::UNSPECIFIED if the switch doesn't exist or can't be
82 // parsed to a valid PromptAcceptance enumerator.
83 PromptAcceptance PromptAcceptanceFromCommandLine(
84 const base::CommandLine& command_line) {
85 const std::string& prompt_acceptance_str =
86 command_line.GetSwitchValueASCII(kExpectedPromptAcceptanceSwitch);
87 int val = -1;
88 if (base::StringToInt(prompt_acceptance_str, &val)) {
89 PromptAcceptance prompt_acceptance = static_cast<PromptAcceptance>(val);
90 if (chrome_cleaner::mojom::IsKnownEnumValue(prompt_acceptance))
91 return prompt_acceptance;
92 }
93 return PromptAcceptance::UNSPECIFIED;
94 }
95
96 // Pointer to ChromePromptPtr object to be used by the child process. The
97 // object must be created, deleted, and accessed on the IPC thread only.
98 chrome_cleaner::mojom::ChromePromptPtr* g_chrome_prompt_ptr = nullptr;
99
100 // Potential failures in the reporter process that should be handled by the
101 // parent process.
102 enum class MockedReporterFailure {
103 kNone = 0,
104 // Crashes at specific moments in the reporter when connected to the IPC.
105 kCrashOnStartup = 1,
106 kCrashAfterConnectedToParentProcess = 2,
107 kCrashWhileWaitingForResponse = 3,
108 kCrashAfterReceivedResponse = 4,
109 // Once an IPC message is sent by the reporter, the parent process will
110 // report a bad message that will lead to an invocation of OnConnectionError.
111 kBadMessageReported = 5,
112 };
113
114 void AddExpectedCrashToCommandLine(MockedReporterFailure reporter_failure,
115 base::CommandLine* command_line) {
116 command_line->AppendSwitchASCII(
117 kExpectedReporterFailureSwitch,
118 base::IntToString(static_cast<int>(reporter_failure)));
119 }
120
121 void CrashIf(MockedReporterFailure reporter_failure) {
122 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
123 const std::string& expected_str =
124 command_line->GetSwitchValueASCII(kExpectedReporterFailureSwitch);
125 int val = -1;
126 if (base::StringToInt(expected_str, &val) &&
127 static_cast<MockedReporterFailure>(val) == reporter_failure) {
128 exit(kFailureExitCode);
129 }
130 }
131
132 // The callback function to be passed to ChromePrompt::PromptUser to check if
133 // the prompt accepted returned by the parent process is equal to
134 // |expected_prompt_acceptance|. Will set |expected_value_received| with the
135 // comparison result, so that the main thread can report failure. Will invoke
136 // |done| callback when done.
137 void PromptUserCallback(const base::Closure& done,
138 PromptAcceptance expected_prompt_acceptance,
139 bool* expected_value_received,
140 PromptAcceptance prompt_acceptance) {
141 *expected_value_received = prompt_acceptance == expected_prompt_acceptance;
142 // It's safe to delete the ChromePromptPtr object here, since it will not be
143 // used anymore by the child process.
144 delete g_chrome_prompt_ptr;
145 g_chrome_prompt_ptr = nullptr;
146 CrashIf(MockedReporterFailure::kCrashAfterReceivedResponse);
147 done.Run();
148 }
149
150 // Mocks the sending of scan results from the child process to the parent
151 // process. Obtains the behavior to be mocked from special switches in
152 // |command_line|. Sets |expected_value_received| as true if the parent
153 // process replies with the expected prompt acceptance value.
154 void SendScanResults(const std::string& chrome_mojo_pipe_token,
155 const base::CommandLine& command_line,
156 const base::Closure& done,
157 bool* expected_value_received) {
158 constexpr int kDefaultUwSId = 10;
159 constexpr char kDefaultUwSName[] = "RemovedUwS";
160
161 mojo::ScopedMessagePipeHandle message_pipe_handle =
162 mojo::edk::CreateChildMessagePipe(chrome_mojo_pipe_token);
163 // This pointer will be deleted by PromptUserCallback.
164 g_chrome_prompt_ptr = new chrome_cleaner::mojom::ChromePromptPtr();
165 g_chrome_prompt_ptr->Bind(chrome_cleaner::mojom::ChromePromptPtrInfo(
166 std::move(message_pipe_handle), 0));
167
168 std::vector<chrome_cleaner::mojom::UwSPtr> removable_uws_found;
169 if (command_line.HasSwitch(kReportUwSFoundSwitch)) {
170 chrome_cleaner::mojom::UwSPtr uws = chrome_cleaner::mojom::UwS::New();
171 uws->id = kDefaultUwSId;
172 uws->name = kDefaultUwSName;
173 uws->observed_behaviours = chrome_cleaner::mojom::ObservedBehaviours::New();
174 removable_uws_found.push_back(std::move(uws));
175 }
176 const ElevationStatus elevation_status =
177 command_line.HasSwitch(kReportElevationRequiredSwitch)
178 ? ElevationStatus::REQUIRED
179 : ElevationStatus::NOT_REQUIRED;
180 const PromptAcceptance expected_prompt_acceptance =
181 PromptAcceptanceFromCommandLine(command_line);
182
183 // This task is posted to the IPC thread so that it will happen after the
184 // request is sent to the parent process and before the response gets
185 // handled on the IPC thread.
186 base::SequencedTaskRunnerHandle::Get()->PostTask(
187 FROM_HERE,
188 base::Bind(&CrashIf,
189 MockedReporterFailure::kCrashWhileWaitingForResponse));
190
191 (*g_chrome_prompt_ptr)
192 ->PromptUser(
193 std::move(removable_uws_found), elevation_status,
194 base::Bind(&PromptUserCallback, done, expected_prompt_acceptance,
195 expected_value_received));
196 }
197
198 // Connects to the parent process and sends mocked scan results. Returns true
199 // if connection was successful and the prompt acceptance results sent by the
200 // parent process are the same as expected.
201 bool ConnectAndSendDataToParentProcess(
202 const std::string& chrome_mojo_pipe_token,
203 const base::CommandLine& command_line) {
204 DCHECK(!chrome_mojo_pipe_token.empty());
205
206 mojo::edk::Init();
207 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
208 base::Thread io_thread("IPCThread");
209 if (!io_thread.StartWithOptions(options))
210 return false;
211 mojo::edk::ScopedIPCSupport ipc_support(
212 io_thread.task_runner(),
213 mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
214 mojo::edk::SetParentPipeHandleFromCommandLine();
215
216 CrashIf(MockedReporterFailure::kCrashAfterConnectedToParentProcess);
217
218 base::MessageLoop message_loop;
219 base::RunLoop run_loop;
220 // After the response from the parent process is received, this will post a
221 // task to unblock the child process's main thread.
222 auto done = base::Bind(
223 [](scoped_refptr<base::SequencedTaskRunner> main_runner,
224 base::Closure quit_closure) {
225 main_runner->PostTask(FROM_HERE, std::move(quit_closure));
226 },
227 base::SequencedTaskRunnerHandle::Get(),
228 base::Passed(run_loop.QuitClosure()));
229
230 bool expected_value_received = false;
231 io_thread.task_runner()->PostTask(
232 FROM_HERE, base::Bind(&SendScanResults, chrome_mojo_pipe_token,
233 command_line, done, &expected_value_received));
234
235 run_loop.Run();
236
237 return expected_value_received;
238 }
239
240 // Mocks a Software Reporter process that returns an exit code specified by
241 // command line switch kExitCodeToReturnSwitch. If a Mojo IPC is available,
242 // this will also connect to the parent process and send mocked scan results
243 // to the parent process using data passed as command line switches.
244 MULTIPROCESS_TEST_MAIN(MockSwReporterProcess) {
245 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
246 CrashIf(MockedReporterFailure::kCrashOnStartup);
247 const std::string& str =
248 command_line->GetSwitchValueASCII(kExitCodeToReturnSwitch);
249 const std::string& chrome_mojo_pipe_token = command_line->GetSwitchValueASCII(
250 chrome_cleaner::kChromeMojoPipeTokenSwitch);
251 int exit_code_to_report = kFailureExitCode;
252 bool success = base::StringToInt(str, &exit_code_to_report) &&
253 (chrome_mojo_pipe_token.empty() ||
254 ConnectAndSendDataToParentProcess(chrome_mojo_pipe_token,
255 *command_line));
256 return success ? exit_code_to_report : kFailureExitCode;
257 }
258
259 // Decorates a ChromePromptImpl object to simulate failures indentified by the
260 // parent process and keeps track of errors handled. By default, delegates all
261 // actions to the decorated object.
262 class ReportBadMessageChromePromptImpl : public ChromePromptImpl {
263 public:
264 ReportBadMessageChromePromptImpl(
265 chrome_cleaner::mojom::ChromePromptRequest request,
266 bool bad_message_expected,
267 base::Closure on_connection_closed)
268 : ChromePromptImpl(std::move(request), std::move(on_connection_closed)),
269 bad_message_expected_(bad_message_expected) {}
270 ~ReportBadMessageChromePromptImpl() override = default;
271
272 void PromptUser(
273 std::vector<chrome_cleaner::mojom::UwSPtr> removable_uws_found,
274 chrome_cleaner::mojom::ElevationStatus elevation_status,
275 const chrome_cleaner::mojom::ChromePrompt::PromptUserCallback& callback)
276 override {
277 if (bad_message_expected_)
278 mojo::ReportBadMessage("bad message");
279
280 ChromePromptImpl::PromptUser(std::move(removable_uws_found),
281 elevation_status, callback);
282 }
283
284 private:
285 bool bad_message_expected_ = false;
286
287 DISALLOW_COPY_AND_ASSIGN(ReportBadMessageChromePromptImpl);
288 };
289
290 // Parameters for this test:
291 // - bool in_browser_cleaner_ui: indicates if InBrowserCleanerUI experiment
292 // is enabled; if so, the parent and the child processes will communicate
293 // via a Mojo IPC.
294 // - ElevationStatus elevation_status: indicates if the scan results sent by
295 // the child process should consider that elevation will be required for
296 // cleanup.
297 // - MockedReporterFailure expected_reporter_failure: indicates errors that
298 // should be simulated in the reporter process.
299 class SRTFetcherTest
300 : public InProcessBrowserTest,
301 public SwReporterTestingDelegate,
302 public ::testing::WithParamInterface<
303 std::tuple<bool, ElevationStatus, MockedReporterFailure>> {
304 public:
305 SRTFetcherTest() = default;
306
307 void SetUpInProcessBrowserTestFixture() override {
308 SetSwReporterTestingDelegate(this);
309
310 std::tie(in_browser_cleaner_ui_, elevation_status_,
311 expected_reporter_failure_) = GetParam();
312 // The config should only accept elevation_status_ if InBrowserCleanerUI
313 // feature is enabled.
314 ASSERT_TRUE(elevation_status_ == ElevationStatus::NOT_REQUIRED ||
315 in_browser_cleaner_ui_);
316
317 if (in_browser_cleaner_ui_)
318 scoped_feature_list_.InitAndEnableFeature(kInBrowserCleanerUIFeature);
319 else
320 scoped_feature_list_.InitAndDisableFeature(kInBrowserCleanerUIFeature);
321 }
322
323 void SetUpOnMainThread() override {
324 // During the test, use a task runner with a mock clock.
325 saved_task_runner_ = base::ThreadTaskRunnerHandle::Get();
326 ASSERT_NE(mock_time_task_runner_, saved_task_runner_);
327 base::MessageLoop::current()->SetTaskRunner(mock_time_task_runner_);
328
329 InProcessBrowserTest::SetUpOnMainThread();
330
331 // SetDateInLocalState calculates a time as Now() minus an offset. Move the
332 // simulated clock ahead far enough that this calculation won't underflow.
333 mock_time_task_runner_->FastForwardBy(
334 base::TimeDelta::FromDays(kDaysBetweenSuccessfulSwReporterRuns * 2));
335
336 ClearLastTimeSentReport();
337 }
338
339 void TearDownOnMainThread() override {
340 // Restore the standard task runner to perform browser cleanup, which will
341 // wait forever with the mock clock.
342 base::MessageLoop::current()->SetTaskRunner(saved_task_runner_);
343 }
344
345 void TearDownInProcessBrowserTestFixture() override {
346 SetSwReporterTestingDelegate(nullptr);
347 }
348
349 // Records that the prompt was shown.
350 void TriggerPrompt(Browser* browser, const std::string& version) override {
351 prompt_trigger_called_ = true;
352 }
353
354 // Spawns and returns a subprocess to mock an execution of the reporter with
355 // the parameters given in |invocation| that will return
356 // |exit_code_to_report_|. If IPC communication needs to be mocked, this will
357 // also provide values that define the expected behavior of the child
358 // process.
359 // Records the launch and parameters used for further verification.
360 base::Process LaunchReporter(
361 const SwReporterInvocation& invocation,
362 const base::LaunchOptions& launch_options) override {
363 ++reporter_launch_count_;
364 reporter_launch_parameters_.push_back(invocation);
365 if (first_launch_callback_)
366 std::move(first_launch_callback_).Run();
367
368 if (exit_code_to_report_ == kReporterNotLaunchedExitCode)
369 return base::Process(); // IsValid() will return false.
370
371 base::CommandLine command_line(
372 base::GetMultiProcessTestChildBaseCommandLine());
373 command_line.AppendArguments(invocation.command_line,
374 /*include_program=*/false);
375 command_line.AppendSwitchASCII(kExitCodeToReturnSwitch,
376 base::IntToString(exit_code_to_report_));
377
378 if (in_browser_cleaner_ui_) {
379 AddPromptAcceptanceToCommandLine(PromptAcceptance::DENIED, &command_line);
380 if (exit_code_to_report_ == chrome_cleaner::kSwReporterCleanupNeeded) {
381 command_line.AppendSwitch(kReportUwSFoundSwitch);
382 if (elevation_status_ == ElevationStatus::REQUIRED)
383 command_line.AppendSwitch(kReportElevationRequiredSwitch);
384 }
385 }
386
387 if (expected_reporter_failure_ != MockedReporterFailure::kNone)
388 AddExpectedCrashToCommandLine(expected_reporter_failure_, &command_line);
389
390 bad_message_expected_ = expected_reporter_failure_ ==
391 MockedReporterFailure::kBadMessageReported;
392 bad_message_reported_ = false;
393
394 base::SpawnChildResult result = base::SpawnMultiProcessTestChild(
395 "MockSwReporterProcess", command_line, launch_options);
396 return std::move(result.process);
397 }
398
399 // Returns the test's idea of the current time.
400 base::Time Now() const override { return mock_time_task_runner_->Now(); }
401
402 // Returns a task runner to use when launching the reporter (which is
403 // normally a blocking action).
404 base::TaskRunner* BlockingTaskRunner() const override {
405 // Use the test's main task runner so that we don't need to pump another
406 // message loop. Since the test calls LaunchReporter instead of actually
407 // doing a blocking reporter launch, it doesn't matter that the task runner
408 // doesn't have the MayBlock trait.
409 return mock_time_task_runner_.get();
410 }
411
412 std::unique_ptr<ChromePromptImpl> CreateChromePromptImpl(
413 chrome_cleaner::mojom::ChromePromptRequest request) override {
414 return base::MakeUnique<ReportBadMessageChromePromptImpl>(
415 std::move(request), bad_message_expected_,
416 base::Bind(&SRTFetcherTest::OnConnectionClosed,
417 base::Unretained(this)));
418 }
419
420 void OnConnectionClosed() override {}
421
422 void OnConnectionError(const std::string& message) override {
423 bad_message_reported_ = true;
424 }
425
426 // Schedules a single reporter to run.
427 void RunReporter(int exit_code_to_report,
428 const base::FilePath& exe_path = base::FilePath()) {
429 exit_code_to_report_ = exit_code_to_report;
430 auto invocation = SwReporterInvocation::FromFilePath(exe_path);
431 invocation.supported_behaviours =
432 SwReporterInvocation::BEHAVIOUR_LOG_EXIT_CODE_TO_PREFS |
433 SwReporterInvocation::BEHAVIOUR_TRIGGER_PROMPT |
434 SwReporterInvocation::BEHAVIOUR_ALLOW_SEND_REPORTER_LOGS;
435
436 SwReporterQueue invocations;
437 invocations.push(invocation);
438 RunSwReporters(invocations, base::Version("1.2.3"));
439 }
440
441 // Schedules a queue of reporters to run.
442 void RunReporterQueue(int exit_code_to_report,
443 const SwReporterQueue& invocations) {
444 exit_code_to_report_ = exit_code_to_report;
445 RunSwReporters(invocations, base::Version("1.2.3"));
446 }
447
448 // Sets |path| in the local state to a date corresponding to |days| days ago.
449 void SetDateInLocalState(const std::string& path, int days) {
450 PrefService* local_state = g_browser_process->local_state();
451 local_state->SetInt64(
452 path, (Now() - base::TimeDelta::FromDays(days)).ToInternalValue());
453 }
454
455 // Sets local state for last time the software reporter ran to |days| days
456 // ago.
457 void SetDaysSinceLastTriggered(int days) {
458 SetDateInLocalState(prefs::kSwReporterLastTimeTriggered, days);
459 }
460
461 // Sets local state for last time the software reporter sent logs to |days|
462 // days ago.
463 void SetLastTimeSentReport(int days) {
464 SetDateInLocalState(prefs::kSwReporterLastTimeSentReport, days);
465 }
466
467 // Clears local state for last time the software reporter sent logs. This
468 // prevents potential false positives that could arise from state not
469 // properly cleaned between successive tests.
470 void ClearLastTimeSentReport() {
471 PrefService* local_state = g_browser_process->local_state();
472 local_state->ClearPref(prefs::kSwReporterLastTimeSentReport);
473 }
474
475 // Retrieves the timestamp of the last time the software reporter sent logs.
476 int64_t GetLastTimeSentReport() {
477 const PrefService* local_state = g_browser_process->local_state();
478 DCHECK(local_state->HasPrefPath(prefs::kSwReporterLastTimeSentReport));
479 return local_state->GetInt64(prefs::kSwReporterLastTimeSentReport);
480 }
481
482 void EnableSBExtendedReporting() {
483 Browser* browser = chrome::FindLastActive();
484 DCHECK(browser);
485 Profile* profile = browser->profile();
486 SetExtendedReportingPref(profile->GetPrefs(), true);
487 }
488
489 // Expects that the reporter has been scheduled to launch again in |days|
490 // days.
491 void ExpectToRunAgain(int days) {
492 EXPECT_TRUE(mock_time_task_runner_->HasPendingTask());
493 EXPECT_LT(base::TimeDelta::FromDays(days) - base::TimeDelta::FromHours(1),
494 mock_time_task_runner_->NextPendingTaskDelay());
495 EXPECT_GE(base::TimeDelta::FromDays(days),
496 mock_time_task_runner_->NextPendingTaskDelay());
497 }
498
499 // Expects that after |days_until_launch| days, the reporter will be
500 // launched |expected_launch_count| times, and TriggerPrompt will be
501 // called if and only if |expect_prompt| is true.
502 void ExpectReporterLaunches(int days_until_launch,
503 int expected_launch_count,
504 bool expect_prompt) {
505 EXPECT_TRUE(mock_time_task_runner_->HasPendingTask());
506 reporter_launch_count_ = 0;
507 reporter_launch_parameters_.clear();
508 prompt_trigger_called_ = false;
509
510 mock_time_task_runner_->FastForwardBy(
511 base::TimeDelta::FromDays(days_until_launch));
512
513 EXPECT_EQ(expected_launch_count, reporter_launch_count_);
514 // Note: a bad message error will not prevent the prompt that is shown to
515 // the user today. Once we hook the new prompt here, we will need to review
516 // this expectation.
517 EXPECT_EQ(expect_prompt &&
518 (expected_reporter_failure_ == MockedReporterFailure::kNone ||
519 expected_reporter_failure_ ==
520 MockedReporterFailure::kBadMessageReported),
521 prompt_trigger_called_);
522
523 EXPECT_EQ(bad_message_expected_, bad_message_reported_);
524 }
525
526 // Expects that after |days_until_launched| days, the reporter will be
527 // launched once with each path in |expected_launch_paths|, and TriggerPrompt
528 // will be called if and only if |expect_prompt| is true.
529 void ExpectReporterLaunches(
530 int days_until_launch,
531 const std::vector<base::FilePath>& expected_launch_paths,
532 bool expect_prompt) {
533 ExpectReporterLaunches(days_until_launch, expected_launch_paths.size(),
534 expect_prompt);
535 EXPECT_EQ(expected_launch_paths.size(), reporter_launch_parameters_.size());
536 for (size_t i = 0; i < expected_launch_paths.size() &&
537 i < reporter_launch_parameters_.size();
538 ++i) {
539 EXPECT_EQ(expected_launch_paths[i],
540 reporter_launch_parameters_[i].command_line.GetProgram());
541 }
542 }
543
544 void ExpectLastTimeSentReportNotSet() {
545 PrefService* local_state = g_browser_process->local_state();
546 EXPECT_FALSE(
547 local_state->HasPrefPath(prefs::kSwReporterLastTimeSentReport));
548 }
549
550 void ExpectLastReportSentInTheLastHour() {
551 const PrefService* local_state = g_browser_process->local_state();
552 const base::Time now = Now();
553 const base::Time last_time_sent_logs = base::Time::FromInternalValue(
554 local_state->GetInt64(prefs::kSwReporterLastTimeSentReport));
555
556 // Checks if the last time sent logs is set as no more than one hour ago,
557 // which should be enough time if the execution does not fail.
558 EXPECT_LT(now - base::TimeDelta::FromHours(1), last_time_sent_logs);
559 EXPECT_GE(now, last_time_sent_logs);
560 }
561
562 // Expects |invocation|'s command line to contain all the switches required
563 // for reporter logging if and only if |expect_switches| is true.
564 void ExpectLoggingSwitches(const SwReporterInvocation& invocation,
565 bool expect_switches) {
566 static const std::set<std::string> logging_switches{
567 chrome_cleaner::kExtendedSafeBrowsingEnabledSwitch,
568 chrome_cleaner::kChromeVersionSwitch,
569 chrome_cleaner::kChromeChannelSwitch};
570
571 const base::CommandLine::SwitchMap& invocation_switches =
572 invocation.command_line.GetSwitches();
573 // Checks if switches that enable logging on the reporter are present on
574 // the invocation if and only if logging is allowed.
575 for (const std::string& logging_switch : logging_switches) {
576 EXPECT_EQ(expect_switches, invocation_switches.find(logging_switch) !=
577 invocation_switches.end());
578 }
579 }
580
581 // A task runner with a mock clock.
582 scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_ =
583 new base::TestMockTimeTaskRunner();
584
585 // The task runner that was in use before installing |mock_time_task_runner_|.
586 scoped_refptr<base::SingleThreadTaskRunner> saved_task_runner_;
587
588 bool in_browser_cleaner_ui_;
589 ElevationStatus elevation_status_;
590 MockedReporterFailure expected_reporter_failure_;
591
592 // Indicates if a bad message error should be simulated by the parent
593 // process.
594 bool bad_message_expected_ = false;
595
596 // Set by ReportBadMessageChromePromptImpl if a bad message error is reported.
597 bool bad_message_reported_ = false;
598
599 bool prompt_trigger_called_ = false;
600 int reporter_launch_count_ = 0;
601 std::vector<SwReporterInvocation> reporter_launch_parameters_;
602 int exit_code_to_report_ = kReporterNotLaunchedExitCode;
603
604 // A callback to invoke when the first reporter of a queue is launched. This
605 // can be used to perform actions in the middle of a queue of reporters which
606 // all launch on the same mock clock tick.
607 base::OnceClosure first_launch_callback_;
608
609 base::test::ScopedFeatureList scoped_feature_list_;
610
611 DISALLOW_COPY_AND_ASSIGN(SRTFetcherTest);
612 };
613
614 class SRTFetcherPromptTest : public DialogBrowserTest {
615 public:
616 void ShowDialog(const std::string& name) override {
617 if (name == "SRTErrorNoFile")
618 DisplaySRTPromptForTesting(base::FilePath());
619 else if (name == "SRTErrorFile")
620 DisplaySRTPromptForTesting(
621 base::FilePath().Append(FILE_PATH_LITERAL("c:\temp\testfile.txt")));
622 else
623 ADD_FAILURE() << "Unknown dialog type.";
624 }
625 };
626
627 } // namespace
628
629 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, NothingFound) {
630 RunReporter(chrome_cleaner::kSwReporterNothingFound);
631 ExpectReporterLaunches(0, 1, false);
632 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
633 }
634
635 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, CleanupNeeded) {
636 RunReporter(chrome_cleaner::kSwReporterCleanupNeeded);
637 ExpectReporterLaunches(0, 1, true);
638 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
639 }
640
641 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, RanRecently) {
642 constexpr int kDaysLeft = 1;
643 SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns - kDaysLeft);
644 RunReporter(chrome_cleaner::kSwReporterNothingFound);
645 ExpectReporterLaunches(0, 0, false);
646 ExpectToRunAgain(kDaysLeft);
647 ExpectReporterLaunches(kDaysLeft, 1, false);
648 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
649 }
650
651 // Test is flaky. crbug.com/705608
652 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, DISABLED_WaitForBrowser) {
653 Profile* profile = browser()->profile();
654
655 // Ensure that even though we're closing the last browser, we don't enter the
656 // "shutting down" state, which will prevent the test from starting another
657 // browser.
658 ScopedKeepAlive test_keep_alive(KeepAliveOrigin::SESSION_RESTORE,
659 KeepAliveRestartOption::ENABLED);
660
661 // Use the standard task runner for browser cleanup, which will wait forever
662 // with the mock clock.
663 base::MessageLoop::current()->SetTaskRunner(saved_task_runner_);
664 CloseBrowserSynchronously(browser());
665 base::MessageLoop::current()->SetTaskRunner(mock_time_task_runner_);
666 ASSERT_EQ(0u, chrome::GetTotalBrowserCount());
667 ASSERT_FALSE(chrome::FindLastActive());
668
669 // Start the reporter while the browser is closed. The prompt should not open.
670 RunReporter(chrome_cleaner::kSwReporterCleanupNeeded);
671 ExpectReporterLaunches(0, 1, false);
672
673 // Create a Browser object directly instead of using helper functions like
674 // CreateBrowser, because they all wait on timed events but do not advance the
675 // mock timer. The Browser constructor registers itself with the global
676 // BrowserList, which is cleaned up when InProcessBrowserTest exits.
677 new Browser(Browser::CreateParams(profile, /*user_gesture=*/false));
678 ASSERT_EQ(1u, chrome::GetTotalBrowserCount());
679
680 // Some browser startup tasks are scheduled to run in the first few minutes
681 // after creation. Make sure they've all been processed so that the only
682 // pending task in the queue is the next reporter check.
683 mock_time_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(10));
684
685 // On opening the new browser, the prompt should be shown and the reporter
686 // should be scheduled to run later.
687 EXPECT_EQ(1, reporter_launch_count_);
688 EXPECT_TRUE(prompt_trigger_called_);
689 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
690 }
691
692 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, Failure) {
693 RunReporter(kReporterNotLaunchedExitCode);
694 ExpectReporterLaunches(0, 1, false);
695 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
696 }
697
698 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, RunDaily) {
699 PrefService* local_state = g_browser_process->local_state();
700 local_state->SetBoolean(prefs::kSwReporterPendingPrompt, true);
701 SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns - 1);
702 ASSERT_GT(kDaysBetweenSuccessfulSwReporterRuns - 1,
703 kDaysBetweenSwReporterRunsForPendingPrompt);
704 RunReporter(chrome_cleaner::kSwReporterNothingFound);
705
706 // Expect the reporter to run immediately, since a prompt is pending and it
707 // has been more than kDaysBetweenSwReporterRunsForPendingPrompt days.
708 ExpectReporterLaunches(0, 1, false);
709 ExpectToRunAgain(kDaysBetweenSwReporterRunsForPendingPrompt);
710
711 // Move the clock ahead kDaysBetweenSwReporterRunsForPendingPrompt days. The
712 // expected run should trigger, but not cause the reporter to launch because
713 // a prompt is no longer pending.
714 local_state->SetBoolean(prefs::kSwReporterPendingPrompt, false);
715 ExpectReporterLaunches(kDaysBetweenSwReporterRunsForPendingPrompt, 0, false);
716
717 // Instead it should now run kDaysBetweenSuccessfulSwReporterRuns after the
718 // first prompt (of which kDaysBetweenSwReporterRunsForPendingPrompt has
719 // already passed.)
720 int days_left = kDaysBetweenSuccessfulSwReporterRuns -
721 kDaysBetweenSwReporterRunsForPendingPrompt;
722 ExpectToRunAgain(days_left);
723 ExpectReporterLaunches(days_left, 1, false);
724 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
725 }
726
727 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ParameterChange) {
728 // If the reporter is run several times with different parameters, it should
729 // only be launched once, with the last parameter set.
730 const base::FilePath path1(L"path1");
731 const base::FilePath path2(L"path2");
732 const base::FilePath path3(L"path3");
733
734 // Schedule path1 with a day left in the reporting period.
735 // The reporter should not launch.
736 constexpr int kDaysLeft = 1;
737 {
738 SCOPED_TRACE("N days left until next reporter run");
739 SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns - kDaysLeft);
740 RunReporter(chrome_cleaner::kSwReporterNothingFound, path1);
741 ExpectReporterLaunches(0, {}, false);
742 }
743
744 // Schedule path2 just as we enter the next reporting period.
745 // Now the reporter should launch, just once, using path2.
746 {
747 SCOPED_TRACE("Reporter runs now");
748 RunReporter(chrome_cleaner::kSwReporterNothingFound, path2);
749 // Schedule it twice; it should only actually run once.
750 RunReporter(chrome_cleaner::kSwReporterNothingFound, path2);
751 ExpectReporterLaunches(kDaysLeft, {path2}, false);
752 }
753
754 // Schedule path3 before any more time has passed.
755 // The reporter should not launch.
756 {
757 SCOPED_TRACE("No more time passed");
758 RunReporter(chrome_cleaner::kSwReporterNothingFound, path3);
759 ExpectReporterLaunches(0, {}, false);
760 }
761
762 // Enter the next reporting period as path3 is still scheduled.
763 // Now the reporter should launch again using path3. (Tests that the
764 // parameters from the first launch aren't reused.)
765 {
766 SCOPED_TRACE("Previous run still scheduled");
767 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns, {path3},
768 false);
769 }
770
771 // Schedule path3 again in the next reporting period.
772 // The reporter should launch again using path3, since enough time has
773 // passed, even though the parameters haven't changed.
774 {
775 SCOPED_TRACE("Run with same parameters");
776 RunReporter(chrome_cleaner::kSwReporterNothingFound, path3);
777 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns, {path3},
778 false);
779 }
780
781 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
782 }
783
784 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, MultipleLaunches) {
785 const base::FilePath path1(L"path1");
786 const base::FilePath path2(L"path2");
787 const base::FilePath path3(L"path3");
788
789 SwReporterQueue invocations;
790 invocations.push(SwReporterInvocation::FromFilePath(path1));
791 invocations.push(SwReporterInvocation::FromFilePath(path2));
792
793 {
794 SCOPED_TRACE("Launch 2 times");
795 SetDaysSinceLastTriggered(kDaysBetweenSuccessfulSwReporterRuns);
796 RunReporterQueue(chrome_cleaner::kSwReporterNothingFound, invocations);
797 ExpectReporterLaunches(0, {path1, path2}, false);
798 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
799 }
800
801 // Schedule a launch with 2 elements, then another with the same 2. It should
802 // just run 2 times, not 4.
803 {
804 SCOPED_TRACE("Launch 2 times with retry");
805 RunReporterQueue(chrome_cleaner::kSwReporterNothingFound, invocations);
806 RunReporterQueue(chrome_cleaner::kSwReporterNothingFound, invocations);
807 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns, {path1, path2},
808 false);
809 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
810 }
811
812 // Another launch with 2 elements is already scheduled. Add a third while the
813 // queue is running.
814 {
815 SCOPED_TRACE("Add third launch while running");
816 invocations.push(SwReporterInvocation::FromFilePath(path3));
817 first_launch_callback_ = base::BindOnce(
818 &SRTFetcherTest::RunReporterQueue, base::Unretained(this),
819 chrome_cleaner::kSwReporterNothingFound, invocations);
820
821 // Only the first two elements should execute since the third was added
822 // during the cycle.
823 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns, {path1, path2},
824 false);
825 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
826
827 // Time passes... Now the 3-element queue should run.
828 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns,
829 {path1, path2, path3}, false);
830 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
831 }
832
833 // Second launch should not occur after a failure.
834 {
835 SCOPED_TRACE("Launch multiple times with failure");
836 RunReporterQueue(kReporterNotLaunchedExitCode, invocations);
837 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns, {path1},
838 false);
839 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
840
841 // If we try again before the reporting period is up, it should not do
842 // anything.
843 ExpectReporterLaunches(0, {}, false);
844
845 // After enough time has passed, should try the queue again.
846 ExpectReporterLaunches(kDaysBetweenSuccessfulSwReporterRuns, {path1},
847 false);
848 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
849 }
850 }
851
852 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_NoSBExtendedReporting) {
853 RunReporter(chrome_cleaner::kSwReporterNothingFound);
854 ExpectReporterLaunches(0, 1, false);
855 ExpectLoggingSwitches(reporter_launch_parameters_.front(), false);
856 ExpectLastTimeSentReportNotSet();
857 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
858 }
859
860 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_EnabledFirstRun) {
861 EnableSBExtendedReporting();
862 // Note: don't set last time sent logs in the local state.
863 // SBER is enabled and there is no record in the local state of the last time
864 // logs have been sent, so we should send logs in this run.
865 RunReporter(chrome_cleaner::kSwReporterNothingFound);
866 ExpectReporterLaunches(0, 1, false);
867 ExpectLoggingSwitches(reporter_launch_parameters_.front(), true);
868 ExpectLastReportSentInTheLastHour();
869 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
870 }
871
872 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_EnabledNoRecentLogging) {
873 // SBER is enabled and last time logs were sent was more than
874 // |kDaysBetweenReporterLogsSent| day ago, so we should send logs in this run.
875 EnableSBExtendedReporting();
876 SetLastTimeSentReport(kDaysBetweenReporterLogsSent + 3);
877 RunReporter(chrome_cleaner::kSwReporterNothingFound);
878 ExpectReporterLaunches(0, 1, false);
879 ExpectLoggingSwitches(reporter_launch_parameters_.front(), true);
880 ExpectLastReportSentInTheLastHour();
881 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
882 }
883
884 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_EnabledRecentlyLogged) {
885 // SBER is enabled, but logs have been sent less than
886 // |kDaysBetweenReporterLogsSent| day ago, so we shouldn't send any logs in
887 // this run.
888 EnableSBExtendedReporting();
889 SetLastTimeSentReport(kDaysBetweenReporterLogsSent - 1);
890 int64_t last_time_sent_logs = GetLastTimeSentReport();
891 RunReporter(chrome_cleaner::kSwReporterNothingFound);
892 ExpectReporterLaunches(0, 1, false);
893 ExpectLoggingSwitches(reporter_launch_parameters_.front(), false);
894 EXPECT_EQ(last_time_sent_logs, GetLastTimeSentReport());
895 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
896 }
897
898 IN_PROC_BROWSER_TEST_P(SRTFetcherTest, ReporterLogging_MultipleLaunches) {
899 EnableSBExtendedReporting();
900 SetLastTimeSentReport(kDaysBetweenReporterLogsSent + 3);
901
902 const base::FilePath path1(L"path1");
903 const base::FilePath path2(L"path2");
904 SwReporterQueue invocations;
905 for (const auto& path : {path1, path2}) {
906 auto invocation = SwReporterInvocation::FromFilePath(path);
907 invocation.supported_behaviours =
908 SwReporterInvocation::BEHAVIOUR_ALLOW_SEND_REPORTER_LOGS;
909 invocations.push(invocation);
910 }
911 RunReporterQueue(chrome_cleaner::kSwReporterNothingFound, invocations);
912
913 // SBER is enabled and last time logs were sent was more than
914 // |kDaysBetweenReporterLogsSent| day ago, so we should send logs in this run.
915 {
916 SCOPED_TRACE("first launch");
917 first_launch_callback_ =
918 base::BindOnce(&SRTFetcherTest::ExpectLastReportSentInTheLastHour,
919 base::Unretained(this));
920 ExpectReporterLaunches(0, {path1, path2}, false);
921 ExpectLoggingSwitches(reporter_launch_parameters_[0], true);
922 }
923
924 // Logs should also be sent for the next run, even though LastTimeSentReport
925 // is now recent, because the run is part of the same set of invocations.
926 {
927 SCOPED_TRACE("second launch");
928 ExpectLoggingSwitches(reporter_launch_parameters_[1], true);
929 ExpectLastReportSentInTheLastHour();
930 }
931 ExpectToRunAgain(kDaysBetweenSuccessfulSwReporterRuns);
932 }
933
934 INSTANTIATE_TEST_CASE_P(
935 NoInBrowserCleanerUI,
936 SRTFetcherTest,
937 testing::Combine(testing::Values(false),
938 testing::Values(ElevationStatus::NOT_REQUIRED),
939 testing::Values(MockedReporterFailure::kNone,
940 MockedReporterFailure::kCrashOnStartup)));
941
942 INSTANTIATE_TEST_CASE_P(
943 InBrowserCleanerUI,
944 SRTFetcherTest,
945 testing::Combine(
946 testing::Values(true),
947 testing::Values(ElevationStatus::NOT_REQUIRED,
948 ElevationStatus::REQUIRED),
949 testing::Values(
950 MockedReporterFailure::kNone,
951 MockedReporterFailure::kCrashOnStartup,
952 MockedReporterFailure::kCrashAfterConnectedToParentProcess,
953 MockedReporterFailure::kCrashWhileWaitingForResponse,
954 MockedReporterFailure::kCrashAfterReceivedResponse,
955 MockedReporterFailure::kBadMessageReported)));
956
957 // This provide tests which allows explicit invocation of the SRT Prompt
958 // useful for checking dialog layout or any other interactive functionality
959 // tests. See docs/testing/test_browser_dialog.md for description of the
960 // testing framework.
961 IN_PROC_BROWSER_TEST_F(SRTFetcherPromptTest, InvokeDialog_SRTErrorNoFile) {
962 RunDialog();
963 }
964
965 IN_PROC_BROWSER_TEST_F(SRTFetcherPromptTest, InvokeDialog_SRTErrorFile) {
966 RunDialog();
967 }
968
969 } // namespace safe_browsing
OLDNEW
« no previous file with comments | « chrome/browser/safe_browsing/srt_client_info_win.cc ('k') | chrome/browser/safe_browsing/srt_fetcher_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698