aboutsummaryrefslogtreecommitdiff
path: root/tests/runtests.sh
blob: 19624991f19ae69c08fbd3711ab588b2579288e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223

#!/bin/sh -eu
#
# Script to run tests. Currently there's only a bootstrap compiler, so that's
# all that this script supports.
#
# Copyright © 2026 Samuel Lidén Borell <samuel@kodafritt.se>
#
# SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later
#

srcdir='.'
modules='somelib otherlib app'
cc="${CC:-cc}"
cflags="${CFLAGS:--g} -std=c89  -Wall -Wextra -pedantic -Walloca -Wbad-function-cast -Wcast-align -Wconversion -Wdate-time -Wmissing-declarations -Wmissing-prototypes -Wmissing-noreturn -Wnested-externs -Wnull-dereference -Wold-style-definition -Wshadow -Wshift-negative-value -Wshift-overflow -Wstrict-aliasing -Wstrict-overflow=5 -Wstrict-prototypes -Wswitch-enum -Wundef -Wunused -Wvla -Wwrite-strings -fsanitize=undefined"
quiet=0

parse_args=1
enable_bootstrap_tests=0
enable_all_tests=1
enabled_any_test=0
enable_clean=0
while [ $# != 0 ]; do
    if [ $parse_args = 1 ]; then
        case "$1" in
        --)
            parse_args=0
            ;;
        -h|--help|-help)
                cat <<EOF
usage: $0 [-q|--quiet] [variable=value]... [compiler-stage]...

Runs the test suite. Without arguments, it runs the tests on all compiler
stages.

The compiler-stage parameter is used to specify what to test or do.
By default all tests (currently only "bootstrap") is run.

    bootstrap   - Test the C bootstrap compiler.
    clean       - Remove generated files (always runs first).

You can override certain variables with key=value arguments
Note: These are NOT environment variables, although they are used for defaults.
Note: Use quotes if the values contain spaces or other shell characters.

    srcdir=path     Sets the source directory, can be either of the runtests.sh
                    script or the root source directory. Default: "."
    CC=command      Sets the C compiler to use.
                    Default: CC environment variable, or "cc" if not set.
    CFLAGS=flags    Sets the flags for the C compiler.
                    Default: Various warnings for GCC/clang and -g. If the
                    CFLAGS environment variable is set, it is prepended.

Specifiy the "-q" option to suppress progress output.

Exit status:
    0 - if all tests are successful.
    1 - if any tests fails (or on environment errors)
    2 - if arguments are wrong
    3 - if prerequisite files are not founds (i.e. the compiler to test)
EOF
            exit
            ;;
        -q|--quiet)
            quiet=1
            ;;
        -*)
            printf >&2 '%s: invalid option: %s\n' "$0" "$1"
            exit 2
            ;;
        bootstrap)
            enable_bootstrap_tests=1
            enable_all_tests=0
            enabled_any_test=1
            ;;
        clean)
            enable_clean=1
            enable_all_tests=0
            ;;
        CC=*)
            cc=${1#CC=}
            ;;
        CFLAGS=*)
            cflags=${1#CFLAGS=}
            ;;
        srcdir=*)
            srcdir=${1#srcdir=}
            ;;
        *=*)
            printf >&2 '%s: unknown flag: %s\n' "$0" "${1#=*}"
            exit 2
            ;;
        *)
            printf >&2 '%s: unknown compiler stage: %s\n' "$0" "$1"
            exit 2
            ;;
        esac
    fi
    shift
done

if [ $enable_all_tests = 1 ]; then
    enable_bootstrap_tests=1
    enabled_any_test=1
fi

# Locate directories
if [ -f "$srcdir"/tests/runtests.sh ]; then
    true
elif [ -f "$srcdir"/runtests.sh ]; then
    srcdir="$srcdir"/..
else
    printf >&2 'Cannot locate source tree in "%s".\n' "$srcdir"
    exit 1
fi

# =======================================================================
# |||||||||||||||||||||||||  Test preparations  |||||||||||||||||||||||||
# =======================================================================
error=0
if [ $enable_bootstrap_tests = 1 ]; then
    # Ensure that the bootstrap compiler has been built
    if [ -f bootstrap/stage1 ]; then
        bsdir=bootstrap
    elif [ -f stage1 ]; then
        bsdir=.
    elif [ -f ../bootstrap/stage1 ]; then
        bsdir=../bootstrap
    else
        echo >&2 'Could not locate the bootstrap compiler (stage1)'
        echo >&2 'You need to build it before you can run the tests.'
        error=1
    fi
    if [ -n "${bsdir:-}" ]; then
        bscomp="$bsdir"/stage1
    fi
fi

if [ -d tests ]; then
    outdir='tests/out'
else
    outdir=out
fi
if [ $enable_clean = 1 ]; then
    for module in $modules; do
        rm -f "$outdir/$module.o" \
              "$outdir/$module.c" \
              "$outdir/$module" \
              "$outdir/interfaces/$module.slul"
    done
    rm -f "$outdir/interfaces/core_wip.slul"
    if [ $enabled_any_test = 0 ]; then
        exit
    fi
fi
if ! mkdir -p "$outdir"; then
    printf >&2 'Cannot create output directory "%s".\n' "$outdir"
    error=1
fi

interfacedir="$srcdir/stdlib/interfaces"
if [ ! -d "$interfacedir" ]; then
    printf >&2 'Interfaces directory %s not found.\n' "$interfacedir"
    error=1
fi

if [ $error = 1 ]; then
    exit 3
fi

# =======================================================================
# |||||||||||||||||||||||||||||  Run tests  |||||||||||||||||||||||||||||
# =======================================================================
if [ $enable_bootstrap_tests = 1 ]; then
    mkdir -p "$outdir"/interfaces
    cp "$srcdir"/stdlib/interfaces/core_wip.slul "$outdir"/interfaces/
    for module in $modules; do
        if [ $quiet = 0 ]; then
            printf "=== bootstrap: %s ===\n" "$module"
        fi
        modpath="$srcdir/tests/$module"
        genfile="$outdir/$module.c"
        if [ -e "$modpath/interface.slul" ]; then
            # It's a library. Always copy it, to allow for testing of
            # error handling in imported interface files
            cp "$modpath/interface.slul" "$outdir/interfaces/$module.slul"
        fi
        if ! "$bscomp" "$outdir"/interfaces "$modpath" "$genfile"; then
            printf >&2 'Failed to compile "%s" with bootstrap compiler!\n' \
                "$module"
            error=1
            continue
        fi
        if ! "$cc" $cflags -I"$srcdir"/bootstrap/rtlincl \
                -c "$genfile" -o "$outdir/$module.o"; then
            printf >&2 'Failed to compile "%s" with C compiler\n' \
                "$genfile"
            error=1
            continue
        fi
        if [ ! -e "$modpath/interface.slul" ]; then
            # It's an executable application
            "$cc" $cflags \
                "$bsdir/rtl/cli.o" \
                "$bsdir/rtl/fail.o" \
                "$bsdir/rtl/message.o" \
                "$bsdir/rtl/string.o" \
                "$bsdir/rtl/writer.o" \
                "$outdir/somelib.o" \
                "$outdir/otherlib.o" \
                "$outdir/$module.o" \
                -o "$outdir/$module"
            if ! "$outdir/$module"; then
                printf >&2 'Test module "%s" returned error code %d.\n' \
                    "$outdir/$module" $?
                error=1
            fi
        fi
    done
fi

if [ $error = 1 ]; then
    exit 1
fi