بنقرة واحدة
testing
Nim testing conventions, unittest framework, and C++ compatibility patterns
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Nim testing conventions, unittest framework, and C++ compatibility patterns
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Nim bindings to libtorch for tensor operations with high-level sugar
Nim type system patterns and pitfalls
Common import patterns and pitfalls for the Tattletale Nim project
Regex functionality in Nim including std/re, std/nre wrappers around PCRE, and the pure Nim nim-regex alternative with linear-time matching guarantees
Nim to Python interoperability including nimpy for calling Python from Nim and exporting Nim to Python, nimporter for packaging Nim modules as Python packages, and cffi/ctypes for calling Nim from Python
Nim's hash table module for key-value storage
| name | testing |
| description | Nim testing conventions, unittest framework, and C++ compatibility patterns |
| license | MIT |
| compatibility | opencode |
| metadata | {"audience":"developers","workflow":"testing"} |
I provide guidance for writing tests in Nim that:
std/unittest frameworkTorchTensorUse this skill when:
Nim's standard library provides a simple testing framework:
import std/unittest
suite "my module tests":
test "addition works":
check 1 + 1 == 2
test "string handling":
let result = "hello".toUpperAscii()
check result == "HELLO"
Key procs:
suite(name, body) - Group related teststest(name, body) - Define a single testcheck(expr) - Assert expression is true, prints failed value on failuredoAssert(expr) - Like check but raises on failure (use for invariants)submitTest(result) - Submit test result from a procedureWhen you declare variables at module scope (top-level) in Nim tests, the generated C++ code uses = {} initialization:
TorchTensor expectedTensor = {}; // This fails!
expectedTensor = myFunction(a, b);
The C++ torch::Tensor type (and other FFI types with cppNonPod) does not accept brace initialization. This causes:
error: ambiguous overload for 'operator=' (operand types are 'at::Tensor' and '<brace-enclosed initializer list>')
Always wrap test code in a proc main():
import std/unittest, workspace/libtorch
proc generateTensor(): TorchTensor =
# This works - Nim generates:
# auto result = myFunction(a, b);
arange(10, kFloat32)
proc runTests*() =
suite "tensor tests":
test "generate tensor":
let tensor = generateTensor()
check tensor.numel() == 10
when isMainModule:
runTests()
This generates proper C++:
auto tensor = generateTensor(); // No {} initialization
Tests that load files should follow this pattern:
import std/unittest, std/os, workspace/safetensors, workspace/libtorch
const FIXTURES_DIR = currentSourcePath().parentDir() / "fixtures"
proc main() =
suite "safetensors loading":
test "load fixture":
let fixturePath = FIXTURES_DIR / "model.safetensors"
check fileExists(fixturePath)
var mf = memfiles.open(fixturePath, mode = fmRead)
defer: mf.close()
let (st, offset) = safetensors.load(mf)
check st.tensors.len > 0
when isMainModule:
main()
Key points:
currentSourcePath().parentDir() / "fixtures" for fixture pathsmemfiles.open with defer: mf.close()continue for missing fixturesDefine test parameters as const at module level:
const Patterns = ["gradient", "alternating", "repeating"]
const Shapes: array[4, seq[int64]] = [
@[int64 8],
@[int64 4, 4],
@[int64 2, 3, 4],
@[int64 3, 2, 2, 2]
]
const TestedDtypes = [F64, F32, F16, I64, I32, I16, I8, U64, U32, U16, U8]
Extract reusable logic into proc with * export:
proc generateExpectedTensor*(pattern: string, shape: seq[int64], dtype: ScalarKind): TorchTensor =
let shapeRef = shape.asTorchView()
let numel = shape.product()
case pattern
of "gradient":
arange(numel, dtype).reshape(shapeRef).to(dtype)
of "alternating":
let flat = arange(numel, kInt64)
let modVal = (flat % 2).to(kFloat64)
modVal.reshape(shapeRef).to(dtype)
else:
raise newException(ValueError, "Unknown pattern: " & pattern)
Note: Each branch of a case must assign to result.
Each module has a task defined in config.nims for running its tests:
# Test toktoktok
nim test_toktoktok
# Test libtorch
nim test_libtorch
# Test safetensors
nim test_safetensors
The command nim test_toktoktok compiles and runs all test files in workspace/toktoktok/tests/ that start with test_ or t_.
The project uses:
--path:. - Makes workspace/module imports worknim cpp -r plus flags for output and cache directoriesFor this project, fixtures are in:
workspace/toktoktok/tests/tokenizers/
Reference fixtures using:
const FIXTURES_DIR = currentSourcePath().parentDir() / "tokenizers"
If you have a parameter named shape and access a field info.shape:
proc generateExpectedTensor*(pattern: string, shape: seq[int64], ...): TorchTensor =
for info in tensors: # error: 'shape' shadows info.shape
check info.shape == shape
Fix: Rename parameter to avoid shadowing:
proc generateExpectedTensor*(pattern: string, shapeSeq: seq[int64], ...): TorchTensor =
for info in tensors:
check info.shape == shapeSeq # Now works
Each branch of a case must explicitly assign to result:
proc foo(x: int): int =
case x
of 1: result = 10 # Must use 'result ='
of 2: 20 # ERROR: doesn't assign!
Follow the naming convention: test_*.nim or t_*.nim in the module's tests/ directory.
# workspace/my_module/tests/test_myfeature.nim
import std/unittest, std/os
import workspace/my_module
proc runMyFeatureTests*() =
suite "my feature tests":
test "basic functionality":
let result = myModule.function()
check result == expectedValue
when isMainModule:
runMyFeatureTests()
The test will be discovered automatically by the test command:
# If it's in my_module:
nim c -r --task:test_my_module
Or run all tests for the module:
nim test_my_module
Create a fixtures/ directory and add test data:
workspace/my_module/tests/fixtures/
Reference in test code:
const FIXTURES_DIR = currentSourcePath().parentDir() / "fixtures"
let fixturePath = FIXTURES_DIR / "test_data.bin"
test_ or t_workspace/module/tests/proc runTests*()when isMainModule: runTests() at the enddefer for resource cleanup (files, etc.)*constFor AI/ML modules, test vectors are generated via Python scripts using torch and safetensors.
workspace/module/
├── tests/
│ ├── test_module.nim # Nim tests
│ ├── fixtures/ # Generated fixture files
│ │ ├── model.safetensors
│ │ └── tokenizer.json
│ └── testgen/ # Python test vector generators
│ └── generate_vectors.py
pyproject.toml with [dependency-groups] for shared dependencies:
[dependency-groups]
test-vectors = [
"torch>=2.0.0",
"safetensors>=0.7.0",
"transformers>=4.40.0",
"numpy>=2.4.2",
]
uv run --group test-vectors python workspace/module/tests/testgen/generate_vectors.pyimport torch
import numpy as np
from safetensors.numpy import save_file
import os
FIXTURES_DIR = os.path.join(
os.path.dirname(os.path.dirname(__file__)),
"fixtures",
)
def generate_vandermonde():
x = torch.arange(1, 6, dtype=torch.float32)
vandermonde = torch.vander(x, increasing=True).T
return vandermonde.to(torch.bfloat16).view(torch.uint16).numpy()
def main():
fixtures = {
"BF16_vandermonde_5x5": generate_vandermonde(),
}
save_file(fixtures, os.path.join(FIXTURES_DIR, "vandermonde.safetensors"))
print("Fixtures generated")
if __name__ == "__main__":
main()
When adding new test vectors, regenerate the fixture files:
uv run --group test-vectors python workspace/module/tests/testgen/generate_vectors.py
Tests involving TorchTensor and other libtorch FFI types should use the shared test utilities:
import workspace/libtorch_testutils
Wrap test code that may throw C++ exceptions:
proc testTensorOps(): bool =
let a = ones(@[2, 3], kFloat32)
let b = zeros(@[2, 3], kFloat32)
let c = a + b
result = c.isDefined()
when isMainModule:
runTest("tensor operations", testTensorOps) # Handles exceptions automatically
Or use the template directly:
check catchCppExceptions(testTensorOps())
assertDefined - Check tensor is initialized:
let tensor = ones(@[2, 3], kFloat32)
assertDefined(tensor) # Raises if not defined
assertDefined(tensor, "weight") # Custom name in error
assertShape - Verify tensor dimensions:
let tensor = randn(@[2, 3, 4])
assertShape(tensor, 2, 3, 4)
assertDtype - Verify tensor dtype:
let tensor = ones(@[2, 3], kFloat32)
assertDtype(tensor, kFloat32)
assertAllClose / assertClose - Compare tensor values:
let actual = computeSomething()
let expected = ones(@[2, 3], kFloat32) * 2.0
assertAllClose(actual, expected) # Default rtol=2e-2, abstol=2e-2
assertClose(actual, expected, rtol=1e-5, abstol=1e-5) # Custom tolerance
printTensor - Print tensor with label:
printTensor(myTensor, "Weight matrix")
printTensorShape - Print shape and dtype:
printTensorShape(myTensor, "Input")
# Output: Input:
# Shape: [2, 3, 4], Dtype: kFloat32
ptrHex - Convert pointer to hex string for aliasing detection:
let tensor = ones(@[2, 3], kFloat32)
echo "data_ptr = 0x", tensor.data_ptr().ptrHex()
echo "shape.data() = 0x", tensor.shape.data().ptrHex()
# Useful for detecting memory aliasing issues
dataPtrHex / shapePtrHex - Convenience wrappers:
let tensor = ones(@[2, 3], kFloat32)
echo "data_ptr = 0x", tensor.dataPtrHex()
echo "shape_ptr = 0x", tensor.shapePtrHex()
# Equivalent to above but more convenient
printTensorShape(myTensor, "Input")
# Output: Input:
# Shape: [2, 3, 4], Dtype: kFloat32
traceExec - Debug macro to trace execution:
traceExec:
let a = ones(@[2, 3])
let b = zeros(@[2, 3])
let c = a + b
# Prints each statement before executing
Complete example:
# workspace/my_module/tests/test_feature.nim
import
std/unittest,
workspace/libtorch,
workspace/libtorch_testutils,
workspace/my_module
proc testBasicFunctionality(): bool =
let input = ones(@[2, 3], kFloat32)
let result = myModule.process(input)
assertDefined(result)
assertShape(result, 2, 3)
result = true
proc testEdgeCase(): bool =
let input = zeros(@[1], kFloat32)
let output = myModule.process(input)
assertAllClose(output, input)
result = true
when isMainModule:
runTest("basic functionality", testBasicFunctionality)
runTest("edge case", testEdgeCase)
workspace/libtorch_testutils for tests with TorchTensorrunTest for formatted output with automatic exception handlingcatchCppExceptions when integrating with std/unittest checkassertDefined, assertShape, etc.) for clear error messagesprintTensor and printTensorShape for debugging failuresworkspace/module/tests/ directorytest_ or t_