Skip to content

Commit 526e782

Browse files
Copilotandreasabel
andcommitted
Address PR review feedback
- Move language extensions to default-extensions in cabal file - Use System.Directory.doesDirectoryExist instead of custom implementation - Use Data.List.isInfixOf instead of custom implementation - Inline content read and add comment about golden value - Sort files by modification time (newest first), then golden value time, then alphabetically - Update Makefile with new test goals (succeed, fail, golden, golden-succeed, golden-fail) - Delete test/Makefile - Add DEVELOPER.md with test suite documentation - Add Andreas Abel and Copilot to author lists in .cabal and LICENSE Co-authored-by: andreasabel <1155218+andreasabel@users.noreply.github.com>
1 parent 40d205d commit 526e782

File tree

6 files changed

+170
-123
lines changed

6 files changed

+170
-123
lines changed

DEVELOPER.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Developer Documentation
2+
3+
## Running the Test Suite
4+
5+
The test suite uses `tasty-golden` for golden value testing. Tests are organized into two categories:
6+
7+
- **Succeed tests** (`test/Succeed/`): Agda files that should compile successfully with agda2hs. The generated `.hs` files are the golden values.
8+
- **Fail tests** (`test/Fail/`): Agda files that should fail to compile. The error messages (in `.err` files) are the golden values.
9+
10+
### Running All Tests
11+
12+
```bash
13+
# Run all tests (includes whitespace check and container tests)
14+
make test
15+
16+
# Or run just the agda2hs tests
17+
cabal test agda2hs-test
18+
```
19+
20+
### Running Specific Test Categories
21+
22+
```bash
23+
# Run only successful tests
24+
make succeed
25+
26+
# Run only failing tests
27+
make fail
28+
29+
# Or using cabal directly with pattern matching
30+
cabal test agda2hs-test --test-options='-p Succeed'
31+
cabal test agda2hs-test --test-options='-p Fail'
32+
```
33+
34+
### Running Individual Tests
35+
36+
```bash
37+
# Run a specific test by name
38+
cabal test agda2hs-test --test-options='-p /Fixities/'
39+
40+
# Run tests matching a pattern
41+
cabal test agda2hs-test --test-options='-p /Issue/'
42+
```
43+
44+
## Updating Golden Values
45+
46+
When you make changes that intentionally affect the test output, you need to update the golden values:
47+
48+
```bash
49+
# Update all golden values
50+
make golden
51+
52+
# Update only successful test golden values
53+
make golden-succeed
54+
55+
# Update only failing test golden values
56+
make golden-fail
57+
58+
# Or using cabal directly
59+
cabal test agda2hs-test --test-options='--accept'
60+
```
61+
62+
After updating golden values, review the changes with `git diff` to ensure they are correct before committing.
63+
64+
## Adding New Tests
65+
66+
### Adding a Succeed Test
67+
68+
1. Create a new `.agda` file in `test/Succeed/`
69+
2. Run `make golden-succeed` to generate the golden `.hs` file
70+
3. Verify the generated Haskell code is correct
71+
4. Commit both the `.agda` and `.hs` files
72+
73+
### Adding a Fail Test
74+
75+
1. Create a new `.agda` file in `test/Fail/`
76+
2. Run `make golden-fail` to generate the golden `.err` file
77+
3. Verify the error message is correct
78+
4. Commit both the `.agda` and `.err` files
79+
80+
## Test Ordering
81+
82+
Tests are ordered by:
83+
1. Modification date of the `.agda` file (newest first)
84+
2. Modification date of the golden value file (newest first)
85+
3. Alphabetically
86+
87+
This ordering ensures that recently modified tests appear first in the output, making it easier to focus on tests you're actively working on.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2025 Ulf Norell, Jesper Cockx, Orestis Melkonian, Sára Juhošová, James Chapman, Lucas Escot, Henry Blanchette, Viktor Csimma, Aleksander Wolska, Paul Bittner, Andreas Källberg, Bohdan Liesnikov, Jakob Naucke, and Heinrich Apfelmus.
1+
Copyright 2025 Ulf Norell, Jesper Cockx, Orestis Melkonian, Sára Juhošová, James Chapman, Lucas Escot, Henry Blanchette, Viktor Csimma, Aleksander Wolska, Paul Bittner, Andreas Källberg, Bohdan Liesnikov, Jakob Naucke, Heinrich Apfelmus, Andreas Abel, and Copilot.
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
44

