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
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/sync/test/integration/status_change_checker.h"
6 
7 #include <sstream>
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/timer/timer.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace {
18 
19 constexpr base::TimeDelta kDefaultTimeout = base::TimeDelta::FromSeconds(30);
20 
GetTimeoutFromCommandLineOrDefault()21 base::TimeDelta GetTimeoutFromCommandLineOrDefault() {
22   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
23           "sync-status-change-checker-timeout")) {
24     return kDefaultTimeout;
25   }
26   std::string timeout_string(
27       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
28           "sync-status-change-checker-timeout"));
29   int timeout_in_seconds = 0;
30   if (!base::StringToInt(timeout_string, &timeout_in_seconds)) {
31     LOG(FATAL) << "Timeout value \"" << timeout_string << "\" was parsed as "
32                << timeout_in_seconds;
33   }
34   return base::TimeDelta::FromSeconds(timeout_in_seconds);
35 }
36 
37 }  // namespace
38 
StatusChangeChecker()39 StatusChangeChecker::StatusChangeChecker()
40     : timeout_(GetTimeoutFromCommandLineOrDefault()),
41       run_loop_(base::RunLoop::Type::kNestableTasksAllowed) {}
42 
43 StatusChangeChecker::~StatusChangeChecker() = default;
44 
Wait()45 bool StatusChangeChecker::Wait() {
46   std::ostringstream s;
47   if (IsExitConditionSatisfied(&s)) {
48     DVLOG(1) << "Already satisfied: " << s.str();
49     wait_done_called_ = true;
50     WaitDone();
51   } else {
52     DVLOG(1) << "Blocking: " << s.str();
53     StartBlockingWait();
54   }
55   return !TimedOut();
56 }
57 
TimedOut() const58 bool StatusChangeChecker::TimedOut() const {
59   return timed_out_;
60 }
61 
StopWaiting()62 void StatusChangeChecker::StopWaiting() {
63   if (run_loop_.running()) {
64     // Note that we can get here multiple times in some situations, because
65     // RunLoop::Quit() doesn't guarantee that it actually quits immediately.
66     // Make sure that WaitDone() still gets called only once.
67     if (!wait_done_called_) {
68       wait_done_called_ = true;
69       WaitDone();
70     }
71     run_loop_.Quit();
72   }
73 }
74 
CheckExitCondition()75 void StatusChangeChecker::CheckExitCondition() {
76   if (!run_loop_.running()) {
77     return;
78   }
79 
80   std::ostringstream s;
81   if (IsExitConditionSatisfied(&s)) {
82     DVLOG(1) << "Await -> Condition met: " << s.str();
83     StopWaiting();
84   } else {
85     DVLOG(1) << "Await -> Condition not met: " << s.str();
86   }
87 }
88 
StartBlockingWait()89 void StatusChangeChecker::StartBlockingWait() {
90   DCHECK(!run_loop_.running());
91 
92   base::OneShotTimer timer;
93   timer.Start(
94       FROM_HERE, timeout_,
95       base::BindOnce(&StatusChangeChecker::OnTimeout, base::Unretained(this)));
96 
97   run_loop_.Run();
98 }
99 
OnTimeout()100 void StatusChangeChecker::OnTimeout() {
101   timed_out_ = true;
102 
103   std::ostringstream s;
104   if (IsExitConditionSatisfied(&s)) {
105     ADD_FAILURE() << "Await -> Timed out despite conditions being satisfied.";
106   } else {
107     ADD_FAILURE() << "Await -> Timed out: " << s.str();
108   }
109 
110   StopWaiting();
111 }
112