diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..7ffd81d --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,12 @@ +# yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json +version: 2 +updates: + - directory: / + package-ecosystem: github-actions + labels: + - Dependency + schedule: + interval: weekly + day: wednesday + time: "12:00" + timezone: Canada/Mountain diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..cfa20fe --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,65 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Build +on: + workflow_call: + secrets: + github-token: + required: true + description: | + The token to use when making authenticated GitHub API calls. + inputs: + build-os: + required: true + type: string + description: | + The operating system to build for. + + Valid options are: + + * `windows` + * `linux` + * `macos` + outputs: + artifact-id: + description: | + The ID of the artifact generated by this workflow. + value: ${{jobs.build.outputs.artifact-id}} + artifact-name: + description: | + The name of the artifact generated by this workflow. + value: ${{jobs.build.outputs.artifact-name}} +jobs: + build: + name: Build + runs-on: ${{(inputs.build-os == 'windows' && 'windows-latest') || (inputs.build-os == 'linux' && 'ubuntu-latest') || (inputs.build-os == 'macos' && 'macos-latest') || ''}} + outputs: + artifact-id: ${{steps.upload-artifacts.outputs.artifact-id}} + artifact-name: dist-${{inputs.build-os}} + steps: + - id: install-tasks + name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.github-token || secrets.GITHUB_TOKEN }} + - id: checkout-repository + name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{secrets.github-token || secrets.GITHUB_TOKEN}} + lfs: true + - id: setup-odin + name: Setup Odin + uses: laytan/setup-odin@v2.5.2 + with: + token: ${{secrets.github-token || secrets.GITHUB_TOKEN}} + release: latest + - id: build-distribution + name: Build distribution + run: task dist + - id: upload-artifacts + name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: dist-${{inputs.build-os}} + path: dist diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..132f027 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,35 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Lint +on: + workflow_call: + secrets: + github-token: + required: true + description: | + The token to use when making authenticated GitHub API calls. +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - id: install-tasks + name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.github-token || secrets.GITHUB_TOKEN }} + - id: checkout-repository + name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{secrets.github-token || secrets.GITHUB_TOKEN}} + lfs: true + - id: setup-odin + name: Setup Odin + uses: laytan/setup-odin@v2.5.2 + with: + token: ${{secrets.github-token || secrets.GITHUB_TOKEN}} + release: latest + - id: lint + name: Lint + run: task lint diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml new file mode 100644 index 0000000..85c5ee8 --- /dev/null +++ b/.github/workflows/pull_request.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Pull Request +on: + pull_request: + branches: + - main +jobs: + build: + name: Build + strategy: + fail-fast: true + matrix: + build-os: + - windows + - linux + - macos + uses: ./.github/workflows/build.yaml + secrets: + github-token: ${{secrets.GITHUB_TOKEN}} + with: + build-os: ${{matrix.build-os}} + test: + name: Test + strategy: + fail-fast: true + matrix: + build-os: + - windows + - linux + - macos + uses: ./.github/workflows/test.yaml + secrets: + github-token: ${{secrets.GITHUB_TOKEN}} + with: + build-os: ${{matrix.build-os}} + lint: + name: Lint + uses: ./.github/workflows/lint.yaml + secrets: + github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml new file mode 100644 index 0000000..68d074a --- /dev/null +++ b/.github/workflows/push.yaml @@ -0,0 +1,38 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Push +on: + push: +jobs: + build: + name: Build + strategy: + fail-fast: true + matrix: + build-os: + - windows + - linux + - macos + uses: ./.github/workflows/build.yaml + secrets: + github-token: ${{secrets.GITHUB_TOKEN}} + with: + build-os: ${{matrix.build-os}} + test: + name: Test + strategy: + fail-fast: true + matrix: + build-os: + - windows + - linux + - macos + uses: ./.github/workflows/test.yaml + secrets: + github-token: ${{secrets.GITHUB_TOKEN}} + with: + build-os: ${{matrix.build-os}} + lint: + name: Lint + uses: ./.github/workflows/lint.yaml + secrets: + github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..32c9f44 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,47 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: Test +on: + workflow_call: + secrets: + github-token: + required: true + description: | + The token to use when making authenticated GitHub API calls. + inputs: + build-os: + required: true + type: string + description: | + The operating system to build for. + + Valid options are: + + * `windows` + * `linux` + * `macos` +jobs: + test: + name: Test + runs-on: ${{(inputs.build-os == 'windows' && 'windows-latest') || (inputs.build-os == 'linux' && 'ubuntu-latest') || (inputs.build-os == 'macos' && 'macos-latest') || ''}} + steps: + - id: install-tasks + name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.github-token || secrets.GITHUB_TOKEN }} + - id: checkout-repository + name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{secrets.github-token || secrets.GITHUB_TOKEN}} + lfs: true + - id: setup-odin + name: Setup Odin + uses: laytan/setup-odin@v2.5.2 + with: + token: ${{secrets.github-token || secrets.GITHUB_TOKEN}} + release: latest + - id: test + name: Test + run: task test diff --git a/.gitignore b/.gitignore index e5d4963..1da7b78 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,12 @@ tags # Environment # .envrc + +# Odin Language Server # +ols.json + +# Artifacts # +.task/ +build/ +dist/ +*.zip diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e2d1ed9 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "github.remotehub", + "github.vscode-pull-request-github", + "github.vscode-github-actions", + "danielgavin.ols", + "vadimcn.vscode-lldb", + "task.vscode-task" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..bde1a67 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/build/odincraft-debug", + "windows": { + "program": "${workspaceFolder}/build/odincraft-debug.exe" + }, + "args": [], + "cwd": "${workspaceFolder}", + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..535605a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "pre-debug", + "detail": "Build the project prior to debugging it.", + "icon": { + "id": "tools", + "color": "terminal.ansiCyan" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "hide": false, + "dependsOn": [], + "dependsOrder": "sequence", + "isBackground": false, + "promptOnClose": false, + "type": "shell", + "command": "task", + "args": [ + "build-debug" + ], + "runOptions": { + "instanceLimit": 1, + "reevaluateOnRerun": true, + "runOn": "default" + }, + "presentation": { + "echo": false, + "reveal": "silent", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false + }, + "problemMatcher": [] + } + ] +} diff --git a/odinfmt.json b/odinfmt.json new file mode 100644 index 0000000..890f688 --- /dev/null +++ b/odinfmt.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/odinfmt.schema.json", + "brace_style": "_1TBS", + "character_width": 80, + "convert_do": false, + "exp_multiline_composite_literals": false, + "indent_cases": false, + "newline_limit": 1, + "newline_style": "LF", + "spaces": 4, + "tabs": false, + "tabs_width": 4 +} diff --git a/src/main.odin b/src/main.odin new file mode 100644 index 0000000..e533583 --- /dev/null +++ b/src/main.odin @@ -0,0 +1,8 @@ +package pinentry_win_cred + +//-- Core Libraries +import "core:fmt" + +main :: proc() { + fmt.println("Hello, Odin!") +} diff --git a/taskfile.yaml b/taskfile.yaml new file mode 100644 index 0000000..b03737b --- /dev/null +++ b/taskfile.yaml @@ -0,0 +1,270 @@ +# yaml-language-server: $schema=https://taskfile.dev/schema.json +version: 3 +vars: + #-- General Project Settings + projectName: Odincraft + projectVersion: 0.0.0 + projectDescription: A simple Minecraft clone written in Odin using OpenGL. + #-- Project Directories + srcDir: src + libDir: lib + assetsDir: assets + testsDir: tests + buildDir: build + distDir: dist + #-- Output Settings + outputBaseName: odincraft + outputNameDebug: '{{.outputBaseName}}-debug' + outputNameRelease: '{{.outputBaseName}}-release' + outputNameTests: '{{.outputBaseName}}-tests' + archiveBaseName: '{{.outputBaseName}}-{{.projectVersion}}' + archiveNameDebug: '{{.archiveBaseName}}-debug.zip' + archiveNameRelease: '{{.archiveBaseName}}-release.zip' + #-- Compilers + compiler: odin{{exeExt}} + archiver: '{{if eq OS "windows"}}7z{{else}}7zz{{end}}{{exeExt}}' + #-- Compiler Flags + compilerDefines: -define:PROJECT_NAME="{{.projectName}}" -define:PROJECT_VERSION="{{.projectVersion}}" -define:PROJECT_DESCRIPTION="{{.projectDescription}}" + compilerTestDefines: '{{.compilerDefines}}' + compilerCollections: '' + compilerTestCollections: '{{.compilerCollections}} -collection:src={{.srcDir}} -collection:test_utils={{.testsDir}}/utils' + compilerBaseFlags: '-build-mode:exe' + compilerDebugFlags: '{{.compilerBaseFlags}} -o:none -debug' + compilerReleaseFlags: '{{.compilerBaseFlags}} -o:minimal' + compilerTestFlags: '-o:minimal -debug' + compilerCheckFlags: '-strict-style -vet-unused -vet-shadowing -vet-using-stmt -vet-using-param -vet-style -vet-semicolon -disallow-do -thread-count:4' + compilerLibCheckFlags: '{{.compilerCheckFlags}} -no-entry-point' + compilerTestsCheckFlags: '{{.compilerCheckFlags}} -no-entry-point' +tasks: + create-build-dir: + desc: Create the `build` directory. + status: + - bash -c 'test -d "{{.buildDir}}"' + cmd: bash -c 'mkdir -p {{.buildDir}}' + create-dist-dir: + desc: Create the `dist` directory. + status: + - bash -c 'test -d "{{.distDir}}"' + cmd: bash -c 'mkdir -p {{.distDir}}' + create-release-dist-dir: + desc: Create the `dist/release` directory. + deps: + - create-dist-dir + status: + - bash -c 'test -d "{{.distDir}}/release"' + cmd: bash -c 'mkdir -p {{.distDir}}/release' + create-debug-dist-dir: + desc: Create the `dist/debug` directory. + deps: + - create-dist-dir + status: + - bash -c 'test -d "{{.distDir}}/debug"' + cmd: bash -c 'mkdir -p {{.distDir}}/debug' + build-release: + desc: Build the project in release mode. + aliases: + - default + - build + deps: + - create-build-dir + sources: + - '{{.srcDir}}/**/*.odin' + generates: + - '{{.buildDir}}/{{.outputNameRelease}}{{exeExt}}' + cmd: '{{.compiler}} build {{.srcDir}} -out:{{.buildDir}}/{{.outputNameRelease}}{{exeExt}} {{.compilerReleaseFlags}} {{.compilerDefines}} {{.compilerCollections}}' + build-debug: + desc: Build the project in debug mode. + deps: + - create-build-dir + sources: + - '{{.srcDir}}/**/*.odin' + generates: + - '{{.buildDir}}/{{.outputNameDebug}}{{exeExt}}' + cmd: '{{.compiler}} build {{.srcDir}} -out:{{.buildDir}}/{{.outputNameDebug}}{{exeExt}} {{.compilerDebugFlags}} {{.compilerDefines}} {{.compilerCollections}}' + build-all: + desc: Build the project in both release and debug modes. + deps: + - build-debug + - build-release + clean-release: + desc: Clean the project in release mode. + aliases: + - clean + status: + - bash -c 'test ! -f "{{.buildDir}}/{{.outputNameRelease}}{{exeExt}}"' + cmd: bash -c 'rm -f {{.buildDir}}/{{.outputNameRelease}}{{exeExt}}' + clean-debug: + desc: Clean the project in debug mode. + status: + - bash -c 'test ! -f "{{.buildDir}}/{{.outputNameDebug}}{{exeExt}}"' + cmds: + - bash -c 'rm -f {{.buildDir}}/{{.outputNameDebug}}{{exeExt}}' + - cmd: bash -c 'rm -f {{.buildDir}}/{{.outputNameDebug}}.pdb' + platforms: + - windows + clean-dist-release: + desc: Clean the release distribution. + status: + - bash -c 'test ! -d "{{.distDir}}/release/"' + cmd: bash -c 'rm -rf {{.distDir}}/release/' + clean-dist-debug: + desc: Clean the debug distribution. + status: + - bash -c 'test ! -d "{{.distDir}}/debug/"' + cmd: bash -c 'rm -rf {{.distDir}}/debug/' + clean-dist-all: + desc: Clean both release and debug distributions. + deps: + - clean-dist-debug + - clean-dist-release + clean-archive-release: + desc: Clean the release archive. + status: + - bash -c 'test ! -f "{{.distDir}}/{{.archiveNameRelease}}"' + cmd: bash -c 'rm -f {{.distDir}}/{{.archiveNameRelease}}' + clean-archive-debug: + desc: Clean the debug archive. + status: + - bash -c 'test ! -f "{{.distDir}}/{{.archiveNameDebug}}"' + cmd: bash -c 'rm -f {{.distDir}}/{{.archiveNameDebug}}' + clean-archive-all: + desc: Clean both release and debug archives. + deps: + - clean-archive-debug + - clean-archive-release + clean-all: + desc: Clean the project in both release and debug modes. + deps: + - clean-release + - clean-debug + - clean-dist-release + - clean-dist-debug + - clean-archive-release + - clean-archive-debug + status: + - bash -c 'test ! -d "{{.buildDir}}"' + - bash -c 'test ! -d "{{.distDir}}"' + cmds: + - bash -c 'rm -rf {{.buildDir}}' + - bash -c 'rm -rf {{.distDir}}' + rebuild-release: + desc: Rebuild the project in release mode. + aliases: + - rebuild + deps: + - clean-release + - build-release + rebuild-debug: + desc: Rebuild the project in debug mode. + aliases: + - rebuild + deps: + - clean-debug + - build-debug + rebuild-all: + desc: Rebuild the project in both release and debug mode. + aliases: + - rebuild + deps: + - clean-all + - build-all + dist-release: + desc: Generate a release distribution. + aliases: + - dist + deps: + - build-release + - create-release-dist-dir + sources: + - '{{.buildDir}}/{{.outputNameRelease}}{{exeExt}}' + generates: + - '{{.distDir}}/release/{{.outputNameRelease}}{{exeExt}}' + cmd: bash -c 'cp {{.buildDir}}/{{.outputNameRelease}}{{exeExt}} {{.distDir}}/release/{{.outputNameRelease}}{{exeExt}}' + dist-debug: + desc: Generate a debug distribution. + deps: + - build-debug + - create-debug-dist-dir + sources: + - '{{.buildDir}}/{{.outputNameDebug}}.*' + generates: + - '{{.distDir}}/debug/{{.outputNameDebug}}.*' + cmds: + - bash -c 'cp {{.buildDir}}/{{.outputNameDebug}}{{exeExt}} {{.distDir}}/debug/{{.outputNameDebug}}{{exeExt}}' + - cmd: bash -c 'cp {{.buildDir}}/{{.outputNameDebug}}.pdb {{.distDir}}/debug/{{.outputNameDebug}}.pdb' + platforms: + - windows + dist-all: + desc: Generate both release and debug distributions. + deps: + - dist-debug + - dist-release + archive-release: + desc: Generate a release archive. + aliases: + - archive + deps: + - dist-release + sources: + - '{{.distDir}}/release/{{.outputNameRelease}}{{exeExt}}' + generates: + - '{{.distDir}}/{{.archiveNameRelease}}' + cmd: '{{.archiver}} a -mx=0 {{.distDir}}/{{.archiveNameRelease}} "{{.distDir}}/release/*"' + archive-debug: + desc: Generate a debug archive. + deps: + - dist-debug + sources: + - '{{.distDir}}/debug/{{.outputNameDebug}}{{exeExt}}' + generates: + - '{{.distDir}}/{{.archiveNameDebug}}' + cmd: '{{.archiver}} a -mx=0 {{.distDir}}/{{.archiveNameDebug}} "{{.distDir}}/debug/*"' + archive-all: + desc: Generate both release and debug archives. + deps: + - archive-debug + - archive-release + run-release: + desc: Run the project in release mode. + aliases: + - run + deps: + - build-release + cmd: '{{.buildDir}}/{{.outputNameRelease}}{{exeExt}}' + run-debug: + desc: Run the project in debug mode. + deps: + - build-debug + cmd: '{{.buildDir}}/{{.outputNameDebug}}{{exeExt}}' + test: + desc: Run the project unit tests. + deps: + - create-build-dir + sources: + - '{{.srcDir}}/**/*.odin' + - '{{.testsDir}}/**/*.odin' + generates: + - '{{.buildDir}}/{{.outputNameTests}}{{exeExt}}' + cmd: '{{.compiler}} test {{.testsDir}} -out:{{.buildDir}}/{{.outputNameTests}}{{exeExt}} {{.compilerTestFlags}} {{.compilerTestDefines}} {{.compilerTestCollections}}' + lint-src: + desc: Lint the project `{{.srcDir}}` directory. + sources: + - '{{.srcDir}}/**/*.odin' + cmd: '{{.compiler}} check {{.srcDir}} {{.compilerCheckFlags}} {{.compilerDefines}} {{.compilerCollections}}' + lint-lib: + desc: Lint the project `{{.libDir}}` directory. + sources: + - '{{.libDir}}/**/*.odin' + cmd: '{{.compiler}} check {{.libDir}}/* {{.compilerLibCheckFlags}} {{.compilerDefines}} {{.compilerCollections}}' + lint-tests: + desc: Lint the project `{{.testsDir}}` directory. + sources: + - '{{.testsDir}}/**/*.odin' + cmd: '{{.compiler}} check {{.testsDir}} {{.compilerTestsCheckFlags}} {{.compilerDefines}} {{.compilerCollections}}' + lint-all: + desc: Lint the entire project. + aliases: + - lint + deps: + - lint-src + - lint-tests diff --git a/tests/tests.odin b/tests/tests.odin new file mode 100644 index 0000000..9a5efe1 --- /dev/null +++ b/tests/tests.odin @@ -0,0 +1,3 @@ +package tests + +//-- Tests diff --git a/tests/utils/expects.odin b/tests/utils/expects.odin new file mode 100644 index 0000000..75dcba5 --- /dev/null +++ b/tests/utils/expects.odin @@ -0,0 +1,181 @@ +package utils + +//-- Core Libraries +import "core:strings" +import "core:testing" +import "core:text/match" + +/* +Expect a value to equal another value. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +* `expected` - The value to test the value against. +*/ +expect_equal :: proc(t: ^testing.T, value: $T, expected: $U) { + testing.expectf( + t, + value == expected, + "Expected \"%s\" to be \"%s\"", + value, + expected, + ) +} + +/* +Expect a value to not equal another value. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +* `expected` - The value to test the value against. +*/ +expect_not_equal :: proc(t: ^testing.T, value: $T, expected: $U) { + testing.expectf( + t, + value != expected, + "Expected \"%s\" to not be \"%s\"", + value, + expected, + ) +} + +/* +Expect a value to be `nil`. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +*/ +expect_nil :: proc(t: ^testing.T, value: any) { + testing.expectf(t, value == nil, "Expected \"%s\" to be \"nil\"", value) +} + +/* +Expect a value to not be `nil`. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +*/ +expect_not_nil :: proc(t: ^testing.T, value: any) { + testing.expectf( + t, + value != nil, + "Expected \"%s\" to not be \"nil\"", + value, + ) +} + +/* +Expect a value to be an empty string. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +*/ +expect_empty_string :: proc(t: ^testing.T, value: string) { + testing.expectf(t, value == "", "Expected \"%s\" to be \"\"", value) +} + +/* +Expect a value to not be an empty string. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +*/ +expect_not_empty_string :: proc(t: ^testing.T, value: string) { + testing.expectf(t, value != "", "Expected \"%s\" to not be \"\"", value) +} + +/* +Expect a value to be a string containing another string. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +* `needle` - The value to check for. +*/ +expect_string_contains :: proc(t: ^testing.T, value: string, needle: string) { + testing.expectf( + t, + strings.contains(value, needle), + "Expected \"%s\" to contain \"%s\"", + value, + needle, + ) +} + +/* +Expect a value to be a string not containing another string. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +* `needle` - The value to check for. +*/ +expect_string_not_contains :: proc( + t: ^testing.T, + value: string, + needle: string, +) { + testing.expectf( + t, + !strings.contains(value, needle), + "Expected \"%s\" to not contain \"%s\"", + value, + needle, + ) +} + +/* +Expect a value to be a string matching a pattern. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +* `pattern` - The value to match against. +*/ +expect_string_matches :: proc(t: ^testing.T, value: string, pattern: string) { + p := strings.clone(value) + caps := [match.MAX_CAPTURES]match.Match{} + _, ok := match.gmatch(&p, pattern, &caps) + testing.expectf(t, ok, "Expected \"%s\" to match \"%s\"", value, pattern) +} + +/* +Expect a value to be a string not matching a pattern. + +### Parameters ### + +* `t` - The testing object. +* `value` - The value to test. +* `pattern` - The value to match against. +*/ +expect_string_not_matches :: proc( + t: ^testing.T, + value: string, + pattern: string, +) { + p := strings.clone(value) + caps := [match.MAX_CAPTURES]match.Match{} + _, ok := match.gmatch(&p, pattern, &caps) + testing.expectf( + t, + !ok, + "Expected \"%s\" to not match \"%s\"", + value, + pattern, + ) +}