From ac83aa509a0c131c8c57088767cd1eb48a1c6bbd Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 29 Jan 2025 11:49:47 +0100 Subject: [PATCH] Speedup CI (#2352) * Test refactoring exercises via solution * Test using solution and in-place --- .gitignore | 3 + bin/test.ps1 | 158 +++++++++++++++++++++++--------------- exercises/Refactoring.sln | 41 ++++++++++ 3 files changed, 140 insertions(+), 62 deletions(-) create mode 100644 exercises/Refactoring.sln diff --git a/.gitignore b/.gitignore index 6a5b87138..4d169de53 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ build/ # Other files .DS_Store + +# Temporary files +exercises/**/*.tmp diff --git a/bin/test.ps1 b/bin/test.ps1 index c42362805..3aa3651ed 100644 --- a/bin/test.ps1 +++ b/bin/test.ps1 @@ -26,97 +26,131 @@ param ( $ErrorActionPreference = "Stop" $PSNativeCommandUseErrorActionPreference = $true -function Invoke-Build-Generators { - Write-Output "Building generators" - & dotnet build ./generators +function Invoke-Tests($Path) { + & dotnet test $Path } -function Clean($BuildDir) { - Write-Output "Cleaning previous build" - Remove-Item -Recurse -Force $BuildDir -ErrorAction Ignore -} +function Prepare-Exercise($Path) { + $files = Get-Content (Join-Path $Path ".meta" "config.json") -Raw | ConvertFrom-Json | Select-Object -ExpandProperty files -function Copy-Exercise($SourceDir, $BuildDir) { - Write-Output "Copying exercises" - Copy-Item $SourceDir -Destination $BuildDir -Recurse -} + foreach ($fileType in @("example", "exemplar")) { + for (($i = 0); $i -lt $files.$fileType.length; $i++) { + $exampleFile = Join-Path $Path $files.$fileType[$i] + $solutionFile = Join-Path $Path $files.solution[$i] -function Enable-All-UnitTests($BuildDir) { - Write-Output "Enabling all tests" - Get-ChildItem -Path $BuildDir -Include "*Tests.cs" -Recurse | ForEach-Object { - (Get-Content $_.FullName) -replace "Skip = ""Remove this Skip property to run this test""", "" | Set-Content $_.FullName + Copy-Item -Path $solutionFile -Destination "${solutionFile}.tmp" + Copy-Item -Path $exampleFile -Destination $solutionFile + } } -} -function Test-Refactoring-Projects($PracticeExercisesDir) { - Write-Output "Testing refactoring projects" - @("tree-building", "ledger", "markdown") | ForEach-Object { - Invoke-Tests -Path "${PracticeExercisesDir}/${_}" + foreach ($testFile in $files.test) { + $testFile = Join-Path $Path $testFile + Copy-Item -Path $testFile -Destination "${testFile}.tmp" + (Get-Content $testFile) -replace "Skip = ""Remove this Skip property to run this test""", "" | Set-Content $testFile } } -function Set-ExampleImplementation { - [CmdletBinding(SupportsShouldProcess)] - param($ExercisesDir, $ReplaceFileName) - - if ($PSCmdlet.ShouldProcess("Exercise ${ReplaceFileName}", "replace solution with example")) { - Get-ChildItem -Path $ExercisesDir -Include "*.csproj" -Recurse | ForEach-Object { - $stub = Join-Path -Path $_.Directory ($_.BaseName + ".cs") - $example = Join-Path -Path $_.Directory ".meta" $ReplaceFileName +function Restore-Exercise($Path) { + Get-ChildItem -Path $Path -Include "*.tmp" -Recurse | ForEach-Object { + $tmpFile = $_.FullName + $originalFile = ($tmpFile -replace "\.tmp$", "") + Move-Item -Path $tmpFile -Destination $originalFile -Force + } +} - Move-Item -Path $example -Destination $stub -Force +function Process-Exercises($Exercises, [ScriptBlock]$Action) { + foreach ($ExerciseType in @("practice", "concept")) { + $Exercises.$ExerciseType | ForEach-Object { + &$Action (Join-Path "exercises" $ExerciseType $_) } } } -function Use-ExampleImplementation { - [CmdletBinding(SupportsShouldProcess)] - param($ConceptExercisesDir, $PracticeExercisesDir) - - if ($PSCmdlet.ShouldProcess("Exercises directory", "replace all solutions with corresponding examples")) { - Write-Output "Replacing concept exercise stubs with exemplar" - Set-ExampleImplementation $ConceptExercisesDir "Exemplar.cs" +function Prepare-Exercises($Exercises) { + Process-Exercises $Exercises { + param($Path) + Prepare-Exercise $Path + } +} - Write-Output "Replacing practice exercise stubs with example" - Set-ExampleImplementation $PracticeExercisesDir "Example.cs" +function Restore-Exercises($Exercises) { + Process-Exercises $Exercises { + param($Path) + Restore-Exercise $Path } } -function Test-ExerciseImplementation($Exercise, $BuildDir, $ConceptExercisesDir, $PracticeExercisesDir) { - Write-Output "Running tests" +function Run-Tests($Path) { + & dotnet test $Path +} - if (-Not $Exercise) { - Invoke-Tests -Path $BuildDir +function Find-Exercise-Path($Exercise, $Exercises) { + if ($Exercises.practice.Contains($Exercise)) { + Join-Path "exercises" "practice" $Exercise + } + elseif ($Exercises.concept.Contains($Exercise)) { + Join-Path "exercises" "concept" $Exercise + } else { + throw "Could not find exercise '${Exercise}'" } - elseif (Test-Path "${ConceptExercisesDir}/${Exercise}") { - Invoke-Tests -Path "${ConceptExercisesDir}/${Exercise}" +} + +function Test-Single-Exercise($Exercise, $Exercises) { + $path = Find-Exercise-Path $Exercise $Exercises + + try { + Prepare-Exercise $path + Run-Tests $path + } finally { + Restore-Exercise $path } - elseif (Test-Path "${PracticeExercisesDir}/${Exercise}") { - Invoke-Tests -Path "${PracticeExercisesDir}/${Exercise}" +} + +function Test-All-Exercises($Exercises) { + try { + Prepare-Exercises $Exercises + Run-Tests "exercises/Exercises.sln" + } finally { + Restore-Exercises $Exercises } - else { - throw "Could not find exercise '${Exercise}'" +} + +function Parse-Exercises { + Get-Content .\config.json -Raw | + ConvertFrom-Json | + Select-Object -ExpandProperty exercises | + ForEach-Object { + @{ + concept = $_.concept | Select-Object -ExpandProperty slug | Sort-Object + practice = $_.practice | Select-Object -ExpandProperty slug | Sort-Object + } } } -function Invoke-Tests($Path) { - & dotnet test $Path +function Build-Generators { + Write-Output "Build generators" + & dotnet build generators +} + +function Test-Refactoring-Exercise-Default-Implementations { + Write-Output "Testing refactoring exercises" + & dotnet test (Join-Path "exercises" "Refactoring.sln") } +function Test-Exercise-Example-Implementations($Exercise) { + Write-Output "Testing example implementations" + $exercises = Parse-Exercises -$buildDir = "${PSScriptRoot}/build" -$practiceExercisesDir = "${buildDir}/practice" -$conceptExercisesDir = "${buildDir}/concept" -$sourceDir = Resolve-Path "exercises" + if ($Exercise) { + Test-Single-Exercise $Exercise $Exercises + } else { + Test-All-Exercises $Exercises + } +} -Clean $buildDir -Copy-Exercise $sourceDir $buildDir -Enable-All-UnitTests $buildDir +Test-Exercise-Example-Implementations $Exercise if (!$Exercise) { - Invoke-Build-Generators - Test-Refactoring-Projects $practiceExercisesDir + Build-Generators + Test-Refactoring-Exercise-Default-Implementations } - -Use-ExampleImplementation $conceptExercisesDir $practiceExercisesDir -Test-ExerciseImplementation -Exercise $Exercise -BuildDir $buildDir -ConceptExercisesDir $conceptExercisesDir -PracticeExercisesDir $practiceExercisesDir diff --git a/exercises/Refactoring.sln b/exercises/Refactoring.sln new file mode 100644 index 000000000..19bc083e0 --- /dev/null +++ b/exercises/Refactoring.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "practice", "practice", "{9CE64A98-EA82-4784-93E4-2C14222C5FDD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TreeBuilding", "practice\tree-building\TreeBuilding.csproj", "{202A66E7-F0BB-4B0D-A7C0-AE92B3B48B7F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ledger", "practice\ledger\Ledger.csproj", "{11EBF5DF-CE22-40D8-B96E-687461A16C1E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdown", "practice\markdown\Markdown.csproj", "{CB0F19FE-C1B9-4EC6-B969-C0F74B70E134}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {202A66E7-F0BB-4B0D-A7C0-AE92B3B48B7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {202A66E7-F0BB-4B0D-A7C0-AE92B3B48B7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {202A66E7-F0BB-4B0D-A7C0-AE92B3B48B7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {202A66E7-F0BB-4B0D-A7C0-AE92B3B48B7F}.Release|Any CPU.Build.0 = Release|Any CPU + {11EBF5DF-CE22-40D8-B96E-687461A16C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11EBF5DF-CE22-40D8-B96E-687461A16C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11EBF5DF-CE22-40D8-B96E-687461A16C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11EBF5DF-CE22-40D8-B96E-687461A16C1E}.Release|Any CPU.Build.0 = Release|Any CPU + {CB0F19FE-C1B9-4EC6-B969-C0F74B70E134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB0F19FE-C1B9-4EC6-B969-C0F74B70E134}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB0F19FE-C1B9-4EC6-B969-C0F74B70E134}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB0F19FE-C1B9-4EC6-B969-C0F74B70E134}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {202A66E7-F0BB-4B0D-A7C0-AE92B3B48B7F} = {9CE64A98-EA82-4784-93E4-2C14222C5FDD} + {11EBF5DF-CE22-40D8-B96E-687461A16C1E} = {9CE64A98-EA82-4784-93E4-2C14222C5FDD} + {CB0F19FE-C1B9-4EC6-B969-C0F74B70E134} = {9CE64A98-EA82-4784-93E4-2C14222C5FDD} + EndGlobalSection +EndGlobal