Buildspec Overview

What is a buildspec?

In buildtest, we refer to buildspec as a YAML file that defines your test that buildtest will parse using the provided schemas and build a shell script from the buildspec file. Every buildspec is validated with a global schema which you can find more if you click here.

Example

Let’s start off with a simple example that declares two variables X and Y and prints the sum of X+Y.

version: "1.0"
buildspecs:
  add_numbers:
    type: script
    executor: generic.local.bash
    description: Add X+Y
    tags: [tutorials]
    vars:
      X: 1
      Y: 2
    run: echo "$X+$Y=" $(($X+$Y))

buildtest will validate the entire file with global.schema.json, the schema requires version and buildspec in order to validate file. The buildspec is where you define each test. The name of the test is add_numbers. The test requires a type field which is the sub-schema used to validate the test section. In this example type: script informs buildtest to use the Script Schema when validating test section.

Each subschema has a list of field attributes that are supported, for example the fields: type, executor, vars and run are all valid fields supported by the script schema. The version field informs which version of subschema to use. Currently all sub-schemas are at version 1.0 where buildtest will validate with a schema script-v1.0.schema.json. In future, we can support multiple versions of subschema for backwards compatibility.

Let’s look at a more interesting example, shown below is a multi line run example using the script schema with test name called systemd_default_target, shown below is the content of test:

version: "1.0"
buildspecs:
  systemd_default_target:
    executor: generic.local.bash
    type: script
    tags: [system]
    description: check if default target is multi-user.target
    run: |
      if [ "multi-user.target" == `systemctl get-default` ]; then
        echo "multi-user is the default target";
        exit 0
      fi
      echo "multi-user is not the default target";
      exit 1

The test name systemd_default_target defined in buildspec section is validated with the following pattern "^[A-Za-z_][A-Za-z0-9_]*$". This test will use the executor generic.local.bash which means it will use the Local Executor with an executor name bash defined in the buildtest settings. The default buildtest settings will provide a bash executor as follows:

system:
  generic:
    hostnames: ["localhost"]
    executors:
      local:
        bash:
          description: submit jobs on local machine using bash shell
          shell: bash

The shell: bash indicates this executor will use bash to run the test scripts. To reference this executor use the format <system>.<type>.<name> in this case generic.local.bash refers to bash executor.

The description field is an optional key that can be used to provide a brief summary of the test. In this example we can a full multi-line run section, this is achieved in YAML using run: | followed by content of run section tab indented 2 spaces.

In this example we introduce a new field status that is used for controlling how buildtest will mark test state. By default, a returncode of 0 is PASS and non-zero is a FAIL. Currently buildtest reports only two states: PASS, FAIL. In this example, buildtest will match the actual returncode with one defined in key returncode in the status section.

Script Schema

The script schema is used for writing simple scripts (bash, sh, python) in Buildspec. To use this schema you must set type: script. The run field is responsible for writing the content of test.

Shown below is schema header for script-v1.0.schema.json.

