git » libfiu » master » tree

[master] / tests / generated / generate-test

#!/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()