Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /main /. /docs /threading_and_tasks_testing.md
blob: d8aac3dca98a9afddd9d656e3edd019f1beb8878 [file] [log] [blame] [view]
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]1# Testing Components Which Post Tasks
2
3[TOC]
4
5## Overview
6
7So you've read the [Threading and Tasks] documentation, surveyed the associated
8[Threading and Tasks FAQ] and have implemented a state-of-the-art component. Now
9you want to test it :). This document will explain how to write matching
10state-of-the-art tests.
11
12## Task Environments
13
14In order to **unit test** a component which post tasks, you'll need to bring up
15the task environmentin the scope of your test(or test fixture).It will need
16to outlive the majority of other members toensure they have access to the task
17system throughout their lifetime.There are a rare exceptions, like
18`base::test::ScopedFeatureList`, that need to outlive the task environment.For
19browser tests, see the[Browser tests](#browser-tests) section below.
20
21Task environments comein various forms but share the same fundamental
22characteristics:
23*There can be only one per test(if yourbase fixture already provides one:
24 see[BaseFixture managed
25TaskEnvironment](#base-fixture-managed-taskenvironment)for the correct
26 paradigm to supplement it).
27*Tasks cannot be posted outside the lifetime of a task environment.
28*Posted tasks will be runor be destroyed before theend of
29~TaskEnvironment().
30*They all derivefrom`base::test::TaskEnvironment`and support its
31[`ValidTraits`]and sometimes more.
32*See usage examplein[task_environment.h].
33*For example, a key characteristicis that its[TimeSource
34 trait](#timesource-trait) can be used to mock time to ease testing of timers,
35 timeouts, etc.
36
37The`TaskEnvironment` memberis typically exposedin theprotected section of
38the test fixture to allow tests to drive it directly(there's no need to expose
39public Run\*() methods that merely forward to the private member).
40
41### base::test::SingleThreadTaskEnvironment
42
Sean Maher03efef12022-09-23 22:43:13[diff] [blame]43Your component uses `base::SingleThreadTaskRunner::GetCurrentDefault()` or
44`base::SequencedTaskRunner::GetCurrentDefault()` to post tasks to the thread it
45was created on? You'll need at least a`base::test::SingleThreadTaskEnvironment`
46in orderfor theseAPIs to be functionaland`base::RunLoop` to run the posted
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]47tasks.
48
49Typicallythis will look something likethis:
50
51foo.h
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]52```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]53class Foo {
54 public:
Sean Maher03efef12022-09-23 22:43:13[diff] [blame]55 Foo() : owning_sequence_(base::SequencedTaskRunner::GetCurrentDefault()) {}
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]56
Trent Beginbe871422020-03-25 23:06:54[diff] [blame]57 DoSomethingAndReply(base::OnceClosure on_done) {
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]58 DCHECK(owning_sequence_->RunsTasksInCurrentSequence());
59 something_was_done_ = true;
60 owning_sequence_->PostTask(on_done);
61 }
62
63 bool something_was_done() const { return something_was_done_; }
64
65 private:
66 bool something_was_done_ = false;
67 scoped_refptr<base::SequencedTaskRunner> owning_sequence_;
68};
69```
70
71foo_unittest.cc
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]72```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]73TEST(FooTest, DoSomething) {
74 base::test::SingleThreadTaskEnvironment task_environment;
75
76 Foo foo;
77 RunLoop run_loop;
78 foo.DoSomethingAndReply(run_loop.QuitClosure());
79 run_loop.Run();
80 EXPECT_TRUE(foo.something_was_done());
81}
82```
83
84Note that`RunLoop().RunUntilIdle()` could be used instead of a`QuitClosure()`
85above but[best
86practices](https://developers.google.com/web/updates/2019/04/chromium-chronicle-1)
87favorQuitClosure() overRunUntilIdle()as the latter can lead to flaky tests.
88
89### Full fledged base::test::TaskEnvironment
90
91If your components depends on`base::ThreadPool`(that's a good thing!), you'll
92need a full`base::test::TaskEnvironment`.Don't be afraid to use a full
93`TaskEnvironment` when appropriate: think of "SingleThread" as being a
94readability term like "const", it documents that ThreadPool isn't usedwhen it's
95not but you shouldn't be afraid to lift it.
96
97Task runners are still obtainedby the product code through
Gabriel Charette9b6c04072022-04-01 23:22:46[diff] [blame]98[base/task/thread_pool.h] without necessitating a test-only task runner injection
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]99seam:).
100
101Typicalusecase:
102
103foo_service.h
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]104```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]105class FooService {
106 public:
107 FooService()
108 : backend_task_runner_(
Gabriel Charette1138d602020-01-29 08:51:52[diff] [blame]109 base::ThreadPool::CreateSequencedTaskRunner(
110 {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]111 backend_(new FooBackend,
112 base::OnTaskRunnerDeleter(backend_task_runner_)) {}
113
114 // Flushes state to disk async and replies.
115 FlushAndReply(base::OnceClosure on_done) {
116 DCHECK(owning_sequence_->RunsTasksInCurrentSequence());
Hong Xu294f4512023-10-21 01:13:10[diff] [blame]117 backend_task_runner_->PostTaskAndReply(FROM_HERE,
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]118 base::BindOnce(&FooBackend::Flush, Unretained(backend_.get()),
Gabriel Charette1138d602020-01-29 08:51:52[diff] [blame]119 std::move(on_done)));
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]120 }
121
122 private:
123 scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
124
125 // See https://youtu.be/m6Kz6pMaIxc?t=882 for memory management best
126 // practices.
127 std::unique_ptr<FooBackend, base::OnTaskRunnerDeleter> backend_;
128};
129```
130
131foo_service_unittest.cc
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]132```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]133TEST(FooServiceTest, FlushAndReply) {
134 base::test::TaskEnvironment task_environment;
135
136 FooService foo_service;
137 RunLoop run_loop;
138 foo_service.FlushAndReply(run_loop.QuitClosure());
139 run_loop.Run();
140 EXPECT_TRUE(VerifyFooStateOnDisk());
141}
142```
143
144### content::BrowserTaskEnvironment
145
146Thisis the same thingas`base::test::TaskEnvironment`with the addition of
147`content::BrowserThread` support.You needthisif-and-only-if the code under
148testisusing`BrowserThread::UI`or`BrowserThread::IO`.For determinism, both
149BrowserThreads will share the main threadand can be drivenbyRunLoop.By
150default the main thread willuse`MainThreadType::UI` but you canoverridethis
151via the[MainThreadType trait](#mainthreadtype-trait) to askfor an IO pump.
152
153`BrowserTaskEnvironment::REAL_IO_THREAD` can be also usedas a construction
154traitfor rare instances that desire distinct physicalBrowserThreads.
155
156### web::WebTaskEnvironment
157
158Thisis the//ios equivalent of `content::BrowserTaskEnvironment` to simulate
159`web::WebThread`.
160
Etienne Pierre-dorayc3c341b2023-12-21 16:57:30[diff] [blame]161### blink::test::TaskEnvironment
Gabriel Charette3ae250ed2020-03-31 16:04:56[diff] [blame]162
Etienne Pierre-dorayc3c341b2023-12-21 16:57:30[diff] [blame]163Thisis the same thingasbase::test::TaskEnvironmentwith the addition of
164blink::MainThreadSchedulerand blink::MainThreadIsolate support.You needthis
165if-and-only-if the code under testisusing blink::Thread::Current()or needs
166v8::Isolate::GetCurrent() to be a blinkIsolate.
Gabriel Charette3ae250ed2020-03-31 16:04:56[diff] [blame]167
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]168## Task Environment Traits and Abilities
169
170### Driving the Task Environment
171
172All task environments support the following methods to run tasks:
173*`base::RunLoop:Run()`: run the main threaduntil the`QuitClosure()`is
174 invoked(note: other threads also runin parallelbydefault).
175*`base::RunLoop::RunUntilIdle()`: run the main threaduntil itis idle.This
176is typicallynot what you wantin multi-threaded environmentsas it may
177 resume before`ThreadPool`is idle.
178*`TaskEnvironment::RunUntilIdle()`:Runs everything theTaskEnvironmentis
179 aware of.This excludes system eventsand any threads outside of the main
180 threadandThreadPool.It should be usedwith carewhen such external factors
181 can be involved.
182*`TaskEnvironment::FastForward*()`:More onthisin theTimeSource section
183 below.
184
185### TimeSource trait
186
187Bydefault tests run under`TimeSource::SYSTEM_TIME` which means delays are
188real-timeand`base::Time::Now()`and`base::TimeTicks::Now()`return live
189system times
190([context](https://chromium-review.googlesource.com/c/chromium/src/+/1742616)).
191
192Whenever testing codewith delays, you should favor`TimeSource::MOCK_TIME`as a
193trait.This makes it such that delayed tasksand`base::Time::Now()`+
194`base::TimeTicks::Now()`use a mock clock.
195
196Underthis mode, the mock clock will start at the current system time but will
197then only advancewhen explicitly requestedby`TaskEnvironment::FastForward*()`
Matt Muelleraec1fa62019-09-20 20:24:56[diff] [blame]198and`TaskEnvironment::AdvanceClock()` methods*or*when`RunLoop::Run()`is
199runningand all managed threads become idle(auto-advances to the soonest
200delayed task,if any, amongst all managed threads).
201
202`TaskEnvironment::FastForwardBy()` repeatedly runs existing immediately
203executable tasksuntil idleandthen advances the mock clock incrementally to
204run thenext delayed task within the time delta.It may advance timeby more
205than the requested amountif running the tasks causes nested
206time-advancing-method calls.
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]207
208This makes it possible to test codewith flush intervals, repeating timers,
209timeouts, etc. without any test-specific seamsin the product code, e.g.:
210
211foo_storage.h
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]212```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]213class FooStorage {
214 public:
215 static constexpr base::TimeDelta::kFlushInterval =
Peter Kastinge5a38ed2021-10-02 03:06:35[diff] [blame]216 base::Seconds(30);
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]217
218 // Sets |key| to |value|. Flushed to disk on the next flush interval.
219 void Set(base::StringPiece key, base::StringPiece value);
220};
221```
222
223foo_unittest.cc
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]224```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]225class FooStorageTest {
226 public:
227 FooStorageTest() = default;
228
229 // Test helper that returns true if |key| is found in the on disk storage.
230 bool FindKeyInOnDiskStorage(base::StringPiece key);
231
232 protected:
233 base::test::TaskEnvironment task_environment{
234 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
235 FooStorage foo_storage_;
236};
237
238TEST_F(FooStorageTest, Set) {
239 foo_storage_.Set("mykey", "myvalue");
240 EXPECT_FALSE(FindKeyInOnDiskStorage("mykey"));
241 task_environment.FastForwardBy(FooStorage::kFlushInterval);
242 EXPECT_TRUE(FindKeyInOnDiskStorage("mykey"));
243}
244```
245
Matt Muelleraec1fa62019-09-20 20:24:56[diff] [blame]246In contrast,`TaskEnvironment::AdvanceClock()` simply advances the mock timeby
247the requested amount,and doesnot run tasks.This may be usefulin
248caseswhere`TaskEnvironment::FastForwardBy()` would resultin a livelock.For
249example,if one taskis blocked on a`WaitableEvent`and thereis a delayed
250task that would signal theevent(e.g., a timeout),then
251`TaskEnvironment::FastForwardBy()` will never complete.Inthiscase, you could
252advance the clock enough that the delayed task becomes runnable,andthen
253`TaskEnvironment::RunUntilIdle()` would run the delayed task, signalling the
254event.
255
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]256```c++
Matt Muelleraec1fa62019-09-20 20:24:56[diff] [blame]257TEST(FooTest, TimeoutExceeded)
258{
259 base::test::TaskEnvironment task_environment{
260 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
261 base::WaitableEvent event;
262 base::RunLoop run_loop;
Gabriel Charette1138d602020-01-29 08:51:52[diff] [blame]263 base::ThreadPool::PostTaskAndReply(
264 FROM_HERE, {base::MayBlock()},
Matt Muelleraec1fa62019-09-20 20:24:56[diff] [blame]265 base::BindOnce(&BlocksOnEvent, base::Unretained(&event)),
266 run_loop.QuitClosure());
Gabriel Charette1138d602020-01-29 08:51:52[diff] [blame]267 base::ThreadPool::PostDelayedTask(
268 FROM_HERE, {},
Matt Muelleraec1fa62019-09-20 20:24:56[diff] [blame]269 base::BindOnce(&WaitableEvent::Signal, base::Unretained(&event)),
270 kTimeout);
271 // Can't use task_environment.FastForwardBy() since BlocksOnEvent blocks
272 // and the task pool will not become idle.
273 // Instead, advance time until the timeout task becomes runnable.
274 task_environment.AdvanceClock(kTimeout);
275 // Now the timeout task is runable.
276 task_environment.RunUntilIdle();
277 // The reply task should already have been executed, but run the run_loop to
278 // verify.
279 run_loop.Run();
280}
281```
282
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]283### MainThreadType trait
284
285The average component only cares about running its tasksand
286`MainThreadType::DEFAULT`is sufficient.Components that care to interact
287asynchronouslywith the system will likely need a`MainThreadType::UI` to be
288able to receive system events(e.g,. UIor clipboard events).
289
290Some components will prefer a main thread that handles asynchronous IO events
291and willuse`MainThreadType::IO`.Such components are typically the ones living
292onBrowserThread::IOand being testedwith a`BrowserTaskEnvironment`
293initializedwith`MainThreadType::IO`.
294
295Note:Thisis strictly about requesting a specific`MessagePumpType`for the
296main thread.It has nothing todowith`BrowserThread::UI`or
297`BrowserThread::IO` which are named threadsin the//content/browser code.
298
299### ThreadPoolExecutionMode trait
300
301Bydefault non-delayed tasks posted to`base::ThreadPool` may run at any point.
302Tests thatrequire more determinism can request
303`ThreadPoolExecutionMode::QUEUED` to enforce that tasks posted to
304`base::ThreadPool` only runwhen`TaskEnvironment::RunUntilIdle()`or
305`TaskEnvironment::FastForward*()` are invoked.Note that`RunLoop::Run()` does
306**not** unblock theThreadPoolinthis modeand thus strictly runs only the main
307thread.
308
309When`ThreadPoolExecutionMode::QUEUED`is mixedwith`TimeSource::MOCK_TIME`,
310time willauto-advance to the soonest task*thatis allowed to run*when
311required(i.e. it will ignore delayed tasksin the thread poolwhilein
312`RunLoop::Run()`).See
313`TaskEnvironmentTest.MultiThreadedMockTimeAndThreadPoolQueuedMode`for an
314example.
315
316This traitis of course irrelevant under`SingleThreadTaskEnvironment`.
317
318### ThreadingMode trait
319
320Prefer anexplicit`SingleThreadTaskEnvironment` overusing
321`ThreadingMode::MAIN_THREAD_ONLY`.The only reason touse
322`ThreadingMode::MAIN_THREAD_ONLY` explicitlyisif the parentclass of your test
323fixture manages the`TaskEnvironment` but takes`TaskEnvironmentTraits` tolet
324its subclasses customize itand you really need a`SingleThreadTaskEnvironment`.
325
326## Base Fixture managed TaskEnvironment
327
328In some cases it makes sense to have thebase fixture of an entire section of
329the codebase be managing the`TaskEnvironment`(e.g.[ViewsTestBase]).It's
330useful if such base fixture exposes `TaskEnvironmentTraits` to their subclasses
331so that individual tests within that domain can fine-tune their traits as
332desired.
333
334This typically looks like this (in this case `FooTestBase` opts to enforce
335`MainThreadType::UI` and leaves other traits to be specified as desired):
336
Raphael Kubo da Costa9d32d0f2019-09-26 17:39:48[diff] [blame]337```c++
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]338// Constructs a FooTestBase with |traits| being forwarded to its
339// TaskEnvironment. MainThreadType always defaults to UI and must not be
340// specified.
341template <typename... TaskEnvironmentTraits>
342NOINLINE explicit FooTestBase(TaskEnvironmentTraits&&... traits)
343 : task_environment_(base::test::TaskEnvironment::MainThreadType::UI,
344 std::forward<TaskEnvironmentTraits>(traits)...) {}
345```
346
347Note, if you'renot familiarwith traits:TaskEnvironment traitsuse
348[base/traits_bag.h]and will automatically complain at compile-timeif an
349enum-based traitis specified more than once(i.e. subclasses willnot compile
350if re-specifying`MainThreadType`in the above example).
351
352## Browser tests
353
354Thisis all niceand fancyfor unit tests, but what about browser\_tests,
355ui\_integration\_tests, etc?Tests that subclass`content::BrowserTestBase` bring
356up the entire environment(tasks& more)bydefault.
357
358The downsideis that you don't have fine-grained control over it like you would
359with all the `TaskEnvironment` methods.
360
361The favored paradigm is `RunLoop::Run()` + `QuitClosure()`. The asynchronous
362nature of Chromium code makes this the most reliable way to wait for an event.
363
364There are fancy versions of this to perform common actions, e.g.
365[content/public/test/browser_test_utils.h]
366[content/public/test/content_browser_test_utils.h] which will let you navigate,
367execute scripts, simulate UI interactions, etc.
368
369But the idea is always the same :
370 1) Instantiate `RunLoop run_loop;`
371 2) Kick off some work and hand-off `run_loop.QuitClosure()`
372 3) `run_loop.Run()` until the `QuitClosure()` is called.
373
374### MOCK_TIME in browser tests
375
376So you fell in love with `TimeSource::MOCK_TIME` but now you'rein a browser
377test... yeah, sorry about that...
378
379The eventual goalis to make it possible toset upTaskEnvironmentTraitsfrom
380your test fixture just like you canoverride command-line, etc. but we're not
381there yet...
382
383In the mean time you can still use the old
384`base::ScopedMockTimeMessageLoopTaskRunner` to mock delayed tasks on the main
385thread (you'reout of luck on other threadsfor now).And you canuse
386`base::subtle::ScopedTimeClockOverrides`if you want tooverride`Now()`.
387
388You think that's a mess? Just think that it used to be this way in unit tests
389too and you'll be happy again:).
390
391## Old paradigms
392
393Here are some paradigms you might see throughout the codebaseand some insight
394on what todo about them(hint: copying themisnot one!).Migration helpis
395welcome[crbug.com/984323](https://crbug.com/984323)!
396
397### base::TestMockTimeTaskRunner
398
399Thisis the ancestor of`SingleThreadTaskEnvironment`+`TimeSource::MOCK_TIME`.
400It's sort of equivalent but prefer task environments for consistency.
401
402The only case where `base::TestMockTimeTaskRunner` is still the only option is
403when writing regression tests that simulate a specific task execution order
404across multiple sequences. To do so, use two `base::TestMockTimeTaskRunner` and
405have the racing components post their tasks to separate ones. You can then
406explicitly run tasks posted to each one from the main test thread in a way that
407deterministically exercises the race resolution under test. This only applies to
408task execution order races, data races still require parallel execution and this
409is the main reason `TaskEnvironment` doesn't multiplex the`ThreadPool` tasks
410onto the main thread(i.e. exercise data races, especiallyin the scope of
411TSAN).
412
413### base::TestSimpleTaskRunner
414
415Preferusing`SingleThreadTaskEnvironment` over`base::TestSimpleTaskRunner`.
416`TestSimpleTaskRunner` isn't as "simple" as it seems specifically because it
417runs tasks in a surprising order (delays aren't respectedand nesting doesn't
418behave as usual). Should you prefer to flush all tasks regardless of delays,
419`TimeSource::MOCK_TIME` and `TaskEnvironment::FastForwardUntilNoTasksRemain()`
420have you covered.
421
422### base::NullTaskRunner
423
424Prefer `SingleThreadTaskEnvironment` or `TaskEnvironment` with
425`ThreadPoolExecutionMode::QUEUED` over `base::NullTaskRunner`. A
426`NullTaskRunner` might seem appealing, but not posting tasks is under-testing
427the potential side-effects of the code under tests. All tests should be okay if
428tasks born from their actions are run or deleted at a later point.
429
430### base::ScopedMockTimeMessageLoopTaskRunner
431
432This is the ancestor of `base::TestMockTimeTaskRunner` which is itself mostly
433deprecated. As mentioned above in the [TimeSource trait](#timesource-trait)
434section: This should never be used anymore except to mock time when there's
435already a task systemin place, e.g.in browser\_tests.
436
437### SetTaskRunnerForTesting() and SetTickClockForTesting()
438
439Prior to`TaskEnvironment::TimeSource::MOCK_TIME`, many components had
440`SetClockForTesting()`in their product code.And before modern[Threadingand
441Tasks], some components hadSetTaskRunnerForTesting().Neither of these
442test-only seams are required anymore now that task environments can mock those
443from under-the-hood.Cleanupin favor of modernTaskEnvironment paradigmsis
444always appreciated([crbug.com/984323](https://crbug.com/984323)).
445
446### Other helper task runners
447
448Different parts of the codebase have their own helper task runners.Please
449migrate awayfrom themor document them above.Ultimately the goalisfor
450`TaskEnvironment`and its subclasses to rule them alland to have a consistent
451task testing API surface onceandfor all.
452
453Itis still okayfor specific areas to have abase fixture that configures a
454default`TaskEnvironment` appropriatefor that areaanduse the
455`TaskEnvironmentTraits` paradigm outlinedin the[BaseFixture managed
456TaskEnvironment](#base-fixture-managed-taskenvironment) section above tolet
457individual tests provide additional traits.
458
459[ThreadingandTasks]: threading_and_tasks.md
460[ThreadingandTasks FAQ]: threading_and_tasks_faq.md
461[`ValidTraits`]: https://cs.chromium.org/chromium/src/base/test/task_environment.h?type=cs&q=ValidTraits&sq=package:chromium&g=0
462[task_environment.h]: https://cs.chromium.org/chromium/src/base/test/task_environment.h
Gabriel Charette9b6c04072022-04-01 23:22:46[diff] [blame]463[base/task/thread_pool.h]: https://cs.chromium.org/chromium/src/base/task/thread_pool.h
Gabriel Charette0b20ee6c2019-09-18 14:06:12[diff] [blame]464[ViewsTestBase]: https://cs.chromium.org/chromium/src/ui/views/test/views_test_base.h
465[base/traits_bag.h]: https://cs.chromium.org/chromium/src/base/traits_bag.h
466[content/public/test/browser_test_utils.h]: https://cs.chromium.org/chromium/src/content/public/test/browser_test_utils.h
467[content/public/test/content_browser_test_utils.h]: https://cs.chromium.org/chromium/src/content/public/test/content_browser_test_utils.h
468[content/public/test/test_utils.h]: https://cs.chromium.org/chromium/src/content/public/test/test_utils.h

[8]ページ先頭

©2009-2025 Movatter.jp