1 // Copyright 2017 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_TEST_TASK_ENVIRONMENT_H_ 6 #define BASE_TEST_TASK_ENVIRONMENT_H_ 7 8 #include <memory> 9 10 #include "base/compiler_specific.h" 11 #include "base/functional/callback_forward.h" 12 #include "base/memory/raw_ptr.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/observer_list_types.h" 15 #include "base/run_loop.h" 16 #include "base/task/lazy_thread_pool_task_runner.h" 17 #include "base/task/sequence_manager/sequence_manager.h" 18 #include "base/task/sequence_manager/task_queue.h" 19 #include "base/task/single_thread_task_runner.h" 20 #include "base/test/scoped_run_loop_timeout.h" 21 #include "base/threading/thread_checker.h" 22 #include "base/time/time.h" 23 #include "base/traits_bag.h" 24 #include "build/build_config.h" 25 26 namespace base { 27 28 class Clock; 29 class FileDescriptorWatcher; 30 class TickClock; 31 32 namespace subtle { 33 class ScopedTimeClockOverrides; 34 } 35 36 namespace test { 37 38 // This header exposes SingleThreadTaskEnvironment and TaskEnvironment. 39 // 40 // SingleThreadTaskEnvironment enables the following APIs within its scope: 41 // - (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle on the main 42 // thread 43 // - RunLoop on the main thread 44 // 45 // TaskEnvironment additionally enables: 46 // - posting to base::ThreadPool through base/task/thread_pool.h. 47 // 48 // Hint: For content::BrowserThreads, use content::BrowserTaskEnvironment. 49 // 50 // Tests should prefer SingleThreadTaskEnvironment over TaskEnvironment when the 51 // former is sufficient. 52 // 53 // Tasks posted to the (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle 54 // run synchronously when RunLoop::Run(UntilIdle) or 55 // TaskEnvironment::RunUntil(Idle|Quit) is called on the main thread. 56 // 57 // The TaskEnvironment requires TestTimeouts::Initialize() to be called in order 58 // to run posted tasks, so that it can watch for problematic long-running tasks. 59 // 60 // The TimeSource trait can be used to request that delayed tasks be under the 61 // manual control of RunLoop::Run() and TaskEnvironment::FastForward*() methods. 62 // 63 // If a TaskEnvironment's ThreadPoolExecutionMode is QUEUED, ThreadPool tasks 64 // run when RunUntilIdle(), RunUntilQuit(), or ~TaskEnvironment is called. If 65 // ThreadPoolExecutionMode is ASYNC, they run as they are posted. 66 // 67 // All TaskEnvironment methods must be called from the main thread. 68 // 69 // Usage: 70 // 71 // class MyTestFixture : public testing::Test { 72 // public: 73 // (...) 74 // 75 // // protected rather than private visibility will allow controlling the 76 // // task environment (e.g. RunUntilIdle(), FastForwardBy(), etc.). from the 77 // // test body. 78 // protected: 79 // // Must generally be the first member to be initialized first and 80 // // destroyed last (some members that require single-threaded 81 // // initialization and tear down may need to come before -- e.g. 82 // // base::test::ScopedFeatureList). Extra traits, like TimeSource, are 83 // // best provided inline when declaring the TaskEnvironment, as 84 // // such: 85 // base::test::TaskEnvironment task_environment_{ 86 // base::test::TaskEnvironment::TimeSource::MOCK_TIME}; 87 // 88 // // Other members go here (or further below in private section.) 89 // }; 90 class TaskEnvironment { 91 protected: 92 // This enables a two-phase initialization for sub classes such as 93 // content::BrowserTaskEnvironment which need to provide the default task 94 // queue because they instantiate a scheduler on the same thread. Subclasses 95 // using this trait must invoke DeferredInitFromSubclass() before running the 96 // task environment. 97 struct SubclassCreatesDefaultTaskRunner {}; 98 99 public: 100 enum class TimeSource { 101 // Delayed tasks and Time/TimeTicks::Now() use the real-time system clock. 102 SYSTEM_TIME, 103 104 // Delayed tasks use a mock clock which only advances when reaching "idle" 105 // during a RunLoop::Run() call on the main thread or a FastForward*() call 106 // to this TaskEnvironment. "idle" is defined as the main thread and thread 107 // pool being out of ready tasks. In that situation : time advances to the 108 // soonest delay between main thread and thread pool delayed tasks, 109 // according to the semantics of the current Run*() or FastForward*() call. 110 // 111 // This also mocks Time/TimeTicks::Now() with the same mock clock. 112 // Time::Now() and TimeTicks::Now() (with respect to its origin) start 113 // without submillisecond components. 114 // 115 // Warning some platform APIs are still real-time, e.g.: 116 // * PlatformThread::Sleep 117 // * WaitableEvent::TimedWait 118 // * ConditionVariable::TimedWait 119 // * Delayed tasks on unmanaged base::Thread's and other custom task 120 // runners. 121 MOCK_TIME, 122 123 DEFAULT = SYSTEM_TIME 124 }; 125 126 // This type will determine what types of messages will get pumped by the main 127 // thread. 128 // Note: If your test needs to use a custom MessagePump you should 129 // consider using a SingleThreadTaskExecutor instead. 130 enum class MainThreadType { 131 // The main thread doesn't pump system messages. 132 DEFAULT, 133 // The main thread pumps UI messages. 134 UI, 135 // The main thread pumps asynchronous IO messages and supports the 136 // FileDescriptorWatcher API on POSIX. 137 IO, 138 }; 139 140 // Note that this is irrelevant (and ignored) under 141 // ThreadingMode::MAIN_THREAD_ONLY 142 enum class ThreadPoolExecutionMode { 143 // Thread pool tasks are queued and only executed when RunUntilIdle(), 144 // FastForwardBy(), or FastForwardUntilNoTasksRemain() are explicitly 145 // called. Note: RunLoop::Run() does *not* unblock the ThreadPool in this 146 // mode (it strictly runs only the main thread). 147 QUEUED, 148 // Thread pool tasks run as they are posted. RunUntilIdle() can still be 149 // used to block until done. 150 // Note that regardless of this trait, delayed tasks are always "queued" 151 // under TimeSource::MOCK_TIME mode. 152 ASYNC, 153 DEFAULT = ASYNC 154 }; 155 156 enum class ThreadingMode { 157 // ThreadPool will be initialized, thus adding support for multi-threaded 158 // tests. 159 MULTIPLE_THREADS, 160 // No thread pool will be initialized. Useful for tests that want to run 161 // single threaded. Prefer using SingleThreadTaskEnvironment over this 162 // trait. 163 MAIN_THREAD_ONLY, 164 DEFAULT = MULTIPLE_THREADS 165 }; 166 167 // On Windows, sets the COM environment for the ThreadPoolInstance. Ignored 168 // on other platforms. 169 enum class ThreadPoolCOMEnvironment { 170 // Do not initialize COM for the pool's workers. 171 NONE, 172 173 // Place the pool's workers in a COM MTA. 174 COM_MTA, 175 176 // Enable the MTA by default in unit tests to match the browser process's 177 // ThreadPoolInstance configuration. 178 // 179 // This has the adverse side-effect of enabling the MTA in non-browser unit 180 // tests as well but the downside there is not as bad as not having it in 181 // browser unit tests. It just means some COM asserts may pass in unit 182 // tests where they wouldn't in integration tests or prod. That's okay 183 // because unit tests are already generally very loose on allowing I/O, 184 // waits, etc. Such misuse will still be caught in later phases (and COM 185 // usage should already be pretty much inexistent in sandboxed processes). 186 DEFAULT = COM_MTA, 187 }; 188 189 // List of traits that are valid inputs for the constructor below. 190 struct ValidTraits { 191 ValidTraits(TimeSource); 192 ValidTraits(MainThreadType); 193 ValidTraits(ThreadPoolExecutionMode); 194 ValidTraits(SubclassCreatesDefaultTaskRunner); 195 ValidTraits(ThreadingMode); 196 ValidTraits(ThreadPoolCOMEnvironment); 197 }; 198 199 // Constructor accepts zero or more traits which customize the testing 200 // environment. 201 template <typename... TaskEnvironmentTraits> 202 requires trait_helpers::AreValidTraits<ValidTraits, 203 TaskEnvironmentTraits...> TaskEnvironment(TaskEnvironmentTraits...traits)204 NOINLINE explicit TaskEnvironment(TaskEnvironmentTraits... traits) 205 : TaskEnvironment(sequence_manager::SequenceManager::PrioritySettings:: 206 CreateDefault(), 207 traits...) {} 208 209 TaskEnvironment(const TaskEnvironment&) = delete; 210 TaskEnvironment& operator=(const TaskEnvironment&) = delete; 211 212 // Waits until no undelayed ThreadPool tasks remain. Then, unregisters the 213 // ThreadPoolInstance and the 214 // (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle. 215 virtual ~TaskEnvironment(); 216 217 // Returns a TaskRunner that schedules tasks on the main thread. 218 scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner(); 219 220 // Returns whether the main thread's TaskRunner has pending tasks. This will 221 // always return true if called right after RunUntilIdle. 222 bool MainThreadIsIdle() const; 223 224 // Returns a RepeatingClosure that ends the next call to RunUntilQuit(). The 225 // quit closures must be obtained from the thread owning the TaskEnvironment 226 // but may then be invoked from any thread. To avoid a potential race 227 // condition, do not call QuitClosure() while RunUntilQuit() is running. 228 RepeatingClosure QuitClosure(); 229 230 // Runs tasks on both the main thread and the thread pool, until a quit 231 // closure is executed. When RunUntilQuit() returns, all previous quit 232 // closures are invalidated, and will have no effect on future calls. Be sure 233 // to create a new quit closure before calling RunUntilQuit() again. 234 void RunUntilQuit(); 235 236 // Runs tasks until both the 237 // (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle and the 238 // ThreadPool's non-delayed queues are empty. While RunUntilIdle() is quite 239 // practical and sometimes even necessary -- for example, to flush all tasks 240 // bound to Unretained() state before destroying test members -- it should be 241 // used with caution per the following warnings: 242 // 243 // WARNING #1: This may run long (flakily timeout) and even never return! Do 244 // not use this when repeating tasks such as animated web pages 245 // are present. 246 // WARNING #2: This may return too early! For example, if used to run until an 247 // incoming event has occurred but that event depends on a task in 248 // a different queue -- e.g. a standalone base::Thread or a system 249 // event. 250 // 251 // As such, prefer RunLoop::Run() with an explicit RunLoop::QuitClosure() when 252 // possible. 253 void RunUntilIdle(); 254 255 // Only valid for instances using |TimeSource::MOCK_TIME|. Fast-forwards 256 // virtual time by |delta|, causing all tasks on the main thread and thread 257 // pool with a remaining delay less than or equal to |delta| to be executed 258 // in their natural order before this method returns. Undelayed tasks are just 259 // delayed tasks with a delay of 0, so they are also executed. |delta| must be 260 // non-negative. Upon returning from this method, NowTicks() will be >= the 261 // initial |NowTicks() + delta|. It is guaranteed to be == iff tasks executed 262 // in this FastForwardBy() didn't result in nested calls to 263 // time-advancing-methods. 264 void FastForwardBy(TimeDelta delta); 265 266 // Similar to `FastForwardBy` but doesn't advance `base::LiveTicks`, behaving 267 // as if the system was suspended for `delta` time and immediately woken up. 268 void SuspendedFastForwardBy(TimeDelta delta); 269 270 // Only valid for instances using TimeSource::MOCK_TIME. 271 // Short for FastForwardBy(TimeDelta::Max()). 272 // 273 // WARNING: This has the same caveat as RunUntilIdle() and is even more likely 274 // to spin forever (any RepeatingTimer will cause this). 275 void FastForwardUntilNoTasksRemain(); 276 277 // Only valid for instances using TimeSource::MOCK_TIME. Advances virtual time 278 // by |delta|. Unlike FastForwardBy, this does not run tasks. Prefer 279 // FastForwardBy() when possible but this can be useful when testing blocked 280 // pending tasks where being idle (required to fast-forward) is not possible. 281 // 282 // Delayed tasks that are ripe as a result of this will be scheduled. 283 // RunUntilIdle() can be used after this call to ensure those tasks have run. 284 // Note: AdvanceClock(delta) + RunUntilIdle() is slightly different from 285 // FastForwardBy(delta) in that time passes instantly before running any task 286 // (whereas FastForwardBy() will advance the clock in the smallest increments 287 // possible at a time). Hence FastForwardBy() is more realistic but 288 // AdvanceClock() can be useful when testing edge case scenarios that 289 // specifically handle more time than expected to have passed. 290 void AdvanceClock(TimeDelta delta); 291 292 // Similar to `AdvanceClock` but doesn't advance `base::LiveTicks`, behaving 293 // as if the system was suspended for `delta` time and immediately woken up. 294 void SuspendedAdvanceClock(TimeDelta delta); 295 UsesMockTime()296 bool UsesMockTime() const { return !!mock_clock_; } 297 298 // Only valid for instances using TimeSource::MOCK_TIME. Returns a 299 // TickClock whose time is updated by FastForward(By|UntilNoTasksRemain). 300 const TickClock* GetMockTickClock() const; 301 302 // Only valid for instances using TimeSource::MOCK_TIME. Returns a 303 // Clock whose time is updated by FastForward(By|UntilNoTasksRemain). The 304 // initial value is implementation defined and should be queried by tests that 305 // depend on it. 306 // TickClock should be used instead of Clock to measure elapsed time in a 307 // process. See time.h. 308 const Clock* GetMockClock() const; 309 310 // Only valid for instances using TimeSource::MOCK_TIME. Returns the current 311 // virtual tick time (based on a realistic Now(), sampled when this 312 // TaskEnvironment was created, and manually advanced from that point on). 313 // This is always equivalent to base::TimeTicks::Now() under 314 // TimeSource::MOCK_TIME. 315 base::TimeTicks NowTicks() const; 316 317 // Only valid for instances using TimeSource::MOCK_TIME. Returns the current 318 // virtual live time (based on a realistic Now(), sampled when this 319 // TaskEnvironment was created, and manually advanced from that point on). 320 // This is always equivalent to base::LiveTicks::Now() under 321 // TimeSource::MOCK_TIME. 322 base::LiveTicks NowLiveTicks() const; 323 324 // Only valid for instances using TimeSource::MOCK_TIME. Returns the number of 325 // pending tasks (delayed and non-delayed) of the main thread's TaskRunner. 326 // When debugging, you can use DescribeCurrentTasks() to see what those are. 327 size_t GetPendingMainThreadTaskCount() const; 328 329 // Only valid for instances using TimeSource::MOCK_TIME. 330 // Returns the delay until the next pending task of the main thread's 331 // TaskRunner if there is one, otherwise it returns TimeDelta::Max(). 332 TimeDelta NextMainThreadPendingTaskDelay() const; 333 334 // Only valid for instances using TimeSource::MOCK_TIME. 335 // Returns true iff the next task is delayed. Returns false if the next task 336 // is immediate or if there is no next task. 337 bool NextTaskIsDelayed() const; 338 339 // For debugging purposes: Dumps information about pending tasks on the main 340 // thread, and currently running tasks on the thread pool. 341 void DescribeCurrentTasks() const; 342 343 // Detach ThreadCheckers (will rebind on next usage), useful for the odd test 344 // suite which doesn't run on the main thread but still has exclusive access 345 // to driving this TaskEnvironment (e.g. WaylandClientTestSuiteServer). 346 void DetachFromThread(); 347 348 class TestTaskTracker; 349 // Callers outside of TaskEnvironment may not use the returned pointer. They 350 // should just use base::ThreadPoolInstance::Get(). 351 static TestTaskTracker* CreateThreadPool(); 352 353 class DestructionObserver : public CheckedObserver { 354 public: 355 DestructionObserver() = default; 356 ~DestructionObserver() override = default; 357 358 DestructionObserver(const DestructionObserver&) = delete; 359 DestructionObserver& operator=(const DestructionObserver&) = delete; 360 361 virtual void WillDestroyCurrentTaskEnvironment() = 0; 362 }; 363 364 // Adds/removes a DestructionObserver to any TaskEnvironment. Observers are 365 // notified when any TaskEnvironment goes out of scope (other than with a move 366 // operation). Must be called on the main thread. 367 static void AddDestructionObserver(DestructionObserver* observer); 368 static void RemoveDestructionObserver(DestructionObserver* observer); 369 370 // Instantiating a ParallelExecutionFence waits for all currently running 371 // ThreadPool tasks before the constructor returns and from then on prevents 372 // additional tasks from running during its lifetime. 373 // 374 // Must be instantiated from the test main thread. 375 class ParallelExecutionFence { 376 public: 377 // Instantiates a ParallelExecutionFence, crashes with an optional 378 // |error_message| if not invoked from test main thread. 379 explicit ParallelExecutionFence(const char* error_message = ""); 380 ~ParallelExecutionFence(); 381 382 ParallelExecutionFence(const ParallelExecutionFence&) = delete; 383 ParallelExecutionFence& operator=(const ParallelExecutionFence& other) = 384 delete; 385 386 private: 387 bool previously_allowed_to_run_ = false; 388 }; 389 390 // The number of foreground workers in the ThreadPool managed by a 391 // TaskEnvironment instance. This can be used to determine the maximum 392 // parallelism in tests that require each parallel task it spawns to be 393 // running at once. Having multiple threads prevents deadlocks should some 394 // blocking APIs not use ScopedBlockingCall. It also allows enough concurrency 395 // to allow TSAN to spot data races. 396 static constexpr int kNumForegroundThreadPoolThreads = 4; 397 398 protected: 399 template <typename... TaskEnvironmentTraits> 400 requires trait_helpers::AreValidTraits<ValidTraits, 401 TaskEnvironmentTraits...> CreateTaskEnvironmentWithPriorities(sequence_manager::SequenceManager::PrioritySettings priority_settings,TaskEnvironmentTraits...traits)402 NOINLINE static TaskEnvironment CreateTaskEnvironmentWithPriorities( 403 sequence_manager::SequenceManager::PrioritySettings priority_settings, 404 TaskEnvironmentTraits... traits) { 405 return TaskEnvironment(std::move(priority_settings), traits...); 406 } 407 408 // Constructor accepts zero or more traits which customize the testing 409 // environment. 410 template <typename... TaskEnvironmentTraits> 411 requires trait_helpers::AreValidTraits<ValidTraits, 412 TaskEnvironmentTraits...> TaskEnvironment(sequence_manager::SequenceManager::PrioritySettings priority_settings,TaskEnvironmentTraits...traits)413 NOINLINE explicit TaskEnvironment( 414 sequence_manager::SequenceManager::PrioritySettings priority_settings, 415 TaskEnvironmentTraits... traits) 416 : TaskEnvironment( 417 std::move(priority_settings), 418 trait_helpers::GetEnum<TimeSource, TimeSource::DEFAULT>(traits...), 419 trait_helpers::GetEnum<MainThreadType, MainThreadType::DEFAULT>( 420 traits...), 421 trait_helpers::GetEnum<ThreadPoolExecutionMode, 422 ThreadPoolExecutionMode::DEFAULT>(traits...), 423 trait_helpers::GetEnum<ThreadingMode, ThreadingMode::DEFAULT>( 424 traits...), 425 trait_helpers::GetEnum<ThreadPoolCOMEnvironment, 426 ThreadPoolCOMEnvironment::DEFAULT>( 427 traits...), 428 trait_helpers::HasTrait<SubclassCreatesDefaultTaskRunner, 429 TaskEnvironmentTraits...>(), 430 trait_helpers::NotATraitTag()) {} 431 432 TaskEnvironment(TaskEnvironment&& other); 433 main_thread_type()434 constexpr MainThreadType main_thread_type() const { 435 return main_thread_type_; 436 } 437 thread_pool_execution_mode()438 constexpr ThreadPoolExecutionMode thread_pool_execution_mode() const { 439 return thread_pool_execution_mode_; 440 } 441 442 // Returns the MockTimeDomain driving this TaskEnvironment if this instance is 443 // using TimeSource::MOCK_TIME, nullptr otherwise. 444 sequence_manager::TimeDomain* GetMockTimeDomain() const; 445 446 sequence_manager::SequenceManager* sequence_manager() const; 447 448 void DeferredInitFromSubclass( 449 scoped_refptr<base::SingleThreadTaskRunner> task_runner); 450 451 // Derived classes may need to control when the task environment goes away 452 // (e.g. ~FooTaskEnvironment() may want to effectively trigger 453 // ~TaskEnvironment() before its members are destroyed). 454 void DestroyTaskEnvironment(); 455 456 private: 457 class MockTimeDomain; 458 459 void InitializeThreadPool(); 460 void ShutdownAndJoinThreadPool(); 461 void DestroyThreadPool(); 462 463 void CompleteInitialization(); 464 465 void FastForwardByInternal(TimeDelta delta, bool advance_live_ticks); 466 467 // The template constructor has to be in the header but it delegates to this 468 // constructor to initialize all other members out-of-line. 469 TaskEnvironment( 470 sequence_manager::SequenceManager::PrioritySettings priority_settings, 471 TimeSource time_source, 472 MainThreadType main_thread_type, 473 ThreadPoolExecutionMode thread_pool_execution_mode, 474 ThreadingMode threading_mode, 475 ThreadPoolCOMEnvironment thread_pool_com_environment, 476 bool subclass_creates_default_taskrunner, 477 trait_helpers::NotATraitTag tag); 478 479 const MainThreadType main_thread_type_; 480 const ThreadPoolExecutionMode thread_pool_execution_mode_; 481 const ThreadingMode threading_mode_; 482 const ThreadPoolCOMEnvironment thread_pool_com_environment_; 483 const bool subclass_creates_default_taskrunner_; 484 485 std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_; 486 487 // Manages the clock under TimeSource::MOCK_TIME modes. Null in 488 // TimeSource::SYSTEM_TIME mode. 489 std::unique_ptr<MockTimeDomain> mock_time_domain_; 490 491 // Overrides Time/TimeTicks::Now() under TimeSource::MOCK_TIME mode. 492 // Null in other modes. 493 std::unique_ptr<subtle::ScopedTimeClockOverrides> time_overrides_; 494 495 sequence_manager::TaskQueue::Handle task_queue_; 496 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; 497 498 // Only set for instances using TimeSource::MOCK_TIME. 499 std::unique_ptr<Clock> mock_clock_; 500 501 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) 502 // Enables the FileDescriptorWatcher API iff running a MainThreadType::IO. 503 std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher_; 504 #endif 505 506 // Owned by the ThreadPoolInstance. 507 raw_ptr<TestTaskTracker, DanglingUntriaged> task_tracker_ = nullptr; 508 509 // Ensures destruction of lazy TaskRunners when this is destroyed. 510 std::unique_ptr<base::internal::ScopedLazyTaskRunnerListForTesting> 511 scoped_lazy_task_runner_list_for_testing_; 512 513 // Sets RunLoop::Run() to LOG(FATAL) if not Quit() in a timely manner. 514 std::unique_ptr<ScopedRunLoopTimeout> run_loop_timeout_; 515 516 std::unique_ptr<bool> owns_instance_ = std::make_unique<bool>(true); 517 518 std::unique_ptr<RunLoop> run_until_quit_loop_; 519 520 // Used to verify thread-affinity of operations that must occur on the main 521 // thread. This is the case for anything that modifies or drives the 522 // |sequence_manager_|. 523 THREAD_CHECKER(main_thread_checker_); 524 }; 525 526 // SingleThreadTaskEnvironment takes the same traits as TaskEnvironment and is 527 // used the exact same way. It's a short-form for 528 // TaskEnvironment{TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, ...}; 529 class SingleThreadTaskEnvironment : public TaskEnvironment { 530 public: 531 template <class... ArgTypes> SingleThreadTaskEnvironment(ArgTypes...args)532 SingleThreadTaskEnvironment(ArgTypes... args) 533 : TaskEnvironment(ThreadingMode::MAIN_THREAD_ONLY, args...) {} 534 }; 535 536 } // namespace test 537 } // namespace base 538 539 #endif // BASE_TEST_TASK_ENVIRONMENT_H_ 540