From 21657b7a78f50a267a3e8574fcd92bde35ba8e3b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 25 Nov 2019 12:24:23 -0800 Subject: [PATCH] Add a rudimentary testsuite. This adds a very primitive test harness and tests relevant to the recent changes to how program startup works, as well as the upcoming changes to support LTO. --- tests/.gitignore | 2 + tests/compile-only/addresses.c | 19 +++++++ tests/compile-only/test.cc | 1 + tests/empty-expected.wat | 13 +++++ tests/exit_status_zero | 1 + tests/general/argc_argv_main.c | 6 ++ .../general/argc_argv_main.c.stdout.expected | 1 + tests/general/argc_argv_main.cc | 6 ++ .../general/argc_argv_main.cc.stdout.expected | 1 + tests/general/ctors_dtors.c | 52 +++++++++++++++++ tests/general/ctors_dtors.c.stdout.expected | 10 ++++ tests/general/ctors_dtors.cc | 16 ++++++ tests/general/ctors_dtors.cc.stdout.expected | 12 ++++ tests/general/empty.c | 1 + tests/general/iostream_main.cc | 6 ++ .../general/iostream_main.cc.stdout.expected | 1 + tests/general/main_errno.c | 12 ++++ tests/general/main_errno.c.stdout.expected | 1 + tests/general/no_arg_main.c | 6 ++ tests/general/no_arg_main.c.stdout.expected | 1 + tests/general/no_arg_main.cc | 6 ++ tests/general/no_arg_main.cc.stdout.expected | 1 + tests/general/void_main.c | 6 ++ tests/general/void_main.c.stdout.expected | 1 + tests/general/void_main.cc | 6 ++ tests/general/void_main.cc.stdout.expected | 1 + tests/run.sh | 32 +++++++++++ tests/testcase.sh | 57 +++++++++++++++++++ 28 files changed, 278 insertions(+) create mode 100644 tests/.gitignore create mode 100644 tests/compile-only/addresses.c create mode 100644 tests/compile-only/test.cc create mode 100644 tests/empty-expected.wat create mode 100644 tests/exit_status_zero create mode 100644 tests/general/argc_argv_main.c create mode 100644 tests/general/argc_argv_main.c.stdout.expected create mode 100644 tests/general/argc_argv_main.cc create mode 100644 tests/general/argc_argv_main.cc.stdout.expected create mode 100644 tests/general/ctors_dtors.c create mode 100644 tests/general/ctors_dtors.c.stdout.expected create mode 100644 tests/general/ctors_dtors.cc create mode 100644 tests/general/ctors_dtors.cc.stdout.expected create mode 100644 tests/general/empty.c create mode 100644 tests/general/iostream_main.cc create mode 100644 tests/general/iostream_main.cc.stdout.expected create mode 100644 tests/general/main_errno.c create mode 100644 tests/general/main_errno.c.stdout.expected create mode 100644 tests/general/no_arg_main.c create mode 100644 tests/general/no_arg_main.c.stdout.expected create mode 100644 tests/general/no_arg_main.cc create mode 100644 tests/general/no_arg_main.cc.stdout.expected create mode 100644 tests/general/void_main.c create mode 100644 tests/general/void_main.c.stdout.expected create mode 100644 tests/general/void_main.cc create mode 100644 tests/general/void_main.cc.stdout.expected create mode 100755 tests/run.sh create mode 100755 tests/testcase.sh diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..8ac0ced --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,2 @@ +*.observed +*.wasm diff --git a/tests/compile-only/addresses.c b/tests/compile-only/addresses.c new file mode 100644 index 0000000..c97dc60 --- /dev/null +++ b/tests/compile-only/addresses.c @@ -0,0 +1,19 @@ +#include +#include + +extern void __dso_handle; +extern void __data_end; +extern void __global_base; +extern void __heap_base; + +int main(int argc, char *argv[]) { + printf("NULL=%p\n", NULL); + printf("__dso_handle=%p\n", &__dso_handle); + printf("__data_end=%p\n", &__data_end); + printf("__global_base=%p\n", &__global_base); + printf("__heap_base=%p\n", &__heap_base); + printf("__builtin_frame_address(0)=%p\n", __builtin_frame_address(0)); + printf("__builtin_alloca(0)=%p\n", __builtin_alloca(0)); + printf("__builtin_wasm_memory_size(0)=%p\n", (void *)(__builtin_wasm_memory_size(0) * PAGE_SIZE)); + return 0; +} diff --git a/tests/compile-only/test.cc b/tests/compile-only/test.cc new file mode 100644 index 0000000..979a1cd --- /dev/null +++ b/tests/compile-only/test.cc @@ -0,0 +1 @@ +int main(){} diff --git a/tests/empty-expected.wat b/tests/empty-expected.wat new file mode 100644 index 0000000..835923c --- /dev/null +++ b/tests/empty-expected.wat @@ -0,0 +1,13 @@ +(module + (type (;0;) (func)) + (type (;1;) (func (result i32))) + (func $__wasm_call_ctors (type 0)) + (func $_start (type 0) + call $__wasm_call_ctors) + (func $__main_void (type 1) (result i32) + i32.const 0) + (table (;0;) 1 1 funcref) + (memory (;0;) 2) + (global (;0;) (mut i32) (i32.const 66560)) + (export "memory" (memory 0)) + (export "_start" (func $_start))) diff --git a/tests/exit_status_zero b/tests/exit_status_zero new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/exit_status_zero @@ -0,0 +1 @@ +0 diff --git a/tests/general/argc_argv_main.c b/tests/general/argc_argv_main.c new file mode 100644 index 0000000..114d1c6 --- /dev/null +++ b/tests/general/argc_argv_main.c @@ -0,0 +1,6 @@ +#include + +int main(int argc, char *argv[]) { + puts("hello from argc argv main!"); + return 0; +} diff --git a/tests/general/argc_argv_main.c.stdout.expected b/tests/general/argc_argv_main.c.stdout.expected new file mode 100644 index 0000000..851e349 --- /dev/null +++ b/tests/general/argc_argv_main.c.stdout.expected @@ -0,0 +1 @@ +hello from argc argv main! diff --git a/tests/general/argc_argv_main.cc b/tests/general/argc_argv_main.cc new file mode 100644 index 0000000..e4f3566 --- /dev/null +++ b/tests/general/argc_argv_main.cc @@ -0,0 +1,6 @@ +#include + +int main(int argc, char *argv[]) { + puts("hello from C++ argc argv main!"); + return 0; +} diff --git a/tests/general/argc_argv_main.cc.stdout.expected b/tests/general/argc_argv_main.cc.stdout.expected new file mode 100644 index 0000000..8d6f69a --- /dev/null +++ b/tests/general/argc_argv_main.cc.stdout.expected @@ -0,0 +1 @@ +hello from C++ argc argv main! diff --git a/tests/general/ctors_dtors.c b/tests/general/ctors_dtors.c new file mode 100644 index 0000000..01c75fb --- /dev/null +++ b/tests/general/ctors_dtors.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +extern char **environ; + +static void from_atexit(void) { + printf("hello from_atexit\n"); +} + +static void another_from_atexit(void) { + printf("hello another_from_atexit\n"); +} + +__attribute__((constructor)) static void from_constructor(void) { + printf("hello from_constructor\n"); +} + +__attribute__((constructor(101))) static void from_constructor_101(void) { + assert(errno == 0); + printf("hello from_constructor101\n"); + + assert(environ && "environment should be initialized by this point"); +} + +__attribute__((constructor(65535))) static void from_constructor_65535(void) { + printf("hello from_constructor65535\n"); +} + +__attribute__((destructor)) static void from_destructor(void) { + printf("hello from_destructor\n"); +} + +__attribute__((destructor(101))) static void from_destructor101(void) { + printf("hello from_destructor101\n"); +} + +__attribute__((destructor(65535))) static void from_destructor65535(void) { + printf("hello from_destructor65535\n"); +} + +int main(int argc, char *argv[]) { + printf("hello main\n"); + assert(argc != 0); + assert(argv != NULL); + assert(argv[argc] == NULL); + + atexit(from_atexit); + atexit(another_from_atexit); + printf("goodbye main\n"); + return 0; +} diff --git a/tests/general/ctors_dtors.c.stdout.expected b/tests/general/ctors_dtors.c.stdout.expected new file mode 100644 index 0000000..dfe8292 --- /dev/null +++ b/tests/general/ctors_dtors.c.stdout.expected @@ -0,0 +1,10 @@ +hello from_constructor101 +hello from_constructor +hello from_constructor65535 +hello main +goodbye main +hello another_from_atexit +hello from_atexit +hello from_destructor65535 +hello from_destructor +hello from_destructor101 diff --git a/tests/general/ctors_dtors.cc b/tests/general/ctors_dtors.cc new file mode 100644 index 0000000..f9e011b --- /dev/null +++ b/tests/general/ctors_dtors.cc @@ -0,0 +1,16 @@ +#include "ctors_dtors.c" + +struct StaticObject { + StaticObject(); + ~StaticObject(); +}; + +StaticObject::StaticObject() { + printf("hello StaticObject::StaticObject\n"); +} + +StaticObject::~StaticObject() { + printf("hello StaticObject::~StaticObject\n"); +} + +static StaticObject static_object; diff --git a/tests/general/ctors_dtors.cc.stdout.expected b/tests/general/ctors_dtors.cc.stdout.expected new file mode 100644 index 0000000..675900b --- /dev/null +++ b/tests/general/ctors_dtors.cc.stdout.expected @@ -0,0 +1,12 @@ +hello from_constructor101 +hello from_constructor +hello from_constructor65535 +hello StaticObject::StaticObject +hello main +goodbye main +hello another_from_atexit +hello from_atexit +hello from_destructor65535 +hello from_destructor +hello StaticObject::~StaticObject +hello from_destructor101 diff --git a/tests/general/empty.c b/tests/general/empty.c new file mode 100644 index 0000000..78f2de1 --- /dev/null +++ b/tests/general/empty.c @@ -0,0 +1 @@ +int main(void) { return 0; } diff --git a/tests/general/iostream_main.cc b/tests/general/iostream_main.cc new file mode 100644 index 0000000..789c143 --- /dev/null +++ b/tests/general/iostream_main.cc @@ -0,0 +1,6 @@ +#include + +int main() { + std::cout << "hello from C++ main with cout!" << std::endl; + return 0; +} diff --git a/tests/general/iostream_main.cc.stdout.expected b/tests/general/iostream_main.cc.stdout.expected new file mode 100644 index 0000000..bbbdf77 --- /dev/null +++ b/tests/general/iostream_main.cc.stdout.expected @@ -0,0 +1 @@ +hello from C++ main with cout! diff --git a/tests/general/main_errno.c b/tests/general/main_errno.c new file mode 100644 index 0000000..6a51552 --- /dev/null +++ b/tests/general/main_errno.c @@ -0,0 +1,12 @@ +#include +#include +#include + +// It isn't required that errno be zero on entry to main, but +// for tidiness' sake, if we ever do things during startup that +// do set errno, we should reset it for tidiness' sake. +int main(void) { + int n = errno; + printf("initial errno is %d: %s\n", n, strerror(n)); + return 0; +} diff --git a/tests/general/main_errno.c.stdout.expected b/tests/general/main_errno.c.stdout.expected new file mode 100644 index 0000000..c537e5f --- /dev/null +++ b/tests/general/main_errno.c.stdout.expected @@ -0,0 +1 @@ +initial errno is 0: Success diff --git a/tests/general/no_arg_main.c b/tests/general/no_arg_main.c new file mode 100644 index 0000000..56d7537 --- /dev/null +++ b/tests/general/no_arg_main.c @@ -0,0 +1,6 @@ +#include + +int main() { + puts("hello from no-arg main!"); + return 0; +} diff --git a/tests/general/no_arg_main.c.stdout.expected b/tests/general/no_arg_main.c.stdout.expected new file mode 100644 index 0000000..d5ae29d --- /dev/null +++ b/tests/general/no_arg_main.c.stdout.expected @@ -0,0 +1 @@ +hello from no-arg main! diff --git a/tests/general/no_arg_main.cc b/tests/general/no_arg_main.cc new file mode 100644 index 0000000..91cfd34 --- /dev/null +++ b/tests/general/no_arg_main.cc @@ -0,0 +1,6 @@ +#include + +int main() { + puts("hello from C++ no-arg main!"); + return 0; +} diff --git a/tests/general/no_arg_main.cc.stdout.expected b/tests/general/no_arg_main.cc.stdout.expected new file mode 100644 index 0000000..1aa719e --- /dev/null +++ b/tests/general/no_arg_main.cc.stdout.expected @@ -0,0 +1 @@ +hello from C++ no-arg main! diff --git a/tests/general/void_main.c b/tests/general/void_main.c new file mode 100644 index 0000000..54920fc --- /dev/null +++ b/tests/general/void_main.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + puts("hello from void main!"); + return 0; +} diff --git a/tests/general/void_main.c.stdout.expected b/tests/general/void_main.c.stdout.expected new file mode 100644 index 0000000..533c359 --- /dev/null +++ b/tests/general/void_main.c.stdout.expected @@ -0,0 +1 @@ +hello from void main! diff --git a/tests/general/void_main.cc b/tests/general/void_main.cc new file mode 100644 index 0000000..25eb91c --- /dev/null +++ b/tests/general/void_main.cc @@ -0,0 +1,6 @@ +#include + +int main(void) { + puts("hello from C++ void main!"); + return 0; +} diff --git a/tests/general/void_main.cc.stdout.expected b/tests/general/void_main.cc.stdout.expected new file mode 100644 index 0000000..d1f259d --- /dev/null +++ b/tests/general/void_main.cc.stdout.expected @@ -0,0 +1 @@ +hello from C++ void main! diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..9954c77 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -ueo pipefail + +runwasm="$1" + +cd compile-only +for options in -O0 -O2 "-O2 -flto"; do + echo "===== Testing compile-only with $options =====" + for file in *.c; do + echo "Testing compile-only $file..." + ../testcase.sh true clang "$options" "$file" + done + for file in *.cc; do + echo "Testing compile-only $file..." + ../testcase.sh true clang++ "$options" "$file" + done +done +cd - >/dev/null + +cd general +for options in -O0 -O2 "-O2 -flto"; do + echo "===== Testing with $options =====" + for file in *.c; do + echo "Testing $file..." + ../testcase.sh "$runwasm" clang "$options" "$file" + done + for file in *.cc; do + echo "Testing $file..." + ../testcase.sh "$runwasm" clang++ "$options" "$file" + done +done +cd - >/dev/null diff --git a/tests/testcase.sh b/tests/testcase.sh new file mode 100755 index 0000000..d5f5824 --- /dev/null +++ b/tests/testcase.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -ueo pipefail + +# A simple testcase runner that runs a command, captures all its command-line +# outputs, and compares them against expected outputs. + +runwasm="$1" +clang="$2" +options="$3" +input="$4" + +wasm="$input.$options.wasm" +stdout_observed="$input.$options.stdout.observed" +stderr_observed="$input.$options.stderr.observed" +exit_status_observed="$input.$options.exit_status.observed" + +if [ -e "$input.options" ]; then + file_options=$(cat "$inpit.options") +else + file_options= +fi + +echo "Testing $input..." +"$clang" $options $file_options "$input" -o "$wasm" +if [ -e "$input.stdin" ]; then + stdin="$input.stdin" +else + stdin="/dev/null" +fi + +exit_status=0 +"$runwasm" "$wasm" \ + < "$stdin" \ + > "$stdout_observed" \ + 2> "$stderr_observed" \ + || exit_status=$? +echo $exit_status > "$exit_status_observed" + +if [ -e "$input.stdout.expected" ]; then + stdout_expected="$input.stdout.expected" +else + stdout_expected="/dev/null" +fi +if [ -e "$input.stderr.expected" ]; then + stderr_expected="$input.stderr.expected" +else + stderr_expected="/dev/null" +fi +if [ -e "$input.exit_status.expected" ]; then + exit_status_expected="$input.exit_status.expected" +else + exit_status_expected=../exit_status_zero +fi + +diff -u "$stderr_expected" "$stderr_observed" +diff -u "$stdout_expected" "$stdout_observed" +diff -u "$exit_status_expected" "$exit_status_observed"