tarantool / test-run

Tarantool functional testing framework
14 stars 14 forks source link

Tarantool Functional testing framework

Coverage Status

Test Suite

Bunch of tests, that lay down in the subfolder (recursively) with suite.ini file. suite.ini is basic ini-file, that consists of one section default, and a number of fields:

Field core must be one of:

Test

Each test consists of files *.test(.lua|.sql|.py)?, *.result, and may have skip condition file *.skipcond. On first run (without .result) .result is generated from output. Each run, in the beggining, .skipcond file is executed. In the local env there's object self, that's Test object. If test must be skipped - you must put self.skip = 1 in this file. Next, .test(.lua|.py)? is executed and file .reject is created, then .reject is compared with .result. If something differs, then 15 last string of this diff file are printed and .reject file is saving in the <vardir>/rejects/<suite> subfolder given in options or set localy as var/rejects/<suite> by default. If not, then .reject file is deleted.

Test configuration

Test configuration file contains config for multiple run. For each test section system runs separated test and compares result with common .result file. For example we need to run one test for different db engines("*" means default configuration):

{
    "my.test.lua": {
        "first": {"a": 1, "b": 2},
        "second": {"a": 1, "b": 3}
    },
    "*": {
        "memtx": {"engine": "memtx"},
        "vinyl": {"engine": "vinyl"}
    }
}

In test case we can get configuration from inspector:

engine = test_run:get_cfg('engine')
-- first run engine is 'memtx'
-- second run engine is 'vinyl'

"engine" value has a special meaning for *.test.sql files: if it is "memtx" or "vinyl", then the corresponding default engine will be set before executing commands from a test file. An engine is set with the following commands:

UPDATE "_session_settings" SET "value" = 'memtx|vinyl' WHERE "name" = 'sql_default_engine'
pragma sql_default_engine='memtx|vinyl'

If the first fails, then the second will be executed. When both fails, fail the test.

Python

Files: <name>.test.py, <name>.result and <name>.skipcond(optionaly).

Environment:

Example:

import os
import time

from lib.admin_connection import AdminConnection
from lib.tarantool_server import TarantoolServer

master = server
admin("box.info.lsn") # equivalent to master.admin("box.info.lsn") and server.admin(...)
sql("select * from t0 where k0=1")
replica = TarantoolServer()
replica.script = 'replication/replica.lua'
replica.vardir = os.path.join(server.vardir, "replica")
replica.deploy()
master.admin("box.insert(0, 1, 'hello')")
print('sleep_1')
time.sleep(0.1)
print('sleep_finished')
print('sleep_2')
admin("require('fiber').sleep(0.1)")
print('sleep_finished')
replica.admin("box.select(0, 0, 1)")
con2 = AdminConnection('localhost', server.admin.port)
con2("box.info.lsn")
replica.stop()
replica.cleanup()
con2.disconnect()

Result:

box.info.lsn
---
- null
...
select * from t0 where k0=1
---
- error:
    errcode: ER_NO_SUCH_SPACE
    errmsg: Space '#0' does not exist
...
box.insert(0, 1, 'hello')
---
- error: '[string "return box.insert(0, 1, ''hello'')"]:1: attempt to call field ''insert''
    (a nil value)'
...
sleep_1
sleep_finished
sleep_2
require('fiber').sleep(0.1)
---
...
sleep_finished
box.select(0, 0, 1)
---
- error: '[string "return box.select(0, 0, 1)"]:1: attempt to call field ''select''
    (a nil value)'
...
box.info.lsn
---
- null
...

Lua

Files: <name>.test.lua, <name>.result and <name>.skipcond(optionaly). Tests interact only with AdminConnection. Supports some preprocessor functions (eg delimiter)

Delimiter example:

env = require('test_run')
test_run = env.new()
box.schema.space.create('temp')
t1 = box.space.temp
t1:create_index('primary', { type = 'hash', parts = {1, 'num'}, unique = true})
t1:insert{0, 1, 'hello'}
test_run:cmd("setopt delimiter ';'")
function test()
    return {1,2,3}
end;
test(
);
test_run:cmd("setopt delimiter ''");
test(
);
test

Delimiter result:

env = require('test_run')
test_run = env.new()
box.schema.space.create('temp')
---
- index: []
  on_replace: 'function: 0x40e4fdf0'
  temporary: false
  id: 512
  engine: memtx
  enabled: false
  name: temp
  field_count: 0
- created
...
t1 = box.space.temp
---
...
t1:create_index('primary', { type = 'hash', parts = {1, 'num'}, unique = true})
---
...
t1:insert{0, 1, 'hello'}
---
- [0, 1, 'hello']
...
test_run:cmd("setopt delimiter ';'")
function test()
    return {1,2,3}
end;
---
...
test(
);
---
- - 1
  - 2
  - 3
...
test_run:cmd("setopt delimiter ''");
test(
---
- error: '[string "test( "]:1: unexpected symbol near ''<eof>'''
...
);
---
- error: '[string "); "]:1: unexpected symbol near '')'''
...
test
---
- 'function: 0x40e533b8'
...

It is possible to use backslash at and of a line to carry it.

function echo(...) \
    return ...     \
end

SQL

*.test.sql files are just SQL statements written line-by-line.

It is possible to mix SQL and Lua commands using \set language lua and \set language sql commands.

Interaction with the test environment

In lua test you can use test_run module to interact with the test environment.

env = require('test_run')
test_run = env.new()
test_run:cmd("<command>")

Base directives:

Server directives:

Connection switch:

Connection directives(low level):

Filter directives:

Set variables:

Dev ops features:

You can power on any tarantool replicas in a loop.

test_run:cmd('setopt delimiter ";"')
function join(inspector, n)
    for i=1,n do
        local rid = tostring(i)
        os.execute('mkdir -p tmp')
        os.execute('cp ../replication/replica.lua ./tmp/replica'..rid..'.lua')
        os.execute('chmod +x ./tmp/replica'..rid..'.lua')
        inspector:cmd("create server replica"..rid.." with rpl_master=default, script='./var/tmp/replica"..rid..".lua'")
        inspector:cmd("start server replica"..rid)
    end
end;
test_run:cmd('setopt delimiter ""');

-- create 30 replicas for current tarantool
join(test_run, 30)

pretest_clean()

Nothing will be done before a Python test and for core = unittest test suites.

For a core = [app|tarantool] test suites this function removes tarantool WAL and snapshot files before each test.

The following files will be removed:

Tags

Usage:

./test-run.py --tags foo
./test-run.py --tags foo,bar app/ app-tap/

test-run will run only those tests, which have at least one of the provided tags.

Show a list of tags:

./test-run.py --tags
./test-run.py app-tap/ --tags

The tags metainfo should be placed within a first comment of a test file.

Examples:

Unsupported features:

Using luatest

test-run supports tests written in the luatest format. *_test.lua files in a core = luatest test suite are run as part of ./test/test-run.py invocation: no extra actions are needed.

You can also run a particular test using a substring of its full name:

$ ./test/test-run.py foo-luatest/bar_test.lua
$ ./test/test-run.py bar_test.lua
$ ./test/test-run.py bar

If you need to run a particular test case from a luatest compatible test, use luatest command directly. In order to use luatest, which is bundled into test-run, source test-run's environment:

$ . <(./test/test-run.py --env)
$ luatest -v -p my_specific_test_case

Used By