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.
Responsible for filtering down the test list based on the given criteria,
creating the harness/suite: Suite
object, and outputting the final result.
Creates the test cluster, runs the individual test function, and cleans up the test cluster.
Creates the output directory, the test.tap
file and any profile related
files then calls 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.
Responsible for the timing, reporting, and execution of a closure.
Handles the setup of child harness/harness: H
objects, loggers, and the
running of closures as subtests.
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
- The
kola
cmd calls intokola/harness: RunTests
kola/harness: RunTests
callskola/harness: filterTests
to build a test list filtered down by the given pattern & platform from all tests inkola/register/register: Tests
object.kola/harness: RunTests
checks if any of the tests do not exactly match the given pattern and have either aMinVersion
orMaxVersion
tag, if so then it will callkola/harness: getClusterSemver
to 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: getClusterSemver
check will be skipped and the test will be run without regard to theMinVersion
orMaxVersion
tags.kola/harness: RunTests
will then construct aharness/suite: Options
object and construct aharness/test: Test
object containing the name of each test and a closure (#1) callingkola/harness: runTest
.kola/harness: RunTests
constructs aharness/suite: Suite
object viaharness/suite: NewSuite
using theharness/suite: Options
andharness/test: Test
objects and proceeds to call theharness/suite: Run
function on theharness/suite: Suite
object.harness/suite: Run
starts by creating or cleaning up the output directory by calling theharness/harness: CleanOutputDir
function. It then creates thetest.tap
file inside of the output directory and prints a string to the file containing1..%d
where %d is the amount of tests being run.harness/suite: Run
then 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: Run
then callsharness/suite: runTests
passingos.Stdout
and thetap io.Writer
object.harness/suite: runTests
starts by setting therunning
variable on theharness/suite: Suite
object, which is the count of running tests, to 1 and creating theharness/harness: H
object.harness/suite: runTests
then callsharness/harness: tRunner
passing theharness/harness: H
object and a closure (#2) which loops each test in theharness/suite: Suite
object callingharness/harness: Run
on each, passing the name of the test, theharness/test: Test
object, and a boolean pointer set to false, followed by a goroutine call to receive from the signal channel on theharness/harness: H
object.harness/harness: tRunner
starts by creating acontext.WithCancel
object, the result ofharness/harness: parentContext
is passed in which will either be the context object of theharness/harness: H
objects parent orcontext.Background()
if the object doesn't have a parent.harness/harness: tRunner
then 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: flushToParent
function), and sendtrue
on theharness/harness: H
signal
channel.harness/harness: tRunner
will then calculate the start time and call the closure it received as an argument with theharness/harness: H
variable as a parameter, this will be the closure that was created inharness/suite: runTests
which will callharness/harness: Run
for each test.harness/harness: Run
runs each function as a subtest of theharness/harness: H
object it is passed with the name passed. It starts by marking thehasSub
variable on theharness/harness: H
object to true and checking that the test name it received is a valid test via theharness/match: fullName
function.harness/harness: Run
will then create a newharness/harness: H
object which has the object it received as the parent and alog
object.harness/harness: Run
then does a goroutine call onharness/harness: tRunner
passing in the newharness/harness: H
object, the closure function it was passed, which is the call tokola/harness: runTest
, and the boolean pointer it was passed.harness/harness: tRunner
will then run through and callkola/harness: runTest
.kola/harness: runTest
is 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: Run
on thekola/register/register: Test
object, which is a function pointer which accepts akola/cluster/cluster: TestCluster
object and is defined inside of the individual test files.
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 }()
}
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.