Oninit® ifxTAP — Assertion Reference
Status: pre-release. This is the v1 assertion set as built and tested on Informix 14.10 and 15.0.
Every routine has the tap_ prefix, flags are SMALLINT 0/1, and each form with optional
arguments is a separate overloaded routine. Arguments are positional, in the order shown. Routines that test a value
or a statement end with a descr; schema checks do not, they take object names only and write their
own description. See Examples for them in use.
Arguments
| Argument | Meaning | Type |
|---|---|---|
n | the number of assertions | INTEGER |
msg | the text | LVARCHAR(2000) |
why | the reason | VARCHAR(255) |
num | how many assertions | INTEGER |
ok | 1 for pass, 0 for fail | SMALLINT |
descr | the description shown on the TAP line | VARCHAR(255) |
have | the value under test | one per overload (see tap_is) |
want | the expected value | one per overload (see tap_is) |
pat | a pattern | LVARCHAR(2000) or VARCHAR(255) |
query | a SELECT as a string | LVARCHAR(2000) |
stmt | a statement as a string | LVARCHAR(2000) |
code | an SQLCODE or ISAM code | INTEGER |
table | a table name | VARCHAR(128) |
view | a view name | VARCHAR(128) |
index | an index name | VARCHAR(128) |
trigger | a trigger name | VARCHAR(128) |
col | a column name | VARCHAR(128) |
name | a routine name | VARCHAR(128) |
types | a comma list of types | VARCHAR(255) |
params | a comma list of parameter types | VARCHAR(255) |
returns | a comma list of return types | VARCHAR(255) |
type | a type or access method name | VARCHAR(128) or VARCHAR(32) |
ftable | the referencing table | VARCHAR(128) |
fcol | the referencing column | VARCHAR(128) |
ptable | the referenced table | VARCHAR(128) |
pcol | the referenced column | VARCHAR(128) |
call | an assertion call as a string | LVARCHAR(2000) |
want_ok | the expected pass/fail flag (1 or 0) | SMALLINT |
want_diag | a LIKE pattern for the diagnostic, or NULL | VARCHAR(255) |
Plan and reporting
Every test file declares how many assertions it expects, or says it does not. A file that says it does not (tap_no_plan()) must end with tap_finish(); ending every file with it is a good habit.
| Routine | Purpose |
|---|---|
tap_plan(n) | Declare how many assertions the file will run; a different count fails the file |
tap_no_plan() | Run without a declared count; the file must still call tap_finish() |
tap_finish() | Close the file. Required after tap_no_plan(): a file with no plan that never reaches it is reported as incomplete. A file with a plan is checked against the plan instead, and is complete when the count matches |
tap_diag(msg) | Emit a diagnostic: a # comment in the TAP output, one for every line of the message |
tap_skip(why, num)tap_skip(why) | Report num assertions (default 1) as skipped with a reason; they count as passing |
tap_todo(why, num) | Mark the next num assertions as known gaps: they are reported, but a failure does not fail the run |
tap_todo_start(why) | Mark every assertion between this call and tap_todo_end as TODO |
tap_todo_end() | End a TODO block |
Core assertions
These test values. Each ends with a descr, which is the text of the TAP line. The tap_re_ assertions (see below) use the regular-expression engine built into Informix 14.10 and later: POSIX extended syntax, so [[:alpha:]], |, ^ and $ work, but Perl shorthand such as \d, \w and (?i) does not. A pattern matches anywhere in the value unless anchored, and an invalid pattern fails the test with the engine’s message.
| Routine | Purpose |
|---|---|
tap_ok(ok, descr) | Basic pass/fail: ok is 1 (pass) or 0 (fail) |
tap_pass(descr) | Always passes |
tap_fail(descr) | Always fails |
tap_is(have, want, descr) | Equal. Overloaded for INTEGER, BIGINT, DECIMAL, MONEY, VARCHAR, LVARCHAR, DATE, DATETIME and INTERVAL; fails if either value is NULL |
tap_isnt(have, want, descr) | Not equal. The same overloads; fails if either value is NULL |
tap_is_null(have, descr) | The value is NULL |
tap_isnt_null(have, descr) | The value is not NULL |
tap_alike(have, pat, descr) | The value matches a LIKE pattern |
tap_unalike(have, pat, descr) | The value does not match a LIKE pattern |
tap_ialike(have, pat, descr) | The same as tap_alike, ignoring case |
tap_unialike(have, pat, descr) | The same as tap_unalike, ignoring case |
tap_matches(have, pat, descr) | The value matches an Informix MATCHES pattern (glob, not a regular expression) |
tap_doesnt_match(have, pat, descr) | The value does not match a MATCHES pattern |
tap_imatches(have, pat, descr) | The same as tap_matches, ignoring case |
tap_doesnt_imatch(have, pat, descr) | The same as tap_doesnt_match, ignoring case |
tap_re_matches(have, pat, descr) | The value matches a regular expression (POSIX extended, not PCRE; anywhere in the value unless anchored with ^ and $) |
tap_re_doesnt_match(have, pat, descr) | The value does not match a regular expression |
tap_re_imatches(have, pat, descr) | The same as tap_re_matches, ignoring case |
tap_re_doesnt_imatch(have, pat, descr) | The same as tap_re_doesnt_match, ignoring case |
tap_is_empty(query, descr) | A SELECT, passed as a string, returns no rows |
tap_isnt_empty(query, descr) | A SELECT, passed as a string, returns at least one row |
DATETIME values compare at YEAR TO FRACTION(5) and INTERVAL values at DAY(9) TO FRACTION(5).
Year-month intervals are not covered in v1. Trailing blanks are ignored when character values are compared, as in
Informix SQL. The case-insensitive forms upper-case both sides, so character ranges in a MATCHES pattern
need upper-case bounds.
Regular expressions need the ifxregex DataBlade, which ships with Informix 14.10 and later. In a database with
ordinary logging it is registered for you and nothing needs doing. An ANSI database is the exception: the server
leaves the registration to a DBA, who registers ifxregex.1.10 in that database with blademgr (the DataBlade
Manager). Until then the library still installs and every other assertion works, but each tap_re_ assertion fails, and the
diagnostic says why: regular expression error: regex_match is not available in this database: the ifxregex DataBlade is not
registered. A pattern that is not a valid expression also fails the test, with the engine’s message, instead of ending the file.
Expected errors
These run a statement and look at the error it raises, if any. Each ends with a descr.
| Routine | Purpose |
|---|---|
tap_throws_ok(stmt, code, descr) | The statement fails with this SQLCODE or ISAM code |
tap_throws_like(stmt, pat, descr) | The statement fails and the error text matches a LIKE pattern |
tap_throws_matching(stmt, pat, descr) | The statement fails and the error text matches a MATCHES pattern |
tap_lives_ok(stmt, descr) | The statement runs without error |
The statement is run with EXECUTE IMMEDIATE, which cannot run a bare SELECT. The error text
available to SPL is the short error-information string, usually the name of the object involved, not the full message
(in an ANSI database it carries the owner, so match with '%name%'). Match on the SQLCODE or ISAM code, not on
an SQLSTATE value.
Schema: tables, views, indexes, triggers, routines
Schema checks take object names only and write their own description, such as table customer should exist. Each tap_has_ routine has a tap_hasnt_ twin.
| Routine | Purpose |
|---|---|
tap_has_table(table) | The table exists |
tap_hasnt_table(table) | The table does not exist |
tap_has_view(view) | The view exists |
tap_hasnt_view(view) | The view does not exist |
tap_has_index(table, index) | The table has an index of this name |
tap_hasnt_index(table, index) | The table has no index of this name |
tap_has_trigger(table, trigger) | The table has a trigger of this name |
tap_hasnt_trigger(table, trigger) | The table has no trigger of this name |
tap_has_column(table, col) | The table or view has the column |
tap_hasnt_column(table, col) | The table or view lacks the column |
tap_has_function(name) | A function of this name exists (any overload) |
tap_hasnt_function(name) | No function of this name exists |
tap_can(name) | A routine, procedure or function, of this name exists |
tap_is_procedure(name) | A procedure (one that returns no value) of this name exists |
tap_takes(name, types) | Some overload of the procedure or function takes exactly these parameter types, in order, given as a comma list such as 'INTEGER,DATE' ('' for none); OUT and INOUT parameters count |
tap_signature(name, params, returns) | Some overload takes exactly these parameter types and returns exactly these types ('' for a procedure or for no parameters): the parameters and the return types of the same overload, which tap_takes and tap_function_returns cannot tie together |
tap_function_returns(name, types) | Some overload of the function returns exactly these types, given as a comma list such as 'INTEGER,VARCHAR' |
Names are matched case-insensitively and without an owner, so an object is found whoever owns it. Where a name exists under
two owners (possible in an ANSI database) the current user's object is used; write owner.name to choose
another. An overloaded routine name matches if any overload does. A synonym is not a table.
Schema: columns
| Routine | Purpose |
|---|---|
tap_col_type_is(table, col, type) | The column has this type, named as in CREATE TABLE: INTEGER, VARCHAR, DECIMAL, LVARCHAR, BOOLEAN and so on (no length or precision) |
tap_col_not_null(table, col) | The column is NOT NULL |
tap_col_is_null(table, col) | The column allows NULL |
tap_col_has_default(table, col) | The column has a default |
tap_col_default_is(table, col, want) | The default is this: the literal with its case kept, or TODAY, USER, CURRENT, SITENAME or NULL in any case. A number compares by value (0 equals 0.00) |
Schema: constraints
The tap_col_is_* and tap_col_has_check checks apply when the constraint is on that one column.
| Routine | Purpose |
|---|---|
tap_has_pk(table) | The table has a primary key |
tap_col_is_pk(table, col) | The column is the table's primary key |
tap_has_fk(table) | The table has a foreign key |
tap_col_is_fk(table, col) | The column is a foreign key |
tap_fk_ok(ftable, fcol, ptable, pcol) | A foreign key from fcol of ftable references pcol of ptable |
tap_has_unique(table) | The table has a unique constraint |
tap_col_is_unique(table, col) | The column has a unique constraint |
tap_has_check(table) | The table has a check constraint |
tap_col_has_check(table, col) | The column has a check constraint |
Schema: indexes
| Routine | Purpose |
|---|---|
tap_index_is_unique(table, index) | The index is unique |
tap_index_is_primary(table, index) | The index backs the primary key |
tap_is_indexed(table, col) | An index leads with this column (a column that is only second in a composite index does not count) |
tap_is_clustered(table, index) | The index is clustered |
tap_index_is_type(table, index, type) | The index uses this access method, such as btree |
Utilities
| Routine | Purpose |
|---|---|
tap_version() | The installed library version, as a string |
tap_check_test(call, want_ok, want_diag, descr) | Self-test helper: runs one assertion call and removes the result it recorded; the call must record exactly one. Checks that result's pass/fail flag and, optionally, its diagnostic (a LIKE pattern, or NULL to skip that check) |