Skip to content

Commit

Permalink
feat: lake test
Browse files Browse the repository at this point in the history
  • Loading branch information
tydeu committed Mar 26, 2024
1 parent 80d2455 commit 445da2c
Show file tree
Hide file tree
Showing 13 changed files with 114 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/lake/Lake/CLI/Actions.lean
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ def uploadRelease (pkg : Package) (tag : String) : LogIO Unit := do
tar pkg.buildArchive pkg.buildDir pkg.buildArchiveFile
logInfo s!"Uploading {tag}/{pkg.buildArchive}"
proc {cmd := "gh", args}

def test (pkg : Package) (args : List String := []) (buildConfig : BuildConfig := {}) : LakeT LogIO UInt32 := do
let pkgName := pkg.name.toString (escape := false)
if pkg.testRunner.isAnonymous then
error s!"{pkgName}: package has no script or executable tagged `@[test_runner]`"
else if let some script := pkg.scripts.find? pkg.testRunner then
script.run args
else if let some exe := pkg.findLeanExe? pkg.testRunner then
let exeFile ← runBuild (exe.build >>= (·.await)) buildConfig
env exeFile.toString args.toArray
else
error s!"{pkgName}: invalid test runner: unknown script or executable `{pkg.testRunner}`"
11 changes: 11 additions & 0 deletions src/lake/Lake/CLI/Help.lean
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ COMMANDS:
init <name> <temp> create a Lean package in the current directory
build <targets>... build targets
exe <exe> <args>... build an exe and run it in Lake's environment
test run the workspace's test script or executable
clean remove build outputs
env <cmd> <args>... execute a command in Lake's environment
update update dependencies and save them to the manifest
Expand Down Expand Up @@ -131,6 +132,15 @@ of the same package, the version materialized is undefined.
A bare `lake update` will upgrade all dependencies."

def helpTest :=
"Run the workspace's test script or executable
USAGE:
lake test [-- <args>...]
Looks for a script or executable tagged `@[test_runner]` in the workspace's
root package and executes it with `args`. "

def helpUpload :=
"Upload build artifacts to a GitHub release
Expand Down Expand Up @@ -256,6 +266,7 @@ def help : (cmd : String) → String
| "build" => helpBuild
| "update" | "upgrade" => helpUpdate
| "upload" => helpUpload
| "test" => helpTest
| "clean" => helpClean
| "script" => helpScriptCli
| "scripts" => helpScriptList
Expand Down
16 changes: 16 additions & 0 deletions src/lake/Lake/CLI/Main.lean
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,20 @@ protected def setupFile : CliM PUnit := do
let imports ← takeArgs
setupFile loadConfig filePath imports buildConfig opts.verbosity

protected def test : CliM PUnit := do
processOptions lakeOption
let opts ← getThe LakeOptions
let config ← mkLoadConfig opts
let ws ← loadWorkspace config
noArgsRem do
let x := test ws.root opts.subArgs (mkBuildConfig opts)
exit <| ← x.run (mkLakeContext ws) |>.run (MonadLog.io opts.verbosity)

protected def checkTest : CliM PUnit := do
processOptions lakeOption
let ws ← loadWorkspace ( ← mkLoadConfig (← getThe LakeOptions))
noArgsRem do exit <| if ws.root.testRunner.isAnonymous then 1 else 0

protected def clean : CliM PUnit := do
processOptions lakeOption
let config ← mkLoadConfig (← getThe LakeOptions)
Expand Down Expand Up @@ -392,6 +406,8 @@ def lakeCli : (cmd : String) → CliM PUnit
| "resolve-deps" => lake.resolveDeps
| "upload" => lake.upload
| "setup-file" => lake.setupFile
| "test" => lake.test
| "check-test" => lake.checkTest
| "clean" => lake.clean
| "script" => lake.script
| "scripts" => lake.script.list
Expand Down
2 changes: 2 additions & 0 deletions src/lake/Lake/Config/Package.lean
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ structure Package where
defaultScripts : Array Script := #[]
/-- Post-`lake update` hooks for the package. -/
postUpdateHooks : Array (OpaquePostUpdateHook config.name) := #[]
/-- Name of the package's test runner script or executable (if any). -/
testRunner : Name := .anonymous

