Skip to content

Commit 409d530

Browse files
[ fix ] test runner to use cabal (#2884) (#2896)
* [ fix ] add test runner (#2884) * [ test ] update bounds This is probably not going to build (missing bytestring / random dependencies in the sample cabal file). * [ fix ] relax directory constraints, fix deprecated use * [ fix ] bytestring dependency * [ fix ] random dependency * [ fix ] test runner for random should ignore outputs * [ fix ] the extra arguments may have spaces in them * [ cleanup ] * CI: remove JOSS build * Test runner: print test name without buffering * Script: fix issue leading to nested _build/_build/... * [ re #2896 ] Restrict clock types * [ oops ] forgot to fix the re-export * [ fix ] typo 🤦 * [ fix ] this casing is killing me --------- Co-authored-by: G. Allais <guillaume.allais@ens-lyon.org>
1 parent 4e0256c commit 409d530

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+373
-261
lines changed

.github/workflows/ci-ubuntu.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ jobs:
7676
if [[ '${{ github.ref }}' == 'refs/heads/experimental' \
7777
|| '${{ github.base_ref }}' == 'experimental' ]]; then
7878
# Pick Agda version for experimental
79-
echo "AGDA_COMMIT=3d04bacca842729f9c0869b9287256321b5f450f" >> "${GITHUB_ENV}";
80-
# Andreas, 2025-10-07: 3d04bacca842729f9c0869b9287256321b5f450f is tags/v2.8.0
79+
echo "AGDA_COMMIT=tags/v2.8.0" >> "${GITHUB_ENV}";
8180
echo "AGDA_HTML_DIR=html/experimental" >> "${GITHUB_ENV}"
8281
else
8382
# Pick Agda version for master
@@ -170,8 +169,7 @@ jobs:
170169
171170
- name: Golden testing
172171
run: |
173-
${{ env.CABAL_V1_INSTALL }} clock
174-
make testsuite INTERACTIVE='' AGDA_EXEC='~/.cabal/bin/agda'
172+
make testsuite INTERACTIVE='' AGDA_EXEC='agda' GHC_EXEC='ghc'
175173
176174
177175
########################################################################

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ GenerateEverything
2727
Haskell
2828
html
2929
log
30+
logs/
3031
MAlonzo
3132
output
3233
runtests

GNUmakefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
GHC_EXEC ?= ghc
12
AGDA_EXEC ?= agda
23
AGDA_OPTIONS=-Werror
34
AGDA_RTS_OPTIONS=+RTS -M4.0G -H3.5G -A128M -RTS
@@ -14,7 +15,7 @@ test: doc/Everything.agda check-whitespace
1415
cd doc && $(AGDA) README.agda
1516

1617
testsuite:
17-
$(MAKE) -C tests test AGDA="$(AGDA)" AGDA_EXEC="$(AGDA_EXEC)" only=$(only)
18+
$(MAKE) -C tests test GHC_EXEC="$(GHC_EXEC)" AGDA="$(AGDA)" only=$(only)
1819

1920
fix-whitespace:
2021
$(CABAL_EXEC) exec -- fix-whitespace

src/System/Clock.agda

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,13 @@ open import System.Clock.Primitive as Prim
3737
-- System-wide monotonic time since an arbitrary point in the past
3838
; monotonic
3939
-- System-wide real time since the Epoch
40-
; realTime
40+
; realtime
4141
-- Amount of execution time of the current process
4242
; processCPUTime
4343
-- Amount of execution time of the current OS thread
4444
; threadCPUTime
4545
-- A raw hardware version of Monotonic ignoring adjustments
4646
; monotonicRaw
47-
-- Linux-specific clocks
48-
-- Similar to Monotonic, includes time spent suspended
49-
; bootTime
50-
-- Faster but less precise alternative to Monotonic
51-
; monotonicCoarse
52-
-- Faster but less precise alternative to RealTime
53-
; realTimeCoarse
5447
)
5548

5649
------------------------------------------------------------------------
@@ -86,9 +79,9 @@ record Timed (A : Set a) : Set a where
8679

8780
time : IO A IO (Timed A)
8881
time io = do
89-
start lift! $ getTime realTime
82+
start lift! $ getTime realtime
9083
a io
91-
end lift! $ getTime realTime
84+
end lift! $ getTime realtime
9285
pure $ mkTimed a $ diff (lower start) (lower end)
9386

9487
time′ : IO {0ℓ} A IO Time

src/System/Clock/Primitive.agda

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,39 @@ open import IO.Primitive.Core using (IO)
1313
open import Foreign.Haskell using (Pair)
1414

1515
data Clock : Set where
16-
monotonic realTime processCPUTime : Clock
17-
threadCPUTime monotonicRaw bootTime : Clock
18-
monotonicCoarse realTimeCoarse : Clock
16+
monotonic realtime processCPUTime : Clock
17+
threadCPUTime monotonicRaw : Clock
1918

20-
{-# COMPILE GHC Clock = data Clock (Monotonic | Realtime | ProcessCPUTime
21-
| ThreadCPUTime | MonotonicRaw | Boottime
22-
| MonotonicCoarse | RealtimeCoarse )
19+
{-# FOREIGN GHC import System.Clock #-}
20+
21+
{-# FOREIGN GHC
22+
data AgdaClock
23+
= AgdaMonotonic
24+
| AgdaRealtime
25+
| AgdaProcessCPUTime
26+
| AgdaThreadCPUTime
27+
| AgdaMonotonicRaw
28+
29+
fromAgdaClock :: AgdaClock -> Clock
30+
fromAgdaClock ac = case ac of
31+
AgdaMonotonic -> Monotonic
32+
AgdaRealtime -> Realtime
33+
AgdaProcessCPUTime -> ProcessCPUTime
34+
AgdaThreadCPUTime -> ThreadCPUTime
35+
AgdaMonotonicRaw -> MonotonicRaw
36+
#-}
37+
38+
{-# COMPILE GHC Clock =
39+
data AgdaClock
40+
( AgdaMonotonic
41+
| AgdaRealtime
42+
| AgdaProcessCPUTime
43+
| AgdaThreadCPUTime
44+
| AgdaMonotonicRaw
45+
)
2346
#-}
2447

2548
postulate getTime : Clock IO (Pair Nat Nat)
2649

27-
{-# FOREIGN GHC import System.Clock #-}
2850
{-# FOREIGN GHC import Data.Function #-}
29-
{-# COMPILE GHC getTime = fmap (\ (TimeSpec a b) -> ((,) `on` fromIntegral) a b) . getTime #-}
51+
{-# COMPILE GHC getTime = fmap (\ (TimeSpec a b) -> ((,) `on` fromIntegral) a b) . getTime . fromAgdaClock #-}

src/Test/Golden.agda

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ open import Relation.Nullary.Decidable.Core using (does)
102102

103103
open import Codata.Musical.Notation using (♯_)
104104
open import IO
105+
open import IO.Handle
105106

106107
open import System.Clock as Clock using (time′; Time; seconds)
107108
open import System.Console.ANSI
@@ -205,9 +206,10 @@ runTest opts testPath = do
205206
true doesDirectoryExist (mkFilePath testPath)
206207
where false fail directoryNotFound
207208

209+
putStr $ concat (testPath ∷ ": " ∷ [])
208210
time time′ $ callCommand $ unwords
209211
$ "cd" ∷ testPath
210-
"&&""sh ./run" ∷ opts .exeUnderTest
212+
"&&""sh ./run"(concat $ "\"" ∷ opts .exeUnderTest ∷ "\"" ∷ [])
211213
"| tr -d '\\r' > output"
212214
∷ []
213215

@@ -304,14 +306,14 @@ runTest opts testPath = do
304306
when b $ writeFile (testPath String.++ "/expected") out
305307

306308
printTiming : Bool Time String IO _
307-
printTiming false _ msg = putStrLn $ concat (testPath ∷ ": "msg ∷ [])
309+
printTiming false _ msg = putStrLn msg
308310
printTiming true time msg =
309311
let time = ℕ.show (time .seconds) String.++ "s"
310312
spent = 9 + sum (List.map String.length (testPath ∷ time ∷ []))
311313
-- ^ hack: both "success" and "FAILURE" have the same length
312314
-- can't use `String.length msg` because the msg contains escape codes
313315
pad = String.replicate (72 ∸ spent) ' '
314-
in putStrLn (concat (testPath ∷ ": "msg ∷ pad ∷ time ∷ []))
316+
in putStrLn (concat (msg ∷ pad ∷ time ∷ []))
315317

316318
-- A test pool is characterised by
317319
-- + a name
@@ -385,6 +387,7 @@ poolRunner opts pool = do
385387

386388
runner : List TestPool IO ⊤
387389
runner tests = do
390+
hSetBuffering stdout noBuffering
388391
-- figure out the options
389392
args getArgs
390393
inj₂ opts options args

tests/Makefile

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
INTERACTIVE ?= --interactive
1+
INTERACTIVE ?= --interactive
2+
GHC_EXEC ?= ghc
3+
CABAL ?= cabal
4+
CABAL_OPTIONS ?= --with-compiler $(GHC_EXEC)
25

3-
runtests: runtests.agda
4-
rm -f _build/runtests
5-
rm -f _build/MAlonzo/Code/Qruntests*
6-
$(AGDA) --compile-dir=_build/ -c runtests.agda
6+
AGDA_BUILD_DIR = _config/_build
7+
CABAL_BUILD_DIR = _config/dist-newstyle
78

8-
test: runtests
9-
./_build/runtests $(AGDA_EXEC) $(INTERACTIVE) --timing --failure-file failures --only $(only)
9+
./runtests: admin/runtests/Main.agda
10+
cd admin/runtests && AGDA="$(AGDA)" sh run
1011

12+
test: ./runtests
13+
./runtests "$(AGDA)" $(INTERACTIVE) --timing --failure-file failures --only $(only)
1114

12-
retest: runtests
13-
./_build/runtests $(AGDA_EXEC) $(INTERACTIVE) --timing --failure-file failures --only-file failures --only $(only)
15+
16+
retest: ./runtests
17+
./runtests "$(AGDA)" $(INTERACTIVE) --timing --failure-file failures --only-file failures --only $(only)
18+
19+
clean:
20+
rm -rf runtests
21+
rm -rf _config/_build
22+
rm -rf _config/dist-newstyle

tests/README.md

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,56 @@
1-
Test suite
2-
==========
1+
# Running the test suite
32

4-
Building the library
5-
####################
3+
You can run the test suite by going back to the main project directory
4+
and running (change accordingly if you have the right versions of Agda
5+
& GHC installed but the executable names are different).
66

7-
You can test the building of the entire library by running the following command from the top-level directory of the library:
8-
```bash
9-
cabal run GenerateEverything -- --include-deprecated
10-
agda -Werror +RTS -M5G -H3.5G -A128M -RTS -i. -isrc -idoc -WnoUserWarning Everything.agda
7+
```shell
8+
AGDA_EXEC=agda-2.6.8 GHC_EXEC=ghc-9.12.2 make testsuite
119
```
1210

13-
Golden tests
14-
############
11+
# Structure of the test suite
1512

16-
Setup
17-
-----
13+
## Test case
1814

19-
The golden tests need the `clock` package from Cabal to run.
20-
This can be installed via:
21-
```bash
22-
cabal v1-install --ghc-options='-O1 +RTS -M6G -RTS' clock
23-
```
15+
Each test case is under 2 levels of nesting (this is hard-coded)
16+
and should comprise:
2417

25-
Running
26-
-------
18+
1. A `Main.agda` file containing a `main` entrypoint
19+
2. An `expected` file containing the expected output of running `Main`
20+
3. A `run` file describing how to run `Main` (most likely implemented
21+
using the `goldenTest` function defined in
22+
[_config/config.sh](_config/config.sh).
23+
4. Optionally, an `input` file for the stdin content to feed to
24+
the executable obtained by compiling `Main`.
2725

28-
The golden tests can be run from the top-level directory of the library with the command:
29-
```bash
30-
make testsuite INTERACTIVE='' AGDA_EXEC='~/.cabal/bin/agda'
31-
```
32-
This should take about 5 minutes to run.
26+
## Configuration
27+
28+
The Agda & GHC version numbers the stdlib is tested against
29+
are specified in [_config/version-numbers.sh](_config/version-numbers.sh)
30+
31+
## Test runner
32+
33+
The test runner is defined in [admin/runtests/](admin/runtests/).
34+
It is compiled using an ad-hoc [run](admin/runtests/run) file using
35+
the shared configuration and the resulting executable is copied to
36+
the root of the tests directory.
37+
38+
If you want to add new tests, you need to list them in
39+
the [runtests](admin/runtests/Main.agda) program.
40+
41+
## Test dependencies
42+
43+
The external dependencies of the whole test suite are spelt out in the generic
44+
[_config/template.cabal](_config/template.cabal) cabal file
45+
46+
You may need to bump these dependencies when changing
47+
the version of the GHC compiler we build against.
48+
49+
# Updating the test suite
50+
51+
1. Update [_config/version-numbers.sh](_config/version-numbers.sh)
52+
2. Update the shell command listing explicit version numbers in the
53+
fenced code block at the top of this README
54+
3. Update bounds in [_config/template.cabal](_config/template.cabal)
55+
4. Update [../.github/workflows/ci-ubuntu.yml](../.github/workflows/ci-ubuntu.yml)
56+
to run the continuous integration with the new GHC and/or Agda versions.

tests/_config/config.sh

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/bin/sh
2+
3+
# This script is intended to be sourced from test scripts.
4+
#
5+
# It provides a number of default config options corresponding
6+
# to the compiler versions the stdlib is being tested with
7+
#
8+
# Usage: . ../../config.sh
9+
10+
set -eu
11+
12+
# Ugh, paths are relative to the script sourcing this file!
13+
. ../../_config/version-numbers.sh
14+
15+
16+
17+
# Main entry point to build and run an Agda program
18+
#
19+
# Typically called like `goldenTest "$1" "echo" "hello world"`
20+
#
21+
# INPUTS:
22+
# $1 is the agda executable (typically "$1" in the ̀ run` file
23+
# because this info is provided by the test runner
24+
# $2 the name of the test as a string
25+
# $3 is OPTIONAL and corresponds to the extra arguments to pass
26+
# to the executable obtained by compiling the Agda program
27+
#
28+
# LOGGING:
29+
# logs/agda-build for the trace produced by Agda
30+
# logs/cabal-build for the trace produced by cabal+ghc
31+
#
32+
# OUTPUT:
33+
# the output obtained by running the Agda program is stored in
34+
# the file named `output`. The test runner will then diff it
35+
# against the expected file.
36+
goldenTest () {
37+
38+
AGDA="$1"
39+
TEST_NAME="stdlib-test-$2"
40+
41+
# Taking extra args for the executable?
42+
if [ -z "${3-}" ]; then
43+
EXTRA_ARGS=""
44+
else
45+
EXTRA_ARGS="-- $3"
46+
fi
47+
48+
# Remember whether the script has an input -- ugh
49+
if [ -f input ]; then
50+
HAS_INPUT="true"
51+
else
52+
touch input
53+
HAS_INPUT="false"
54+
fi
55+
56+
# Specialise template agda-lib & cabal files
57+
sed "s/TEST_NAME/$TEST_NAME/g" ../../_config/template.agda-lib > "$TEST_NAME".agda-lib
58+
sed "s/TEST_NAME/$TEST_NAME/g" ../../_config/template.cabal > "$TEST_NAME".cabal
59+
60+
# Set up clean logs directory
61+
rm -rf logs/
62+
mkdir logs
63+
64+
# Use shared directories to avoid rechecking stdlib modules
65+
AGDA_BUILD_DIR=../../_config/_build
66+
CABAL_BUILD_DIR=../../_config/dist-newstyle
67+
68+
rm -f _build
69+
mkdir -p "$AGDA_BUILD_DIR"
70+
ln -sf "$AGDA_BUILD_DIR" .
71+
rm -f dist-newstyle
72+
mkdir -p "$CABAL_BUILD_DIR"
73+
ln -sf "$CABAL_BUILD_DIR" .
74+
75+
# Compile the Agda module and build the generated code
76+
$AGDA --library-file=../../_config/libraries --compile-dir=_build -c --ghc-dont-call-ghc Main.agda > logs/agda-build
77+
cabal build "$TEST_NAME" --with-compiler "$GHC_EXEC" > logs/cabal-build
78+
79+
# Run the test
80+
cabal exec -v0 "$TEST_NAME" --with-compiler "$GHC_EXEC" $EXTRA_ARGS < input > output
81+
82+
# Clean up after ourselves
83+
if ! "$HAS_INPUT"; then
84+
rm input
85+
fi
86+
rm "$TEST_NAME".cabal
87+
rm "$TEST_NAME".agda-lib
88+
rm _build
89+
rm dist-newstyle
90+
}

tests/_config/libraries

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../standard-library.agda-lib

0 commit comments

Comments
 (0)