Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion atlas.nimble
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Package
version = "0.9.1"
version = "0.9.2"
author = "Araq"
description = "Atlas is a simple package cloner tool. It manages an isolated project."
license = "MIT"
Expand Down
1 change: 1 addition & 0 deletions config.nims
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ task unitTests, "Runs unit tests":
exec "nim c -d:debug -r tests/testsemverUnit.nim"
exec "nim c -d:debug -r tests/testautoinit.nim"
exec "nim c -d:debug -r tests/testsearch.nim"
exec "nim c -d:debug -r tests/truntests.nim"

task tester, "Runs integration tests":
exec "nim c -d:debug -r tests/tester.nim"
Expand Down
5 changes: 5 additions & 0 deletions doc/atlas.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@

```

### Test [--parallel] [paths...]

Use `atlas test` to run all tests in `tests/*.nim`. Pass Nim files to run specific tests: `atlas test tests/mytest.nim`.

Tests can be run in parallel: `atlas test --parallel`. Note that the test output is *not* synchronized.

### Link <path>

Expand Down Expand Up @@ -187,7 +192,7 @@

Sometimes two URLs can conflict for the same dependency shortname. For example, when a project uses a forked dependency with bug fixes. These conflicts need to be manually resolved using `pkgOverrides` in `atlas.config`. The format is package name and the selected URL:

```json

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 195 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]
"pkgOverrides": {
"asynctools": "https://github.com/timotheecour/asynctools"
},
Expand All @@ -199,7 +204,7 @@
a simple pattern matching language and are flexible enough to integrate private
gitlab repositories.

```json

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 207 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]
{
"resolver": "SemVer",
"nameOverrides": {
Expand All @@ -226,7 +231,7 @@

For example, here is how to override any github link:

```json

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / linux-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-version-2-2 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-version-2-0 (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-i386-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / windows-amd64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]

Check warning on line 234 in doc/atlas.md

View workflow job for this annotation

GitHub Actions / macos-arm64-nim-devel (master)

language 'json' not supported [LanguageXNotSupported]
"urlOverrides": {
"https://github.com/$+": "https://utopia.forall/$#"
}
Expand Down
20 changes: 13 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ Upcoming Nim version 2.0 will ship with `atlas`. Building from source:
```sh
git clone https://github.com/nim-lang/atlas.git
cd atlas
nim c src/atlas.nim
# copy src/atlas[.exe] somewhere in your PATH
nim build
# copy bin/atlas[.exe] somewhere in your PATH
```

Or with Nimble:

```sh
nimble install https://github.com/nim-lang/atlas@\#head
```

# Documentation
Expand All @@ -22,8 +28,7 @@ Create a new project. A project contains everything we need and can safely be de
this tutorial:

```sh
mkdir project
cd project
mkdir project && cd project
atlas init
```

Expand All @@ -47,12 +52,13 @@ echo "import malebolgia" >myproject.nim
nim c myproject.nim
```

The project structure looks like this:
### Project Structure

```
```sh
$project / project.nimble
$project / nim.cfg
$project / nim.cfg # Atlas generated file
$project / other main project files...
$project / deps / # Folder where deps are stored
$project / deps / atlas.config
$project / deps / dependency-A
$project / deps / dependency-B
Expand Down
46 changes: 39 additions & 7 deletions src/atlas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
## Simple tool to automate frequent workflows: Can "clone"
## a Nimble dependency and its dependencies recursively.

import std / [parseopt, files, dirs, strutils, os, osproc, tables, sets, json, uri, paths]
import std / [parseopt, files, dirs, strutils, os, osproc, tables, sets, json, uri, paths, algorithm]
import basic / [versions, context, osutils, configutils, reporters,
nimbleparser, gitops, pkgurls, nimblecontext, compiledpatterns, packageinfos]
import depgraphs, nimenv, lockfiles, confighandler, dependencies, pkgsearch
import depgraphs, nimenv, lockfiles, confighandler, dependencies, pkgsearch, runtests


from std/terminal import isatty
Expand Down Expand Up @@ -49,8 +49,6 @@ Command:
update <url|pkgname> update a package and all of its dependencies
search <keyA> [keyB ...]
search for package that contains the given keywords
extract <file.nimble> extract the requirements and custom commands from
the given Nimble file
updateDeps [filter] update every dependency that has a remote
URL that matches `filter` if a filter is given
tag [major|minor|patch]
Expand All @@ -61,15 +59,16 @@ Command:
rep [atlas.lock] replay the state of the projects according to the lock file
changed <atlas.lock> list any packages that differ from the lock file
outdated list the packages that are outdated
build|test|doc|tasks currently delegates to `nimble build|test|doc`
task <taskname> currently delegates to `nimble <taskname>`
test [tests...] run all tests `tests/t*.nim` or specified tests; supports `--parallel`
env <nimversion> setup a Nim virtual environment
--keep keep the c_code subdirectory

Options:
--feature=<feature> enables the given feature, pass multiple for multiple features
for project specific use: `feature.<project>.<feature>`
(note always be passed when you want to use features)
--parallel enables parallel execution on some tasks using countProcessors()
--parallel:<N> enables parallel execution using `N` processes
--keepCommits do not perform any `git checkouts`
--noexec do not perform any action that may run arbitrary code
--autoenv detect the minimal Nim $version and setup a
Expand Down Expand Up @@ -459,6 +458,15 @@ proc parseAtlasOptions(params: seq[string], action: var string, args: var seq[st
of "maxver": context().defaultAlgo = MaxVer
of "semver": context().defaultAlgo = SemVer
else: writeHelp()
of "parallel":
# number of parallel test commands to run
if val != "":
try:
context().parallelCount = parseInt(val)
except CatchableError:
fatal "Invalid value for --parallel: '" & val & "'"
else:
context().parallelCount = countProcessors()
of "verbosity":
case val.normalize
of "normal": setAtlasVerbosity(Info)
Expand Down Expand Up @@ -489,6 +497,21 @@ proc parseAtlasOptions(params: seq[string], action: var string, args: var seq[st
createDir(depsDir())

proc atlasRun*(params: seq[string]) =
# Support forwarding args after "--" to subcommands like `test`.
var mainParams: seq[string] = @[]
var postDashParams: seq[string] = @[]
var seenDashDash = false
for p in params:
if not seenDashDash and p == "--":
seenDashDash = true
continue
if seenDashDash:
postDashParams.add p
else:
mainParams.add p

context().extraParams = postDashParams

var action = ""
var args: seq[string] = @[]
template singleArg() =
Expand All @@ -505,7 +528,7 @@ proc atlasRun*(params: seq[string]) =
if args.len != 0:
fatal action & " command takes no arguments"

parseAtlasOptions(params, action, args)
parseAtlasOptions(mainParams, action, args)

if action notin ["init", "tag", "search", "list"]:
doAssert project().string != "" and project().dirExists(), "project was not set"
Expand Down Expand Up @@ -616,6 +639,15 @@ proc atlasRun*(params: seq[string]) =
setupNimEnv args[0], KeepNimEnv in context().flags
of "outdated":
listOutdated()
of "test":
let runCode = NoExec notin context().flags
var testsToRun: seq[string] = @[]
for a in args:
if not a.startsWith("-") and a.endsWith(".nim"):
testsToRun.add a
let code = runTests(project(), postDashParams, runCode, context().parallelCount, testsToRun)
if code != 0:
quit(code)
else:
fatal "Invalid action: " & action

Expand Down
2 changes: 2 additions & 0 deletions src/basic/context.nim
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ type
pluginsFile*: Path
proxy*: Uri
features*: HashSet[string]
extraParams*: seq[string]
parallelCount*: Natural

var atlasContext = AtlasContext()

Expand Down
116 changes: 116 additions & 0 deletions src/runtests.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import std/[osproc, os, strutils, sequtils, math, paths, algorithm]
import basic/reporters

## Parallel test runner utilities using osproc.execProcesses.

proc buildTestCommand*(nimPath, testFile: string; extraArgs: seq[string] = @[]; runCode = true): string =
## Compose a Nim compile (and optional run) command for a test file.
var cmd = quoteShell(nimPath) & " c -d:debug"
if extraArgs.len > 0:
for a in extraArgs:
cmd.add " " & quoteShell(a)
if runCode:
cmd.add " -r"
cmd.add " " & quoteShell(testFile)
result = cmd

proc runCommandsParallel*(commands: seq[string]; parallel: int = 0): int =
## Run commands with limited parallelism; returns first non‑zero exit code or 0.
## If `parallel <= 0`, uses `countProcessors()`; otherwise, runs in batches of `parallel`.
if commands.len == 0:
return 0
if parallel <= 0:
return execProcesses(commands, n = countProcessors(),
beforeRunEvent = proc (idx: int) = discard,
afterRunEvent = proc (idx: int, p: Process) = discard
)
var i = 0
while i < commands.len:
let j = min(i + parallel, commands.len)
let code = execProcesses(commands[i ..< j], n = parallel,
beforeRunEvent = proc (idx: int) = discard,
afterRunEvent = proc (idx: int, p: Process) = discard
)
if code != 0:
return code
i = j
return 0

proc runTestsParallel*(tests: seq[string]; nimPath = findExe("nim"); extraArgs: seq[string] = @[]; runCode = true; parallel: int = countProcessors()): int =
## Build and execute Nim test commands in parallel.
if nimPath.len == 0:
return -1
var cmds: seq[string] = @[]
for tf in tests:
cmds.add buildTestCommand(nimPath, tf, extraArgs, runCode)
result = runCommandsParallel(cmds, parallel)

proc discoverTests*(projectDir: Path): seq[string] =
## Find and return all tests matching tests/t*.nim inside projectDir.
let old = os.getCurrentDir()
defer: os.setCurrentDir(old)
os.setCurrentDir($projectDir)
for f in walkFiles("tests/t*.nim"):
result.add f
result.sort(system.cmp[string])

proc runTestsSerial*(projectDir: Path; extraArgs: seq[string] = @[]; runCode = true; onlyTests: seq[string] = @[]): int =
## Sequentially compile and (optionally) run each discovered test.
if projectDir.len == 0 or not dirExists($projectDir):
fatal "No project directory detected", "atlas:test"
return 1
let nimPath = findExe("nim")
if nimPath.len == 0:
fatal "Nim compiler not found in PATH", "atlas:test"
return 1
let old = os.getCurrentDir()
defer: os.setCurrentDir(old)
os.setCurrentDir($projectDir)
var tests =
if onlyTests.len > 0: onlyTests
else: discoverTests(projectDir)
if tests.len == 0:
warn "atlas:test", "No tests found matching 'tests/t*.nim'"
return 0
for tf in tests:
info "atlas:test", "running:", tf
let cmd = buildTestCommand(nimPath, tf, extraArgs, runCode)
let code = execShellCmd(cmd)
if code != 0:
fatal "Test failed: " & tf, "atlas:test", code
return code
notice "atlas:test", "All tests passed"
return 0

proc runTests*(projectDir: Path; extraArgs: seq[string] = @[]; runCode = true; parallel: int; onlyTests: seq[string] = @[]): int =
## Run tests either serially or in parallel (parallel<=0 uses CPU count).
if projectDir.len == 0 or not dirExists($projectDir):
fatal "No project directory detected", "atlas:test"
return 1
let nimPath = findExe("nim")
if nimPath.len == 0:
fatal "Nim compiler not found in PATH", "atlas:test"
return 1
let tests =
if onlyTests.len > 0: onlyTests
else: discoverTests(projectDir)
if tests.len == 0:
warn "atlas:test", "No tests found matching 'tests/t*.nim'"
return 0
if parallel > 1:
let code = runTestsParallel(tests, nimPath, extraArgs, runCode, parallel)
if code != 0:
fatal "A test failed", "atlas:test", code
else:
notice "atlas:test", "All tests passed"
return code
else:
return runTestsSerial(projectDir, extraArgs, runCode, tests)

when isMainModule:
# Example usage: run all tests matching tests/t*.nim in parallel.
var tests: seq[string] = @[]
for f in walkFiles("tests/t*.nim"):
tests.add(f)
let code = runTestsParallel(tests)
quit(code)
2 changes: 1 addition & 1 deletion tests/testintegration.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import testerutils

ensureGitHttpServer()

if execShellCmd("nim c -o:$# -d:release src/atlas.nim" % [atlasExe]) != 0:
if execShellCmd("nim c --nimcache:.nimcache -o:$# -d:release src/atlas.nim" % [atlasExe]) != 0:
quit("FAILURE: compilation of atlas failed")

proc integrationTest() =
Expand Down
39 changes: 39 additions & 0 deletions tests/truntests.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import std/[os, strutils, unittest]
import testerutils

suite "atlas test runner":
# Ensure atlas binary exists
if execShellCmd("nim c --nimcache:.nimcache -o:$# -d:release src/atlas.nim" % [atlasExe]) != 0:
quit "Failed to build atlas binary"

let ws = "tests/ws_runtests"
if not dirExists(ws): createDir(ws)

withDir ws:
# Clean previous markers and prepare tests dir
if fileExists("ran_a.txt"): removeFile("ran_a.txt")
if fileExists("ran_b.txt"): removeFile("ran_b.txt")
if not dirExists("tests"): createDir("tests")

test "runs all tests by default":
exec atlasExe & " --project:. test"
check fileExists("ran_a.txt")
check fileExists("ran_b.txt")

# Reset markers
if fileExists("ran_a.txt"): removeFile("ran_a.txt")
if fileExists("ran_b.txt"): removeFile("ran_b.txt")

test "runs a single specified test":
exec atlasExe & " --project:. test tests/ta.nim"
check fileExists("ran_a.txt")
check not fileExists("ran_b.txt")

# Reset markers
if fileExists("ran_a.txt"): removeFile("ran_a.txt")
if fileExists("ran_b.txt"): removeFile("ran_b.txt")

test "runs multiple specified tests":
exec atlasExe & " --project:. test tests/ta.nim tests/tb.nim"
check fileExists("ran_a.txt")
check fileExists("ran_b.txt")
1 change: 1 addition & 0 deletions tests/ws_runtests/ran_a.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ok
1 change: 1 addition & 0 deletions tests/ws_runtests/ran_b.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ok
5 changes: 5 additions & 0 deletions tests/ws_runtests/tests/ta.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import std/[unittest, os]
suite "A":
test "a":
writeFile("ran_a.txt", "ok")
check true
5 changes: 5 additions & 0 deletions tests/ws_runtests/tests/tb.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import std/[unittest, os]
suite "B":
test "b":
writeFile("ran_b.txt", "ok")
check true
Loading