github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/docs/writing_tests.md (about) 1 # Tast: Writing Tests (go/tast-writing) 2 3 [TOC] 4 5 ## Adding tests 6 7 ### Test names 8 9 Tests are identified by names like `login.Chrome` or `platform.ConnectToDBus`. 10 The portion before the period, called the _category_, is the final component of 11 the test's package name, while the portion after the period is the name of the 12 exported Go function that implements the test. 13 14 Test function names should follow [Go's naming conventions], and [acronyms 15 should be fully capitalized]. Test names should not end with `Test`, both 16 because it's redundant and because the `_test.go` filename suffix is reserved in 17 Go for unit tests. 18 19 Test names are automatically derived from tests' package and function names and 20 should not be explicitly specified when defining tests. 21 22 [Go's naming conventions]: https://golang.org/doc/effective_go.html#names 23 [acronyms should be fully capitalized]: https://github.com/golang/go/wiki/CodeReviewComments#initialisms 24 25 ### Code location 26 27 Public tests built into the default `cros` local and remote [test bundles] are 28 checked into the [tast-tests repository] under the 29 [src/go.chromium.org/tast-tests/cros/local/bundles/cros/] and 30 [src/go.chromium.org/tast-tests/cros/remote/bundles/cros/] directories (which may also be 31 accessed by the `local_tests` and `remote_tests` symlinks at the top of the 32 repository). Private tests are checked into private repositories such as the 33 [tast-tests-private repository], and built into non-`cros` test bundles. 34 35 Tests are categorized into packages based on the functionality that 36 they exercise; for example, the [ui package] contains local tests that exercise 37 the ChromeOS UI. The category package needs to be directly under the bundle 38 package. Thus the category package path should be matched with 39 `go.chromium.org/tast/core/(local|remote)/bundles/(?P<bundlename>[^/]+)/(?P<category>[^/]+)`. 40 41 A local test named `ui.DoSomething` should be defined in a file named 42 `src/go.chromium.org/tast-tests/cros/local/bundles/cros/ui/do_something.go` (i.e. convert the 43 test name to lowercase and insert underscores between words). 44 45 Support packages used by multiple test categories located in 46 [src/go.chromium.org/tast-tests/cros/local/] and [src/go.chromium.org/tast-tests/cros/remote/], alongside the 47 `bundles/` directories. For example, the [chrome package] can be used by local 48 tests to interact with Chrome. 49 50 If there's a support package that's specific to a single category, it's often 51 best to place it underneath the category's directory. See the [Scoping and 52 shared code] section. 53 54 Packages outside `go.chromium.org/tast-tests/cros/local/...` should not import packages in `go.chromium.org/tast-tests/cros/local/...`, and 55 packages outside `go.chromium.org/tast-tests/cros/remote/...` should not import packages in `go.chromium.org/tast-tests/cros/remote/...`. 56 If local and remote packages should share the same code, put them in `go.chromium.org/tast-tests/cros/common/...`. 57 58 [test bundles]: overview.md#Test-bundles 59 [tast-tests repository]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD 60 [tast-tests-private repository]: https://chrome-internal.googlesource.com/chromeos/platform/tast-tests-private/+/HEAD 61 [src/go.chromium.org/tast-tests/cros/local/bundles/cros/]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/bundles/cros/ 62 [src/go.chromium.org/tast-tests/cros/remote/bundles/cros/]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/remote/bundles/cros/ 63 [ui package]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/bundles/cros/ui/ 64 [src/go.chromium.org/tast-tests/cros/local/]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/ 65 [src/go.chromium.org/tast-tests/cros/remote/]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/remote/ 66 [chrome package]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/chrome/ 67 [Scoping and shared code]: #Scoping-and-shared-code 68 69 ### Test registration 70 71 A test needs to be registered by calling `testing.AddTest()` in the test entry 72 file, which is located directly under a category package. The registration 73 needs to be done in `init()` function in the file. The registration should be 74 declarative, which means: 75 - `testing.AddTest()` should be the only statement of `init()`'s body. 76 - `testing.AddTest()` should take a pointer of a `testing.Test` composite literal. 77 78 Each field of testing.Test should be constant-like. Fields should not be set 79 using the invocation of custom functions (however, append() is allowed), or 80 using variables. In particular, we say constant-like is any of these things: 81 82 - An array literal of constant-like. 83 - A go constant. 84 - A literal value. 85 - A var defined as an array literal of go constants or literal values (N.B. not 86 general constant-likes). 87 - A var forwarding (set to) another constant-like var. 88 - A call to append on some constant-likes. 89 - A call to hwdep.D, but please apply the spirit of constant-like to the 90 arguments to hwdep.D. 91 92 The test registration code will be similar to the following: 93 94 ```go 95 // Copyright 2018 The ChromiumOS Authors 96 // Use of this source code is governed by a BSD-style license that can be 97 // found in the LICENSE file. 98 99 package ui 100 101 import ( 102 "context" 103 104 "go.chromium.org/tast/core/testing" 105 ) 106 107 func init() { 108 testing.AddTest(&testing.Test{ 109 Func: DoSomething, 110 Desc: "Does X to verify Y", 111 Contacts: []string{"team@google.com", "me@chromium.org"}, 112 BugComponent: "b:12345", 113 Attr: []string{"group:mainline", "informational"}, 114 SoftwareDeps: []string{"chrome"}, 115 Timeout: 3 * time.Minute, 116 }) 117 } 118 119 func DoSomething(ctx context.Context, s *testing.State) { 120 // The actual test goes here. 121 } 122 ``` 123 124 Tests have to specify the descriptions in `Desc`, which should be a string literal. 125 126 Tests have to specify email addresses of persons and groups who are familiar 127 with those tests in `Contacts`. The first element of the slice should be a group 128 alias for the team ultimately responsible for the test. Subsequent elements 129 should be individuals or groups who can be contacted for code reviews, bugs, 130 and any issue with the test's usage. To help aid triage and on-call rotations, 131 partner owned tests must specify a Google email contact that can be 132 reached by on-call rotations. Any google.com or chromium.org groups listed should 133 accept email posts from non-members within the organization. Users who no longer 134 work on Chrome OS or with test's owning team should remove themselves as a 135 contact. 136 137 Tests have to specify a `BugComponent`, which should be a string with a prefix 138 indicating the bug tracker. The string's contents point to the location where 139 bugs regarding the test should initially be filed. A prefix is used to 140 distinguish between different bug trackers. For Buganizer, use "b:" plus 141 the componentid, e.g. "b:1034625". For Chromium bug tracker, use "crbug:" plus 142 the component label, e.g. "crbug:Blink>JavaScript>WebAssembly". 143 144 Tests have to specify [attributes] to describe how they are used in ChromeOS 145 testing. A test belongs to zero or more groups by declaring attributes with 146 `group:`-prefix. Typically functional tests belong to the mainline group by 147 declaring the `group:mainline` attribute. New mainline tests should have the 148 `informational` attribute, as tests without this attribute will block the Commit 149 Queue on failure otherwise. The `Attr` fields should be an array literal of 150 string literals. 151 152 The `SoftwareDeps` field lists [software dependencies] that should be satisfied 153 in order for the test to run. Its value should be an array literal of string 154 literals or (possibly qualified) identifiers which are constant value. 155 156 Tests should always set the `Timeout` field to specify the maximum duration for 157 which Func may run before the test is aborted. If not specified, a reasonable 158 default will be used, but tests should not depend on it. 159 160 #### Disabling tests 161 162 If a test has no `group:*` attribute assigned it will be effectively disabled, 163 it will not be run by any automation. 164 If a test needs to be disabled leave a comment in the test source with the 165 reason. If applicable, create a bug explaining under what circumstances the 166 test can be enabled. 167 168 [Contacts]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#Test 169 [attributes]: test_attributes.md 170 [software dependencies]: test_dependencies.md 171 172 ### Adding new test categories 173 174 When adding a new test category, you must update the test bundle's `imports.go` 175 file (either [local/bundles/cros/imports.go] or [remote/bundles/cros/imports.go]) to 176 underscore-import the new package so its `init` functions will be executed to 177 register tests. 178 179 [local/bundles/cros/imports.go]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/bundles/cros/imports.go 180 [remote/bundles/cros/imports.go]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/remote/bundles/cros/imports.go 181 182 ## Coding style and best practices 183 184 Test code should be formatted by [gofmt] and checked by [go vet], [golint] and 185 [tast-lint]. These tools are configured to run as pre-upload hooks, so don't 186 skip them. 187 188 Tast code should also follow Go's established best practices as described by 189 these documents: 190 191 * [Effective Go] 192 * [Go Code Review Comments] 193 194 The [Go FAQ] may also be helpful. Additional resources are linked from the [Go 195 Documentation] page. 196 197 [gofmt]: https://golang.org/cmd/gofmt/ 198 [go vet]: https://golang.org/cmd/vet/ 199 [golint]: https://github.com/golang/lint 200 [tast-lint]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/src/go.chromium.org/tast/core/cmd/tast-lint/ 201 [Effective Go]: https://golang.org/doc/effective_go.html 202 [Go Code Review Comments]: https://github.com/golang/go/wiki/CodeReviewComments 203 [Go FAQ]: https://golang.org/doc/faq 204 [Go Documentation]: https://golang.org/doc/ 205 206 ### Documentation 207 208 Packages and exported identifiers (e.g. types, functions, constants, variables) 209 should be documented by [Godoc]-style comments. Godoc comments are optional for 210 test functions, since the `Test.Desc` field already contains a brief description 211 of the test. 212 213 [Godoc]: https://blog.golang.org/godoc-documenting-go-code 214 215 ### Unit tests 216 217 Support packages should be exercised by unit tests when possible. Unit tests can 218 cover edge cases that may not be typically seen when using the package, and they 219 greatly aid in future refactorings (since it can be hard to determine the full 220 set of Tast-based tests that must be run to exercise the package). See [How to 221 Write Go Code: Testing] and [Go's testing package] for more information about 222 writing unit tests for Go code. The [Best practices for writing ChromeOS unit 223 tests] document contains additional suggestions that may be helpful (despite 224 being C++-centric). 225 226 Setting `FEATURES=test` when emerging a test bundle package 227 (`tast-local-tests-cros` or `tast-remote-tests-cros`) will run all unit tests 228 for the corresponding packages in the `tast-tests` repository (i.e. 229 `go.chromium.org/tast-tests/cros/local/...` or `go.chromium.org/tast-tests/cros/remote/...`, respectively). 230 231 During development, the [fast_build.sh] script can be used to quickly build and 232 run tests for a single package or all packages. 233 234 [How to Write Go Code: Testing]: https://golang.org/doc/code.html#Testing 235 [Go's testing package]: https://golang.org/pkg/testing/ 236 [Best practices for writing ChromeOS unit tests]: https://chromium.googlesource.com/chromiumos/docs/+/main/testing/unit_tests.md 237 [fast_build.sh]: modifying_tast.md#fast_build_sh 238 239 ### Import 240 241 Entries in import declaration must be grouped by empty line, and sorted in 242 following order. 243 244 - Standard library packages 245 - Third-party packages 246 - chromiumos/ packages 247 248 In each group, entries must be sorted in the lexicographical order. For example: 249 250 ```go 251 import ( 252 "context" 253 "fmt" 254 255 "github.com/godbus/dbus/v5" 256 "golang.org/x/sys/unix" 257 258 "go.chromium.org/tast/core/errors" 259 "go.chromium.org/tast-tests/cros/local/chrome" 260 ) 261 ``` 262 263 Note that, although github.com and golang.org are different domains, they 264 should be in a group. 265 266 This is how `goimports --local=chromiumos/` sorts. It may be valuable to run 267 the command. Note that, 1) the command preserves existing group. So, it may 268 be necessary to remove empty lines in import() in advance, and 2) use the 269 command to add/remove import entries based on the following code. The path 270 resolution may require setting `GOPATH` properly. 271 272 ## Test structure 273 274 As seen in the test declaration above, each test is comprised of a single 275 exported function that receives a [testing.State] struct. This is defined in the 276 [Tast testing package] (not to be confused with [Go's `testing` package] for 277 unit testing) and is used to log progress and report failures. 278 279 [testing.State]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#State 280 [Tast testing package]: https://chromium.googlesource.com/chromiumos/platform/tast/+/main/src/go.chromium.org/tast/core/testing/ 281 282 ### Startup and shutdown 283 284 If a test requires the system to be in a particular state before it runs, it 285 should include code that tries to get the system into that state if it isn't 286 there already. Previous tests may have aborted mid-run; it's not safe to make 287 assumptions that they undid all temporary changes that they made. 288 289 Tests should also avoid performing unnecessary de-initialization steps on 290 completion: UI tests should leave Chrome logged in at completion instead of 291 restarting it, for example. Since later tests can't safely make assumptions 292 about the initial state of the system, they'll need to e.g. restart Chrome again 293 regardless, which takes even more time. In addition to resulting in a faster 294 overall running time for the suite, leaving the system in a logged-in state 295 makes it easier for developers to manually inspect it after running the test 296 when diagnosing a failure. 297 298 Note that tests should still undo atypical configuration that leaves the system 299 in a non-fully-functional state, though. For example, if a test needs to 300 temporarily stop a service, it should restart it before exiting. 301 302 Use [defer] statements to perform cleanup when your test exits. `defer` is 303 explained in more detail in the [Defer, Panic, and Recover] blog post. 304 305 Put more succintly: 306 307 > Assume you're getting a reasonable environment when your test starts, but 308 > don't make assumptions about Chrome's initial state. Similarly, try to leave 309 > the system in a reasonable state when you go, but don't worry about what 310 > Chrome is doing. 311 312 [defer]: https://tour.golang.org/flowcontrol/12 313 [Defer, Panic, and Recover]: https://blog.golang.org/defer-panic-and-recover 314 315 ### Contexts and timeouts 316 317 Tast uses [context.Context] to implement timeouts. A test function takes as its 318 first argument a [context.Context] with an associated deadline that expires when 319 the test's timeout is reached. The default timeout is 2 minutes for [local tests] 320 and 5 minutes for [remote tests]. The context's `Done` function returns a [channel] 321 that can be used within a [select] statement to wait for expiration, after which 322 the context's `Err` function returns a non-`nil` error. 323 324 The [testing.Poll] function makes it easier to honor timeouts while polling for 325 a condition: 326 327 ```go 328 if err := testing.Poll(ctx, func (ctx context.Context) error { 329 var url string 330 if err := MustSucceedEval(ctx, "location.href", &url); err != nil { 331 return testing.PollBreak(errors.Wrap(err, "failed to evaluate location.href")) 332 } 333 if url != targetURL { 334 return errors.Errorf("current URL is %s", url) 335 } 336 return nil 337 }, &testing.PollOptions{Timeout: 10 * time.Second}); err != nil { 338 return errors.Wrap(err, "failed to navigate") 339 } 340 ``` 341 342 Return a [testing.PollBreak] error to stop the polling. Useful when you get an 343 unexpected error inside the polling. 344 345 Sleeping without polling for a condition is discouraged, since it makes tests 346 flakier (when the sleep duration isn't long enough) or slower (when the duration 347 is too long). If you really need to do so, use [testing.Sleep] to honor the context 348 timeout. 349 350 Any function that performs a blocking operation should take a [context.Context] 351 as its first argument and return an error if the context expires before the 352 operation finishes. 353 354 Several blog posts discuss these patterns in more detail: 355 356 * [Go Concurrency Patterns: Context] 357 * [Go Concurrency Patterns: Timing out, moving on] 358 359 Note: there is an old equivalent "golang.org/x/net/context" package, but for 360 consistency, the built-in "context" package is preferred. 361 362 > As a rule of thumb, a timeout should be **double of the expected worst case 363 > performance**. If you're unsure, measure time multiple times in the worst case 364 > scenario and double that. Do not use timeouts to catch performance 365 > regressions. Instead consider writing a performance test. 366 > When a test hits a timeout that was sufficient before, investigate why it hit 367 > the timeout before increasing it. 368 369 The performance and worst case scenario can be obtained using the [time 370 calculation script]. It parses the test result logs to obtain the average and 371 max time from various executions. 372 373 [context.Context]: https://golang.org/pkg/context/ 374 [channel]: https://tour.golang.org/concurrency/2 375 [local tests]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast/src/go.chromium.org/tast/coreinternal/bundle/local.go 376 [remote tests]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast/src/go.chromium.org/tast/coreinternal/bundle/remote.go 377 [select]: https://tour.golang.org/concurrency/5 378 [testing.Poll]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#Poll 379 [testing.PollBreak]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#PollBreak 380 [testing.Sleep]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#Sleep 381 [Go Concurrency Patterns: Context]: https://blog.golang.org/context 382 [Go Concurrency Patterns: Timing out, moving on]: https://blog.golang.org/go-concurrency-patterns-timing-out-and 383 [time calculation script]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast-tests/tools/test_time_calculation.py 384 385 ### Reserve time for clean-up task 386 387 For any function with a corresponding clean-up function, prefer using the [defer] 388 statement to keep the two function calls close together (see the 389 [Startup and shutdown](#startup-and-shutdown) section for detail): 390 ```go 391 a := pkga.NewA(ctx, ...) 392 defer func(ctx context.Context) { 393 if err := a.CleanUp(ctx); err != nil { 394 // ... 395 } 396 }(ctx) 397 ``` 398 Before creating `A`, make sure that the clean-up function has sufficient time to 399 run: 400 ```go 401 ctxForCleanUpA := ctx 402 ctx, cancel := ctxutil.Shorten(ctx, pkga.TimeForCleanUpA) 403 defer cancel() 404 a := pkga.NewA(ctx, ...) 405 defer func(ctx context.Context) { 406 if err := a.CleanUp(ctx); err != nil { 407 // ... 408 } 409 }(ctxForCleanUpA) 410 ``` 411 412 It [ctxutil.Shorten]s `ctx` before calling `pkga.NewA` to ensure that after 413 `pkga.NewA()`, `a.CleanUp()` still has time to perform the clean-up. Note that 414 `pkga` should provide `TimeForCleanUpA` constant for its callers to reserve time 415 for `a.CleanUp()`. 416 Also, instead of assigning the shortened `ctx` to `sCtx`, it copies the original 417 `ctx` to `ctxForCleanUpA` before shortening it. It is because we want to use 418 `ctx` for the main logic and leave the longer name for the clean-up logic. 419 420 Another approach was used but discouraged now: 421 ```go 422 a := pkga.NewA(ctx, ...) 423 defer func(ctx context.Context) { 424 if err := a.CleanUp(ctx); err != nil { 425 // ... 426 } 427 }(ctx) 428 ctx, cancel := a.ReserveForCleanUp(ctx) 429 defer cancel() 430 ``` 431 The reason why it is discouraged is because it needs `pkga.NewA()` to shorten 432 `ctx` at the beginning of the function to ensure that it leaves enough time for 433 `a.CleanUp()` to call. 434 435 [ctxutil.Shorten]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/ctxutil#Shorten 436 437 ### Concurrency 438 439 Concurrency is rare in integration tests, but it enables doing things like 440 watching for a D-Bus signal that a process emits soon after being restarted. It 441 can also sometimes be used to make tests faster, e.g. by restarting multiple 442 independent Upstart jobs simultaneously. 443 444 The preferred way to synchronize concurrent work in Go programs is by passing 445 data between [goroutines] using a [channel]. This large topic is introduced in 446 the [Share Memory by Communicating] blog post, and the [Go Concurrency Patterns] 447 talk is also a good summary. [The Go Memory Model] provides guarantees about the 448 effects of memory reads and writes across goroutines. 449 450 [goroutines]: https://tour.golang.org/concurrency/1 451 [Share Memory by Communicating]: https://blog.golang.org/share-memory-by-communicating 452 [Go Concurrency Patterns]: https://talks.golang.org/2012/concurrency.slide 453 [The Go Memory Model]: https://golang.org/ref/mem 454 455 ### Scoping and shared code 456 457 Global variables in Go are [scoped at the package level] rather than the file 458 level: 459 460 > The scope of an identifier denoting a constant, type, variable, or function 461 > ... declared at top level (outside any function) is the package block. 462 463 As such, all tests within a package like `platform` or `ui` share the same 464 namespace. It is ok to declare top level unexported symbols 465 (e.g. functions, constants, etc), but please be careful of conflicts. Also, 466 please avoid referencing identifiers declared in other files; otherwise 467 `repo upload` will fail with lint errors. 468 469 If you need to share functionality between tests in the same package, please 470 introduce a new descriptively-named subpackage; see e.g. the [chromecrash] 471 package within the `ui` package, used by the [ui.ChromeCrashLoggedIn] and 472 [ui.ChromeCrashNotLoggedIn] tests. Subpackages are described in more detail 473 later in this document. Importing a subpackage is allowed only in the category 474 package containing it; otherwise `repo upload` will fail with lint errors. 475 476 [scoped at the package level]: https://golang.org/ref/spec#Declarations_and_scope 477 [chromecrash]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/bundles/cros/ui/chromecrash/ 478 [ui.ChromeCrashLoggedIn]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/bundles/cros/ui/chrome_crash_logged_in.go 479 [ui.ChromeCrashNotLoggedIn]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/bundles/cros/ui/chrome_crash_not_logged_in.go 480 481 ### Test consolidation 482 483 Much praise has been written for verifying just one thing per test. A quick 484 sampling of internal links: 485 486 * [TotT 227] 487 * [TotT 324] 488 * [TotT 339] 489 * [TotT 520] 490 * [Unit Testing Best Practices Do's and Don'ts] 491 492 While this is sound advice for fast-running, deterministic unit tests, it isn't 493 necessarily always the best approach for integration tests: 494 495 * There are unavoidable sources of non-determinism in ChromeOS integration 496 tests. DUTs can experience hardware or networking issues, and flakiness 497 becomes more likely as more tests are run. 498 * When a lengthy setup process is repeated by many tests in a single suite, 499 lab resources are consumed for a longer period of time and other testing is 500 delayed. 501 502 If you need to verify multiple related aspects of a single feature that requires 503 a time-consuming setup process like logging in to Chrome, starting Android, or 504 launching a container, it's often preferable to write a single test that just 505 does the setup once and then verifies all aspects of the feature. As described 506 in the [Errors and Logging] section, multiple errors can be reported by a single 507 test, so coverage need not be reduced when tests are consolidated and an early 508 expectation fails. 509 510 For lightweight testing that doesn't need to interact with Chrome or restart 511 services, it's fine to use fine-grained tests — there's almost no per-test 512 overhead in Tast; the overhead comes from repeating the same slow operations 513 _within_ multiple tests. 514 515 *** aside 516 If all the time-consuming setup in your test suite is covered by a tast 517 [fixtures], then splitting your test into multiple fine-grained tests 518 will incur negligible overhead. 519 *** 520 521 [TotT 227]: http://go/tott/227 522 [TotT 324]: http://go/tott/324 523 [TotT 339]: http://go/tott/339 524 [TotT 520]: http://go/tott/520 525 [Unit Testing Best Practices Do's and Don'ts]: http://go/unit-test-practices#behavior-testing-dos-and-donts 526 [Errors and Logging]: #errors-and-logging 527 [fixtures]: #Fixtures 528 529 ### Device dependencies 530 531 A Tast test either passes (by reporting zero errors) or fails (by reporting one 532 or more errors, timing out, or panicking). If a test requires functionality that 533 isn't provided by the DUT, the test is skipped entirely. 534 535 Avoid writing tests that probe the DUT's capabilities at runtime, e.g. 536 537 ```go 538 // WRONG: Avoid testing for software or hardware features at runtime. 539 func CheckCamera(ctx context.Context, s *testing.State) { 540 if !supports720PCamera() { 541 s.Log("Skipping test; device unsupported") 542 return 543 } 544 // ... 545 } 546 ``` 547 548 This approach results in the test incorrectly passing even though it actually 549 didn't verify anything. (Tast doesn't let tests report an "N/A" state at runtime 550 since it would be slower than skipping the test altogether and since it will 551 prevent making intelligent scheduling decisions in the future about where tests 552 should be executed.) 553 554 Instead, specify [software dependencies] when declaring tests: 555 556 ```go 557 // OK: Specify dependencies when declaring the test. 558 func init() { 559 testing.AddTest(&testing.Test{ 560 Func: CheckCamera, 561 SoftwareDeps: []string{"camera_720p", "chrome"}, 562 // ... 563 }) 564 } 565 ``` 566 567 The above document describes how to define new dependencies. 568 569 Also, there is an API Features which allows tests to get information regarding 570 DUT features. However, it is purely used for informational purpose only. Do not 571 use it to alter test behavior. Use it for only for informational purpose. Use 572 [parameterized tests] for tests to have different behavior for different 573 DUT features. 574 575 If a test depends on the DUT being in a specific configurable state (e.g. tablet 576 mode), it should put it into that state. For example, [chrome.ExtraArgs] can be 577 passed to [chrome.New] to pass additional command-line flags (e.g. 578 `--force-tablet-mode=touch_view`) when starting Chrome. 579 580 The [tast-users mailing list] is a good place to ask questions about test 581 dependencies. 582 583 [chrome.ExtraArgs]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast-tests.git/src/go.chromium.org/tast-tests/cros/local/chrome#ExtraArgs 584 [chrome.New]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast-tests.git/src/go.chromium.org/tast-tests/cros/local/chrome#New 585 [tast-users mailing list]: https://groups.google.com/a/chromium.org/forum/#!forum/tast-users 586 [parameterized tests]: #Parameterized-tests 587 588 ### Fixtures 589 590 Sometimes a lengthy setup process (e.g. restarting Chrome and logging in, which 591 takes at least 6-7 seconds) is needed by multiple tests. Rather than running the 592 same setup for each of those tests, tests can declare the shared setup, which is 593 named "fixtures" in Tast. 594 595 Tests sharing the same fixture run consecutively. A fixture implements several 596 _lifecycle methods_ that are called by the framework as it executes tests 597 associated with the fixture. `SetUp()` of the fixture runs once just before the 598 first of them starts, and `TearDown()` is called once just after the last of 599 them completes. `Reset()` runs after each but the last test to roll back changes 600 a test made to the environment. 601 602 * Fixture `SetUp()` 603 * Test 1 runs 604 * Fixture `Reset()` 605 * Test 2 runs 606 * Fixture `Reset()` 607 * ... 608 * Fixture `Reset()` 609 * Test N runs 610 * Fixture `TearDown()` 611 612 `Reset()` should be a light-weight and idempotent operation. If it fails 613 (returns a non-nil error), framework falls back to `TearDown()` and `SetUp()` to 614 completely restart the fixture. 615 Tests should not leave too much change on system environment, so that the next 616 `Reset()` does not fail. 617 618 *** aside 619 Currently Reset errors do not mark a test as failed. We plan to change this 620 behavior in the future ([b/187795248](http://b/187795248)). 621 *** 622 623 Fixtures also have `PreTest()` and `PostTest()` methods, which run before and 624 after each test. They get called with [`testing.FixtTestState`] with which you 625 can report errors as a test. It's a good place to set up logging for individual 626 test for example. 627 628 For details of these fixture lifecycle methods, please see the GoDoc 629 [`testing.FixtureImpl`]. 630 631 Each test can declare its fixture by setting [`testing.Test.Fixture`] an fixture 632 name. The fixture's `SetUp()` returns an arbitrary value that can be obtained by 633 calling `s.FixtValue()` in the test. Because `s.FixtValue()` returns an 634 `interface{}`, type assertion is needed to cast it to the actual type. 635 However, `s.FixtValue()` will always return nil when local tests/fixtures 636 try to access values from remote fixtures because Tast does not know the actual 637 types of fixture values to deserialize them. Therefore, there is another 638 function `s.FixtFillValue(v, any)` which requires user to pass in a pointer, 639 and it will store the deserialized result in the value pointed to by pointer. 640 641 Fixtures are composable. A fixture can declare its parent fixture with 642 `testing.Fixture.Parent`. Parent's `SetUp()` is executed before the fixture's 643 `SetUp()` is executed, parent's `TearDown()` is executed after the fixtures's 644 `TearDown()`, and so on. Fixtures can use the parent's value in the same way 645 tests use it. 646 647 Local tests/fixtures can depend on a remote fixture if they live in test bundles 648 with the same name (e.g. local `cros` and remote `cros`). 649 650 Fixtures are registered by calling [`testing.AddFixture`] with [`testing.Fixture`] 651 struct in `init()`. `testing.Fixture.Name` specifies the fixture name, 652 `testing.Fixture.Impl` specifies implementation of the fixture, 653 `testing.Fixture.Parent` specifies the parent fixture if any, 654 `testing.Fixture.SetUpTimeout` and the like specify methods' timeout, 655 and the other fields are analogous to `testing.Test`. 656 657 Fixtures can be registered outside bundles directory. It's best to initialize 658 and register fixtures outside bundles if it is shared by tests in multiple 659 categories. 660 661 #### Examples 662 663 * Rather than calling [chrome.New] at the beginning of each test, tests can 664 declare that they require a logged-in Chrome instance by setting 665 [`testing.Test.Fixture`] to "[chromeLoggedIn]" in `init()`. This enables Tast to 666 just perform login once and then share the same Chrome instance with all tests 667 that specify the fixture. See the [chromeLoggedIn] documentation for more 668 details, and [example.ChromeFixture] for a test using the fixture. 669 670 * If you want a new Chrome fixture with custom options, call 671 [`testing.AddFixture`] from [chrome/fixture.go] with different options, and give 672 it a unique name. 673 674 #### Theory behind fixtures 675 676 On designing composable fixtures, understanding the theory behind fixtures might 677 help. 678 679 Let us think of a space representing all possible system states. A fixture's 680 purpose is to change the current system state to be in a certain subspace. For 681 example, the fixture [chromeLoggedIn]'s purpose is to provide a clean 682 environment similar to soon after logging into a Chrome session. This can be 683 rephased that there's a subspace where "the system state is clean similar to 684 soon after logging into a Chrome session" and the fixture's designed to change 685 the system state to some point inside the subspace. 686 687 To denote these concepts a bit formally: let `U` be a space representing all 688 possible system states. Let `f` be a function that maps a fixture to its target 689 system state subspace. Then, for any fixture `X`, `f(X) ⊆ U`. Note that `f(F)` 690 is a subspace of `U`, not a point in `U`; there can be some degrees of freedom 691 in a resulting system state. 692 693 A fixture's property is as follows: if a test depends on a fixture `F` directly 694 or indirectly, it can assume that the system state is in `f(F)` on its start. 695 This also applies to fixtures: if a fixture depends on a fixture `F` directly or 696 indirectly, it can assume that the system state is in `f(F)` on its setup. 697 698 To fulfill this property, all fixtures should satisfy the following rule: if a 699 fixture `X` has a child fixture `Y`, then `f(X) ⊇ f(Y)`. Otherwise, calling 700 `Y`'s reset may put the system state to one not accepted by `X`, failing to 701 fulfill the aforementioned property. 702 703 #### Preconditions 704 705 Preconditions, predecessor of fixtures, are not recommended for new tests. 706 707 [`testing.Fixture`]: https://pkg.go.dev/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/coreinternal/testing#Fixture 708 [`testing.FixtureImpl`]: https://pkg.go.dev/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/coreinternal/testing#FixtureImpl 709 [`testing.FixtTestState`]: https://pkg.go.dev/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/coreinternal/testing#FixtTestState 710 [chrome.New]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast-tests.git/src/go.chromium.org/tast-tests/cros/local/chrome#New 711 [chromeLoggedIn]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast-tests/src/go.chromium.org/tast-tests/cros/local/chrome/fixture.go 712 [`testing.Test.Fixture`]: https://pkg.go.dev/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/coreinternal/testing#Test.Fixture 713 [chrome/fixture.go]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast-tests/src/go.chromium.org/tast-tests/cros/local/chrome/fixture.go 714 [example.ChromeFixture]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/tast-tests/src/go.chromium.org/tast-tests/cros/local/bundles/cros/example/chrome_fixture.go 715 [`testing.AddFixture`]: https://pkg.go.dev/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#AddFixture 716 717 ## Common testing patterns 718 719 ### Table-driven tests 720 721 It is sometimes the case that multiple scenarios with very slight differences 722 should be tested. In this case you can write a [table-driven test], which is a 723 common pattern in Go unit tests. [testing.State.Run] can be used to start a 724 subtest. 725 726 ```go 727 for _, tc := range []struct { 728 format string 729 filename string 730 duration time.Duration 731 }{ 732 { 733 format: "VP8", 734 filename: "sample.vp8", 735 duration: 3 * time.Second, 736 }, 737 { 738 format: "VP9", 739 filename: "sample.vp9", 740 duration: 3 * time.Second, 741 }, 742 { 743 format: "H.264", 744 filename: "sample.h264", 745 duration: 5 * time.Second, 746 }, 747 } { 748 s.Run(ctx, tc.format, func(ctx context.Context, s *testing.State) { 749 if err := testPlayback(ctx, tc.filename, tc.duration); err != nil { 750 s.Error("Playback test failed: ", err) 751 } 752 }) 753 } 754 ``` 755 756 [table-driven test]: https://github.com/golang/go/wiki/TableDrivenTests 757 [testing.State.Run]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#State.Run 758 759 ## Errors and logging 760 761 The [testing.State] struct provides functions that tests may use to report their 762 status: 763 764 * `Log` and `Logf` record informational messages about the test's progress. 765 * `Error` and `Errorf` record errors and mark the test as failed but allow it 766 to continue, similar to [Google Test]'s `EXPECT_` set of macros. Multiple 767 errors may be reported by a single test. 768 * `Fatal` and `Fatalf` record errors and stop the test immediately, similar to 769 the `ASSERT_` set of macros. 770 771 Note that higher-level functions for stating expectations and assertions are not 772 provided; this was a conscious decision. See ["Where is my favorite helper 773 function for testing?"] from the [Go FAQ]. That answer refers to [Go's testing 774 package] rather than Tast's, but the same reasoning and suggestions are 775 applicable to Tast tests. 776 777 [Google Test]: https://github.com/google/googletest 778 ["Where is my favorite helper function for testing?"]: https://golang.org/doc/faq#testing_framework 779 780 ### When to log 781 782 When you're about to do something that could take a while or even hang, log a 783 message using `Log` or `Logf` first. This both lets developers know what's 784 happening when they run your test interactively and helps when looking at logs 785 to investigate timeout failures. 786 787 On the other hand, avoid logging unnecessary information that would clutter the 788 logs. If you want to log a verbose piece of information to help determine the 789 cause of an error, only do it after the error has occurred. Also, if you are 790 interested in which part of a test is time-consuming, please see the 791 [Reporting timing] section for details. 792 793 See the [fmt package]'s documentation for available "verbs". 794 795 [fmt package]: https://golang.org/pkg/fmt/ 796 [Reporting timing]: #Reporting-timing 797 798 <a name="log-vs-logf"></a> 799 800 ### Log/Error/Fatal vs. Logf/Errorf/Fatalf 801 802 `Log`, `Error`, and `Fatal` should be used in conjunction with a single string 803 literal or when passing a string literal followed by a single value: 804 805 ```go 806 s.Log("Doing something slow") 807 s.Log("Loading ", url) 808 s.Error("Encountered an error: ", err) 809 s.Fatal("Everything is broken: ", err) 810 ``` 811 812 `Logf`, `Errorf`, and `Fatalf` should only be used in conjunction with `printf`-style 813 format strings: 814 815 ```go 816 s.Logf("Read %q from %v", data, path) 817 s.Errorf("Failed to load %v: %v", url, err) 818 s.Fatalf("Got invalid JSON object %+v", obj) 819 ``` 820 821 When concatenating a string and a value using default formatting, use 822 `s.Log("Some value: ", val)` rather than the more-verbose 823 `s.Logf("Some value: %v", val)`. 824 825 The same considerations apply to `testing.ContextLog` vs. `testing.ContextLogf`. 826 827 <a name="error-pkg"></a> 828 829 ### Error construction 830 831 To construct new errors or wrap other errors, use the [go.chromium.org/tast/core/errors] 832 package rather than standard libraries (`errors.New`, `fmt.Errorf`) or any other 833 third-party libraries. It records stack traces and chained errors, and leaves 834 nicely formatted logs when tests fail. 835 836 To construct a new error, use [errors.New] or [errors.Errorf]. 837 838 ```go 839 errors.New("process not found") 840 errors.Errorf("process %d not found", pid) 841 ``` 842 843 To construct an error by adding context to an existing error, use [errors.Wrap] or [errors.Wrapf]. 844 845 ```go 846 errors.Wrap(err, "failed to connect to Chrome browser process") 847 errors.Wrapf(err, "failed to connect to Chrome renderer process %d", pid) 848 ``` 849 850 To examine sentinel errors which may be `Wrap`ed, use [errors.Is] or 851 [errors.As]. The usage is the same as the functions with the same names in the 852 official errors package. 853 854 Sometimes you may want to define custom error types, for example, to inspect and 855 react to errors. In that case, embed `*errors.E` to your custom error struct. 856 857 ```go 858 type CustomError struct { 859 *errors.E 860 } 861 862 if err := doSomething(); err != nil { 863 return &CustomError{E: errors.Wrap(err, "something failed")} 864 } 865 ``` 866 867 It is recommended to wrap when you cross package boundary, which represents 868 some kind of barrier beneath which everything is an implementation detail. 869 Otherwise it is fine to return an error without wrapping, if you can't really 870 add much context to make debugging easier. Use your best judgement to decide 871 wrap or not. 872 873 Following quotes from 874 *[The Go programming language] 5.4.1 Error-Handling Strategies* 875 are useful to design good errors: 876 877 > - When designing error messages, be deliberate, so that each one is a meaningful description of the problem with sufficient and relevant detail. 878 > - In general, the call `f(x)` is responsible for reporting the attempted operation `f` and the argument value `x` as they relate to the context of the error. 879 > - The caller is responsible for adding further information that it has but the call `f(x)` does not. 880 881 [go.chromium.org/tast/core/errors]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors 882 [errors.New]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors#New 883 [errors.Errorf]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors#Errorf 884 [errors.Wrap]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors#Wrap 885 [errors.Wrapf]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors#Wrapf 886 [errors.Is]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors#Is 887 [errors.As]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors#As 888 [The Go programming language]: https://www.gopl.io/ 889 890 ### Formatting 891 892 <a name="error-fmt"></a> 893 894 Please follow [Go's error string conventions] when producing `error` values. 895 896 > Error strings should not be capitalized (unless beginning with proper nouns or 897 > acronyms) or end with punctuation, since they are usually printed following 898 > other context. 899 900 For example: 901 902 ```go 903 if err := doSomething(id); err != nil { 904 return errors.Wrapf(err, "doing something to %q failed", id) 905 } 906 ``` 907 908 <a name="log-fmt"></a> 909 910 Log and error messages printed by tests via `testing.State`'s `Log`, `Logf`, 911 `Error`, `Errorf`, `Fatal`, or `Fatalf` methods, or via `testing.ContextLog` or 912 `testing.ContextLogf`, should be capitalized phrases without any trailing 913 punctuation that clearly describe what is about to be done or what happened: 914 915 ```go 916 s.Log("Asking Chrome to log in") 917 ... 918 if err != nil { 919 s.Fatal("Failed to log in: ", err) 920 } 921 s.Logf("Logged in as user %v with ID %v", user, id) 922 ``` 923 924 <a name="common-fmt"></a> 925 926 In all cases, please avoid multiline strings since they make logs difficult to 927 read. To preserve multiline output from an external program, please write it to 928 an [output file] instead of logging it. 929 930 When including a path, URL, or other easily-printable value in a log message or 931 an error, omit leading colons or surrounding quotes: 932 933 ```go 934 s.Logf("Trying to log in up to %d time(s)", numLogins) 935 errors.Errorf("%v not found", path) 936 ``` 937 938 Use quotes when including arbitrary data that may contain hard-to-print 939 characters like spaces: 940 941 ```go 942 s.Logf("Successfully read %q from %v", data, path) 943 ``` 944 945 Use a colon followed by a space when appending a separate clause that contains 946 additional detail (typically an error): 947 948 ```go 949 s.Error("Failed to log in: ", err) 950 ``` 951 952 Semicolons are appropriate for joining independent clauses: 953 954 ```go 955 s.Log("Attempt failed; trying again") 956 ``` 957 958 [Go's error string conventions]: https://github.com/golang/go/wiki/CodeReviewComments#error-strings 959 [output file]: #Output-files 960 961 ### Support packages 962 963 Support packages should not record test failures directly. Instead, return 964 `error` values (using the [errors package]) and allow tests to decide 965 how to handle them. Support packages' exported functions should typically take 966 [context.Context] arguments and use them to return an error early when the 967 test's deadline is reached and to log informative messages using 968 `testing.ContextLog` and `testing.ContextLogf`. 969 970 Similarly, support packages should avoid calling `panic` when errors are 971 encountered. When a test is running, `panic` has the same effect as `State`'s 972 `Fatal` and `Fatalf` methods: the test is aborted immediately. Returning an 973 `error` gives tests the ability to choose how to respond. 974 975 The [Error handling and Go] and [Errors are values] blog posts offer guidance on 976 using the `error` type. 977 978 [errors package]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/errors 979 [Error handling and Go]: https://blog.golang.org/error-handling-and-go 980 [Errors are values]: https://blog.golang.org/errors-are-values 981 982 ### Test subpackages 983 984 The above guidelines do not necessarily apply to test subpackages that are 985 located in subdirectories below test files. If a subpackage actually contains 986 the test implementation (typically because it's shared across several tests), 987 it's okay to pass `testing.State` to it so it can report test errors itself. 988 989 Subpackages are typically aware of how they will be used, so an argument can be 990 made for letting them abort testing using `Fatal` or even `panic` in cases where 991 it improves code readability (e.g. for truly exceptional cases like I/O 992 failures). Use your best judgement. 993 994 Note that it's still best to practice [information hiding] and pass only as much 995 data is needed. Avoid passing `testing.State` when it's not actually necessary: 996 997 * If a function just needs the output directory, pass a path. 998 * If a function just needs to log its progress, pass a `context.Context` so it 999 can call `testing.ContextLog`. 1000 1001 [information hiding]: https://en.wikipedia.org/wiki/Information_hiding 1002 1003 ### Reporting timing 1004 1005 The [timing package] can be used to measure and report the time taken by 1006 different "stages" of a test. It helps you identify which stage takes an 1007 unexpectedly long time to complete. 1008 1009 An example to time a test with two stages: 1010 1011 ```go 1012 func TestFoo(ctx context.Context, s *testing.State) { 1013 // Tast framework already adds a stage for the test function. 1014 stageA(ctx) 1015 stageB(ctx) 1016 } 1017 1018 func stageA(ctx context.Context) { 1019 ctx, st := timing.Start(ctx, "stage_a") 1020 defer st.End() 1021 ... 1022 } 1023 1024 func stageB(ctx context.Context) { 1025 ctx, st := timing.Start(ctx, "stage_b") 1026 defer st.End() 1027 ... 1028 } 1029 ``` 1030 1031 By default, the result will be written to `timing.json` (see [timing#Log.Write] 1032 for details) in the Tast [results dir]. The above example will generate: 1033 1034 ```json 1035 [4.000, "example.TestFoo", [ 1036 [1.000, "stage_a"], 1037 [3.000, "stage_b"]]] 1038 ``` 1039 1040 [timing package]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/timing 1041 [timing#Log.Write]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/timing#Log.Write 1042 [results dir]: running_tests.md#Interpreting-test-results 1043 1044 ### Logged in users home directory 1045 1046 Within the scope of a test, it might be useful to put files in the users 1047 Downloads directory or My Files directory. To achieve there are 2 helper methods 1048 that calculate the logged in user's hash and return the path to either Downloads 1049 or MyFiles. To use these, do the following: 1050 1051 ```go 1052 import "go.chromium.org/tast-tests/cros/local/cryptohome" 1053 1054 downloadsPath, err := cryptohome.DownloadsPath(ctx, cr.NormalizedUser()) 1055 if err != nil { 1056 s.Fatal("Failed to get users Download path: ", err) 1057 } 1058 ``` 1059 1060 An alternative `MyFilesPath` if you require the My Files location directly. 1061 Please avoid using the `/home/chronos/user` path directly as these are being 1062 deprecated. 1063 ## Output files 1064 1065 Tests can write output files that are automatically copied to the host system 1066 that was used to initiate testing: 1067 1068 ```go 1069 func WriteOutput(s *testing.State) { 1070 if err := ioutil.WriteFile(filepath.Join(s.OutDir(), "my_output.txt"), 1071 []byte("Here's my output!"), 0644); err != nil { 1072 s.Error(err) 1073 } 1074 } 1075 ``` 1076 1077 As described in the [Running tests] document, a test's output files are copied 1078 to a `tests/<test-name>/` subdirectory within the results directory. 1079 1080 [Running tests]: running_tests.md 1081 1082 ### Performance measurements 1083 1084 The [perf] package is provided to record the results of performance tests. See 1085 the [perf] documentation for more details. 1086 1087 [perf]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast-tests.git/src/go.chromium.org/tast-tests/cros/common/perf 1088 1089 ## Data files 1090 1091 Tests can register ancillary data files that will be copied to the DUT and made 1092 available while the test is running; consider a JavaScript file that Chrome 1093 loads or a short binary audio file that is played in a loop, for example. 1094 1095 ### Internal data files 1096 1097 Small non-binary data files should be directly checked into a `data/` 1098 subdirectory under the test package as _internal data files_. Prefix their names 1099 by the test file's name (e.g. `data/user_login_some_data.txt` for a test file 1100 named `user_login.go`) to make ownership obvious. 1101 1102 Per the [Chromium guidelines for third-party code], place 1103 (appropriately-licensed) data that wasn't created by Chromium developers within 1104 a `third_party` subdirectory under the `data` directory. 1105 1106 [Chromium guidelines for third-party code]: https://chromium.googlesource.com/chromium/src.git/+/HEAD/docs/adding_to_third_party.md 1107 1108 ### External data files 1109 1110 Larger data files like audio, video, or graphics files should be stored in 1111 Google Cloud Storage and registered as _external data files_ to avoid 1112 permanently bloating the test repository. External data files are not installed 1113 to test images but are downloaded at run time by `local_test_runner` on DUT. 1114 1115 To add external data files, put _external link files_ named 1116 `<original-name>.external` in `data/` subdirectory whose content is JSON in the 1117 [external link format]. 1118 1119 For example, a data file belonging to a test named `ui.UserLogin` in the default 1120 `cros` bundle might be declared in `user_login_some_image.jpg.external` with the 1121 following content: 1122 1123 ``` 1124 { 1125 "url": "gs://chromiumos-test-assets-public/tast/cros/ui/user_login_some_image_20181210.jpg", 1126 "size": 12345, 1127 "sha256sum": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" 1128 } 1129 ``` 1130 1131 > Old versions of external data files should be retained indefinitely in Google 1132 > Cloud Storage so as to not break tests on older system images. Include the 1133 > date as a suffix in the filename to make it easy to add a new version when 1134 > needed, e.g. `user_login_data_20180812.bin`. 1135 1136 If data files are produced as [build artifacts of ChromeOS], they can be also 1137 used as external data files. However, build artifacts are available only for 1138 ChromeOS images built by official builders; for developer builds, tests 1139 requiring build artifacts will fail. 1140 1141 An example external link file to reference a build artifact is below: 1142 1143 ``` 1144 { 1145 "type": "artifact", 1146 "name": "license_credits.html" 1147 } 1148 ``` 1149 1150 To upload a file to Google Cloud Storage you can use the [`gsutil cp`] command. 1151 1152 To list all uploaded versions of the file, use the `gsutil ls -a` command. 1153 1154 External files are cached in two locations: /usr/local/share/tast/data_pushed on 1155 the DUT and /tmp/tast/devserver on the host machine. To ensure the reproducibility 1156 of tests and prevent stale cache data from being served, cloud storage files should 1157 never be overwritten once they have been used in a CQ run or dry-run. If 1158 overwriting a cloud storage file, remember to manually clear the cache folders 1159 before running Tast tests to prevent stale files from being served. 1160 1161 [external link format]: https://chromium.googlesource.com/chromiumos/platform/tast/+/main/src/go.chromium.org/tast/coreinternal/extdata/extdata.go 1162 [example.DataFiles]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/bundles/cros/example/data_files.go 1163 [build artifacts of ChromeOS]: https://goto.google.com/cros-build-google-storage 1164 [`gsutil cp`]: https://cloud.google.com/storage/docs/gsutil/commands/cp 1165 1166 ### Internal vs. external 1167 1168 As internal data files are much easier to view and modify than external data 1169 files, it's usually better to check in textual data. Only store binaries as 1170 external data. 1171 1172 ### Executables 1173 1174 If your test depends on outside executables, use Portage to build and package 1175 those executables separately and include them in test ChromeOS system images. 1176 Tast [intentionally](design_principles.md) does not support compiling or 1177 deploying other packages that tests depend on. 1178 1179 ### Sharing data files between test packages 1180 1181 If a data file is needed by a support package that's used by tests in multiple 1182 packages, it should be stored in a `data` subdirectory within the support 1183 package and symlinked into each test package's `data` subdirectory. See the 1184 [media_session_test.html] file used by the [mediasession package] and shared by 1185 the [ui.PlayPauseChrome] and [arc.MediaSessionGain] tests, for example. 1186 1187 [media_session_test.html]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/chrome/mediasession/data/media_session_test.html 1188 [mediasession package]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast-tests.git/src/go.chromium.org/tast-tests/cros/local/chrome/mediasession 1189 [ui.PlayPauseChrome]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/bundles/cros/ui/play_pause_chrome.go 1190 [arc.MediaSessionGain]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/main/src/go.chromium.org/tast-tests/cros/local/bundles/cros/arc/media_session_gain.go 1191 1192 ### Using data files in tests 1193 1194 To register data files (regardless of whether they're checked into the test 1195 repository or stored externally), in your test's `testing.AddTest` call, set the 1196 `testing.Test` struct's `Data` field to contain a slice of data file names 1197 (omitting the `data/` subdirectory, and the `.external` suffix for external data 1198 files): 1199 1200 ```go 1201 testing.AddTest(&testing.Test{ 1202 ... 1203 Data: []string{"user_login_data.bin"}, 1204 ... 1205 }) 1206 ``` 1207 1208 Later, within the test function, pass the same filename to [testing.State]'s 1209 `DataPath` function to receive the path to the data file on the DUT: 1210 1211 ```go 1212 b, err := ioutil.ReadFile(s.DataPath("user_login_data.bin")) 1213 ``` 1214 1215 See the [example.DataFiles] test for a complete example of using both local and 1216 external data files. 1217 1218 [example.DataFiles]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/bundles/cros/example/data_files.go 1219 1220 ## Runtime variables 1221 1222 Occasionally tests need to access dynamic or secret data (i.e. *out-of-band* 1223 data), and that's when runtime variables become useful. 1224 1225 ### Setting values 1226 1227 To set runtime variables, add (possibly repeated) `-var=name=value` flags to 1228 `tast run`. 1229 1230 ### Accessing values 1231 1232 Tast users can access runtime variables in two different ways. One way 1233 is to declare global runtime variables which can be used by all testing 1234 entities: services, fixtures, library functions and tests. Other entities 1235 can use the variables by importing the package that defines the variables. 1236 The other way is to declare test runtime variables which can be used by 1237 fixture and tests. 1238 1239 #### Global runtime variables (recommended for new code) 1240 To declare a global runtime variable, use testing.RegisterVarString in an 1241 entity. It should be a top-level variable declaration which should include 1242 the name of the variable, default value and description. A duplicate of the 1243 variable name in the same bundle will result in an error during registration 1244 when a bundle starts. Other files can access the variable by importing the 1245 package that contains the declaration of the variable. 1246 1247 Example: 1248 1249 ```go 1250 package example 1251 1252 ... 1253 1254 var exampleStrVar = testing.RegisterVarString( 1255 "example.AccessVars.globalString", 1256 "Default value", 1257 "An example variable of string type", 1258 ) 1259 1260 ... 1261 1262 func AccessVars(ctx context.Context, s *testing.State) { 1263 strVal := exampleStrVar.Value() 1264 } 1265 ``` 1266 1267 All variables should have the prefix “<package_name>.” to avoid name collision. 1268 If one violates this convention, runtime error will happen. 1269 1270 #### Test runtime variables 1271 To declare test runtime variables, set the `testing.Test` struct's `Vars` 1272 or `VarDeps` field inside your tests' `testing.AddTest` call. 1273 `Vars` specifies optional runtime variables, and `VarDeps` specifies required 1274 runtime variables to run the test. `VarDeps` should be the default choice, 1275 and `Vars` should be used only when there's a fallback in case the variables 1276 are missing. 1277 1278 `Vars` and `VarDeps` should be an array literal of string literals or constants. 1279 The test can later access the values by calling `s.Var` or `s.RequiredVar` 1280 methods. 1281 1282 For variables only used in a single test, prefix them with the test name 1283 (e.g. `arc.Boot.foo` for a variable used only in `arc.Boot`). 1284 For variables used from multiple tests, prefix them with the category name which mainly uses the variable 1285 (e.g. `arc.foo`). Such variables can be used from any tests, not only ones in the same category. 1286 1287 Variables without a dot in its name are called global variables. They are set by the framework, and individual tests don't have control over them. 1288 Other variables should follow these rules: 1289 1290 * Variable name should have the form of `foo.Bar.something` or `foo.something`, where `something` matches `[A-Za-z][A-Za-z0-9_]*` 1291 * Only the test `foo.Bar` can access `foo.Bar.something` 1292 * Any tests can access `foo.something` 1293 1294 If one violates this convention, runtime error will happen. 1295 1296 ### Skipping tests if a variable is not set. 1297 1298 If you wish to skip tests if a variable is not set, your should use 1299 `VarDeps` field inside those tests' `testing.AddTest` call regardless which 1300 methods you choose to access the variable. 1301 1302 When runtime variables in `VarDeps` are missing, by default the test fails 1303 before it runs. `-maybemissingvars=<regex>` can be used to specify possibly 1304 missing runtime variables and if every missing runtime variable in `VarDeps` 1305 matches with the regex, the test is skipped. 1306 1307 ### Secret variables 1308 1309 This feature is for internal developers, who has access to `tast-tests-private` package. 1310 1311 #### What is it 1312 1313 This feature allows you to store secret key/value pairs in a private repository, and use them from public tests. 1314 1315 For example, tests no longer have to be private just because they access secret GAIA credentials. 1316 1317 #### How to do it 1318 1319 Let `foo.Bar` be the test which should access secret username and password. 1320 1321 If the variables are only used from the test, create the file `tast-tests-private/vars/foo.Bar.yaml` with the contents: 1322 1323 ```Yaml 1324 foo.Bar.user: someone@something.com 1325 foo.Bar.password: whatever 1326 ``` 1327 1328 If the values are shared among tests, create `foo.yaml` file instead. 1329 1330 ```Yaml 1331 foo.user: someone@something.com 1332 foo.password: whatever 1333 ``` 1334 1335 Then the test can access the variables just like normal variables assigned to the `tast` command with `-var`. 1336 Secret variables cannot be used to define global variables. 1337 1338 **Don't log secrets in tests** to avoid possible data leakage. 1339 1340 ```go 1341 func init() { 1342 testing.AddTest(&testing.Test{ 1343 Func: Bar, 1344 ... 1345 VarDeps: []string{"foo.Bar.user", "foo.Bar.password"}, 1346 // or foo.user, foo.password 1347 }) 1348 } 1349 1350 func Bar(ctx context.Context, s *testing.State) { 1351 user := s.RequiredVar("foo.Bar.user") 1352 ... 1353 } 1354 ``` 1355 1356 See [example.SecretVars](https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/bundles/cros/example/secret_vars.go) for working example. 1357 1358 #### Naming convention 1359 1360 * The file defining `foo.Bar.something` should be `foo.Bar.yaml` 1361 * The file defining `foo.something` should be `foo.yaml` 1362 1363 If one violates this convention, Tast linter will complain. Please honor the linter errors. 1364 1365 ## Parameterized tests 1366 1367 When multiple scenarios with very slight differences should be tested, the most 1368 common pattern is to write [table-driven tests]. However testing everything in 1369 a single test is sometimes undesirable for several reasons: 1370 1371 * Tests should have different attributes. For example, we might want 1372 to set some of them [critical] to avoid regressions, while keeping others 1373 [informational] due to test flakiness. 1374 * Tests should declare different dependencies. For example, VP8 playback 1375 test should declare the "hardware-accelerated VP8 decoding" hardware 1376 dependency, while other playback tests should declare their respective 1377 dependencies. 1378 * Test results should be reported separately. For example, video playback 1379 performance tests may want to report performance metrics separately for 1380 different video formats (VP8/VP9/H.264/...). 1381 1382 In such cases, *parameterized tests* can be used to define multiple similar 1383 tests with different test properties. 1384 1385 To parameterize a test, specify a slice of [`testing.Param`] in the `Params` 1386 field on test registration. `Params` should be a literal since test 1387 registration should be [declarative]. If `Params` is non-empty, 1388 `testing.AddTest` expands the the test into one or more tests corresponding to 1389 each item in `Params` by merging `testing.Test` and [`testing.Param`] with the 1390 rules described below. 1391 1392 Here is an example of a parameterized test registration: 1393 1394 ```go 1395 func init() { 1396 testing.AddTest(&testing.Test{ 1397 Func: Playback, 1398 Desc: "Tests media playback", 1399 Contacts: []string{"someone@chromium.org"}, 1400 Attr: []string{"group:mainline"}, 1401 Params: []testing.Param{{ 1402 Name: "vp8", 1403 Val: "sample.vp8", 1404 ExtraData: []string{"sample.vp8"}, 1405 ExtraAttr: []string{"informational"}, 1406 }, { 1407 Name: "vp9", 1408 Val: "sample.vp9", 1409 ExtraData: []string{"sample.vp9"}, 1410 // No ExtraAttr; this test is critical. 1411 }, { 1412 Name: "h264", 1413 Val: "sample.h264", 1414 ExtraSoftwareDeps: []string{"chrome_internal"}, // H.264 codec is unavailable on ChromiumOS 1415 ExtraData: []string{"sample.h264"}, 1416 ExtraAttr: []string{"informational"}, 1417 }}, 1418 }) 1419 } 1420 1421 func Playback(ctx context.Context, s *testing.State) { 1422 filename := s.Param().(string) 1423 if err := playback(ctx, filename); err != nil { 1424 s.Fatal("Failed to playback: ", err) 1425 } 1426 } 1427 ``` 1428 1429 `Name` in [`testing.Param`] is appended to the base test name with a leading dot 1430 to compute the test name, just like `category.TestName.parameter_name`. 1431 If `Name` is empty, the base test name is used as-is. `Name` should be in 1432 `lower_snake_case` style. `Name` must be unique within a parameterized test. 1433 1434 `Val` in [`testing.Param`] is an arbitrary value that can be accessed in the 1435 test body via the `testing.State.Param` method. Since it returns the value as 1436 `interface{}`, it should be type-asserted to the original type immediately. 1437 All `Val` in a parameterized test must have the same type. 1438 1439 `Pre` and `Timeout` in [`testing.Param`] are equivalent to those in 1440 `testing.Test`. They can be set only if the corresponding fields in the base 1441 test are not set. 1442 1443 `Extra*` in [`testing.Param`] (such as `ExtraAttr`) contains items added to 1444 their corresponding base test properties (such as `Attr`) to obtain the test 1445 properties. 1446 1447 Because test registration should be declarative as written in 1448 [test registration], `Params` should be an array literal containing `Param` 1449 struct literals. In each `Param` struct, `Name` should be a string literal with 1450 `snake_case` name if present. `ExtraAttr`, `ExtraData`, `ExtraSoftwareDeps` and 1451 `Pre` should follow the rule of the corresponding `Attr`, `Data` ,`SoftwareDeps` 1452 and `Pre` in [test registration]. 1453 1454 See documentation of [`testing.Param`] for the full list of customizable 1455 properties. 1456 1457 [table-driven tests]: #Table_driven-tests 1458 [critical]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/test_attributes.md 1459 [informational]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/test_attributes.md 1460 [`testing.Param`]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#Param 1461 [declarative]: #Test-registration 1462 [test registration]: #Test-registration 1463 1464 ## Remote procedure calls with gRPC 1465 1466 In many cases, remote tests have to run some Go functions on the DUT, possibly 1467 calling some support libraries for local tests (e.g. the [chrome] package). 1468 For this purpose, Tast supports defining, implementing, and calling into [gRPC] 1469 services. 1470 1471 For the general usage of gRPC-Go, see also the [official tutorial]. 1472 1473 [chrome]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/local/chrome/ 1474 [gRPC]: https://grpc.io 1475 [official tutorial]: https://grpc.io/docs/tutorials/basic/go/ 1476 1477 ### Defining gRPC services 1478 1479 gRPC services are defined as protocol buffer files stored under 1480 `tast-tests/src/go.chromium.org/tast-tests/cros/services`. The directory is organized in the 1481 similar way as test bundles at 1482 `tast-tests/src/go.chromium.org/tast-tests/cros/{local,remote}/bundles`. Below is an example of 1483 an imaginary gRPC service `arc.BootService`: 1484 1485 ``` 1486 tast-tests/src/go.chromium.org/tast-tests/cros/services/ 1487 cros/ ... test bundle name where this service is included 1488 arc/ ... service category name 1489 gen.go ... Go file containing go generate directives 1490 boot_service.proto ... gRPC service definition 1491 boot_service.pb.go ... generated gRPC bindings 1492 ``` 1493 1494 gRPC services are defined in `.proto` files. `boot_service.proto` would look like: 1495 1496 ```proto 1497 syntax = "proto3"; 1498 1499 package tast.cros.arc; 1500 1501 import "google/protobuf/empty.proto"; 1502 1503 option go_package = "go.chromium.org/tast-tests/cros/services/cros/arc"; 1504 1505 // BootService allows remote tests to boot ARC on the DUT. 1506 service BootService { 1507 // CheckBoot logs into a new Chrome session, starts ARC and waits for its 1508 // successful boot. 1509 rpc CheckBoot (CheckBootRequest) returns (google.protobuf.Empty) {} 1510 } 1511 1512 message CheckBootRequest { 1513 enum AndroidImpl { 1514 DEFAULT = 0; 1515 CONTAINER = 1; 1516 VM = 2; 1517 } 1518 // impl specifies which ARC implementation to use. 1519 AndroidImpl impl = 1; 1520 } 1521 ``` 1522 1523 Protocol buffers files should follow the 1524 [official protocol buffers style guide], as well as several Tast-specific 1525 guidelines: 1526 1527 * **File names**: Name `.proto` files in the same way as [test `.go` files]. 1528 For example, a service named `TPMStressService` should be defined in 1529 `tpm_stress_service.proto`. 1530 * **Package names**: Protocol buffer package name specified in the `package` 1531 directive should be `tast.<bundle-name>.<category-name>`. Go package name 1532 specified in the `option go_package` directive should be 1533 `go.chromium.org/tast-tests/cros/services/<bundle-name>/<category-name>`. 1534 * **Service names**: Name services with `Service` suffix. 1535 * **Message names**: Method request/response messages should be named 1536 `FooBarRequest`/`FooBarResponse`. 1537 * **Comments**: Write comments in the [godoc style] since these protocol 1538 buffers are used only by Tast tests in Go. 1539 1540 `gen.go` is a small file containing a [`go generate` directive] to regenerate 1541 `.pb.go` files, looking like the following: 1542 1543 ```go 1544 // Copyright 2019 The ChromiumOS Authors 1545 // Use of this source code is governed by a BSD-style license that can be 1546 // found in the LICENSE file. 1547 1548 //go:generate protoc -I . --go_out=plugins=grpc:../../../../.. boot_service.proto 1549 1550 package arc 1551 1552 // Run the following command in CrOS chroot to regenerate protocol buffer bindings: 1553 // 1554 // ~/trunk/src/platform/tast/tools/go.sh generate go.chromium.org/tast-tests/cros/services/cros/arc 1555 ``` 1556 1557 To regenerate `.pb.go` files, run the command mentioned in the file in ChromeOS 1558 chroot (remember to replace the last argument of the command with the path to 1559 the directory containing the protocol buffer files). This has to be done 1560 manually whenever `.proto` files are edited. Updated `.pb.go` files should be 1561 included and submitted in CLs adding/modifying/deleting `.proto` files. 1562 1563 [official protocol buffers style guide]: https://developers.google.com/protocol-buffers/docs/style 1564 [test `.go` files]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#code-location 1565 [test functions]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#code-location 1566 [godoc style]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#documentation 1567 [`go generate` directive]: https://golang.org/pkg/cmd/go/internal/generate/ 1568 1569 ### Implementing gRPC services 1570 1571 gRPC service implementations should be placed at the same location as local 1572 tests, i.e. `tast-tests/src/go.chromium.org/tast-tests/cros/local/bundles`. For example, an 1573 imaginary `arc.BootService` would be implemented in 1574 `tast-tests/src/go.chromium.org/tast-tests/cros/local/bundles/arc/boot_service.go`. 1575 1576 gRPC services can be registered with [`testing.AddService`] by passing 1577 [`testing.Service`] containing service descriptions. The most important field is 1578 `Register`, specifying a function to register a gRPC service to [`grpc.Server`]. 1579 Below is an implementation of the `arc.BootService`: 1580 1581 ```go 1582 // tast-tests/src/go.chromium.org/tast-tests/cros/local/bundles/arc/boot_service.go 1583 package arc 1584 1585 func init() { 1586 testing.AddService(&testing.Service{ 1587 Register: func(srv *grpc.Server, s *testing.ServiceState) { 1588 pb.RegisterBootServiceServer(srv, &BootService{s}) 1589 }, 1590 }) 1591 } 1592 1593 // BootService implements tast.cros.arc.BootService. 1594 type BootService struct { 1595 s *testing.ServiceState 1596 } 1597 1598 func (*BootService) CheckBoot(ctx context.Context, req *pb.CheckBootRequest) (*empty.Empty, error) { 1599 ... 1600 } 1601 ``` 1602 1603 For consistency, please follow these guidelines on implementing gRPC services: 1604 1605 * **File names**: Name gRPC service implementation `.go` files in the exactly 1606 same way as [test `.go` files]. For example, a service named 1607 `TPMStressService` should be implemented in `tpm_stress_service.go`. 1608 This means that gRPC implementation files always have `_service.go` suffix. 1609 * **Implementation type**: A type implementing gRPC service should have 1610 exactly the same name as the service name. The type should be the only 1611 exported symbol in the `_service.go` file. Exactly one gRPC service 1612 implementation should be registered in a single file. 1613 * **Inter-file references**: Similarly to test files, `_service.go` file 1614 should not refer symbols in different files in the same directory. 1615 Consequently, a gRPC service has to be implemented in a single file. 1616 If the file gets too long, please consider introducing a subpackage just 1617 like tests. 1618 1619 `context.Context` passed to a gRPC method can be used to call some of 1620 `testing.Context*` functions: 1621 1622 * `testing.ContextLog`, `testing.ContextLogf`, `testing.ContextLogger` work 1623 fine. Emitted logs are recorded as if they were emitted by a remote test 1624 that called into a gRPC method. 1625 * `testing.ContextOutDir` returns a path to a temporary directory. Files saved 1626 in the directory during a gRPC method call are copied back to the host 1627 machine's test output directory, as if they were saved by a remote test that 1628 called into a gRPC method. Note that this function does not allow gRPC 1629 methods to read output files from a remote test nor previous gRPC method 1630 calls. Files are overwritten in the case of name conflicts. 1631 * `testing.ContextSoftwareDeps` does not work. This function is planned to be 1632 deprecated ([crbug.com/1135996]). 1633 1634 `Register` function receives [`testing.ServiceState`] which you can keep in 1635 a field of the struct type implementing the gRPC service. It allows the service 1636 to access service-specific information, such as runtime variables and data files 1637 (not implemented yet: [crbug.com/1027381]). 1638 1639 [`testing.AddService`]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#AddService 1640 [`testing.Service`]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#Service 1641 [`grpc.Server`]: https://godoc.org/google.golang.org/grpc#Server 1642 [test `.go` files]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#code-location 1643 [`testing.ServiceState`]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/testing#ServiceState 1644 [crbug.com/1135996]: https://crbug.com/1135996 1645 [crbug.com/1027381]: https://crbug.com/1027381 1646 1647 ### Calling gRPC services 1648 1649 A remote test should declare in its metadata which gRPC services it will call 1650 into. Undeclared gRPC method calls shall be rejected internally. For example, 1651 an imaginary remote test `arc.RemoteBoot` would be declared as: 1652 1653 ```go 1654 func init() { 1655 testing.AddTest(&testing.Test{ 1656 Func: RemoteTest, 1657 SoftwareDeps: []string{"chrome", "android_p"}, 1658 ServiceDeps: []string{"tast.cros.arc.BootService"}, 1659 }) 1660 } 1661 ``` 1662 1663 Call [`rpc.Dial`] in remote tests to establish a connection to the gRPC server. 1664 On success, it returns a struct containing [`grpc.ClientConn`] with which 1665 you can construct gRPC stubs. 1666 1667 ```go 1668 cl, err := rpc.Dial(ctx, s.DUT(), s.RPCHint()) 1669 if err != nil { 1670 s.Fatal("Failed to connect to the RPC service on the DUT: ", err) 1671 } 1672 defer cl.Close(ctx) 1673 1674 bc := pb.NewBootServiceClient(cl.Conn) 1675 1676 req := pb.CheckBootRequest{Impl: pb.CheckBootRequest_VM} 1677 var res empty.Empty 1678 if err := bc.CheckBoot(ctx, &req, &res); err != nil { 1679 ... 1680 } 1681 ``` 1682 1683 [`rpc.Dial`]: https://godoc.org/chromium.googlesource.com/chromiumos/platform/tast.git/src/go.chromium.org/tast/core/rpc#Dial 1684 [`grpc.ClientConn`]: https://godoc.org/google.golang.org/grpc#ClientConn 1685 1686 ### Panics in gRPC services 1687 1688 If a gRPC call fails with "error reading from server: EOF", it may indicate the 1689 service panicked. The panic message is not currently logged or propagated to 1690 the caller. This issue is tracked in [b/187794185]. 1691 1692 [b/187794185]: https://buganizer.corp.google.com/issues/187794185 1693 1694 ### Notes on designing gRPC services 1695 1696 As with anything involving protos, when updating them, please make sure you 1697 maintain compatibility with older versions of the proto, as it is possible that 1698 you can have a new gRPC server with an old gRPC client, and vice versa. To do 1699 this, just ensure that you follow the principles outlined in 1700 [this](https://protobuf.dev/programming-guides/proto3/#updating) document. 1701 1702 Tast's gRPC services don't necessarily have to provide general-purpose APIs. 1703 It is perfectly fine to define gRPC services specific to a particular test case. 1704 For example, one may want to write a local test which exercises some features, 1705 and a remote test that performs the same testing *after rebooting the DUT*. 1706 In this case, they can put the whole local test content to a subpackage, and 1707 introduce a local test and a gRPC service both of which call into the 1708 subpackage. 1709 1710 [crbug.com/1027368]: https://crbug.com/1027368 1711 1712 ## Companion DUTs (Multi-DUTs) Support 1713 1714 Most tests are written to test against one DUT, but multiple DUTs are needed 1715 for tests that are testing the interaction between two or more DUTs. Tast uses 1716 companion DUTs feature to support those tests. Please notice that Tast 1717 currently only support companion DUTs for remote tests, and they are not 1718 accessible by local tests. 1719 1720 ### Tast Companion DUT CLI 1721 1722 Users can use the run flag -companiondut to specify a companion DUT to be used 1723 in tests. The flag is repeatable so users can specify more than one companion 1724 DUT. The value of the flag specifies a role and a dut address in this format, 1725 <Role>:<Host>. 1726 1727 #### Examples 1728 1729 Here is an example of how a companion DUT is specified on the Tast command 1730 line. In this example, primary DUT has the address 127.0.0.1:2222 and the 1731 companion DUT has the address 127.0.0.1:2223. 1732 1733 ``` 1734 % tast run --companiondut=cd1:127.0.0.1:2223 127.0.0.1:2222 <tests> 1735 ``` 1736 1737 ### Accessing Companion DUTs In A Remote Test 1738 1739 Users can use the function testing.State.CompanionDUT to get the pointer to 1740 dut.DUT of a companion DUT. The function take the role name of the companion 1741 DUT as the input parameter. 1742 1743 #### Examples 1744 1745 ``` 1746 // CompanionDUTs ensures DUT and companion DUTs are accessible in test. 1747 // Tast command line: 1748 // tast run -build=true -companiondut=cd1:dut1 dut0 meta.CompanionDUTs 1749 func CompanionDUTs(ctx context.Context, s *testing.State) { 1750 cl, err := rpc.Dial(ctx, s.DUT(), s.RPCHint(), "cros") 1751 if err != nil { 1752 s.Fatal("Failed to connect to the RPC service on the DUT: ", err) 1753 } 1754 defer cl.Close(ctx) 1755 1756 companionDUT := s.CompanionDUT("cd1") 1757 if companionDUT == nil { 1758 s.Fatal("Failed to get companion DUT cd1") 1759 } 1760 companionCl, err := rpc.Dial(ctx, companionDUT, s.RPCHint(), "cros") 1761 if err != nil { 1762 s.Fatal("Failed to connect to the RPC service on the companion DUT: ", err) 1763 } 1764 defer companionCl.Close(ctx) 1765 } 1766 ``` 1767 1768 ### Software/Hardware Dependencies Of Companion DUTs 1769 1770 Users can use HardwareDepsForAll and SoftwareDepsAll of testing.Test to 1771 specify the hardware/software dependencies on all DUTs used in a test. 1772 Furthermore, users can use HardwareDepsForAll and SoftwareDepsAll of 1773 testing.Test to specify the ExtraHardwareDepsForAll and 1774 ExtraSoftwareDepsForAll of testing.Param to specify the hardware/software 1775 dependencies on all DUTs of a parameterized test. 1776 1777 ### Example of Specifying Companion Dependencies In A Test 1778 1779 ``` 1780 type Test struct { 1781 … 1782 // SoftwareDepsForAll lists software features of all DUTs that 1783 // are required to run the test. 1784 // It is a map of companion roles and software features. 1785 // The role for primary DUT should be "". 1786 // The primary DUT software dependency will be the union of 1787 // SoftwareDeps and SoftwareDepsForAll[""]. 1788 // If any dependencies are not satisfied, the test will be skipped. 1789 SoftwareDepsForAll map[string][]string 1790 1791 // HardwareDepsForAll describes hardware features and setup of all 1792 // DUTs that are required to run the test. 1793 // It is a map of companion roles and hardware features. 1794 // The role for primary DUT should be "". 1795 // The primary DUT hardware dependency will be the union of 1796 // HardwareDeps and HardwareDepsForAll[""]. 1797 // If any dependencies are not satisfied, the test will be skipped. 1798 HardwareDepsForAll map[string]hwdep.Deps 1799 … 1800 } 1801 ``` 1802 1803 #### Specifying Hardware/Software Dependencies Of Companion DUTs In Parameterized Test 1804 1805 ``` 1806 type Param struct { 1807 … 1808 1809 // ExtraSoftwareDepsForAll lists software features of all DUTs 1810 // that are required to run the test case for this param, 1811 // in addition to SoftwareDepsForAll in the enclosing Test. 1812 // The primary DUT software dependency will be the union of 1813 // SoftwareDeps, SoftwareDepsForAll[""], ExtraSoftwareDeps and 1814 // ExtraSoftwareDepsForAll[""]. 1815 // It is a map of companion roles and software features. 1816 ExtraSoftwareDepsForAll map[string][]string 1817 1818 // ExtraHardwareDepsForAll describes hardware features and setup 1819 // companion DUTs that are required to run the test case for this param, 1820 // in addition to HardwareDepsForAll in the enclosing Test. 1821 // It is a map of companion roles and hardware features. 1822 // The role for primary DUT should be "" 1823 // The primary DUT hardware dependency will be the union of 1824 // HardwareDeps, HardwareDepsForAll[""], ExtraHardwareDeps and 1825 // ExtraHardwareDep and ExtraHardwareDepsForAll[""]. 1826 ExtraHardwareDepsForAll map[string]hwdep.Deps 1827 } 1828 ``` 1829 1830 ### Example of Specifying Companion Dependencies In A Test 1831 1832 ``` 1833 func init() { 1834 testing.AddTest(&testing.Test{ 1835 Func: CompanionDepsUsage, 1836 … 1837 SoftwareDeps: []string{"chrome"}, 1838 SoftwareDepsForAll: map[string][]string{ 1839 // Additional primary DUT dependency. 1840 // As a result, primary will have dependency on "lacros" 1841 "": []string{"lacros"}, 1842 // Companion DUT 1 dependency. 1843 "cd1": []string{"chrome"}, 1844 }, 1845 HardwareDepsForAll: map[string]hwdep.Deps { 1846 // Companion DUT 1 dependency. 1847 "cd1": hwdep.D(hwdep.InternalDisplay()), 1848 }, 1849 }) 1850 } 1851 ``` 1852 1853 ### Example of Specifying Companion Dependencies In Parameterized Tests 1854 1855 ``` 1856 func init() { 1857 testing.AddTest(&testing.Test{ 1858 Func: CompanionDepsParamUsage, 1859 … 1860 SoftwareDeps: []string{"chrome"}, 1861 … 1862 Params: []testing.Param{ 1863 { 1864 … 1865 Name: "P1", 1866 ExtraSoftwareDepsAll: map[string][]string{ 1867 "cd1": []string{ "android_p"}, 1868 }, 1869 }, 1870 ExtraHardwareDepsForAll: map[string]hwdep.Deps { 1871 "cd1": hwdep.D(hwdep.InternalDisplay()), 1872 }, 1873 { 1874 … 1875 Name: "p2", 1876 ExtraSoftwareDepsForAll: map[string][]string{ 1877 "cd1": []string{ "android_vm"}, 1878 }, 1879 }, 1880 ExtraHardwareDepsForAll: map[string]hwdep.Deps { 1881 "cd1": hwdep.D(hwdep.InternalDisplay()), 1882 }, 1883 }, 1884 }, 1885 }) 1886 } 1887 ``` 1888 1889 1890 1891 1892 1893 ## Utilities 1894 1895 Tast contains many utilities for common operations. Some of them are briefly 1896 described below; see the package links for additional documentation and 1897 examples. 1898 1899 ### lsbrelease 1900 1901 The [`lsbrelease`] package provides access to the fields in `/etc/lsb-release`. 1902 Usually Tast tests are not supposed to that information to change their 1903 behavior, so `lsbrelease` contains a list of packages that are allowed to use 1904 it. Attempting to use `lsbrelease` in a package that is not in the allow list 1905 will cause a panic. 1906 1907 [`lsbrelease`]: https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/src/go.chromium.org/tast/core/lsbrelease/lsbrelease.go 1908 1909 ### testexec 1910 1911 The [`testexec`] package provides a convenient interface to run processes on 1912 the DUT. It should be used instead of the standard `os/exec` package. 1913 1914 [`testexec`]: https://chromium.googlesource.com/chromiumos/platform/tast-tests/+/HEAD/src/go.chromium.org/tast-tests/cros/common/testexec/testexec.go 1915 1916 ## Use of third party libraries 1917 1918 1. Add an ebuild to package the code as a Portage package in 1919 [third_party/chromiumos-overlay], and they will effectively review third party 1920 licensing ([example]( 1921 https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/1146070)). 1922 2. Add the dependency to tast-build-deps ([example]( 1923 https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/1737396)). 1924 Please send this CL to ebuild-reviews@ and tast-owners@. 1925 1926 Tast doesn't have any official process to review third party libraries. 1927 Just take usual precautions on introducing libraries, such as 1928 1929 - Is the library popular? 1930 - Is the author reliable? Do they respond to issues, solve bugs and accept pull requests? 1931 - Is the API well-documented? 1932 - Does the library cover all your requirements? 1933 1934 If you are in doubt, please feel free to send your proposal to 1935 tast-reviewers@google.com. 1936 1937 When testing locally, remember to `cros_workon --host start tast-build-deps` 1938 and `./update_chroot` to build Tast with your new dependencies. 1939 1940 ### Dependencies with 9999 ebuilds 1941 1942 Dependencies are picked up from the host, not any board. So if you have updated 1943 a dependency with a 9999 ebuild, then you'll need to workon start it. For 1944 example if you've added something to system_api: 1945 ``` 1946 (cr) ~/chromiumos/src/scripts $ cros_workon --host start chromeos-base/system_api tast-build-deps 1947 (cr) ~/chromiumos/src/scripts $ ./update_chroot 1948 ``` 1949 1950 And cleanup after you're done with local changes: 1951 ``` 1952 (cr) ~/chromiumos/src/scripts $ cros_workon --host stop chromeos-base/system_api tast-build-deps 1953 ``` 1954 1955 [third_party/chromiumos-overlay]: https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/refs/heads/main/dev-go/ 1956 1957 ### Test promotion process for mainline tests 1958 1959 The group:mainline non-informational tests are run in ChromeOS lab for CQs. 1960 1961 Please see the Google internal link go/tast-add-test (Googler only) for the 1962 promotion process from informational to non-informational. 1963 1964 [go/tast-add-test]: http://goto.google.com/tast-add-test 1965