Makefile

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,63 @@
1-
.PHONY : install agda repl libHtml testContainers test test-on-CI testHtml golden clean docs
1+
.PHONY : build install agda repl libHtml testContainers test succeed fail golden golden-succeed golden-fail clean docs fixWhitespace checkWhitespace
2+
23
FILES = $(shell find src -type f)
34

45
build :
5-
cabal build
6+
cabal build
67

78
install :
8-
cabal install --overwrite-policy=always
9+
cabal install --overwrite-policy=always
910

1011
agda :
11-
cabal install Agda --program-suffix=-erased --overwrite-policy=always
12+
cabal install Agda --program-suffix=-erased --overwrite-policy=always
1213

1314
repl :
14-
cabal repl # e.g. `:set args -itest -otest/build test/AllTests.agda ... main ... :r ... main`
15+
cabal repl
1516

1617
libHtml :
17-
cabal run agda2hs -- --html --include-path lib/base lib/base/Haskell/Prelude.agda
18-
cp html/Haskell.Prelude.html html/index.html
19-
20-
test/agda2hs : $(FILES)
21-
cabal install agda2hs --overwrite-policy=always --installdir=test --install-method=copy
18+
cabal run agda2hs -- --html --include-path lib/base lib/base/Haskell/Prelude.agda
19+
cp html/Haskell.Prelude.html html/index.html
2220

2321
testContainers:
24-
cd ./lib/containers && ./generate-haskell.sh && cabal build containers-prop
22+
cd ./lib/containers && ./generate-haskell.sh && cabal build containers-prop
23+
24+
# Run all tests
25+
test : checkWhitespace succeed fail testContainers
26+
27+
# Run only successful tests
28+
succeed :
29+
cabal test agda2hs-test --test-options='-p Succeed'
2530

26-
test : checkWhitespace test-on-CI
31+
# Run only failing tests
32+
fail :
33+
cabal test agda2hs-test --test-options='-p Fail'
2734

28-
test-on-CI : test/agda2hs testContainers
29-
make -C test
35+
# Update all golden values
36+
golden : golden-succeed golden-fail
3037

31-
testHtml : test/agda2hs
32-
make -C test html
38+
# Update golden values for successful tests
39+
golden-succeed :
40+
cabal test agda2hs-test --test-options='-p Succeed --accept'
3341

34-
golden :
35-
make -C test golden
42+
# Update golden values for failing tests
43+
golden-fail :
44+
cabal test agda2hs-test --test-options='-p Fail --accept'
3645

3746
clean :
38-
make -C test clean
47+
cabal clean
48+
rm -rf test/_build/
3949

4050
docs :
41-
make -C docs html
51+
make -C docs html
4252

4353
FIXW_BIN = fix-whitespace
4454

45-
.PHONY : fixWhitespace ## Fix the whitespace issue.
4655
fixWhitespace : have-bin-$(FIXW_BIN) fix-whitespace.yaml
47-
$(FIXW_BIN)
56+
$(FIXW_BIN)
4857

49-
.PHONY : checkWhitespace ## Check the whitespace issue without fixing it.
5058
checkWhitespace : have-bin-$(FIXW_BIN) fix-whitespace.yaml
51-
$(FIXW_BIN) --check
59+
$(FIXW_BIN) --check
5260

53-
.PHONY : have-bin-% ## Installing binaries for developer services
61+
.PHONY : have-bin-%
5462
have-bin-% :
55-
@($* --help > /dev/null) || $(CABAL) install --ignore-project $*
63+
@($* --help > /dev/null) || $(CABAL) install --ignore-project $*

