1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139

base / test / scoped_run_loop_timeout.h [blame]

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TEST_SCOPED_RUN_LOOP_TIMEOUT_H_
#define BASE_TEST_SCOPED_RUN_LOOP_TIMEOUT_H_

#include <optional>
#include <string>

#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/time/time.h"

namespace content {
FORWARD_DECLARE_TEST(ContentBrowserTest, RunTimeoutInstalled);
}

namespace base::test {

FORWARD_DECLARE_TEST(TaskEnvironmentTest, SetsDefaultRunTimeout);

// Configures all RunLoop::Run() calls on the current thread to run the
// supplied |on_timeout| callback if they run for longer than |timeout|.
//
// Specifying Run() timeouts per-thread avoids the need to cope with Run()s
// executing concurrently with ScopedRunLoopTimeout initialization or
// teardown, and allows "default" timeouts to be specified by suites, rather
// than explicitly configuring them for every RunLoop, in each test.
//
// This is used by test classes including TaskEnvironment and TestSuite to
// set a default Run() timeout on the main thread of all tests which use them.
//
// Tests which have steps which need to Run() for longer than their suite's
// default (if any) allows can override the active timeout by creating a nested
// ScopedRunLoopTimeout on their stack, e.g:
//
//   ScopedRunLoopTimeout default_timeout(kDefaultRunTimeout);
//   ... do other test stuff ...
//   RunLoop().Run(); // Run for up to kDefaultRunTimeout.
//   ...
//   {
//     ScopedRunLoopTimeout specific_timeout(kTestSpecificTimeout);
//     RunLoop().Run(); // Run for up to kTestSpecificTimeout.
//   }
//   ...
//   RunLoop().Run(); // Run for up to kDefaultRunTimeout.
//
// The currently-active timeout can also be temporarily disabled:
//   ScopedDisableRunLoopTimeout disable_timeout;
//
// By default LOG(FATAL) will be invoked on Run() timeout. Test binaries
// can opt-in to using ADD_FAILURE() instead by calling
// SetAddGTestFailureOnTimeout() during process initialization.
//
// TaskEnvironment applies a default Run() timeout.

class ScopedRunLoopTimeout {
 public:
  // This callback is the one called upon run loop timeouts.
  // RunLoop inner mechanism will call this callback after having quit the run
  // loop. Implementer might chose to log locations, crash the process, dump a
  // stack trace, depending on the desired behaviour for run loop timeouts.
  // Invoking `on_timeout_log` might return a personalized timeouts message
  // string. This callback was sent at ScopedRunLoopTimeout creation. Invoking
  // this callback is not mandatory, as it depends on the desired behaviour of
  // this function.
  using TimeoutCallback = base::RepeatingCallback<void(
      const Location& timeout_enabled_from_here,
      RepeatingCallback<std::string()> on_timeout_log,
      const Location& run_from_here)>;

  ScopedRunLoopTimeout(const Location& timeout_enabled_from_here,
                       TimeDelta timeout);
  ~ScopedRunLoopTimeout();

  // Invokes |on_timeout_log| if |timeout| expires, and appends it to the
  // logged error message. If `timeout` is not specified the current timeout is
  // used and only the log message is overridden.
  ScopedRunLoopTimeout(const Location& timeout_enabled_from_here,
                       std::optional<TimeDelta> timeout,
                       RepeatingCallback<std::string()> on_timeout_log);

  ScopedRunLoopTimeout(const ScopedRunLoopTimeout&) = delete;
  ScopedRunLoopTimeout& operator=(const ScopedRunLoopTimeout&) = delete;

  // Returns true if there is a Run() timeout configured on the current thread.
  static bool ExistsForCurrentThread();

  // Important note:
  // The two following static methods will alter the behaviour on run loop
  // timeouts. If both methods are being called (whatever the ordering), the
  // behaviour will be chained, which means that both callbacks will be invoked.
  // If the custom callback handling is reset (`SetTimeoutCallbackForTesting`
  // called with `nullptr`), then we reset the behaviour to its previous state,
  // which is, if `SetAddGTestFailureOnTimeout`, it will invoke GTest timeout
  // handling. Otherwise, it will invoke the default function.

  // Add GTest timeout handler.
  static void SetAddGTestFailureOnTimeout();

  // Add provided callback as timeout handler.
  static void SetTimeoutCallbackForTesting(std::unique_ptr<TimeoutCallback> cb);

 private:
  TimeoutCallback GetTimeoutCallback();

 protected:
  FRIEND_TEST_ALL_PREFIXES(ScopedRunLoopRunTimeoutTest, TimesOut);
  FRIEND_TEST_ALL_PREFIXES(ScopedRunLoopRunTimeoutTest, RunTasksUntilTimeout);
  FRIEND_TEST_ALL_PREFIXES(TaskEnvironmentTest, SetsDefaultRunTimeout);
  FRIEND_TEST_ALL_PREFIXES(content::ContentBrowserTest, RunTimeoutInstalled);

  // Exposes the RunLoopTimeout to the friend tests (see above).
  static const RunLoop::RunLoopTimeout* GetTimeoutForCurrentThread();

  raw_ptr<const RunLoop::RunLoopTimeout> const nested_timeout_;
  RunLoop::RunLoopTimeout run_timeout_;
};

class ScopedDisableRunLoopTimeout {
 public:
  ScopedDisableRunLoopTimeout();
  ~ScopedDisableRunLoopTimeout();

  ScopedDisableRunLoopTimeout(const ScopedDisableRunLoopTimeout&) = delete;
  ScopedDisableRunLoopTimeout& operator=(const ScopedDisableRunLoopTimeout&) =
      delete;

 private:
  const raw_ptr<const RunLoop::RunLoopTimeout> nested_timeout_;
};

}  // namespace base::test

#endif  // BASE_TEST_SCOPED_RUN_LOOP_TIMEOUT_H_