Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ on:

jobs:
ci:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
container:
image: exercism/gdscript-test-runner

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f

- name: Verify all exercises
run: bin/verify-exercises
run: godot --headless -s bin/verify-exercises.gd
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ Exercism exercises in GDScript.

## Testing

To set up for testing, clone https://github.com/exercism/gdscript-test-runner and move its contents to `/opt/test-runner`.
To set up for testing, clone https://github.com/exercism/gdscript-test-runner and move its contents to `/opt/exercism/gdscript/test-runner`:

To test the exercises, run `./bin/verify-exercises`.
```sh
git clone https://github.com/exercism/gdscript-test-runner.git
sudo mkdir -p /opt/exercism/gdscript/
sudo mv gdscript-test-runner/ /opt/exercism/gdscript/test-runner/
```

To test the exercises, run `godot --headless -s bin/verify-exercises.gd`.
This command will iterate over all exercises and check to see if their exemplar/example implementation passes all the tests.

### Track linting
Expand Down
41 changes: 0 additions & 41 deletions bin/verify-exercises

This file was deleted.

90 changes: 90 additions & 0 deletions bin/verify-exercises.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
extends SceneTree

# godot --headless -s bin/verify-exercises.gd

func _init():
# Calling `quit(1)` doesn't stop the program immediately, so a `return` is necessary.
# That's why errors are checked directly in `_init()`, instead of calling `quit(1)`
# in each method.
var temp_dir_base = DirAccess.create_temp("gdscript-verify-exercises")
if temp_dir_base == null:
push_error("Failed to create base temporary directory: %s", DirAccess.get_open_error())
quit(1)
return
var temp_dir_base_path = temp_dir_base.get_current_dir()

for exercise_base in [
"exercises/practice",
"exercises/concept"
]:
if run_tests(exercise_base, temp_dir_base_path) != OK:
quit(1)
return
quit()

func run_tests(exercise_base: String, temp_dir_base_path: String) -> Error:
for slug in DirAccess.get_directories_at(exercise_base):
var result = run_test(slug, exercise_base + "/" + slug, temp_dir_base_path)
if result != OK:
return result
return OK


func run_test(slug: String, exercise_dir: String, temp_dir_base_path: String) -> Error:
var temp_dir: String = temp_dir_base_path + "/" + slug
DirAccess.make_dir_recursive_absolute(temp_dir)

var config_path = exercise_dir + "/.meta/config.json"
var config_file = FileAccess.open(config_path, FileAccess.READ)
if not config_file:
push_error("Failed to read config: " + config_path)
return DirAccess.get_open_error()

var json = JSON.new()
var parse_return_value = json.parse(config_file.get_as_text())
var config
if parse_return_value == OK:
config = json.data
if typeof(config) != TYPE_DICTIONARY:
push_error("Expected TYPE_DICTIONARY for JSON in: " + config_path)
return ERR_UNCONFIGURED
else:
push_error("JSON Parse Error: ", json.get_error_message(), " in ", config_path, " at line ", json.get_error_line())
return parse_return_value

var solution_name: String = config.get("files", {}).get("solution", [])[0]
var test_path = exercise_dir + "/" + config.get("files", {}).get("test", [])[0]
var example_path = exercise_dir + "/" + config.get("files", {}).get("example", [])[0]
var dest_solution = temp_dir + "/" + solution_name

# Copy test and example files into temp dir
DirAccess.copy_absolute(test_path, temp_dir + "/" + test_path.get_file())
DirAccess.copy_absolute(example_path, dest_solution)

# Run external test script
var args = [slug, temp_dir, temp_dir]
var output = []
var exit_code = OS.execute("/opt/exercism/gdscript/test-runner/bin/run.sh", args, output, true)
if exit_code != 0:
push_error("Test runner failed for ", slug, " with ", output)
return ERR_SCRIPT_FAILED
print(output[0])

