Skip to content

Commit 1ea6b5b

Browse files
authored
Merge pull request GaijinEntertainment#2128 from GaijinEntertainment/language-testing
daScriptTests migrated to dastest framework
2 parents 9f85346 + b78ad1f commit 1ea6b5b

499 files changed

Lines changed: 8923 additions & 11321 deletions

File tree

Some content is hidden

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

.github/copilot-instructions.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Task-specific instructions are split into skill files under `skills/`. You MUST
3636

3737
Multiple skill files may apply to a single task. For example, creating a new daslib module requires reading `skills/das_formatting.md`, `skills/daslib_modules.md`, and possibly `skills/documentation_rst.md`.
3838

39+
### Updating Instructions with New Knowledge
40+
41+
When you discover something new about daslang syntax, semantics, or conventions — whether through compiler errors, user corrections, or experimentation — **update this file** (and its `.github/copilot-instructions.md` mirror) with the new knowledge. Add it to the appropriate section (gen2 syntax, common gotchas, etc.) so future sessions benefit. If it relates to a specific skill area, update the relevant `skills/*.md` file too.
42+
3943
## daslang Language — Gen2 Syntax (REQUIRED)
4044

4145
All code examples and documentation MUST use gen2 syntax (add `options gen2` at the top of every file). Key rules:
@@ -57,6 +61,7 @@ All code examples and documentation MUST use gen2 syntax (add `options gen2` at
5761
- **Bitfield dot access:** read with `f.flag` (returns bool), write with `f.flag = true/false`
5862
- **`typeinfo`** gen2 syntax: trait name goes **outside** parentheses — `typeinfo trait_name(type<T>)`, NOT `typeinfo(trait_name type<T>)`. With subtrait: `typeinfo has_method<name>(type<T>)`. With two traits: `typeinfo trait<sub;extra>(type<T>)`
5963
- **`static_if`:** `static_if (condition) { ... }` — parentheses required in gen2
64+
- **Type function call:** `take(type<int>, 1, 2)` — NOT `take < int > (1, 2)`. The `type<T>` is passed as a regular argument
6065

6166
### Important defaults
6267

@@ -186,6 +191,7 @@ All code examples and documentation MUST use gen2 syntax (add `options gen2` at
186191
- Blocks CANNOT be stored in containers, returned from functions, or captured — use lambdas or function pointers for those use cases
187192
- `match`, `multi_match`, `static_match` macros (from `daslib/match.das`) handle side effects automatically — do NOT add `[sideeffects]` annotations to functions that only use match
188193
- `[export] def main()` returns `void` — do NOT `return true` or return other values from main
194+
- **`feint` vs `print` in tests**: `feint` is a no-op with the same signature and `SideEffects::modifyExternal` as `print` — it won't be optimized out, but produces no output. Use `feint` in tests instead of `print` unless testing actual print/logging behavior
189195
- **`push` vs `emplace` vs `push_clone`** for arrays: `push(arr, val)` copies `val` into the array (fails for non-copyable types like `array<int>`); `emplace(arr, val)` **moves** `val` into the array (source is zeroed/destroyed after); `push_clone(arr, val)` **clones** `val` into the array (works for any type, preserves source). When generating code that operates on user structs with potentially non-copyable fields, prefer `push_clone` (preserves source) or `emplace` (when source consumption is intentional).
190196
- **Non-copyable types**: `array<T>`, `table<K;V>`, lambdas, and any struct containing them cannot be copied with `=` or `push`. Use `:=` (clone-assign), `push_clone`, or `<-` (move) instead. The compiler error is clear: "can't copy non-copyable type"
191197

@@ -205,20 +211,14 @@ All code examples and documentation MUST use gen2 syntax (add `options gen2` at
205211
- `include/daScript/` — C++ headers
206212
- `daslib/` — Standard library modules (86 .das files)
207213
- `dastest/` — Test framework
208-
- `tests/` — Test suite (with per-module subfolders: `tests/decs/`, `tests/match/`, `tests/json/`, etc.)
214+
- `tests/` — Test suite (35 subdirectories, ~226 `.das` files). See `tests/README.md` for a full index of every test file, what it tests, and whether it expects compile errors.
209215
- `doc/source/reference/language/` — RST language documentation (36 files)
210216
- `doc/source/stdlib/` — RST standard library documentation (auto-generated + handmade)
211217
- `doc/reflections/` — Documentation generation tools (das2rst.das, rst.das, gen_module_examples.py)
212218
- `tutorials/language/` — Language tutorial `.das` files (42 progressive tutorials)
213219
- `tutorials/integration/cpp/` — C++ integration tutorials (embedding daslang in C++ host applications)
214220
- `tutorials/macros/` — Macro tutorials (call macros, reader macros, etc.)
215221
- `doc/source/reference/tutorials/` — RST companion pages for each tutorial
216-
- `tests/linq/` — LINQ module tests (15 test files, ~500 tests)
217-
- `tests/functional/` — Functional module tests
218-
- `tests/json/` — JSON module tests (4 test files, ~148 tests)
219-
- `tests/regex/` — Regex module tests (8 test files, 278 tests)
220-
- `tests/interfaces/` — Interface module tests (4 test files, 67 tests)
221-
- `tests/soa/` — SOA module tests (4 test files, 126 tests: basic, iteration, container ops, non-copyable fields)
222222
- `modules/` — External plugin modules
223223

224224
## Keywords Reference

.github/workflows/build.yml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -309,29 +309,25 @@ jobs:
309309
# when exceptions disabled, we still throw in tests and there'll be memory leaks
310310
echo -e "leak:format_error\n" > suppressions.txt
311311
export LSAN_OPTIONS="suppressions='suppressions.txt'"
312-
[ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daScriptTest -jit
313-
./daScriptTest
314312
# Use more time to pass UBSAN and disable leaks in JIT mode because we exit using exit(), and do not call destructors.
315-
[ "${{ env.das_llvm_disabled }}" = "ON" ] || ASAN_OPTIONS=detect_leaks=0 ./daslang _dasroot_/dastest/dastest.das -jit -- --color --test ../tests || ASAN_OPTIONS=detect_leaks=0 ./daslang _dasroot_/dastest/dastest.das -jit -- --color --isolated-mode --timeout 4800 --test ../tests
313+
# TEMPORARILY DISABLED: -jit dastest
314+
# [ "${{ env.das_llvm_disabled }}" = "ON" ] || ASAN_OPTIONS=detect_leaks=0 ./daslang _dasroot_/dastest/dastest.das -jit -- --color --test ../tests || ASAN_OPTIONS=detect_leaks=0 ./daslang _dasroot_/dastest/dastest.das -jit -- --color --isolated-mode --timeout 4800 --test ../tests
316315
./daslang _dasroot_/dastest/dastest.das -- --color --test ../tests || ./daslang _dasroot_/dastest/dastest.das -- --color --isolated-mode --timeout 3600 --test ../tests
317316
;;
318317
windows32)
319318
cd bin/"${{ matrix.cmake_preset }}"
320-
./daScriptTest.exe
321319
./daslang _dasroot_/dastest/dastest.das -- --color --test ../../tests || ./daslang _dasroot_/dastest/dastest.das -- --color --isolated-mode --timeout 3600 --test ../../tests
322320
;;
323321
windows*)
324322
cd bin/"${{ matrix.cmake_preset }}"
325-
[ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daScriptTest.exe -jit
326-
./daScriptTest.exe
327-
[ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --test ../../tests || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --isolated-mode --timeout 3600 --test ../../tests
323+
# TEMPORARILY DISABLED: -jit dastest
324+
# [ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --test ../../tests || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --isolated-mode --timeout 3600 --test ../../tests
328325
./daslang _dasroot_/dastest/dastest.das -- --color --test ../../tests || ./daslang _dasroot_/dastest/dastest.das -- --color --isolated-mode --timeout 3600 --test ../../tests
329326
;;
330327
*)
331328
cd bin
332-
[ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daScriptTest -jit
333-
./daScriptTest
334-
[ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --test ../tests || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --isolated-mode --timeout 3600 --test ../tests
329+
# TEMPORARILY DISABLED: -jit dastest
330+
# [ "${{ env.das_llvm_disabled }}" = "ON" ] || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --test ../tests || ./daslang _dasroot_/dastest/dastest.das -jit -- --color --isolated-mode --timeout 3600 --test ../tests
335331
./daslang _dasroot_/dastest/dastest.das -- --color --test ../tests || ./daslang _dasroot_/dastest/dastest.das -- --color --isolated-mode --timeout 3600 --test ../tests
336332
;;
337333
esac

CLAUDE.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Task-specific instructions are split into skill files under `skills/`. You MUST
3636

3737
Multiple skill files may apply to a single task. For example, creating a new daslib module requires reading `skills/das_formatting.md`, `skills/daslib_modules.md`, and possibly `skills/documentation_rst.md`.
3838

39+
### Updating Instructions with New Knowledge
40+
41+
When you discover something new about daslang syntax, semantics, or conventions — whether through compiler errors, user corrections, or experimentation — **update this file** (and its `.github/copilot-instructions.md` mirror) with the new knowledge. Add it to the appropriate section (gen2 syntax, common gotchas, etc.) so future sessions benefit. If it relates to a specific skill area, update the relevant `skills/*.md` file too.
42+
3943
## daslang Language — Gen2 Syntax (REQUIRED)
4044

4145
All code examples and documentation MUST use gen2 syntax (add `options gen2` at the top of every file). Key rules:
@@ -57,6 +61,7 @@ All code examples and documentation MUST use gen2 syntax (add `options gen2` at
5761
- **Bitfield dot access:** read with `f.flag` (returns bool), write with `f.flag = true/false`
5862
- **`typeinfo`** gen2 syntax: trait name goes **outside** parentheses — `typeinfo trait_name(type<T>)`, NOT `typeinfo(trait_name type<T>)`. With subtrait: `typeinfo has_method<name>(type<T>)`. With two traits: `typeinfo trait<sub;extra>(type<T>)`
5963
- **`static_if`:** `static_if (condition) { ... }` — parentheses required in gen2
64+
- **Type function call:** `take(type<int>, 1, 2)` — NOT `take < int > (1, 2)`. The `type<T>` is passed as a regular argument
6065

6166
### Important defaults
6267

@@ -186,6 +191,7 @@ All code examples and documentation MUST use gen2 syntax (add `options gen2` at
186191
- Blocks CANNOT be stored in containers, returned from functions, or captured — use lambdas or function pointers for those use cases
187192
- `match`, `multi_match`, `static_match` macros (from `daslib/match.das`) handle side effects automatically — do NOT add `[sideeffects]` annotations to functions that only use match
188193
- `[export] def main()` returns `void` — do NOT `return true` or return other values from main
194+
- **`feint` vs `print` in tests**: `feint` is a no-op with the same signature and `SideEffects::modifyExternal` as `print` — it won't be optimized out, but produces no output. Use `feint` in tests instead of `print` unless testing actual print/logging behavior
189195
- **`push` vs `emplace` vs `push_clone`** for arrays: `push(arr, val)` copies `val` into the array (fails for non-copyable types like `array<int>`); `emplace(arr, val)` **moves** `val` into the array (source is zeroed/destroyed after); `push_clone(arr, val)` **clones** `val` into the array (works for any type, preserves source). When generating code that operates on user structs with potentially non-copyable fields, prefer `push_clone` (preserves source) or `emplace` (when source consumption is intentional).
190196
- **Non-copyable types**: `array<T>`, `table<K;V>`, lambdas, and any struct containing them cannot be copied with `=` or `push`. Use `:=` (clone-assign), `push_clone`, or `<-` (move) instead. The compiler error is clear: "can't copy non-copyable type"
191197

@@ -205,20 +211,14 @@ All code examples and documentation MUST use gen2 syntax (add `options gen2` at
205211
- `include/daScript/` — C++ headers
206212
- `daslib/` — Standard library modules (86 .das files)
207213
- `dastest/` — Test framework
208-
- `tests/` — Test suite (with per-module subfolders: `tests/decs/`, `tests/match/`, `tests/json/`, etc.)
214+
- `tests/` — Test suite (35 subdirectories, ~226 `.das` files). See `tests/README.md` for a full index of every test file, what it tests, and whether it expects compile errors.
209215
- `doc/source/reference/language/` — RST language documentation (36 files)
210216
- `doc/source/stdlib/` — RST standard library documentation (auto-generated + handmade)
211217
- `doc/reflections/` — Documentation generation tools (das2rst.das, rst.das, gen_module_examples.py)
212218
- `tutorials/language/` — Language tutorial `.das` files (42 progressive tutorials)
213219
- `tutorials/integration/cpp/` — C++ integration tutorials (embedding daslang in C++ host applications)
214220
- `tutorials/macros/` — Macro tutorials (call macros, reader macros, etc.)
215221
- `doc/source/reference/tutorials/` — RST companion pages for each tutorial
216-
- `tests/linq/` — LINQ module tests (15 test files, ~500 tests)
217-
- `tests/functional/` — Functional module tests
218-
- `tests/json/` — JSON module tests (4 test files, ~148 tests)
219-
- `tests/regex/` — Regex module tests (8 test files, 278 tests)
220-
- `tests/interfaces/` — Interface module tests (4 test files, 67 tests)
221-
- `tests/soa/` — SOA module tests (4 test files, 126 tests: basic, iteration, container ops, non-copyable fields)
222222
- `modules/` — External plugin modules
223223

224224
## Keywords Reference

CMakeLists.txt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -909,17 +909,9 @@ SET(DAS_DASCRIPT_MAIN_SRC
909909
CACHE INTERNAL "DAS_DASCRIPT_MAIN_SRC"
910910
)
911911

912-
# Test module
913-
914-
915912
# Tests
916913
if (NOT ${DAS_TESTS_DISABLED})
917-
include(examples/test/CMakeLists.txt)
918914
include(examples/pathTracer/CMakeLists.txt)
919-
920-
FOREACH(_example ${DAS_ALL_EXAMPLES})
921-
TARGET_LINK_LIBRARIES(${_example} libDaScriptTest)
922-
ENDFOREACH()
923915
endif()
924916

925917
# Profile module

dastest/dastest.das

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ options multiple_contexts
1717

1818
let private jsonResultPrefix = "##dastest##\n"
1919

20+
struct FileTiming {
21+
uri : string
22+
dt : int // microseconds
23+
}
24+
25+
var private fileTimings : array<FileTiming>
26+
var private timingOutliers : int = 0
27+
2028

2129
def private collect_input_paths(args : array<string>; var paths : array<string>) {
2230
for (i in range(length(args) - 1)) {
@@ -109,6 +117,8 @@ def main() {
109117
return
110118
}
111119

120+
timingOutliers = args |> get_int_arg("--timing-outliers", 0)
121+
112122
let isolatedMode = args |> has_value("--isolated-mode")
113123
let ctx <- SuiteCtx(args)
114124
if (!isolatedMode) {
@@ -124,6 +134,9 @@ def main() {
124134
} else {
125135
log::red("FAIL {uri} {time_dt_hr(fileDt)}")
126136
}
137+
if (timingOutliers > 0) {
138+
fileTimings |> push(FileTiming(uri = clone_string(uri), dt = fileDt))
139+
}
127140
res += status
128141
}
129142
} else {
@@ -202,6 +215,9 @@ def main() {
202215
for (l in isoRes.log) {
203216
log::info_raw(l)
204217
}
218+
if (timingOutliers > 0) {
219+
fileTimings |> push(FileTiming(uri = clone_string(isoRes.uri), dt = isoRes.fileDt))
220+
}
205221
if (isoRes.exitCode == 0 && isoRes.parsedResult && isoRes.status.failed + isoRes.status.errors == 0) {
206222
log::green("PASS {isoRes.uri} {time_dt_hr(isoRes.fileDt)}")
207223
} else {
@@ -238,10 +254,54 @@ def timeout_tests(var res : SuiteResult; start_time : int64; timeout_sec : float
238254
}
239255

240256

257+
def private print_timing_outliers() {
258+
let n = length(fileTimings)
259+
// compute mean
260+
var sum = 0.0lf
261+
for (ft in fileTimings) {
262+
sum += double(ft.dt)
263+
}
264+
let mean = sum / double(n)
265+
// compute stddev
266+
var variance = 0.0lf
267+
for (ft in fileTimings) {
268+
let diff = double(ft.dt) - mean
269+
variance += diff * diff
270+
}
271+
let stddev = sqrt(variance / double(n))
272+
// sort descending by time
273+
sort(fileTimings) <| $(a, b) {
274+
return a.dt > b.dt
275+
}
276+
let count = min(timingOutliers, n)
277+
let threshold = mean + 2.0lf * stddev
278+
// only print if there are actual outliers (top entry above threshold) or if few files
279+
var meanStr = ""
280+
meanStr = fmt(":.3f", mean / 1000000.0lf)
281+
var stddevStr = ""
282+
stddevStr = fmt(":.3f", stddev / 1000000.0lf)
283+
var thresholdStr = ""
284+
thresholdStr = fmt(":.3f", threshold / 1000000.0lf)
285+
log::info("Timing: mean {meanStr}s, stddev {stddevStr}s, threshold {thresholdStr}s")
286+
log::info("Top {count} slowest files:")
287+
for (i in range(count)) {
288+
let dtSec = double(fileTimings[i].dt) / 1000000.0lf
289+
let marker = double(fileTimings[i].dt) > threshold ? " [outlier]" : ""
290+
var dtStr = ""
291+
dtStr = fmt(":.3f", dtSec)
292+
var line = " {dtStr}s {fileTimings[i].uri}{marker}"
293+
log::info(line)
294+
}
295+
}
296+
297+
241298
def finish_tests(res : SuiteResult; start_time : int64) {
242299
let resCode = res.failed + res.errors
243300
let totalDt = get_time_usec(start_time)
244301
log::info("\n{to_string(res)}")
302+
if (resCode == 0 && timingOutliers > 0 && length(fileTimings) > 1) {
303+
print_timing_outliers()
304+
}
245305
if (resCode == 0) {
246306
log::green("SUCCESS! {time_dt_hr(totalDt)}")
247307
} else {

dastest/fs.das

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ def scan_dir(path : string; var cache : table<string; void?>; var res : array<st
8484
return
8585
}
8686
if (fStat.is_dir) {
87+
if (n |> starts_with("_")) {
88+
return
89+
}
8790
f |> scan_dir(cache, res, suffix)
8891
} elif (fStat.is_reg && f |> ends_with(suffix) && !cache |> key_exists(f)) {
8992
cache |> insert(f, null)

doc/reflections/das2rst.das

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ get_value|insert_clone|emplace_new|insert_default|emplace_default|get_with_defau
136136
group_by_regex("Smart ptr infrastructure", mod, %regex~(add_ptr_ref|smart_ptr|get_const_ptr|move|move_new|get_ptr$)%%),
137137
group_by_regex("Macro infrastructure", mod, %regex~(is_folding|is_compiling|is_in_completion|is_reporting_compilation_errors)%%),
138138
group_by_regex("Profiler", mod, %regex~(profile|reset_profiler|dump_profile_info|collect_profile_info)$%%),
139-
group_by_regex("System infrastructure", mod, %regex~(panic|print|sprint|sprint_json|to_log|to_compiler_log|error|terminate|breakpoint|stackwalk|get_das_root|is_in_aot|aot_enabled|is_intern_strings|eval_main_loop)$%%),
139+
group_by_regex("System infrastructure", mod, %regex~(panic|print|feint|sprint|sprint_json|to_log|to_compiler_log|error|terminate|breakpoint|stackwalk|get_das_root|is_in_aot|aot_enabled|is_intern_strings|eval_main_loop)$%%),
140140
group_by_regex("Memory manipulation", mod, %regex~(intptr|memcmp|variant_index|set_variant_index|hash|memcpy|lock_data|map_to_array|map_to_ro_array)$%%),
141141
group_by_regex("Binary serializer", mod, %regex~(binary_load|binary_save)$%%),
142142
group_by_regex("Path and command line", mod, %regex~(get_command_line_arguments)$%%),

doc/source/stdlib/builtin.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,6 +2310,7 @@ System infrastructure
23102310
* :ref:`breakpoint () <function-builtin_breakpoint>`
23112311
* :ref:`error (text: string) <function-builtin_error_string>`
23122312
* :ref:`eval_main_loop (block: block\<():void\>) <function-builtin_eval_main_loop_block_ls__c_void_gr_>`
2313+
* :ref:`feint (text: string) <function-builtin_feint_string>`
23132314
* :ref:`get_das_root () : string <function-builtin_get_das_root>`
23142315
* :ref:`is_in_aot () : bool <function-builtin_is_in_aot>`
23152316
* :ref:`is_intern_strings () : bool <function-builtin_is_intern_strings>`
@@ -2354,6 +2355,15 @@ Executes the application main loop by repeatedly invoking `block` until it retur
23542355

23552356
:Arguments: * **block** : block<void> implicit
23562357

2358+
.. _function-builtin_feint_string:
2359+
2360+
.. das:function:: feint(text: string)
2361+
2362+
No-op replacement for `print`. Has the same signature and side-effect annotations as `print`, but intentionally does nothing. Use `feint` in tests where print-like behavior is needed to prevent the call from being optimized out, but no actual output is desired.
2363+
2364+
2365+
:Arguments: * **text** : string implicit
2366+
23572367
.. _function-builtin_get_das_root:
23582368

23592369
.. das:function:: get_das_root() : string

doc/source/stdlib/detail/builtin.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
.. |handmade/function-builtin-print| replace:: to be documented in |handmade/function-builtin-print|.rst
3636

37+
.. |handmade/function-builtin-feint| replace:: to be documented in |handmade/function-builtin-feint|.rst
38+
3739
.. |handmade/function-builtin-error| replace:: to be documented in |handmade/function-builtin-error|.rst
3840

3941
.. |handmade/function-builtin-sprint| replace:: to be documented in |handmade/function-builtin-sprint|.rst

0 commit comments

Comments
 (0)