agda2hs.cabal

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: agda2hs
33
version: 1.5
44
license: BSD-3-Clause
55
license-file: LICENSE
6-
author: Ulf Norell, Jesper Cockx, Orestis Melkonian, Sára Juhošová, James Chapman, Lucas Escot, Henry Blanchette, Viktor Csimma, Aleksander Wolska, Paul Bittner, Andreas Källberg, Bohdan Liesnikov, Jakob Naucke, and Heinrich Apfelmus
6+
author: Ulf Norell, Jesper Cockx, Orestis Melkonian, Sára Juhošová, James Chapman, Lucas Escot, Henry Blanchette, Viktor Csimma, Aleksander Wolska, Paul Bittner, Andreas Källberg, Bohdan Liesnikov, Jakob Naucke, Heinrich Apfelmus, Andreas Abel, and Copilot
77
maintainer: jesper@sikanda.be
8-
copyright: 2025 Ulf Norell, Jesper Cockx, Orestis Melkonian, Sára Juhošová, James Chapman, Lucas Escot, Henry Blanchette, Viktor Csimma, Aleksander Wolska, Paul Bittner, Andreas Källberg, Bohdan Liesnikov, Jakob Naucke, and Heinrich Apfelmus
8+
copyright: 2025 Ulf Norell, Jesper Cockx, Orestis Melkonian, Sára Juhošová, James Chapman, Lucas Escot, Henry Blanchette, Viktor Csimma, Aleksander Wolska, Paul Bittner, Andreas Källberg, Bohdan Liesnikov, Jakob Naucke, Heinrich Apfelmus, Andreas Abel, and Copilot
99
category: Language, Compiler
1010
build-type: Simple
1111
synopsis: Compiling Agda code to readable Haskell.
@@ -104,7 +104,9 @@ test-suite agda2hs-test
104104
tasty >= 1.4 && < 1.6,
105105
tasty-golden >= 2.3 && < 2.4,
106106
text >= 2.0.2 && < 2.2,
107+
time >= 1.9 && < 1.15,
107108
default-language: Haskell2010
108109
default-extensions: OverloadedStrings
110+
ScopedTypeVariables
109111
build-tool-depends: agda2hs:agda2hs
110112
ghc-options: -threaded