instance : Nonempty Package :=
have : Inhabited Environment := Classical.inhabited_of_nonempty inferInstance
Expand Down
9 changes: 9 additions & 0 deletions src/lake/Lake/DSL/Attributes.lean
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ initialize defaultTargetAttr : OrderedTagAttribute ←
unless valid do
throwError "attribute `default_target` can only be used on a target (e.g., `lean_lib`, `lean_exe`)"

initialize testRunnerAttr : OrderedTagAttribute ←
registerOrderedTagAttribute `test_runner "mark a Lake script or executable as the package's test runner"
fun name => do
let valid ← getEnv <&> fun env =>
scriptAttr.hasTag env name ||
leanExeAttr.hasTag env name
unless valid do
throwError "attribute `test_runner` can only be used on a `script` or `lean_exe`"

initialize moduleFacetAttr : OrderedTagAttribute ←
registerOrderedTagAttribute `module_facet "mark a definition as a Lake module facet"

Expand Down
1 change: 1 addition & 0 deletions src/lake/Lake/Load/Elab.lean
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ where
|>.insert ``externLibAttr
|>.insert ``targetAttr
|>.insert ``defaultTargetAttr
|>.insert ``testRunnerAttr
|>.insert ``moduleFacetAttr
|>.insert ``packageFacetAttr
|>.insert ``libraryFacetAttr
Expand Down
10 changes: 9 additions & 1 deletion src/lake/Lake/Load/Package.lean
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ def Package.finalize (self : Package) (deps : Array Package) : LogIO Package :=
else
error s!"post-update hook was defined in `{decl.pkg}`, but was registered in `{self.name}`"
| .error e => error e
let testRunners := testRunnerAttr.getAllEntries env
let testRunner ←
if testRunners.size > 1 then
error s!"{self.name}: only one script or executable can be tagged `@[test_runner]`"
else if h : testRunners.size > 0 then
pure (testRunners[0]'h)
else
pure .anonymous

-- Deprecation warnings
unless self.config.manifestFile.isNone do
Expand All @@ -117,7 +125,7 @@ def Package.finalize (self : Package) (deps : Array Package) : LogIO Package :=
return {self with
opaqueDeps := deps.map (.mk ·)
leanLibConfigs, leanExeConfigs, externLibConfigs
opaqueTargetConfigs, defaultTargets, scripts, defaultScripts,
opaqueTargetConfigs, defaultTargets, scripts, defaultScripts, testRunner
postUpdateHooks
}

Expand Down
2 changes: 2 additions & 0 deletions src/lake/tests/test/Test.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def main (args : List String) : IO PUnit := do
IO.println s!"exe: {args}"
7 changes: 7 additions & 0 deletions src/lake/tests/test/exe.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Lake
open System Lake DSL

package test

@[test_runner]
lean_exe test
4 changes: 4 additions & 0 deletions src/lake/tests/test/none.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Lake
open System Lake DSL

package test
9 changes: 9 additions & 0 deletions src/lake/tests/test/script.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Lake
open System Lake DSL

package test

@[test_runner]
script test args do
IO.println s!"script: {args}"
return 0
22 changes: 22 additions & 0 deletions src/lake/tests/test/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euxo pipefail

LAKE=${LAKE:-../../.lake/build/bin/lake}

# Script test runner
$LAKE -f script.lean test | grep -F "script: []"
$LAKE -f script.lean test -- hello | grep --color "hello"

# Executable test runner
$LAKE -f exe.lean test | grep -F "exe: []"
$LAKE -f exe.lean test -- hello | grep --color "hello"

# Test runner validation
($LAKE -f two.lean test 2>&1 && false || true) | grep --color "only one"
($LAKE -f none.lean test 2>&1 && false || true) | grep --color "package has no"

# Test runner checker
$LAKE -f exe.lean check-test
$LAKE -f script.lean check-test
($LAKE -f none.lean check-test && false || true)
($LAKE -f two.lean check-test && false || true)
10 changes: 10 additions & 0 deletions src/lake/tests/test/two.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Lake
open System Lake DSL

package test

@[test_runner]
lean_exe testExe

@[test_runner]
script testScript do return 1

0 comments on commit 445da2c

Please sign in to comment.