From c42e58b86871dfaefc4d91f44fd84406ecf619af Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Tue, 13 Apr 2021 02:49:38 +0200 Subject: [PATCH] add initial test infrastructure --- .ci/build-cs | 20 ++++++++++- meson.build | 12 +++++++ meson_options.txt | 12 +++++++ tests/meson.build | 38 ++++++++++++++++++++ tests/runner.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++ tests/simple.cube | 21 +++++++++++ 6 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 tests/meson.build create mode 100644 tests/runner.cc create mode 100644 tests/simple.cube diff --git a/.ci/build-cs b/.ci/build-cs index b598e1a..649bd87 100755 --- a/.ci/build-cs +++ b/.ci/build-cs @@ -136,11 +136,29 @@ cd build args="" if [ -n "${cross}" ]; then + if [ "${meson_system}" = "windows" ]; then + cat << EOF > meson-exewrapper +#!/bin/sh +export WINEDEBUG=-all +export WINEPREFIX="$(pwd)/.wine" +export DISPLAY= +wine "\$@" +EOF + else + cat << EOF > meson-exewrapper +#!/bin/sh +qemu-${qemu_cpu} -L /usr/${expected_triplet} "\$@" +EOF + fi + chmod +x meson-exewrapper cat << EOF > crossfile [binaries] c = '${CC}' cpp = '${CXX}' +ar = '${AR}' +as = '${AS}' strip = '${STRIP}' +exe_wrapper = '$(pwd)/meson-exewrapper' [host_machine] system = '${meson_system}' @@ -154,7 +172,7 @@ if [ -n "$BUILDTYPE" ]; then args="${args} --buildtype=$BUILDTYPE" fi -meson .. ${args} || exit 1 +meson .. -Dtests_cross=true ${args} || exit 1 ninja all || exit 1 ninja test || exit 1 cd .. diff --git a/meson.build b/meson.build index b152c97..1a5dc3d 100644 --- a/meson.build +++ b/meson.build @@ -47,10 +47,22 @@ if get_option('cpp_std') == 'none' endif endif +build_root = meson.current_build_dir() + subdir('include') subdir('src') subdir('tools') +if meson.is_cross_build() and get_option('tests') + build_tests = get_option('tests_cross') +else + build_tests = get_option('tests') +endif + +if build_tests + subdir('tests') +endif + pkg = import('pkgconfig') pkg.generate( diff --git a/meson_options.txt b/meson_options.txt index 88ca41e..dc6c6ee 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -9,3 +9,15 @@ option('linenoise', value: 'auto', description: 'Use linenoise for the REPL' ) + +option('tests', + type: 'boolean', + value: 'true', + description: 'Whether to build tests' +) + +option('tests_cross', + type: 'boolean', + value: 'false', + description: 'Whether to build tests when cross-compiling' +) diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..d5193fe --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,38 @@ +lang_tests = [ + # test_name test_file expected_fail + ['simple example', 'simple', false], +] + +lib_tests = [ +] + +test_runner = executable('runner', + ['runner.cc'], + dependencies: libcubescript, + include_directories: libcubescript_includes, + cpp_args: extra_cxxflags, + install: false +) + +penv = environment() +penv.append('PATH', join_paths(build_root, 'src')) + +foreach tcase: lang_tests + test(tcase[0], + test_runner, + args: [join_paths(meson.current_source_dir(), tcase[1] + '.cube')], + should_fail: tcase[2], + env: penv + ) +endforeach + +foreach tcase: lib_tests + test_exe = executable(tcase[0], + [tcase[0] + '.cc'], + dependencies: libcubescript, + include_directories: libcubescript_includes, + cpp_args: extra_cxxflags, + instalL: false + ) + test(tcase[0], tcase[0], should_fail: tcase[1], env: penv) +endforeach diff --git a/tests/runner.cc b/tests/runner.cc new file mode 100644 index 0000000..871e768 --- /dev/null +++ b/tests/runner.cc @@ -0,0 +1,89 @@ +/* a rudimentary test runner for cubescript files */ + +#include +#include +#include + +#include + +namespace cs = cubescript; + +struct skip_test {}; + +static bool do_run_file(cs::state &cs, char const *fname) { + FILE *f = std::fopen(fname, "rb"); + if (!f) { + return false; + } + + std::fseek(f, 0, SEEK_END); + auto len = std::ftell(f); + std::fseek(f, 0, SEEK_SET); + + auto buf = std::make_unique(len + 1); + if (!buf) { + std::fclose(f); + return false; + } + + if (std::fread(buf.get(), 1, len, f) != std::size_t(len)) { + std::fclose(f); + return false; + } + + buf[len] = '\0'; + + cs.run(std::string_view{buf.get(), std::size_t(len)}, fname); + return true; +} + +int main(int argc, char **argv) { + if (argc != 2) { + std::fprintf(stderr, "error: incorrect number of arguments\n"); + } + + cs::state gcs; + cs::std_init_all(gcs); + + gcs.new_command("echo", "C", [](auto &s, auto args, auto &) { + std::printf("%s\n", args[0].get_string(s).view().data()); + }); + + gcs.new_command("skip_test", "", [](auto &, auto, auto &) { + throw skip_test{}; + }); + + /* takes a string so we can print it */ + gcs.new_command("assert", "ssN", [](auto &s, auto args, auto &ret) { + auto val = args[0]; + val.force_code(s); + if (!s.run(val.get_code()).get_bool()) { + if (args[2].get_integer() > 1) { + throw cs::error{ + s, "assertion failed: [%s] (%s)", + args[0].get_string(s).data(), args[1].get_string(s).data() + }; + } else { + throw cs::error{ + s, "assertion failed: [%s]", + args[0].get_string(s).data() + }; + } + } + ret = std::move(args[0]); + }); + + try { + do_run_file(gcs, argv[1]); + } catch (skip_test) { + return 77; + } catch (cs::error const &e) { + std::fprintf(stderr, "error: %s", e.what().data()); + return 1; + } catch (...) { + std::fprintf(stderr, "error: unknown error"); + return 1; + } + + return 0; +} diff --git a/tests/simple.cube b/tests/simple.cube new file mode 100644 index 0000000..535c606 --- /dev/null +++ b/tests/simple.cube @@ -0,0 +1,21 @@ +// a simple test mainly to serve as an example + +echo "hello world" + +// addition +assert [= (+ 5 10) 15] +assert [!= (+ 4 5) 11] + +// multiplication +assert [= (* 5 2) 10] +assert [!= (* 3 4) 13] + +// string equality +assert [=s "hello world" "hello world"] +assert [!=s "hello world" "dlrow olleh"] + +// simple loop +x = 5 +loop i 3 [x = (* $x 2); echo $x] + +assert [= $x 40]