# Check test result
var results_path = temp_dir + "/results.json"
if not FileAccess.file_exists(results_path):
push_error("Missing results.json for ", slug, " at ", results_path)
return ERR_FILE_NOT_FOUND
var results_file = FileAccess.open(results_path, FileAccess.READ)
parse_return_value = json.parse(results_file.get_as_text())
var results
if parse_return_value == OK:
results = json.data
if results.get("status") != "pass":
push_error("Tests for ", slug, " have failed:")
push_error(JSON.stringify(results, "\t"))
return FAILED
else:
push_error("JSON Parse Error: ", json.get_error_message(), " in ", config_path, " at line ", json.get_error_line())
return parse_return_value
return OK
41 changes: 33 additions & 8 deletions docs/TESTS.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
# Tests

<!-- TODO: write document
## Installing the Exercism GDScript track test runner

This document should describe everything related to running tests in the track.
You'll need [Godot installed][installation] correctly to use the test runner.

If your track uses skipped tests, this document can explain why thet is used and
how to unskip tests.
To build and test GDScript scripts for Exercism, Godot will be run in "headless" mode from the CLI.
These instructions currently require Linux and bash.

This document can also link to the testing framework documentation.
### Step 1: Installing the overall test runner infrastructure

The contents of this document are displayed on the track's documentation
page at `https://exercism.org/docs/tracks/<track>/tests`.
To set up for testing, clone [https://github.com/exercism/gdscript-test-runner][gdscript-test-runner] and move its contents to `/opt/exercism/gdscript/test-runner/`:

See https://exercism.org/docs/building/tracks/docs for more information. -->
```sh
git clone https://github.com/exercism/gdscript-test-runner.git
sudo mkdir -p /opt/exercism/gdscript/
sudo mv gdscript-test-runner/ /opt/exercism/gdscript/test-runner/
```

### Step 2: Downloading the single-exercise test runner script

Assuming you have the `exercism` tool set up and have downloaded at least one GDScript exercise, it should have created an `exercism/gdscript` folder to house the exercises.
Save [the test runner][test-local-gdscript-solution] in this folder and mark the file executable, or just create a link to it:

```sh
cd ~/exercism/gdscript # replace with your exercism/gdscript directory
ln /opt/exercism/gdscript/test-runner/bin/test-local-gdscript-solution.sh
```

## Running tests

With the installation steps done, you should be able start from any exercise directory (e.g., `~/exercism/gdscript/two-fer`) and run the script (living one level up, in `../`) to test your local solution to that exercise:

```sh
../test-local-gdscript-solution.sh
```

[installation]: https://exercism.org/docs/tracks/gdscript/installation
[gdscript-test-runner]: https://github.com/exercism/gdscript-test-runner
[test-local-gdscript-solution]: https://raw.githubusercontent.com/exercism/gdscript-test-runner/refs/heads/main/bin/test-local-gdscript-solution.sh
20 changes: 12 additions & 8 deletions exercises/shared/.docs/tests.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Tests

<!-- TODO: write document
## Running tests

This document should contain instructions on how to run the exercise's tests.
With the [installation steps][test-installation-steps] done, you should be able start from any exercise directory (e.g., `~/exercism/gdscript/two-fer`) and run the script (living one level up, in `../`) to test your local solution to that exercise:

The instructions should be short and to the point.
```sh
../test-local-gdscript-solution.sh
```

The docs/TESTS.md file can contain a more verbose description on how to run tests.
## Solving an exercise

When a student downloads an exercise via the CLI, this file's contents are
included into the HELP.md file.
Solving an exercise means making all its tests pass.

See https://exercism.org/docs/building/tracks/shared-files for more information.
-->
To help you get started, each exercise comes with a GDScript file named after the exercise (e.g., `exercism/gdscript/two-fer/two_fer.gd`).
You can use this file as a starting point for building your solution.
Feel free to change it as needed, but the tests expect the function names to be as originally provided.

[test-installation-steps]: https://exercism.org/docs/tracks/gdscript/tests