#!/usr/bin/env python3
"""
Generate C testcases based on the given configuration file.
It can be useful for testing many C functions in the same way, like when
testing a wrapper.
"""
import sys
import configparser
import argparse
from string import Template
def listify(x):
if isinstance(x, str):
return [x]
return x
TEST_SUCCESS_TMPL = r"""
int success(void) {
$prep
fiu_disable("$fp");
$call
if (! ($success_cond) ) {
printf("$fp - success condition is false\n");
return -1;
}
return 0;
}
"""
TEST_FAILURE_TMPL = r"""
static int external_cb_was_called = 0;
int external_cb(const char *name, int *failnum,
void **failinfo, unsigned int *flags) {
external_cb_was_called++;
*failinfo = (void *) $errno_on_fail;
return *failnum;
}
int failure(void) {
$prep
fiu_enable_external("$fp", 1, NULL, 0, external_cb);
$call
fiu_disable("$fp");
if (external_cb_was_called != 1) {
printf("$fp - external callback not invoked\n");
return -1;
}
if (! ($errno_cond) ) {
printf("$fp - errno not set appropriately: ");
printf("errno:%d, cond:$errno_cond\n", errno);
return -1;
}
if (! ($failure_cond) ) {
printf("$fp - failure condition is false\n");
return -1;
}
return 0;
}
"""
TEST_MAIN_TMPL = r"""
int main(void) {
int s, f;
s = success();
f = failure();
return s + f;
}
"""
TEST_SKIPPED_TMPL = r"""
int main(void) {
printf("$fp: skipping test\n");
return 0;
}
"""
def generate(options, outfile):
outfile.write("/* AUTOGENERATED FILE - DO NOT EDIT */\n\n")
outfile.write("#include <fiu.h>\n")
outfile.write("#include <fiu-control.h>\n")
outfile.write("#include <stdio.h>\n")
outfile.write("#include <errno.h>\n")
includes = options.get("include", [])
if isinstance(includes, str):
includes = includes.split()
for i in includes:
outfile.write("#include <%s>\n" % i)
else:
outfile.write("\n\n")
if options['if']:
outfile.write("#if %s\n" % options['if'])
if 'errno_on_fail' in options:
options['errno_cond'] = \
'errno == %s' % options['errno_on_fail']
else:
# Default the cond to true, and set failinfo to 0 in case it's
# used.
options['errno_cond'] = '1'
options['errno_on_fail'] = '0'
outfile.write(Template(TEST_SUCCESS_TMPL).substitute(options))
outfile.write(Template(TEST_FAILURE_TMPL).substitute(options))
outfile.write(Template(TEST_MAIN_TMPL).substitute(options))
if options['if']:
outfile.write("#else\n")
outfile.write(Template(TEST_SKIPPED_TMPL).substitute(options))
outfile.write("#endif\n")
def main():
parser = argparse.ArgumentParser(
description ="Generate C testcases")
parser.add_argument("-c", "--conf", metavar = "C",
required = True, type = argparse.FileType('rt'),
help = "configuration file")
parser.add_argument("-o", "--out", metavar = "F",
required = True,
help = "generated file")
args = parser.parse_args()
# Defaults for the optional configuration parameters.
conf_defaults = {
'include': (),
'prep': '',
'if': '',
# These are C conditions that are always true.
'success_cond': '1',
'failure_cond': '1',
# For errno_on_fail, we have a smarter logic so don't do
# anything.
}
conf = configparser.ConfigParser(conf_defaults)
conf.read_file(args.conf)
section = conf.sections()[0]
outfile = open(args.out, 'w')
generate(dict(conf.items(section)), outfile)
if __name__ == "__main__":
main()