add initial test infrastructure
parent
de4a0c65c7
commit
c42e58b868
20
.ci/build-cs
20
.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 ..
|
||||
|
|
12
meson.build
12
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(
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,89 @@
|
|||
/* a rudimentary test runner for cubescript files */
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <cubescript/cubescript.hh>
|
||||
|
||||
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<char[]>(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;
|
||||
}
|
|
@ -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]
|
Loading…
Reference in New Issue