Adds some markdown files which detail how the kola runner works internally as well as what constitutes a platform in mantle (and some common things that need to be implemented).
8.7 KiB
Kola Runner
Table of Contents
The kola runner is very similar to the standard go test runner with some
modifications to allow tighter control over the run loop and extensibility
for logging (without parsing) & output control.
General Function Overview
kola/harness
kola/harness: RunTests
Responsible for filtering down the test list based on the given criteria,
creating the harness/suite: Suite object, and outputting the final result.
kola/harness: runTest
Creates the test cluster, runs the individual test function, and cleans up the test cluster.
harness/suite
harness/suite: Run
Creates the output directory, the test.tap file and any profile related
files then calls harness/suite: runTests.
harness/suite: runTests
Sets up the harness/harness: H object and calls harness/harness: tRunner
with a closure to call harness/harness: Run for each test.
harness/harness
harness/harness: tRunner
Responsible for the timing, reporting, and execution of a closure.
harness/harness: Run
Handles the setup of child harness/harness: H objects, loggers, and the
running of closures as subtests.
Visual Function Flow
The first 4 steps handle filtering down the test list, creating the clusters, and building the suite. The next 4 set up the reporting structure of the test group and run the child tests. The following 2 get ready to run each individual test. And the final step runs the actual test function registered in the test.
cmd/kola/kola
|
v
kola/harness: RunTests
|
v
harness/suite: Run
|
v
harness/suite: runTests
|
v
harness/harness: tRunner
|
v
harness/harness: Run
|
v
harness/harness: tRunner
|
v
kola/harness: runTest
|
v
harness/harness: Run
|
v
harness/harness: tRunner
|
v
kola/register/register: Run
Detailed Workflow
- The
kolacmd calls intokola/harness: RunTests kola/harness: RunTestscallskola/harness: filterTeststo build a test list filtered down by the given pattern & platform from all tests inkola/register/register: Testsobject.kola/harness: RunTestschecks if any of the tests do not exactly match the given pattern and have either aMinVersionorMaxVersiontag, if so then it will callkola/harness: getClusterSemverto spin up a machine on the given platform to extract the OS semver. The tests will then be filtered down again with the semver. Note that if a pattern matches an individual test thekola/harness: getClusterSemvercheck will be skipped and the test will be run without regard to theMinVersionorMaxVersiontags.kola/harness: RunTestswill then construct aharness/suite: Optionsobject and construct aharness/test: Testobject containing the name of each test and a closure (#1) callingkola/harness: runTest.kola/harness: RunTestsconstructs aharness/suite: Suiteobject viaharness/suite: NewSuiteusing theharness/suite: Optionsandharness/test: Testobjects and proceeds to call theharness/suite: Runfunction on theharness/suite: Suiteobject.harness/suite: Runstarts by creating or cleaning up the output directory by calling theharness/harness: CleanOutputDirfunction. It then creates thetest.tapfile inside of the output directory and prints a string to the file containing1..%dwhere %d is the amount of tests being run.harness/suite: Runthen checks if the following options were selected and if so creates the corresponding files in the output path:
| Option | Filename |
|---|---|
| MemProfile | mem.prof |
| BlockProfile | block.prof |
| CpuProfile | cpu.prof |
| ExecutionTrace | exec.trace |
harness/suite: Runthen callsharness/suite: runTestspassingos.Stdoutand thetap io.Writerobject.harness/suite: runTestsstarts by setting therunningvariable on theharness/suite: Suiteobject, which is the count of running tests, to 1 and creating theharness/harness: Hobject.harness/suite: runTeststhen callsharness/harness: tRunnerpassing theharness/harness: Hobject and a closure (#2) which loops each test in theharness/suite: Suiteobject callingharness/harness: Runon each, passing the name of the test, theharness/test: Testobject, and a boolean pointer set to false, followed by a goroutine call to receive from the signal channel on theharness/harness: Hobject.harness/harness: tRunnerstarts by creating acontext.WithCancelobject, the result ofharness/harness: parentContextis passed in which will either be the context object of theharness/harness: Hobjects parent orcontext.Background()if the object doesn't have a parent.harness/harness: tRunnerthen defers a closure which will detect the status of the test run, calculate the ending time, run any subtests, callharness/harness: report(which will flush the test result to the parent via theharness/harness: flushToParentfunction), and sendtrueon theharness/harness: Hsignalchannel.harness/harness: tRunnerwill then calculate the start time and call the closure it received as an argument with theharness/harness: Hvariable as a parameter, this will be the closure that was created inharness/suite: runTestswhich will callharness/harness: Runfor each test.harness/harness: Runruns each function as a subtest of theharness/harness: Hobject it is passed with the name passed. It starts by marking thehasSubvariable on theharness/harness: Hobject to true and checking that the test name it received is a valid test via theharness/match: fullNamefunction.harness/harness: Runwill then create a newharness/harness: Hobject which has the object it received as the parent and alogobject.harness/harness: Runthen does a goroutine call onharness/harness: tRunnerpassing in the newharness/harness: Hobject, the closure function it was passed, which is the call tokola/harness: runTest, and the boolean pointer it was passed.harness/harness: tRunnerwill then run through and callkola/harness: runTest.kola/harness: runTestis the harness responsible for running a single test grouping (test groupings tests. It will create the cluster that will be used by the tests, validate that the machines spun up properly, and then callkola/register/register: Runon thekola/register/register: Testobject, which is a function pointer which accepts akola/cluster/cluster: TestClusterobject and is defined inside of the individual test files.
Common Closures
kola/harness: RunTests
Accepts a harness/harness: H object and calls kola/harness: runTest
func(h *harness.H) {
runTest(h, []*register.Test{test}, pltfrm, false)
}
harness/suite: runTests
Accepts a harness/harness: H object. Loops each test in the
harness/suite: Suite object calling harness/harness: Run. This is being
pass as an argument to harness/harness: tRunner. harness/harness:tRunner
will time the the outer block and call harness/harness: Run which will run
the test function as a subtest.
For instance, harness/harness: tRunner will be called with the
harness/harness: H object representing the entire test run. It will then
execute this closure which will loop through every test and call
harness/harness: Run which will run each as a subtest for reporting purposes
inside of goroutines.
func(t *H) {
for name, test := range s.tests {
t.Run(name, test, util.BoolToPtr(false))
}
// Run catching the signal rather than the tRunner as a separate
// goroutine to avoid adding a goroutine during the sequential
// phase as this pollutes the stacktrace output when aborting.
go func() { <-t.signal }()
}
Logging
The kola runner supports custom reporting via the
harness/reporters: Reporter interface. By default plain text will be output
into stdout and a JSON file will be produced inside of the _kola_temp run
log (e.x.: _kola_temp/<platform>-latest/reports/report.json). New output
formats can be added by creating a new struct which implements the
harness/reporters: Reporter interface and instantiating an object of said
reporter inside of the harness: Options object created in
kola/harness: RunTests.
For example this is how the JSON reporter is added.