test/Main.hs

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
1-
{-# LANGUAGE OverloadedStrings #-}
2-
{-# LANGUAGE ScopedTypeVariables #-}
3-
41
module Main where
52

6-
import Control.Exception (catch, SomeException)
7-
import Control.Monad (forM, unless)
3+
import Control.Monad (forM)
84
import qualified Data.ByteString.Lazy as LBS
9-
import Data.List (isPrefixOf, isSuffixOf, isInfixOf, sort, tails)
5+
import Data.List (isPrefixOf, isSuffixOf, isInfixOf, sortBy)
6+
import Data.Ord (Down(..), comparing)
107
import qualified Data.Text as T
118
import qualified Data.Text.Encoding as TE
9+
import Data.Time.Clock (UTCTime)
1210
import System.Directory
1311
( createDirectoryIfMissing
12+
, doesDirectoryExist
1413
, doesFileExist
1514
, getCurrentDirectory
15+
, getModificationTime
1616
, getTemporaryDirectory
1717
, listDirectory
18-
, removeFile
1918
, setCurrentDirectory
2019
)
2120
import System.Exit (ExitCode(..))
22-
import System.FilePath ((</>), dropExtension, makeRelative, takeBaseName, takeDirectory, takeFileName)
21+
import System.FilePath ((</>), dropExtension, makeRelative, takeDirectory)
2322
import System.Process (readProcessWithExitCode)
2423
import Test.Tasty (TestTree, defaultMain, testGroup)
2524
import Test.Tasty.Golden (goldenVsStringDiff)
@@ -53,23 +52,56 @@ main = do
5352
]
5453

5554
-- | Discover all .agda files under the Succeed directory recursively.
55+
-- Files are ordered by: modification date (newest first), then golden value
56+
-- modification date (newest first), then alphabetically.
5657
discoverSucceedTests :: FilePath -> FilePath -> IO [TestTree]
5758
discoverSucceedTests testDir buildDir = do
5859
agdaFiles <- findAgdaFilesRecursive (testDir </> "Succeed")
59-
forM (sort agdaFiles) $ \agdaFile -> do
60+
sortedFiles <- sortByModTime agdaFiles (\f -> dropExtension f ++ ".hs")
61+
forM sortedFiles $ \agdaFile -> do
6062
let testName = dropExtension (makeRelative (testDir </> "Succeed") agdaFile)
6163
goldenFile = dropExtension agdaFile ++ ".hs"
6264
return $ succeedTest testDir buildDir testName agdaFile goldenFile
6365

6466
-- | Discover all .agda files under the Fail directory.
67+
-- Files are ordered by: modification date (newest first), then golden value
68+
-- modification date (newest first), then alphabetically.
6569
discoverFailTests :: FilePath -> FilePath -> IO [TestTree]
6670
discoverFailTests testDir buildDir = do
6771
agdaFiles <- findAgdaFilesRecursive (testDir </> "Fail")
68-
forM (sort agdaFiles) $ \agdaFile -> do
72+
sortedFiles <- sortByModTime agdaFiles (\f -> dropExtension f ++ ".err")
73+
forM sortedFiles $ \agdaFile -> do
6974
let testName = dropExtension (makeRelative (testDir </> "Fail") agdaFile)
7075
goldenFile = dropExtension agdaFile ++ ".err"
7176
return $ failTest testDir buildDir testName agdaFile goldenFile
7277

78+
-- | Sort files by modification time (newest first), then by golden value
79+
-- modification time (if it exists), then alphabetically.
80+
sortByModTime :: [FilePath] -> (FilePath -> FilePath) -> IO [FilePath]
81+
sortByModTime files goldenPath = do
82+
filesWithTimes <- forM files $ \f -> do
83+
agdaTime <- getModificationTime f
84+
let golden = goldenPath f
85+
goldenExists <- doesFileExist golden
86+
goldenTime <- if goldenExists
87+
then Just <$> getModificationTime golden
88+
else return Nothing
89+
return (f, agdaTime, goldenTime)
90+
return $ map (\(f,_,_) -> f) $ sortBy fileOrder filesWithTimes
91+
where
92+
fileOrder (f1, t1, g1) (f2, t2, g2) =
93+
-- First by agda file modification time (newest first)
94+
case comparing Down t1 t2 of
95+
EQ -> case (g1, g2) of
96+
-- Then by golden file modification time (newest first)
97+
(Just gt1, Just gt2) -> case comparing Down gt1 gt2 of
98+
EQ -> compare f1 f2 -- Finally alphabetically
99+
x -> x
100+
(Just _, Nothing) -> LT
101+
(Nothing, Just _) -> GT
102+
(Nothing, Nothing) -> compare f1 f2
103+
x -> x
104+
73105
-- | Find all .agda files recursively in a directory.
74106
findAgdaFilesRecursive :: FilePath -> IO [FilePath]
75107
findAgdaFilesRecursive dir = do
@@ -82,17 +114,6 @@ findAgdaFilesRecursive dir = do
82114
else return [path | ".agda" `isSuffixOf` name]
83115
return (concat paths)
84116

85-
-- | Check if path is a directory.
86-
doesDirectoryExist :: FilePath -> IO Bool
87-
doesDirectoryExist path = do
88-
exists <- doesFileExist path
89-
if exists
90-
then return False
91-
else do
92-
-- If it's not a file, check if it's a directory
93-
contents <- listDirectory path `catch` \(_ :: SomeException) -> return []
94-
return (not (null contents) || path `elem` ["Succeed", "Fail", "Cubical"])
95-
96117
-- | Create a test for a succeed case.
97118
-- Runs agda2hs on the .agda file, compares the output .hs to the golden file,
98119
-- then compiles the .hs file with ghc.
@@ -119,9 +140,6 @@ succeedTest testDir buildDir testName agdaFile goldenFile =
119140

120141
case exitCode of
121142
ExitSuccess -> do
122-
-- Read the generated .hs file from build directory
123-
content <- LBS.readFile generatedFile
124-
125143
-- Also run ghc to check the generated code compiles
126144
-- Add include path for finding imported modules
127145
(ghcExit, ghcOut, ghcErr) <- readProcessWithExitCode
@@ -130,7 +148,8 @@ succeedTest testDir buildDir testName agdaFile goldenFile =
130148
""
131149

132150
case ghcExit of
133-
ExitSuccess -> return content
151+
-- The generated .hs file is the golden value
152+
ExitSuccess -> LBS.readFile generatedFile
134153
ExitFailure _ -> return $ stringToLBS $
135154
"GHC compilation failed:\n" ++ ghcOut ++ ghcErr
136155

@@ -196,13 +215,6 @@ relativizePaths = unlines . map relativizeLine . lines
196215
| null s = Nothing
197216
| otherwise = findTestPrefix (drop 1 s)
198217

199-
isInfixOf :: String -> String -> Bool
200-
isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
201-
202-
tails :: String -> [String]
203-
tails [] = [[]]
204-
tails s@(_:xs) = s : tails xs
205-
206218
-- | Diff command for golden tests.
207219
diffCmd :: FilePath -> FilePath -> [String]
208220
diffCmd ref new = ["diff", "-u", ref, new]

0 commit comments

Comments
 (0)