Repository navigation
Run the FieldWorks parser conformance suite against HermitCrab in CI #615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
johnml1135
wants to merge
15
commits into
fix/signature-drops-id-under-identity-rule
Choose a base branch
from
hermitcrab-conformance-runner
base: fix/signature-drops-id-under-identity-rule
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
3c364a1
Add a batch command to the HermitCrab tool
johnml1135 62c9255
Pin the HermitCrab conformance suite to one commit
johnml1135 9c4be00
Add the hc-conformance runner and its tests
johnml1135 7952575
Add an Explicit test that runs the suite's stress fixtures
johnml1135 82cfb37
Run the conformance suite in CI
johnml1135 dd7e2d2
Document how machine runs the conformance suite
johnml1135 7d18eab
Stop a rejected batch line leaking --start into the next
johnml1135 61213b6
Count only an engine crash as a match for expect_crash in self-check
johnml1135 6451c04
Name the fixture when --check-manifest cannot read its grammar
johnml1135 caff4ae
Let the stress parity test pass for a fixture with variants
johnml1135 c72e61f
Compare the suite before and after the runner steps in local_check
johnml1135 4d41c1d
Sort capability and key names with the ordinal comparer
johnml1135 ad9765b
Say how the adapter command template treats quotes
johnml1135 1bd1dc9
Remove SelfCheckEngine, whose Run had no caller
johnml1135 4d9b454
Format ConformanceCommandLineTests with csharpier
johnml1135 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,4 +2,6 @@ | |
| *.csproj | ||
| *.props | ||
| *.targets | ||
| *.xml | ||
| *.xml | ||
| /conformance/ | ||
| /conformance.tmp-*/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| name: Conformance | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - "**" | ||
| paths: | ||
| - "conformance.commit" | ||
| - "src/SIL.Machine/**" | ||
| - "src/SIL.Machine.Morphology.HermitCrab*/**" | ||
| - "tests/SIL.Machine.Morphology.HermitCrab.Tests/**" | ||
| - ".github/workflows/conformance.yml" | ||
| pull_request: | ||
| paths: | ||
| - "conformance.commit" | ||
| - "src/SIL.Machine/**" | ||
| - "src/SIL.Machine.Morphology.HermitCrab*/**" | ||
| - "tests/SIL.Machine.Morphology.HermitCrab.Tests/**" | ||
| - ".github/workflows/conformance.yml" | ||
|
|
||
| jobs: | ||
| conformance: | ||
| name: Conformance suite | ||
| runs-on: ubuntu-22.04 | ||
| timeout-minutes: 30 | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v6 | ||
|
|
||
| - name: Setup .NET | ||
| uses: actions/setup-dotnet@v5 | ||
| with: | ||
| dotnet-version: | | ||
| 10.0.x | ||
|
|
||
| # Building the runner clones sillsdev/fieldworks-parser-conformance into conformance/ at the commit in | ||
| # conformance.commit; see EnsureConformanceSuite.targets. | ||
| # | ||
| # Release, because edge-cases/deep-optional-affix-nesting has a 15 s wall-clock budget that Debug uses | ||
| # most of on a small runner. Built once; every step below runs the built runner. | ||
| - name: Build the runner | ||
| run: dotnet build -c Release src/SIL.Machine.Morphology.HermitCrab.Conformance/SIL.Machine.Morphology.HermitCrab.Conformance.csproj | ||
|
|
||
| # Every fixture's words.yaml must match what the C# engine produces, in process, including the fixtures | ||
| # with a budget_ms. | ||
| - name: Run conformance self-check | ||
| run: >- | ||
| dotnet run --no-build -c Release --project src/SIL.Machine.Morphology.HermitCrab.Conformance -- | ||
| --fixtures conformance --include-pathological | ||
|
|
||
| # The suite's stress fixtures (conformance/stress) must agree with the engine too. | ||
| - name: Run stress self-check | ||
| if: always() | ||
| run: >- | ||
| dotnet run --no-build -c Release --project src/SIL.Machine.Morphology.HermitCrab.Conformance -- | ||
| --fixtures conformance/stress --include-pathological | ||
|
|
||
| # The suite's generated manifest must match a fresh computation, and every grammar and words.yaml must | ||
| # validate against the suite's DTD and schema. | ||
| - name: Check the suite manifest | ||
| if: always() | ||
| run: >- | ||
| dotnet run --no-build -c Release --project src/SIL.Machine.Morphology.HermitCrab.Conformance -- | ||
| --fixtures conformance --check-manifest | ||
|
|
||
| # Exits 1 on an uncovered construct, on a dead rule (exercised by zero words) missing from | ||
| # conformance/dead-rules-allowed.tsv, on an allowed rule some word now exercises, and when the suite's | ||
| # coverage.csv or rules.csv is stale. It writes nothing; the suite owns both files. | ||
| - name: Run coverage report | ||
| if: always() | ||
| run: >- | ||
| dotnet run --no-build -c Release --project src/SIL.Machine.Morphology.HermitCrab.Conformance -- | ||
| --fixtures conformance --coverage-report | ||
|
|
||
| # A pin that is not on the suite's main could be lost when that branch is deleted. | ||
| - name: Check the pinned commit is on the suite's main | ||
| if: always() | ||
| run: | | ||
| pin="$(tr -d '[:space:]' < conformance.commit)" | ||
| git -C conformance fetch --quiet origin main | ||
| if ! git -C conformance merge-base --is-ancestor "$pin" origin/main; then | ||
| echo "::error file=conformance.commit::$pin is not an ancestor of origin/main in the conformance suite" | ||
| exit 1 | ||
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 7a0f7419fc706425171080ddbe3db263197b2d0d |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
src/SIL.Machine.Morphology.HermitCrab.Conformance/AdapterEngine.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace SIL.Machine.Morphology.HermitCrab.Conformance; | ||
|
|
||
| public class AdapterEngine : IEngine | ||
| { | ||
| private readonly List<string> _templateTokens; | ||
| private readonly int _timeoutMs; | ||
|
|
||
| public AdapterEngine(string commandTemplate, IReadOnlySet<string> capabilities, int timeoutMs = 5 * 60 * 1000) | ||
| { | ||
| _templateTokens = new List<string>(SIL.Machine.Morphology.HermitCrab.Program.SplitCommandLine(commandTemplate)); | ||
| if (_templateTokens.Count == 0) | ||
| throw new InvalidOperationException("empty --adapter command template"); | ||
| _timeoutMs = timeoutMs; | ||
| Capabilities = capabilities; | ||
| } | ||
|
|
||
| public string Name => "adapter"; | ||
|
|
||
| public IReadOnlySet<string> Capabilities { get; } | ||
|
|
||
| public List<TsvRow> Run(MaterializedFixture fixture) | ||
| { | ||
| string outputPath = Path.Combine(Path.GetTempPath(), "hc-conformance-" + Guid.NewGuid().ToString("N") + ".tsv"); | ||
| try | ||
| { | ||
| // A fixture with a morpher: block reaches an adapter only through {morpher}; without it the | ||
| // adapter would run at the defaults the fixture overrides. | ||
| if (!fixture.Manifest.Morpher.IsEmpty && !_templateTokens.Exists(t => t.Contains("{morpher}"))) | ||
| { | ||
| throw new InvalidOperationException( | ||
| $"fixture '{fixture.Id}' sets morpher: settings but the --adapter command template has no " | ||
| + "{morpher} placeholder to pass them through; add it or drop the morpher-settings capability" | ||
| ); | ||
| } | ||
| var tokens = new List<string>(_templateTokens.Count); | ||
| foreach (string token in _templateTokens) | ||
| { | ||
| tokens.Add( | ||
| token | ||
| .Replace("{grammar}", Path.GetFullPath(fixture.GrammarPath)) | ||
| .Replace("{words}", Path.GetFullPath(fixture.WordsPath)) | ||
| .Replace("{output}", outputPath) | ||
| .Replace("{morpher}", Path.GetFullPath(fixture.MorpherPath)) | ||
| ); | ||
| } | ||
|
|
||
| var psi = ChildProcessEnvironment.CreateStartInfo(tokens[0]); | ||
| psi.RedirectStandardOutput = true; | ||
| psi.RedirectStandardError = true; | ||
| for (int i = 1; i < tokens.Count; i++) | ||
| psi.ArgumentList.Add(tokens[i]); | ||
|
|
||
| using Process process = | ||
| Process.Start(psi) | ||
| ?? throw new InvalidOperationException($"failed to start adapter process '{tokens[0]}'"); | ||
|
|
||
| Task<string> stdoutTask = process.StandardOutput.ReadToEndAsync(); | ||
| Task<string> stderrTask = process.StandardError.ReadToEndAsync(); | ||
|
|
||
| int effectiveTimeoutMs = | ||
| fixture.Manifest.Budget != null ? (int)fixture.Manifest.Budget.WallClockMs : _timeoutMs; | ||
|
|
||
| bool exited = process.WaitForExit(effectiveTimeoutMs); | ||
| if (!exited) | ||
| { | ||
| try | ||
| { | ||
| process.Kill(entireProcessTree: true); | ||
| } | ||
| catch (InvalidOperationException) { } | ||
| throw new TimeoutException( | ||
| $"adapter process for fixture '{fixture.Id}' did not exit within {effectiveTimeoutMs}ms" | ||
| ); | ||
| } | ||
|
|
||
| string stdout = stdoutTask.GetAwaiter().GetResult(); | ||
| string stderr = stderrTask.GetAwaiter().GetResult(); | ||
|
|
||
| if (process.ExitCode != 0) | ||
| { | ||
| throw new EngineCrashException( | ||
| $"adapter process exited {process.ExitCode} for fixture '{fixture.Id}'.\nstdout:\n{stdout}\nstderr:\n{stderr}" | ||
| ); | ||
| } | ||
| if (!File.Exists(outputPath)) | ||
| { | ||
| throw new InvalidOperationException( | ||
| $"adapter process for fixture '{fixture.Id}' did not produce its output file.\nstdout:\n{stdout}\nstderr:\n{stderr}" | ||
| ); | ||
| } | ||
| return SignatureTsv.ReadFile(outputPath); | ||
| } | ||
| finally | ||
| { | ||
| try | ||
| { | ||
| if (File.Exists(outputPath)) | ||
| File.Delete(outputPath); | ||
| } | ||
| catch (IOException) { } | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.