{
  "$id": "script-v1.0.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "script schema version 1.0",
  "description": "The script schema is of ``type: script`` in sub-schema which is used for running shell scripts",
  "type": "object",
  "required": ["type", "run", "executor"],
  "additionalProperties": false,

The "type": "object" means sub-schema is a JSON object where we define a list of key/value pair. The "required" field specifies a list of fields that must be specified in order to validate the Buildspec. In this example, type, run, and executor are required fields. The additionalProperties: false informs schema to reject any extra properties not defined in the schema. In our previous example, the JSON object is variables.

The executor key is required for all sub-schemas which instructs buildtest which executor to use when running the test. The executors are defined in Configuring buildtest. In our first example we define variables using the vars property which is a Key/Value pair for variable assignment. The run section is required for script schema which defines the content of the test script.

For more details on script schema see schema docs at https://buildtesters.github.io/buildtest/

Return Code Matching

buildtest can report PASS/FAIL based on returncode, by default a 0 exit code is PASS and everything else is FAIL. The returncode can be a list of exit codes to match. In this example we have four tests called exit1_fail, exit1_pass, returncode_list_mismatch and returncode_int_match. We expect exit1_fail and returncode_mismatch to FAIL while exit1_pass and returncode_int_match will PASS.

version: "1.0"
buildspecs:

  exit1_fail:
    executor: generic.local.sh
    type: script
    description: exit 1 by default is FAIL
    tags: [tutorials, fail]
    run: exit 1

  exit1_pass:
    executor: generic.local.sh
    type: script
    description: report exit 1 as PASS
    run: exit 1
    tags: [tutorials, pass]
    status:
      returncode: [1]

  returncode_list_mismatch:
    executor: generic.local.sh
    type: script
    description: exit 2 failed since it failed to match returncode 1
    run: exit 2
    tags: [tutorials, fail]
    status:
      returncode: [1, 3]

  returncode_int_match:
    executor: generic.local.sh
    type: script
    description: exit 128 matches returncode 128
    run: exit 128
    tags: [tutorials, pass]
    status:
      returncode: 128

To demonstrate we will build this test and pay close attention to the status column in output.

$ buildtest build -b tutorials/pass_returncode.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:35
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/pass_returncode.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+------------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                      |
+============================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/pass_returncode.yml |
+------------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+------------------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/pass_returncode.yml



name                      description
------------------------  ---------------------------------------------------
exit1_fail                exit 1 by default is FAIL
exit1_pass                report exit 1 as PASS
returncode_list_mismatch  exit 2 failed since it failed to match returncode 1
returncode_int_match      exit 128 matches returncode 128

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                     | id       | type   | executor         | tags                  | testpath
--------------------------+----------+--------+------------------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 exit1_fail               | 2bc66cba | script | generic.local.sh | ['tutorials', 'fail'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/pass_returncode/exit1_fail/0/exit1_fail_build.sh
 exit1_pass               | 56a7caa5 | script | generic.local.sh | ['tutorials', 'pass'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/pass_returncode/exit1_pass/0/exit1_pass_build.sh
 returncode_list_mismatch | 61b70f13 | script | generic.local.sh | ['tutorials', 'fail'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/pass_returncode/returncode_list_mismatch/0/returncode_list_mismatch_build.sh
 returncode_int_match     | 193eb872 | script | generic.local.sh | ['tutorials', 'pass'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/pass_returncode/returncode_int_match/0/returncode_int_match_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                     | id       | executor         | status   |   returncode
--------------------------+----------+------------------+----------+--------------
 exit1_fail               | 2bc66cba | generic.local.sh | FAIL     |            1
 exit1_pass               | 56a7caa5 | generic.local.sh | PASS     |            1
 returncode_list_mismatch | 61b70f13 | generic.local.sh | FAIL     |            2
 returncode_int_match     | 193eb872 | generic.local.sh | PASS     |          128

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 2/4 Percentage: 50.000%
Failed Tests: 2/4 Percentage: 50.000%


Writing Logfile to: /tmp/buildtest_86x145su.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

The returncode field can be an integer or list of integers but it may not accept duplicate values. If you specify a list of exit codes, buildtest will check actual returncode with list of expected returncodes specified by returncode field.

Shown below are examples of invalid returncodes:

# empty list is not allowed
returncode: []

# floating point is not accepted in list
returncode: [1, 1.5]

# floating point not accepted
returncode: 1.5

# duplicates are not allowed
returncode: [1, 2, 5, 5]

Classifying tests with tags

The tags field can be used to classify tests which can be used to organize tests or if you want to Building By Tags (buildtest build --tags <TAGNAME>). Tags can be defined as a string or list of strings. In this example, the test string_tag defines a tag name network while test list_of_strings_tags define a list of tags named network and ping.

version: "1.0"
buildspecs:
  string_tag:
    type: script
    executor: generic.local.bash
    description: tags can be a string
    tags: network
    run: hostname

  list_of_strings_tags:
    type: script
    executor: generic.local.bash
    description: tags can be a list of strings
    tags: [network, ping]
    run: ping -c 4 www.google.com

Each item in tags must be a string and no duplicates are allowed, for example in this test, we define a duplicate tag network which is not allowed.

version: "1.0"
buildspecs:
  duplicate_string_tags:
    type: script
    executor: generic.local.bash
    description: duplicate strings in tags list is not allowed
    tags: [network, network]
    run: hostname

If we run this test and inspect the logs we will see an error message in schema validation:

2020-09-29 10:56:43,175 [parser.py:179 - _validate() ] - [INFO] Validating test - 'duplicate_string_tags' with schemafile: script-v1.0.schema.json
2020-09-29 10:56:43,175 [buildspec.py:397 - parse_buildspecs() ] - [ERROR] ['network', 'network'] is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['tags']:
    {'oneOf': [{'type': 'string'},
               {'$ref': '#/definitions/list_of_strings'}]}

On instance['tags']:
    ['network', 'network']

If tags is a list, it must contain one item, therefore an empty list (i.e tags: []) is invalid.

Declaring Environment Variables

You can define environment variables using the env property, this is compatible with shells: bash, sh, zsh, csh and tcsh. It does not work with shell: python. In example below we declare three tests using environment variable with default shell (bash), csh, and tcsh

version: "1.0"
buildspecs:
  bash_env_variables:
    executor: generic.local.bash
    description: Declare environment variables in default shell (bash)
    type: script
    env:
      FIRST_NAME: avocado
      LAST_NAME: dinosaur
    tags: [tutorials]
    run: |
      hostname
      whoami
      echo $USER
      printf "${FIRST_NAME} ${LAST_NAME}\n"

  csh_env_declaration:
    executor: generic.local.csh
    type: script
    description: "csh shell example to declare environment variables"
    shell: /bin/csh
    tags: [tutorials]
    env:
      SHELL_NAME: "csh"
    run: echo "This is running $SHELL_NAME"

  tcsh_env_declaration:
    executor: generic.local.csh
    type: script
    description: "tcsh shell example to declare environment variables"
    shell: /bin/tcsh
    tags: [tutorials]
    env:
      path: "/usr/local/bin:$PATH"
    run: echo $path

This test can be run by issuing the following command: buildtest build -b tutorials/environment.yml. If we inspect one of the test script we will see that buildtest generates a build script that invokes the test using the shell wrapper /bin/csh for the csh test and gets the returncode.

#!/bin/bash


############# START VARIABLE DECLARATION ########################
export BUILDTEST_TEST_NAME=csh_env_declaration
export BUILDTEST_TEST_ROOT=/Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.csh/environment/csh_env_declaration/0
export BUILDTEST_BUILDSPEC_DIR=/Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials
export BUILDTEST_STAGE_DIR=/Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.csh/environment/csh_env_declaration/0/stage
export BUILDTEST_TEST_ID=501ec5d3-e614-4ae8-9c1e-4849ce340c76
############# END VARIABLE DECLARATION   ########################


# source executor startup script
source /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/executor/generic.local.csh/before_script.sh
# Run generated script
/bin/csh /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.csh/environment/csh_env_declaration/0/stage/csh_env_declaration.csh
# Get return code
returncode=$?
# Exit with return code
exit $returncode

This generated test looks something like this

#!/bin/csh
# Declare environment variables
setenv SHELL_NAME csh


# Content of run section
echo "This is running $SHELL_NAME"

Environment variables are defined using export in bash, sh, zsh while csh and tcsh use setenv.

Variable Declaration

Variables can be defined using vars property, this is compatible with all shells except for python. The variables are defined slightly different in csh,tcsh as pose to bash, sh, and zsh. In example below we define tests with bash and csh.

In YAML strings can be specified with or without quotes however in bash, variables need to be enclosed in quotes " if you are defining a multi word string (name="First Last").

If you need define a literal string it is recommended to use the literal block | that is a special character in YAML. If you want to specify " or ' in string you can use the escape character \ followed by any of the special character. In example below we define several variables such as X, Y that contain numbers, variable literalstring is a literal string processed by YAML. The variable singlequote and doublequote defines a variable with the special character ' and ". The variables current_user and files_homedir store result of a shell command. This can be done using var=$(<command>) or var=`<command>` where <command> is a Linux command.

Note

You can use the escape character \ to set special character, for instance you can declare a variable in string with quotes by using \".

version: "1.0"
buildspecs:
  variables_bash:
    type: script
    executor: generic.local.bash
    description: Declare shell variables in bash
    tags: [tutorials]
    vars:
      X: 1
      Y: 2
      literalstring: |
        "this is a literal string ':' "
      singlequote: "'singlequote'"
      doublequote: "\"doublequote\""
      current_user: "$(whoami)"
      files_homedir: "`find $HOME -type f -maxdepth 1`"

    run: |
      echo "$X+$Y=" $(($X+$Y))
      echo $literalstring
      echo $singlequote
      echo $doublequote

      echo $current_user
      echo $files_homedir

Next we build this test by running buildtest build -b $BUILDTEST_ROOT/tutorials/vars.yml.

$ buildtest build -b $BUILDTEST_ROOT/tutorials/vars.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:35
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/vars.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+-------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                           |
+=================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/vars.yml |
+-------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+-------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/vars.yml



name            description
--------------  -------------------------------
variables_bash  Declare shell variables in bash

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name           | id       | type   | executor           | tags          | testpath
----------------+----------+--------+--------------------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------
 variables_bash | 5cd95412 | script | generic.local.bash | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.bash/vars/variables_bash/0/variables_bash_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name           | id       | executor           | status   |   returncode
----------------+----------+--------------------+----------+--------------
 variables_bash | 5cd95412 | generic.local.bash | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 1/1 Percentage: 100.000%
Failed Tests: 0/1 Percentage: 0.000%


Writing Logfile to: /tmp/buildtest_luxf1hm9.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

Let’s check the generated script from the previous build, you will notice that buildtest will define the shell variables at top of script followed content defined in run section.

#!/bin/bash
# Declare shell variables
X=1
Y=2
literalstring="this is a literal string ':' "

singlequote='singlequote'
doublequote="doublequote"
current_user=$(whoami)
files_homedir=`find $HOME -type f -maxdepth 1`


# Content of run section
echo "$X+$Y=" $(($X+$Y))
echo $literalstring
echo $singlequote
echo $doublequote

echo $current_user
echo $files_homedir

Customize Shell

buildtest will default to bash shell when running test, but we can configure shell option using the shell field. The shell field is defined in schema as follows:

"shell": {
  "type": "string",
  "description": "Specify a shell launcher to use when running jobs. This sets the shebang line in your test script. The ``shell`` key can be used with ``run`` section to describe content of script and how its executed",
  "pattern": "^(/bin/bash|/bin/sh|/bin/csh|/bin/tcsh|/bin/zsh|bash|sh|csh|tcsh|zsh|python).*"
},

The shell pattern is a regular expression where one can specify a shell name along with shell options. The shell will configure the shebang in the test-script. In this example, we illustrate a few tests using different shell field.

version: "1.0"
buildspecs:
  _bin_sh_shell:
    executor: generic.local.sh
    type: script
    description: "/bin/sh shell example"
    shell: /bin/sh
    tags: [tutorials]
    run: "bzip2 --help"

  _bin_bash_shell:
    executor: generic.local.bash
    type: script
    description: "/bin/bash shell example"
    shell: /bin/bash
    tags: [tutorials]
    run: "bzip2 -h"

  bash_shell:
    executor: generic.local.bash
    type: script
    description: "bash shell example"
    shell: bash
    tags: [tutorials]
    run: "echo $SHELL"

  sh_shell:
    executor: generic.local.sh
    type: script
    description: "sh shell example"
    shell: sh
    tags: [tutorials]
    run: "echo $SHELL"

  shell_options:
    executor: generic.local.sh
    type: script
    description: "shell options"
    shell: "sh -x"
    tags: [tutorials]
    run: |
      echo $SHELL
      hostname

The generated test-script for buildspec _bin_sh_shell will specify shebang /bin/sh because we specified shell: /bin/sh:

#!/bin/sh
# Content of run section
bzip2 --help

If you don’t specify a shell path such as shell: sh, then buildtest will resolve path by looking in $PATH and build the shebang line.

In test shell_options we specify shell: "sh -x", buildtest will tack on the shell options into the called script as follows:

#!/bin/bash


############# START VARIABLE DECLARATION ########################
export BUILDTEST_TEST_NAME=shell_options
export BUILDTEST_TEST_ROOT=/Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.sh/shell_examples/shell_options/0
export BUILDTEST_BUILDSPEC_DIR=/Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials
export BUILDTEST_STAGE_DIR=/Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.sh/shell_examples/shell_options/0/stage
export BUILDTEST_TEST_ID=95c11f54-bbb1-4154-849d-44313e4417c2
############# END VARIABLE DECLARATION   ########################


# source executor startup script
source /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/executor/generic.local.sh/before_script.sh
# Run generated script
sh -x /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.sh/shell_examples/shell_options/0/stage/shell_options.sh
# Get return code
returncode=$?
# Exit with return code
exit $returncode

If you prefer csh or tcsh for writing scripts just set shell: csh or shell: tcsh, note you will need to match this with appropriate executor. For now use executor: generic.local.csh to run your csh/tcsh scripts. In this example below we define a script using csh, take note of run section we can write csh style.

version: "1.0"
buildspecs:
  csh_shell:
    executor: generic.local.csh
    type: script
    description: "csh shell example"
    shell: csh
    tags: [tutorials]
    vars:
      file: "/etc/csh.cshrc"
    run: |
      if (-e $file) then
        echo "$file file found"
      else
        echo "$file file not found"
        exit 1
      endif

Customize Shebang

You may customize the shebang line in testscript using shebang field. This takes precedence over the shell property which automatically detects the shebang based on shell path.

In next example we have two tests bash_login_shebang and bash_nonlogin_shebang which tests if shell is Login or Non-Login. The #!/bin/bash -l indicates we want to run in login shell and expects an output of Login Shell while test bash_nonlogin_shebang should run in default behavior which is non-login shell and expects output Not Login Shell. We match this with regular expression with stdout stream.

version: "1.0"
buildspecs:
  bash_login_shebang:
    type: script
    executor: generic.local.bash
    shebang: "#!/bin/bash -l"
    description: customize shebang line with bash login shell
    tags: tutorials
    run: shopt -q login_shell && echo 'Login Shell' || echo 'Not Login Shell'
    status:
      regex:
        exp: "^Login Shell$"
        stream: stdout

  bash_nonlogin_shebang:
    type: script
    executor: generic.local.bash
    shebang: "#!/bin/bash"
    description: customize shebang line with default bash (nonlogin) shell
    tags: tutorials
    run: shopt -q login_shell && echo 'Login Shell' || echo 'Not Login Shell'
    status:
      regex:
        exp: "^Not Login Shell$"
        stream: stdout

Now let’s run this test as we see the following.

$ buildtest build -b tutorials/shebang.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:36
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/shebang.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+----------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                              |
+====================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/shebang.yml |
+----------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+----------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/shebang.yml



name                   description
---------------------  ---------------------------------------------------------
bash_login_shebang     customize shebang line with bash login shell
bash_nonlogin_shebang  customize shebang line with default bash (nonlogin) shell

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                  | id       | type   | executor           | tags      | testpath
-----------------------+----------+--------+--------------------+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 bash_login_shebang    | 7d64823f | script | generic.local.bash | tutorials | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.bash/shebang/bash_login_shebang/0/bash_login_shebang_build.sh
 bash_nonlogin_shebang | 03f2ff3d | script | generic.local.bash | tutorials | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.bash/shebang/bash_nonlogin_shebang/0/bash_nonlogin_shebang_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                  | id       | executor           | status   |   returncode
-----------------------+----------+--------------------+----------+--------------
 bash_login_shebang    | 7d64823f | generic.local.bash | PASS     |            0
 bash_nonlogin_shebang | 03f2ff3d | generic.local.bash | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 2/2 Percentage: 100.000%
Failed Tests: 0/2 Percentage: 0.000%


Writing Logfile to: /tmp/buildtest_q8stfj3i.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

If we look at the generated test for bash_login_shebang we see the shebang line is passed into the script:

#!/bin/bash -l
# Content of run section
shopt -q login_shell && echo 'Login Shell' || echo 'Not Login Shell'

Python Shell

You can use script schema to write python scripts using the run property. This can be achieved if you use the generic.local.python executor assuming you have this defined in your buildtest configuration.

Here is a python example calculating area of circle

version: "1.0"
buildspecs:
  circle_area:
    executor: generic.local.python
    type: script
    shell: python
    description: "Calculate circle of area given a radius"
    tags: [tutorials, python]
    run: |
      import math
      radius = 2
      area = math.pi * radius * radius
      print("Circle Radius ", radius)
      print("Area of circle ", area)

The shell: python will let us write python script in the run section. The tags field can be used to classify test, the field expects an array of string items.

Note

Python scripts are very picky when it comes to formatting, in the run section if you are defining multiline python script you must remember to use 2 space indent to register multiline string. buildtest will extract the content from run section and inject in your test script. To ensure proper formatting for a more complex python script you may be better off writing a python script in separate file and call it in run section.

Skipping test

By default, buildtest will run all tests defined in buildspecs section, if you want to skip a test use the skip field which expects a boolean value. Shown below is an example test.

version: "1.0"
buildspecs:
  skip:
    type: script
    executor: generic.local.bash
    description: This test is skipped
    skip: Yes
    tags: [tutorials]
    run: hostname

  unskipped:
    type: script
    executor: generic.local.bash
    description: This test is not skipped
    skip: No
    tags: [tutorials]
    run: hostname

The first test skip will be ignored by buildtest because skip: true is defined while unskipped will be processed as usual.

Note

YAML and JSON have different representation for boolean. For json schema valid values are true and false see https://json-schema.org/understanding-json-schema/reference/boolean.html however YAML has many more representation for boolean see https://yaml.org/type/bool.html. You may use any of the YAML boolean, however it’s best to stick with json schema values true and false.

Here is an example build, notice message [skip] test is skipped during the build stage

$ buildtest build -b tutorials/skip_tests.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:36
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/skip_tests.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+-------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                 |
+=======================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/skip_tests.yml |
+-------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1
[skip](/home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/skip_tests.yml): test is skipped.

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+-------------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/skip_tests.yml



name       description
---------  ------------------------
unskipped  This test is not skipped

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name      | id       | type   | executor           | tags          | testpath
-----------+----------+--------+--------------------+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------
 unskipped | 98cf88e0 | script | generic.local.bash | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.bash/skip_tests/unskipped/0/unskipped_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name      | id       | executor           | status   |   returncode
-----------+----------+--------------------+----------+--------------
 unskipped | 98cf88e0 | generic.local.bash | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 1/1 Percentage: 100.000%
Failed Tests: 0/1 Percentage: 0.000%


Writing Logfile to: /tmp/buildtest_ziyqp_yt.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

Defining Metrics

buildtest provides a method to define test metrics in the buildspecs which can be used to store arbitrary content from the output/error file into named metric. A metric is defined using the metrics property where each element under the metrics property is the name of the metric which must be a unique name. A metric can apply regular expression on stdout, stderr like in this example below. The metrics are captured in the test report which can be queried via buildtest report or buildtest inspect. Shown below is an example where we define two metrics named hpcg_rating and hpcg_state.

version: "1.0"
buildspecs:

  metric_regex_example:
    executor: generic.local.sh
    type: script
    description: capture result metric from output
    run: echo "HPCG result is VALID with a GFLOP/s rating of=63.6515"
    tags: tutorials
    metrics:
      hpcg_rating:
        regex:
          exp: 'rating of=(\d+\.\d+)$'
          stream: stdout

      hpcg_state:
        regex:
          exp: '(VALID)'
          stream: stdout

The metrics will not impact behavior of test, it will only impact the test report. By default a metric will be an empty dictionary if there is no metrics property. If we fail to match a regular expression, the metric will be defined as an empty string.

Note

If your regular expression contains an escape character \ you must surround your string in single quotes ' as pose to double quotes "

Let’s build this test.

$ buildtest build -b tutorials/metrics_regex.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:36
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/metrics_regex.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+----------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                    |
+==========================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/metrics_regex.yml |
+----------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+----------------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/metrics_regex.yml



name                  description
--------------------  ---------------------------------
metric_regex_example  capture result metric from output

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                 | id       | type   | executor         | tags      | testpath
----------------------+----------+--------+------------------+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 metric_regex_example | 936060b5 | script | generic.local.sh | tutorials | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/metrics_regex/metric_regex_example/0/metric_regex_example_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                 | id       | executor         | status   |   returncode
----------------------+----------+------------------+----------+--------------
 metric_regex_example | 936060b5 | generic.local.sh | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 1/1 Percentage: 100.000%
Failed Tests: 0/1 Percentage: 0.000%


Writing Logfile to: /tmp/buildtest_y4a2cnpr.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

We can query the metrics via buildtest report which will display all metrics as a comma separted Key/Value pair. We can use buildtest report --format metrics to extract all metrics for a test. Internally, we store the metrics as a dictionary but when we print them out via buildtest report we join them together into a single string. Shown below is the metrics for the previous build.

$ buildtest report --filter buildspec=tutorials/metrics_regex.yml --format name,metrics
Reading report file: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/report.json 

+----------------------+------------------------------------------------+
| name                 | metrics                                        |
+======================+================================================+
| metric_regex_example | hpcg_rating=rating of=63.6515,hpcg_state=VALID |
+----------------------+------------------------------------------------+

You can define a metric based on variables or environment variables which requires you have set vars or env property in the buildspec. The vars and env is a property under the metric name that can be used to reference name of variable or environment variable. If you reference an invalid name, buildtest will assign the metric an empty string. In this next example, we define two metrics gflop and foo that are assigned to variable GFLOPS and environment variable FOO.

version: "1.0"
buildspecs:
  metric_variable_assignment:
    executor: generic.local.sh
    type: script
    description: capture result metric based on variables and environment variable
    vars:
      GFLOPS: "63.6515"
    env:
      FOO: BAR
    run: |
      echo $GFLOPS
      echo $FOO
    tags: tutorials
    metrics:
       gflops:
         vars: "GFLOPS"
       foo:
         env: "FOO"

Now let’s build the test.

$ buildtest build -b tutorials/metrics_variable.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:37
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/metrics_variable.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+-------------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                       |
+=============================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/metrics_variable.yml |
+-------------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+-------------------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/metrics_variable.yml



name                        description
--------------------------  -----------------------------------------------------------------
metric_variable_assignment  capture result metric based on variables and environment variable

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                       | id       | type   | executor         | tags      | testpath
----------------------------+----------+--------+------------------+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 metric_variable_assignment | 97eb1b43 | script | generic.local.sh | tutorials | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/metrics_variable/metric_variable_assignment/0/metric_variable_assignment_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                       | id       | executor         | status   |   returncode
----------------------------+----------+------------------+----------+--------------
 metric_variable_assignment | 97eb1b43 | generic.local.sh | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 1/1 Percentage: 100.000%
Failed Tests: 0/1 Percentage: 0.000%


Writing Logfile to: /tmp/buildtest_lyr_4aaa.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

Now if we query the previous test, we will see the two metrics gflops and foo are captured in the test.

$ buildtest report --filter buildspec=tutorials/metrics_variable.yml --format name,metrics
Reading report file: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/report.json 

+----------------------------+------------------------+
| name                       | metrics                |
+============================+========================+
| metric_variable_assignment | gflops=63.6515,foo=BAR |
+----------------------------+------------------------+

You can also define metrics with the compiler schema which works slightly different when it comes to variable and environment assignment. Since you can define vars and env in defaults or config section. Let’s take a look at this next example where we compile an openmp code that will use the OMP_NUM_THREADS environment as the metric that is assigned to name openmp_threads. Since we have defined OMP_NUM_THREADS under the defaults and config section we will use the environment variable that corresponds to each compiler.

version: "1.0"
buildspecs:
  metrics_variable_compiler:
    type: compiler
    description: define metrics with compiler schema
    executor: generic.local.bash
    tags: [tutorials, compile]
    source: "src/hello_omp.c"
    compilers:
      name: ["^(builtin_gcc|gcc)"]
      default:
        gcc:
          cflags: -fopenmp
          env:
            OMP_NUM_THREADS: 4
      config:
        builtin_gcc:
          env:
            OMP_NUM_THREADS: 1
        gcc/9.3.0-n7p74fd:
          env:
            OMP_NUM_THREADS: 2

    metrics:
      openmp_threads:
        env: "OMP_NUM_THREADS"

Note

This test uses a custom site configuration that defines gcc multiple compilers.

Let’s build this test as follows

$ buildtest -c config/laptop.yml build -b tutorials/compilers/metrics_openmp.yml


User:  siddiq90
Hostname:  DOE-7086392.local
Platform:  Darwin
Current Time:  2021/07/24 00:14:33
buildtest path: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest
buildtest version:  0.10.0
python path: /Users/siddiq90/.local/share/virtualenvs/buildtest-KLOcDrW0/bin/python
python version:  3.7.3
Test Directory:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests
Configuration File:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/config/laptop.yml
Command: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest -c config/laptop.yml build -b tutorials/compilers/metrics_openmp.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+

+------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                    |
+==========================================================================================+
| /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/compilers/metrics_openmp.yml |
+------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+

 schemafile                | validstate   | buildspec
---------------------------+--------------+------------------------------------------------------------------------------------------
 compiler-v1.0.schema.json | True         | /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/compilers/metrics_openmp.yml



name                       description
-------------------------  -----------------------------------
metrics_variable_compiler  define metrics with compiler schema
metrics_variable_compiler  define metrics with compiler schema
metrics_variable_compiler  define metrics with compiler schema

+----------------------+
| Stage: Building Test |
+----------------------+





 name                      | id       | type     | executor           | tags                     | compiler           | testpath
---------------------------+----------+----------+--------------------+--------------------------+--------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------
 metrics_variable_compiler | e45976b8 | compiler | generic.local.bash | ['tutorials', 'compile'] | builtin_gcc        | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.bash/metrics_openmp/metrics_variable_compiler/11/metrics_variable_compiler_build.sh
 metrics_variable_compiler | 8bc71f19 | compiler | generic.local.bash | ['tutorials', 'compile'] | gcc/9.3.0-n7p74fd  | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.bash/metrics_openmp/metrics_variable_compiler/12/metrics_variable_compiler_build.sh
 metrics_variable_compiler | 7127eb46 | compiler | generic.local.bash | ['tutorials', 'compile'] | gcc/10.2.0-37fmsw7 | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.bash/metrics_openmp/metrics_variable_compiler/13/metrics_variable_compiler_build.sh

+---------------------+
| Stage: Running Test |
+---------------------+

 name                      | id       | executor           | status   |   returncode
---------------------------+----------+--------------------+----------+--------------
 metrics_variable_compiler | e45976b8 | generic.local.bash | FAIL     |          127
 metrics_variable_compiler | 8bc71f19 | generic.local.bash | PASS     |            0
 metrics_variable_compiler | 7127eb46 | generic.local.bash | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+

Passed Tests: 2/3 Percentage: 66.667%
Failed Tests: 1/3 Percentage: 33.333%


Writing Logfile to: /Users/siddiq90/buildtest/buildtest_0a04808e.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest.log

Now if we filter the results, notice that builtin_gcc got metrics openmp_threads=1 since that is the value set under the builtin_gcc compiler instance under the config section. The gcc/9.3.0-n7p74fd compiler got value of 2 because we have an entry defined under the config section while gcc/10.2.0-37fmsw7 compiler got the value of 4 from the default section that is inherited for all gcc compilers.

$ buildtest report --filter buildspec=tutorials/compilers/metrics_openmp.yml --format name,compiler,metrics
Reading report file: /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/report.json

+---------------------------+--------------------+------------------+
| name                      | compiler           | metrics          |
+===========================+====================+==================+
| metrics_variable_compiler | builtin_gcc        | openmp_threads=1 |
+---------------------------+--------------------+------------------+
| metrics_variable_compiler | gcc/9.3.0-n7p74fd  | openmp_threads=2 |
+---------------------------+--------------------+------------------+
| metrics_variable_compiler | gcc/10.2.0-37fmsw7 | openmp_threads=4 |
+---------------------------+--------------------+------------------+

run_only

The run_only property is used for running test given a specific condition has met. For example, you may want a test to run only if its particular system (Linux, Darwin), operating system, scheduler, etc…

run_only - user

buildtest will skip test if any of the conditions are not met. Let’s take an example in this buildspec we define a test name run_only_as_root that requires root user to run test. The run_only is a property of key/value pairs and user is one of the field. buildtest will only build & run test if current user matches user field. We detect current user using $USER and match with input field user. buildtest will skip test if there is no match.

version: "1.0"
buildspecs:
  run_only_as_root:
    description: "This test will only run if current user is root"
    executor: generic.local.bash
    type: script
    tags: ["tutorials"]
    run_only:
      user: root
    run: echo $USER

Now if we run this test we see buildtest will skip test run_only_as_root because current user is not root.

$ buildtest build -b tutorials/root_user.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:38
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/root_user.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                |
+======================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/root_user.yml |
+------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1
[run_only_as_root][/home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/root_user.yml]: test is skipped because this test is expected to run as user: root but detected user: None.
No buildspecs to process because there are no valid buildspecs

run_only - platform

Similarly, we can run test if it matches target platform. In this example we have two tests run_only_platform_darwin and run_only_platform_linux that are run if target platform is Darwin or Linux. This is configured using platform field which is a property of run_only object. buildtest will match target platform using platform.system() with field platform, if there is no match buildtest will skip test. In this test, we define a python shell using shell: python and run platform.system(). We expect the output of each test to have Darwin and Linux which we match with stdout using regular expression.

version: "1.0"
buildspecs:
  run_only_platform_darwin:
    description: "This test will only run if target platform is Darwin"
    executor: generic.local.python
    type: script
    tags: ["tutorials"]
    run_only:
      platform: Darwin
    shell: python
    run: |
      import platform
      print(platform.system())
    status:
      regex:
        stream: stdout
        exp: "^Darwin$"

  run_only_platform_linux:
    description: "This test will only run if target platform is Linux"
    executor: generic.local.python
    type: script
    tags: ["tutorials"]
    run_only:
      platform: Linux
    shell: python
    run: |
      import platform
      print(platform.system())
    status:
      regex:
        stream: stdout
        exp: "^Linux"

This test was ran on a MacOS (Darwin) so we expect test run_only_platform_linux to be skipped.

$ buildtest build -b tutorials/run_only_platform.yml 


User:  siddiq90
Hostname:  DOE-7086392.local
Platform:  Darwin
Current Time:  2021/07/06 18:54:27
buildtest path: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest
buildtest version:  0.9.6
python path: /Users/siddiq90/.local/share/virtualenvs/buildtest-KLOcDrW0/bin/python
python version:  3.7.3
Test Directory:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests
Configuration File:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest/settings/config.yml
Command: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest build -b tutorials/run_only_platform.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+-----------------------------------------------------------------------------------+
| Discovered Buildspecs                                                             |
+===================================================================================+
| /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/run_only_platform.yml |
+-----------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1
[run_only_platform_linux][/Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/run_only_platform.yml]: test is skipped because this test is expected to run on platform: Linux but detected platform: Darwin.

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+-----------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/run_only_platform.yml



name                      description
------------------------  ----------------------------------------------------
run_only_platform_darwin  This test will only run if target platform is Darwin

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                     | id       | type   | executor             | tags          | testpath
--------------------------+----------+--------+----------------------+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
 run_only_platform_darwin | 964e3016 | script | generic.local.python | ['tutorials'] | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.python/run_only_platform/run_only_platform_darwin/3/run_only_platform_darwin_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                     | id       | executor             | status   |   returncode
--------------------------+----------+----------------------+----------+--------------
 run_only_platform_darwin | 964e3016 | generic.local.python | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 1/1 Percentage: 100.000%
Failed Tests: 0/1 Percentage: 0.000%


Writing Logfile to: /var/folders/1m/_jjv09h17k37mkktwnmbkmj0002t_q/T/buildtest__md43sa1.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest.log

run_only - scheduler

buildtest can run test if a particular scheduler is available. In this example, we introduce a new field scheduler that is part of run_only property. This field expects one of the following values: [lsf, slurm, cobalt, pbs] and buildtest will check if target system supports detects the scheduler. In this example we require lsf scheduler because this test runs bmgroup which is a LSF binary.

Note

buildtest assumes scheduler binaries are available in $PATH, if no scheduler is found buildtest sets this to an empty list

version: "1.0"
buildspecs:
  show_host_groups:
    type: script
    executor: generic.local.bash
    description: Show information about host groups using bmgroup
    tags: lsf
    run_only:
      scheduler: lsf
    run: bmgroup

If we build this test on a target system without LSF notice that buildtest skips test show_host_groups.

$ buildtest build -b general_tests/sched/lsf/bmgroups.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:38
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b general_tests/sched/lsf/bmgroups.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+-------------------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                             |
+===================================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/general_tests/sched/lsf/bmgroups.yml |
+-------------------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1
[show_host_groups][/home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/general_tests/sched/lsf/bmgroups.yml]: test is skipped because ['run_only']['scheduler'] got value: lsf but detected scheduler: [].
No buildspecs to process because there are no valid buildspecs

run_only - linux_distro

buildtest can run test if it matches a Linux distro, this is configured using linux_distro field that is a list of Linux distros that is returned via distro.id(). In this example, we run test only if host distro is darwin.

version: "1.0"
buildspecs:
  run_only_macos_distro:
    type: script
    executor: generic.local.bash
    description: "Run test only if distro is darwin."
    tags: [mac]
    run_only:
      linux_distro:
        - darwin
    run: uname
    status:
      regex:
        stream: stdout
        exp: "^Darwin$"

  run_only_linux_distro:
    type: script
    executor: generic.local.bash
    description: "Run test only if distro is CentOS."
    tags: [mac]
    run_only:
      linux_distro:
        - centos
    run: uname

This test will run successfully because this was ran on a Mac OS (darwin) system.

$ buildtest build -b tutorials/run_only_distro.yml 


User:  siddiq90
Hostname:  DOE-7086392.local
Platform:  Darwin
Current Time:  2021/07/06 18:54:28
buildtest path: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest
buildtest version:  0.9.6
python path: /Users/siddiq90/.local/share/virtualenvs/buildtest-KLOcDrW0/bin/python
python version:  3.7.3
Test Directory:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests
Configuration File:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest/settings/config.yml
Command: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest build -b tutorials/run_only_distro.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+---------------------------------------------------------------------------------+
| Discovered Buildspecs                                                           |
+=================================================================================+
| /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/run_only_distro.yml |
+---------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1
[run_only_linux_distro][/Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/run_only_distro.yml]: test is skipped because this test is expected to run on linux distro: ['centos'] but detected linux distro: darwin.

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+---------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/run_only_distro.yml



name                   description
---------------------  ----------------------------------
run_only_macos_distro  Run test only if distro is darwin.

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                  | id       | type   | executor           | tags    | testpath
-----------------------+----------+--------+--------------------+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------
 run_only_macos_distro | 9d4d0d97 | script | generic.local.bash | ['mac'] | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.bash/run_only_distro/run_only_macos_distro/0/run_only_macos_distro_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                  | id       | executor           | status   |   returncode
-----------------------+----------+--------------------+----------+--------------
 run_only_macos_distro | 9d4d0d97 | generic.local.bash | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 1/1 Percentage: 100.000%
Failed Tests: 0/1 Percentage: 0.000%


Writing Logfile to: /var/folders/1m/_jjv09h17k37mkktwnmbkmj0002t_q/T/buildtest_6asbja74.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest.log

Running test across multiple executors

The executor property can support regular expression to search for compatible executors, this can be used if you want to run a test across multiple executors. In buildtest, we use re.fullmatch with the input pattern defined by executor property against a list of available executors defined in configuration file. You can retrieve a list of executors by running buildtest config executors.

In example below we will run this test on generic.local.bash and generic.local.sh executor based on the regular expression.

version: "1.0"
buildspecs:
  executor_regex_script_schema:
    type: script
    executor: "generic.local.(bash|sh)"
    description: regular expression test with executor using script schema
    tags: [tutorials]
    run: date

If we build this test, notice that there are two tests, one for each executor.

$ buildtest build -b tutorials/executor_regex_script.yml 


User:  siddiq90
Hostname:  DOE-7086392.local
Platform:  Darwin
Current Time:  2021/07/06 18:54:11
buildtest path: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest
buildtest version:  0.9.6
python path: /Users/siddiq90/.local/share/virtualenvs/buildtest-KLOcDrW0/bin/python
python version:  3.7.3
Test Directory:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests
Configuration File:  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest/settings/config.yml
Command: /Users/siddiq90/Documents/GitHubDesktop/buildtest/bin/buildtest build -b tutorials/executor_regex_script.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+---------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                 |
+=======================================================================================+
| /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/executor_regex_script.yml |
+---------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+---------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /Users/siddiq90/Documents/GitHubDesktop/buildtest/tutorials/executor_regex_script.yml



name                          description
----------------------------  ---------------------------------------------------------
executor_regex_script_schema  regular expression test with executor using script schema
executor_regex_script_schema  regular expression test with executor using script schema

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name                         | id       | type   | executor           | tags          | testpath
------------------------------+----------+--------+--------------------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 executor_regex_script_schema | 225b1101 | script | generic.local.bash | ['tutorials'] | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.bash/executor_regex_script/executor_regex_script_schema/1/executor_regex_script_schema_build.sh
 executor_regex_script_schema | bdc3a622 | script | generic.local.sh   | ['tutorials'] | /Users/siddiq90/Documents/GitHubDesktop/buildtest/var/tests/generic.local.sh/executor_regex_script/executor_regex_script_schema/1/executor_regex_script_schema_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name                         | id       | executor           | status   |   returncode
------------------------------+----------+--------------------+----------+--------------
 executor_regex_script_schema | 225b1101 | generic.local.bash | PASS     |            0
 executor_regex_script_schema | bdc3a622 | generic.local.sh   | PASS     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 2/2 Percentage: 100.000%
Failed Tests: 0/2 Percentage: 0.000%


Writing Logfile to: /var/folders/1m/_jjv09h17k37mkktwnmbkmj0002t_q/T/buildtest_n4z9jpc_.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /Users/siddiq90/Documents/GitHubDesktop/buildtest/buildtest.log

Passing Test based on runtime

buildtest can determine state of test based on runtime property which is part of status object. This can be used if you want to control how test PASS or FAIL based on execution time of test. In example below we have five tests that make use of runtime property for passing a test. The runtime property support min and max property that can mark test pass based on minimum and maximum runtime. A test will pass if it’s execution time is greater than min time and less than max time. If min is specified without max property the upperbound is not set, likewise max without min will pass if test is less than max time. The lower bound is not set, but test runtime will be greater than 0 sec.

In test timelimit_min, we sleep for 2 seconds and it will pass because minimum runtime is 1.0 seconds. Similarly, timelimit_max will pass because we sleep for 2 seconds with a max time of 5.0.

version: "1.0"
buildspecs:
  timelimit_min_max:
    type: script
    executor: generic.local.sh
    description: "Run a sleep job for 2 seconds and test pass if its within 1.0-3.0sec"
    tags: ["tutorials"]
    run: sleep 2
    status:
      runtime:
        min: 1.0
        max: 3.0


  timelimit_min:
    type: script
    executor: generic.local.sh
    description: "Run a sleep job for 2 seconds and test pass if its exceeds min time of 1.0 sec"
    tags: ["tutorials"]
    run: sleep 2
    status:
      runtime:
        min: 1.0

  timelimit_max:
    type: script
    executor: generic.local.sh
    description: "Run a sleep job for 2 seconds and test pass if it's within max time: 5.0 sec"
    tags: ["tutorials"]
    run: sleep 2
    status:
      runtime:
        max: 5.0


  timelimit_min_fail:
    type: script
    executor: generic.local.sh
    description: "This test fails because it runs less than mintime of 10 second"
    tags: ["tutorials"]
    run: sleep 2
    status:
      runtime:
        min: 10.0

  timelimit_max_fail:
    type: script
    executor: generic.local.sh
    description: "This test fails because it exceeds maxtime of 1.0 second"
    tags: ["tutorials"]
    run: sleep 3
    status:
      runtime:
        max: 1.0
$ buildtest build -b tutorials/runtime_status_test.yml


User:  docs
Hostname:  build-14364669-project-280831-buildtest
Platform:  Linux
Current Time:  2021/07/30 22:01:38
buildtest path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest
buildtest version:  0.10.1
python path: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/envs/v0.10.1/bin/python
python version:  3.6.12
Test Directory:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests
Configuration File:  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest/settings/config.yml
Command: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/bin/buildtest build -b tutorials/runtime_status_test.yml

+-------------------------------+
| Stage: Discovering Buildspecs |
+-------------------------------+ 

+----------------------------------------------------------------------------------------------------------------+
| Discovered Buildspecs                                                                                          |
+================================================================================================================+
| /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/runtime_status_test.yml |
+----------------------------------------------------------------------------------------------------------------+
Discovered Buildspecs:  1
Excluded Buildspecs:  0
Detected Buildspecs after exclusion:  1

+---------------------------+
| Stage: Parsing Buildspecs |
+---------------------------+ 

 schemafile              | validstate   | buildspec
-------------------------+--------------+----------------------------------------------------------------------------------------------------------------
 script-v1.0.schema.json | True         | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/tutorials/runtime_status_test.yml



name                description
------------------  ------------------------------------------------------------------------------
timelimit_min_max   Run a sleep job for 2 seconds and test pass if its within 1.0-3.0sec
timelimit_min       Run a sleep job for 2 seconds and test pass if its exceeds min time of 1.0 sec
timelimit_max       Run a sleep job for 2 seconds and test pass if it's within max time: 5.0 sec
timelimit_min_fail  This test fails because it runs less than mintime of 10 second
timelimit_max_fail  This test fails because it exceeds maxtime of 1.0 second

+----------------------+
| Stage: Building Test |
+----------------------+ 

 name               | id       | type   | executor         | tags          | testpath
--------------------+----------+--------+------------------+---------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 timelimit_min_max  | e1c4d361 | script | generic.local.sh | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/runtime_status_test/timelimit_min_max/0/timelimit_min_max_build.sh
 timelimit_min      | 56f3ae21 | script | generic.local.sh | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/runtime_status_test/timelimit_min/0/timelimit_min_build.sh
 timelimit_max      | c809f9f4 | script | generic.local.sh | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/runtime_status_test/timelimit_max/0/timelimit_max_build.sh
 timelimit_min_fail | b51bc30b | script | generic.local.sh | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/runtime_status_test/timelimit_min_fail/0/timelimit_min_fail_build.sh
 timelimit_max_fail | b1ad9cb2 | script | generic.local.sh | ['tutorials'] | /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/tests/generic.local.sh/runtime_status_test/timelimit_max_fail/0/timelimit_max_fail_build.sh





+---------------------+
| Stage: Running Test |
+---------------------+ 

 name               | id       | executor         | status   |   returncode
--------------------+----------+------------------+----------+--------------
 timelimit_min_max  | e1c4d361 | generic.local.sh | PASS     |            0
 timelimit_min      | 56f3ae21 | generic.local.sh | PASS     |            0
 timelimit_max      | c809f9f4 | generic.local.sh | PASS     |            0
 timelimit_min_fail | b51bc30b | generic.local.sh | FAIL     |            0
 timelimit_max_fail | b1ad9cb2 | generic.local.sh | FAIL     |            0

+----------------------+
| Stage: Test Summary  |
+----------------------+ 
    
Passed Tests: 3/5 Percentage: 60.000%
Failed Tests: 2/5 Percentage: 40.000%


Writing Logfile to: /tmp/buildtest_k6r9pews.log
A copy of logfile can be found at $BUILDTEST_ROOT/buildtest.log -  /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/buildtest.log

If we look at the test results, we expect the first three tests timelimit_min, timelimit_max, timelimit_min_max will pass while the last two tests fail because it fails to comply with runtime property.

$ buildtest report --filter buildspec=tutorials/runtime_status_test.yml --format name,id,state,runtime --latest
Reading report file: /home/docs/checkouts/readthedocs.org/user_builds/buildtest/checkouts/v0.10.1/var/report.json 

+--------------------+----------+---------+-----------+
| name               | id       | state   |   runtime |
+====================+==========+=========+===========+
| timelimit_min_max  | e1c4d361 | PASS    |   2.00647 |
+--------------------+----------+---------+-----------+
| timelimit_min      | 56f3ae21 | PASS    |   2.00678 |
+--------------------+----------+---------+-----------+
| timelimit_max      | c809f9f4 | PASS    |   2.00671 |
+--------------------+----------+---------+-----------+
| timelimit_min_fail | b51bc30b | FAIL    |   2.00669 |
+--------------------+----------+---------+-----------+
| timelimit_max_fail | b1ad9cb2 | FAIL    |   3.00682 |
+--------------------+----------+---------+-----------+