Prexonite, a .NET hosted scripting language with a focus on meta-programming and embedded DSLs
0

Configure Feed

Select the types of activity you want to include in your feed.

Generate test fixtures from JSON configuration

Christian Klauser (Jul 10, 2026, 1:21 PM +0200) 60274bb9 102bb37e

+956 -547
-12
.config/dotnet-tools.json
··· 1 - { 2 - "version": 1, 3 - "isRoot": true, 4 - "tools": { 5 - "dotnet-t4": { 6 - "version": "2.3.1", 7 - "commands": [ 8 - "t4" 9 - ] 10 - } 11 - } 12 - }
-2
.gitignore
··· 98 98 Prexonite/Scanner.cs 99 99 Prexonite/Scanner.cs.old 100 100 PrexoniteTests/OldPrexoniteTests.csproj.xml 101 - PrexoniteTests/Tests/Configurations/PsrUnitTests.cs 102 - PrexoniteTests/Tests/Configurations/VMTestConfigurations.cs 103 101 Prexonite/Properties/Resources.Designer.cs 104 102 105 103 # Ignore NuGet packages folder
+1 -1
CLAUDE.md
··· 120 120 - **Grammar Assembly**: `.atg` fragments merged via MSBuild into `Prexonite__gen.atg` 121 121 - **Parser Generation**: PxCoco generates `Parser.cs` from grammar 122 122 - **Lexer Generation**: CSFlex generates `Lexer.cs` from `Prexonite.lex` 123 - - **T4 Templates**: Some code generation via TextTemplatingFileGenerator 123 + - **Test fixture generation**: `PrexoniteTests.Generators` generates NUnit fixtures from the JSON test configurations 124 124 125 125 # Meta-Programming Features 126 126
+20
PrexoniteTests.Generators/PrexoniteTests.Generators.csproj
··· 1 + <Project Sdk="Microsoft.NET.Sdk"> 2 + 3 + <PropertyGroup> 4 + <TargetFramework>net10.0</TargetFramework> 5 + <IsRoslynComponent>true</IsRoslynComponent> 6 + <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> 7 + </PropertyGroup> 8 + 9 + <ItemGroup> 10 + <Reference Include="Microsoft.CodeAnalysis"> 11 + <HintPath>$(MSBuildSDKsPath)/../Roslyn/bincore/Microsoft.CodeAnalysis.dll</HintPath> 12 + <Private>false</Private> 13 + </Reference> 14 + <Reference Include="Microsoft.CodeAnalysis.CSharp"> 15 + <HintPath>$(MSBuildSDKsPath)/../Roslyn/bincore/Microsoft.CodeAnalysis.CSharp.dll</HintPath> 16 + <Private>false</Private> 17 + </Reference> 18 + </ItemGroup> 19 + 20 + </Project>
+446
PrexoniteTests.Generators/TestConfigurationGenerator.cs
··· 1 + using System.Collections.Immutable; 2 + using System.Text; 3 + using System.Text.Json; 4 + using Microsoft.CodeAnalysis; 5 + using Microsoft.CodeAnalysis.Text; 6 + 7 + namespace PrexoniteTests.Generators; 8 + 9 + [Generator] 10 + public sealed class TestConfigurationGenerator : IIncrementalGenerator 11 + { 12 + const string ConfigurationFileName = "testconfig.json"; 13 + 14 + static readonly DiagnosticDescriptor InvalidConfiguration = new( 15 + "PRXTEST001", 16 + "Invalid test configuration", 17 + "{0}", 18 + "PrexoniteTests", 19 + DiagnosticSeverity.Error, 20 + isEnabledByDefault: true); 21 + 22 + static readonly DiagnosticDescriptor MissingConfiguration = new( 23 + "PRXTEST002", 24 + "Missing test configuration", 25 + "Expected one version {0} test configuration named testconfig.json", 26 + "PrexoniteTests", 27 + DiagnosticSeverity.Error, 28 + isEnabledByDefault: true); 29 + 30 + static readonly JsonSerializerOptions JsonOptions = new() 31 + { 32 + PropertyNameCaseInsensitive = true, 33 + AllowTrailingCommas = true, 34 + ReadCommentHandling = JsonCommentHandling.Skip, 35 + }; 36 + 37 + public void Initialize(IncrementalGeneratorInitializationContext context) 38 + { 39 + var configurations = context.AdditionalTextsProvider 40 + .Where(static file => string.Equals( 41 + Path.GetFileName(file.Path), 42 + ConfigurationFileName, 43 + StringComparison.OrdinalIgnoreCase)) 44 + .Select(static (file, cancellationToken) => Parse(file, cancellationToken)) 45 + .Collect(); 46 + 47 + context.RegisterSourceOutput(configurations, Emit); 48 + } 49 + 50 + static ParseResult Parse(AdditionalText file, CancellationToken cancellationToken) 51 + { 52 + try 53 + { 54 + var text = file.GetText(cancellationToken)?.ToString(); 55 + if (text is null) 56 + return ParseResult.Failure(file.Path, "Could not read the test configuration."); 57 + 58 + var configuration = JsonSerializer.Deserialize<TestConfiguration>(text, JsonOptions); 59 + if (configuration is null) 60 + return ParseResult.Failure(file.Path, "The test configuration is empty."); 61 + 62 + configuration.Suites ??= []; 63 + foreach (var suite in configuration.Suites) 64 + { 65 + suite.File = NormalizePath(suite.File ?? ""); 66 + suite.Tests ??= []; 67 + suite.UnitsUnderTest ??= []; 68 + suite.TestDependencies ??= []; 69 + foreach (var dependency in suite.UnitsUnderTest.Concat(suite.TestDependencies)) 70 + { 71 + dependency.File = NormalizePath(dependency.File ?? ""); 72 + dependency.Dependencies ??= []; 73 + for (var i = 0; i < dependency.Dependencies.Count; i++) 74 + dependency.Dependencies[i] = NormalizePath(dependency.Dependencies[i] ?? ""); 75 + } 76 + } 77 + 78 + return ParseResult.Success(file.Path, configuration); 79 + } 80 + catch (JsonException exception) 81 + { 82 + return ParseResult.Failure( 83 + file.Path, 84 + $"Invalid JSON at line {exception.LineNumber}, byte {exception.BytePositionInLine}: {exception.Message}"); 85 + } 86 + } 87 + 88 + static void Emit(SourceProductionContext context, ImmutableArray<ParseResult> results) 89 + { 90 + var configurations = new Dictionary<int, TestConfiguration>(); 91 + foreach (var result in results) 92 + { 93 + if (result.Error is { } error) 94 + { 95 + context.ReportDiagnostic(Diagnostic.Create( 96 + InvalidConfiguration, 97 + Location.None, 98 + $"{result.Path}: {error}")); 99 + continue; 100 + } 101 + 102 + var configuration = result.Configuration!; 103 + if (configuration.Version is not (1 or 2)) 104 + { 105 + context.ReportDiagnostic(Diagnostic.Create( 106 + InvalidConfiguration, 107 + Location.None, 108 + $"{result.Path}: unsupported configuration version {configuration.Version}.")); 109 + continue; 110 + } 111 + 112 + if (!configurations.TryAdd(configuration.Version, configuration)) 113 + { 114 + context.ReportDiagnostic(Diagnostic.Create( 115 + InvalidConfiguration, 116 + Location.None, 117 + $"More than one version {configuration.Version} test configuration was supplied.")); 118 + } 119 + } 120 + 121 + foreach (var version in new[] { 1, 2 }) 122 + { 123 + if (!configurations.ContainsKey(version)) 124 + context.ReportDiagnostic(Diagnostic.Create(MissingConfiguration, Location.None, version)); 125 + } 126 + 127 + if (!configurations.TryGetValue(1, out var v1) || 128 + !configurations.TryGetValue(2, out var v2)) 129 + return; 130 + 131 + var v1IsValid = Validate(context, v1, 1); 132 + var v2IsValid = Validate(context, v2, 2); 133 + if (!v1IsValid || !v2IsValid) 134 + return; 135 + 136 + context.AddSource("PsrUnitTests.g.cs", SourceText.From(GenerateScriptTests(v1, v2), Encoding.UTF8)); 137 + context.AddSource("VMTestConfigurations.g.cs", SourceText.From(GenerateVmFixtures(v1, v2), Encoding.UTF8)); 138 + } 139 + 140 + static bool Validate(SourceProductionContext context, TestConfiguration configuration, int expectedVersion) 141 + { 142 + var valid = true; 143 + if (configuration.Suites.Count == 0) 144 + { 145 + Report($"Version {expectedVersion} test configuration contains no suites."); 146 + valid = false; 147 + } 148 + 149 + var classNames = new HashSet<string>(StringComparer.Ordinal); 150 + foreach (var suite in configuration.Suites) 151 + { 152 + if (string.IsNullOrWhiteSpace(suite.File)) 153 + { 154 + Report($"Version {expectedVersion} contains a suite without a file name."); 155 + valid = false; 156 + continue; 157 + } 158 + 159 + if (suite.Tests.Count == 0) 160 + { 161 + Report($"Suite '{suite.File}' contains no tests."); 162 + valid = false; 163 + } 164 + 165 + var className = ToPsrClassName(suite.File) + (expectedVersion == 2 ? "V2" : ""); 166 + if (!classNames.Add(className)) 167 + { 168 + Report($"Suite '{suite.File}' produces duplicate class name '{className}'."); 169 + valid = false; 170 + } 171 + 172 + var methodNames = new HashSet<string>(StringComparer.Ordinal); 173 + foreach (var test in suite.Tests) 174 + { 175 + if (string.IsNullOrWhiteSpace(test)) 176 + { 177 + Report($"Suite '{suite.File}' contains an empty test name."); 178 + valid = false; 179 + } 180 + else if (!methodNames.Add(ValidTestName(test))) 181 + { 182 + Report($"Suite '{suite.File}' contains test names that map to the same C# identifier '{ValidTestName(test)}'."); 183 + valid = false; 184 + } 185 + } 186 + 187 + foreach (var dependency in suite.UnitsUnderTest.Concat(suite.TestDependencies)) 188 + { 189 + if (string.IsNullOrWhiteSpace(dependency.File)) 190 + { 191 + Report($"Suite '{suite.File}' contains a dependency without a file name."); 192 + valid = false; 193 + } 194 + } 195 + } 196 + 197 + return valid; 198 + 199 + void Report(string message) => context.ReportDiagnostic( 200 + Diagnostic.Create(InvalidConfiguration, Location.None, message)); 201 + } 202 + 203 + static string GenerateScriptTests(TestConfiguration v1, TestConfiguration v2) 204 + { 205 + var source = Header(); 206 + source.AppendLine("namespace PrexoniteTests.Tests.Configurations;"); 207 + source.AppendLine(); 208 + 209 + foreach (var suite in v1.Suites) 210 + { 211 + var className = ToPsrClassName(suite.File); 212 + source.AppendLine("[GeneratedCode(\"TestConfigurationGenerator\", \"1.0\")]"); 213 + source.Append("internal abstract class ").Append(className).AppendLine(" : ScriptedUnitTestContainer"); 214 + source.AppendLine("{"); 215 + source.AppendLine(" [OneTimeSetUp]"); 216 + source.AppendLine(" public void SetupTestFile()"); 217 + source.AppendLine(" {"); 218 + source.AppendLine(" var model = new TestModel"); 219 + source.AppendLine(" {"); 220 + source.Append(" TestSuiteScript = ").Append(Literal(suite.File)).AppendLine(","); 221 + AppendDependencies(source, "UnitsUnderTest", suite.UnitsUnderTest, includeTestFramework: false); 222 + AppendDependencies(source, "TestDependencies", suite.TestDependencies, includeTestFramework: true); 223 + source.AppendLine(" };"); 224 + source.AppendLine(" Initialize();"); 225 + source.AppendLine(" Runner.Configure(model, this);"); 226 + source.AppendLine(" }"); 227 + source.AppendLine(); 228 + AppendTests(source, suite.Tests, "RunUnitTest"); 229 + source.AppendLine("}"); 230 + source.AppendLine(); 231 + } 232 + 233 + foreach (var suite in v2.Suites) 234 + { 235 + var className = ToPsrClassName(suite.File) + "V2"; 236 + source.AppendLine("[GeneratedCode(\"TestConfigurationGenerator\", \"1.0\")]"); 237 + source.Append("internal abstract class ").Append(className).AppendLine(" : V2UnitTestContainer"); 238 + source.AppendLine("{"); 239 + source.Append(" protected ").Append(className).Append("(bool compileToCil) : base(") 240 + .Append(Literal(suite.File)).AppendLine(", compileToCil)"); 241 + source.AppendLine(" {"); 242 + source.AppendLine(" }"); 243 + source.AppendLine(); 244 + AppendTests(source, suite.Tests, "RunTestCase"); 245 + source.AppendLine("}"); 246 + source.AppendLine(); 247 + } 248 + 249 + return source.ToString(); 250 + } 251 + 252 + static string GenerateVmFixtures(TestConfiguration v1, TestConfiguration v2) 253 + { 254 + var source = Header(); 255 + source.AppendLine("namespace PrexoniteTests.Tests.Configurations;"); 256 + source.AppendLine(); 257 + 258 + foreach (var suite in v1.Suites) 259 + { 260 + var className = ToPsrClassName(suite.File); 261 + var baseName = ToIdentifier(suite.File); 262 + AppendV1Fixture(source, baseName + "_Interpreted", className, 263 + "new UnitTestConfiguration.InMemory()"); 264 + AppendV1Fixture(source, baseName + "_CilStatic", className, 265 + "new UnitTestConfiguration.InMemory { CompileToCil = true }"); 266 + AppendV1Fixture(source, baseName + "_CilIsolated", className, 267 + "new UnitTestConfiguration.InMemory { CompileToCil = true, Linking = FunctionLinking.FullyIsolated }"); 268 + } 269 + 270 + foreach (var suite in v2.Suites) 271 + { 272 + var className = ToPsrClassName(suite.File) + "V2"; 273 + var baseName = ToIdentifier(suite.File) + "V2"; 274 + AppendV2Fixture(source, baseName + "_InterpretedV2", className, compileToCil: false); 275 + AppendV2Fixture(source, baseName + "_CilIsolatedV2", className, compileToCil: true); 276 + } 277 + 278 + foreach (var vmClass in new[] 279 + { 280 + "PrexoniteTests.Tests.VMTests", 281 + "PrexoniteTests.Tests.PartialApplication", 282 + "PrexoniteTests.Tests.Lazy", 283 + "PrexoniteTests.Tests.Translation", 284 + "PrexoniteTests.Tests.BuiltInTypeTests", 285 + "PrexoniteTests.Tests.ShellExtensions", 286 + }) 287 + { 288 + var baseName = vmClass[(vmClass.LastIndexOf('.') + 1)..]; 289 + AppendVmFixture(source, baseName + "_Interpreted", vmClass, false, null); 290 + AppendVmFixture(source, baseName + "_CilStatic", vmClass, true, "FunctionLinking.FullyStatic"); 291 + AppendVmFixture(source, baseName + "_CilIsolated", vmClass, true, "FunctionLinking.FullyIsolated"); 292 + } 293 + 294 + return source.ToString(); 295 + } 296 + 297 + static StringBuilder Header() => new( 298 + """ 299 + // <auto-generated /> 300 + using System.CodeDom.Compiler; 301 + using NUnit.Framework; 302 + using Prexonite.Compiler.Cil; 303 + 304 + """); 305 + 306 + static void AppendDependencies( 307 + StringBuilder source, 308 + string property, 309 + List<TestDependency> dependencies, 310 + bool includeTestFramework) 311 + { 312 + source.Append(" ").Append(property).AppendLine(" ="); 313 + source.AppendLine(" ["); 314 + foreach (var dependency in dependencies) 315 + { 316 + source.AppendLine(" new TestDependency"); 317 + source.AppendLine(" {"); 318 + source.Append(" ScriptName = ").Append(Literal(dependency.File)).AppendLine(","); 319 + source.Append(" Dependencies = ["); 320 + var entries = dependency.Dependencies.Select(Literal).ToList(); 321 + if (includeTestFramework) 322 + entries.Add("PrexoniteUnitTestFramework"); 323 + source.Append(string.Join(", ", entries)).AppendLine("],"); 324 + source.AppendLine(" },"); 325 + } 326 + source.AppendLine(" ],"); 327 + } 328 + 329 + static void AppendTests(StringBuilder source, IEnumerable<string> tests, string runner) 330 + { 331 + foreach (var test in tests) 332 + { 333 + source.AppendLine(" [Test]"); 334 + source.Append(" public void ").Append(ValidTestName(test)).AppendLine("()"); 335 + source.AppendLine(" {"); 336 + source.Append(" ").Append(runner).Append('(').Append(Literal(test)).AppendLine(");"); 337 + source.AppendLine(" }"); 338 + source.AppendLine(); 339 + } 340 + } 341 + 342 + static void AppendV1Fixture(StringBuilder source, string name, string baseClass, string runner) 343 + { 344 + source.AppendLine("[TestFixture]"); 345 + source.AppendLine("[GeneratedCode(\"TestConfigurationGenerator\", \"1.0\")]"); 346 + source.Append("internal sealed class ").Append(name).Append(" : ").AppendLine(baseClass); 347 + source.AppendLine("{"); 348 + source.Append(" private readonly UnitTestConfiguration _runner = ").Append(runner).AppendLine(";"); 349 + source.AppendLine(" protected override UnitTestConfiguration Runner => _runner;"); 350 + source.AppendLine("}"); 351 + source.AppendLine(); 352 + } 353 + 354 + static void AppendV2Fixture(StringBuilder source, string name, string baseClass, bool compileToCil) 355 + { 356 + source.AppendLine("[TestFixture]"); 357 + source.AppendLine("[GeneratedCode(\"TestConfigurationGenerator\", \"1.0\")]"); 358 + source.Append("internal sealed class ").Append(name).Append(" : ").AppendLine(baseClass); 359 + source.AppendLine("{"); 360 + source.Append(" public ").Append(name).Append("() : base(") 361 + .Append(compileToCil ? "true" : "false").AppendLine(")"); 362 + source.AppendLine(" {"); 363 + source.AppendLine(" }"); 364 + source.AppendLine("}"); 365 + source.AppendLine(); 366 + } 367 + 368 + static void AppendVmFixture( 369 + StringBuilder source, 370 + string name, 371 + string baseClass, 372 + bool compileToCil, 373 + string? linking) 374 + { 375 + source.AppendLine("[TestFixture]"); 376 + source.AppendLine("[GeneratedCode(\"TestConfigurationGenerator\", \"1.0\")]"); 377 + source.Append("internal sealed class ").Append(name).Append(" : ").AppendLine(baseClass); 378 + source.AppendLine("{"); 379 + source.Append(" public ").Append(name).AppendLine("()"); 380 + source.AppendLine(" {"); 381 + source.Append(" CompileToCil = ").Append(compileToCil ? "true" : "false").AppendLine(";"); 382 + if (linking is not null) 383 + source.Append(" StaticLinking = ").Append(linking).AppendLine(";"); 384 + source.AppendLine(" }"); 385 + source.AppendLine("}"); 386 + source.AppendLine(); 387 + } 388 + 389 + static string ToPsrClassName(string fileName) => "Unit_" + ToIdentifier(fileName); 390 + 391 + static string NormalizePath(string path) => path.Replace('\\', '/'); 392 + 393 + static string ToIdentifier(string fileName) 394 + { 395 + var name = Path.GetFileName(fileName.Replace('\\', '/')); 396 + const string suffix = ".test.pxs"; 397 + if (name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) 398 + name = name[..^suffix.Length]; 399 + return name.Replace('-', '_'); 400 + } 401 + 402 + static string ValidTestName(string richName) 403 + { 404 + var characters = richName.ToCharArray(); 405 + for (var i = 0; i < characters.Length; i++) 406 + { 407 + var character = characters[i]; 408 + if (!(character is >= 'a' and <= 'z') && 409 + !(character is >= 'A' and <= 'Z') && 410 + !(character is >= '0' and <= '9') && 411 + character != '_') 412 + characters[i] = '_'; 413 + } 414 + return new string(characters); 415 + } 416 + 417 + static string Literal(string value) => JsonSerializer.Serialize(value); 418 + 419 + sealed record ParseResult(string Path, TestConfiguration? Configuration, string? Error) 420 + { 421 + public static ParseResult Success(string path, TestConfiguration configuration) => 422 + new(path, configuration, null); 423 + 424 + public static ParseResult Failure(string path, string error) => new(path, null, error); 425 + } 426 + 427 + sealed class TestConfiguration 428 + { 429 + public int Version { get; set; } 430 + public List<TestSuite> Suites { get; set; } = []; 431 + } 432 + 433 + sealed class TestSuite 434 + { 435 + public string File { get; set; } = ""; 436 + public List<TestDependency> UnitsUnderTest { get; set; } = []; 437 + public List<TestDependency> TestDependencies { get; set; } = []; 438 + public List<string> Tests { get; set; } = []; 439 + } 440 + 441 + sealed class TestDependency 442 + { 443 + public string File { get; set; } = ""; 444 + public List<string> Dependencies { get; set; } = []; 445 + } 446 + }
+5 -21
PrexoniteTests/PrexoniteTests.csproj
··· 8 8 <ItemGroup> 9 9 <PackageReference Include="System.Text.RegularExpressions" /> 10 10 <ProjectReference Include="..\Prexonite\Prexonite.csproj" /> 11 + <ProjectReference Include="..\PrexoniteTests.Generators\PrexoniteTests.Generators.csproj" 12 + OutputItemType="Analyzer" 13 + ReferenceOutputAssembly="false" /> 11 14 <PackageReference Include="Microsoft.NET.Test.Sdk" /> 12 15 <PackageReference Include="Moq" /> 13 16 <PackageReference Include="NUnit" /> ··· 15 18 </ItemGroup> 16 19 17 20 <ItemGroup> 18 - <!-- These files might not exist initially, so we track the by hand. 19 - To make this compatible with the implicit Compile rule, we have to remove the items first, 20 - otherwise, the tool will complain about duplicate items. --> 21 - <Compile Remove="Tests\Configurations\PsrUnitTests.cs" /> 22 - <Compile Remove="Tests\Configurations\VMTestConfigurations.cs" /> 23 - <Compile Include="Tests\Configurations\PsrUnitTests.cs" /> 24 - <Compile Include="Tests\Configurations\VMTestConfigurations.cs" /> 25 - </ItemGroup> 26 - 27 - <ItemGroup> 28 - <TextTemplate Include="**\*.tt" /> 21 + <AdditionalFiles Include="psr-tests\testconfig.json" /> 22 + <AdditionalFiles Include="psr-tests\_2\testconfig.json" /> 29 23 </ItemGroup> 30 24 31 25 <ItemGroup> 32 26 <Folder Include="psr-tests\_2" /> 33 27 </ItemGroup> 34 28 35 - <!-- Text Template --> 36 - <Target Name="TextTemplateTransform" BeforeTargets="BeforeBuild"> 37 - <!-- https://notquitepure.info/2018/12/12/T4-Templates-at-Build-Time-With-Dotnet-Core/ --> 38 - <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool run t4 -I=%(TextTemplate.RelativeDir) %(TextTemplate.Identity)" /> 39 - </Target> 40 - 41 - <Target Name="TextTemplateClean" AfterTargets="Clean"> 42 - <Delete Files="@(Generated)" /> 43 - </Target> 44 - 45 29 </Project>
-144
PrexoniteTests/Tests/Configurations/LoadConfiguration.t4
··· 1 - <#@ assembly name="System.Core" #> 2 - <#@ template debug="true" hostSpecific="true" #> 3 - <#@ import namespace="System.Text" #> 4 - <#@ import namespace="System.Linq" #> 5 - <#+ 6 - static readonly string ttEscape = Path.Combine("..", ".."); 7 - static readonly string testPathPrefix = Path.Combine(ttEscape, @"psr-tests"); 8 - const string testConfigurationName = "testconfig.txt"; 9 - const string testPattern = @"*.test.pxs"; 10 - 11 - const char configSectionSep = '>'; 12 - const char configEntrySep = '|'; 13 - const char configDependencySep = '<'; 14 - 15 - class Dependency 16 - { 17 - public string FileName; 18 - public IEnumerable<string> Dependencies; 19 - } 20 - 21 - class Configuration 22 - { 23 - public string TestFileName; 24 - public IEnumerable<string> TestCases; 25 - public IEnumerable<Dependency> UnitsUnderTest; 26 - public IEnumerable<Dependency> TestDependencies; 27 - } 28 - 29 - ///<summary>A Prexonite Script 2.0 test suite that relies on self-assembling build plans 30 - /// to load its dependencies.</summary> 31 - class ConfigurationV2 32 - { 33 - public string TestFileName; 34 - public IEnumerable<string> TestCases; 35 - } 36 - 37 - Dependency _getDependencies(string spec) 38 - { 39 - var ds = spec.Split(configDependencySep); 40 - return new Dependency 41 - { 42 - FileName = _normalizePath(ds[0]), 43 - Dependencies = ds.Skip(1).Select(_normalizePath), 44 - }; 45 - } 46 - 47 - // Normalize directory separator for cross-platform testing 48 - static string _normalizePath(string path) => path 49 - .Replace('/', Path.DirectorySeparatorChar) 50 - .Replace('\\', Path.DirectorySeparatorChar); 51 - 52 - 53 - // [(test_file_name, [under_test], [test_deps], [test_case])] 54 - IEnumerable<Configuration> _getTestConfiguration() 55 - { 56 - 57 - var path = Path.GetFullPath(Host.ResolvePath(Path.Combine(testPathPrefix, testConfigurationName))); 58 - using var sr = new StreamReader( 59 - new FileStream(path, FileMode.Open, FileAccess.Read), 60 - Encoding.UTF8); 61 - var line = sr.ReadLine(); 62 - while(line != null && line.Length > 2) 63 - { 64 - var fs = line.Split(new [] {configSectionSep},4); 65 - if(fs.Length < 4) 66 - continue; 67 - 68 - var tfn = _normalizePath(fs[0]); 69 - var entrySep = new []{configEntrySep}; 70 - var underTest = fs[1].Split(entrySep).Skip(1).Select(_getDependencies); 71 - var deps = fs[2].Split(entrySep).Skip(1).Select(_getDependencies); 72 - var testCases = fs[3].Split(entrySep).Skip(1); 73 - yield return new Configuration { 74 - TestFileName = tfn, 75 - TestCases = testCases, 76 - UnitsUnderTest = underTest, 77 - TestDependencies = deps, 78 - }; 79 - 80 - line = sr.ReadLine(); 81 - } 82 - } 83 - 84 - IEnumerable<ConfigurationV2> _getTestConfigurationV2() 85 - { 86 - var path = Path.GetFullPath(Host.ResolvePath(Path.Combine(testPathPrefix, "_2", testConfigurationName))); 87 - using var sr = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read), Encoding.UTF8); 88 - var line = sr.ReadLine(); 89 - while (line != null) 90 - { 91 - if (line.Length < 3) 92 - { 93 - continue; 94 - } 95 - 96 - var fs = line.Split(new[]{configSectionSep}, 2); 97 - var tfn = _normalizePath(fs[0]); 98 - var entrySep = new[] {configEntrySep}; 99 - var testCases = fs[1].Split(entrySep).Skip(1); 100 - yield return new ConfigurationV2() 101 - { 102 - TestFileName = tfn, 103 - TestCases = testCases, 104 - }; 105 - 106 - line = sr.ReadLine(); 107 - } 108 - } 109 - 110 - string _toIdentifier(string testFileName) 111 - { 112 - testFileName = Path.GetFileName(testFileName); 113 - return testFileName.Substring(0,testFileName.Length - (testPattern.Length-1)).Replace("-","_"); 114 - } 115 - 116 - string _toTestFilePath(string testFileName) 117 - { 118 - var abs = Path.GetFullPath(Host.ResolvePath(Path.Combine(testPathPrefix, testFileName))); 119 - var root = Path.GetFullPath(Host.ResolvePath(testPathPrefix)); 120 - return "." + abs.Substring(root.Length); 121 - } 122 - 123 - string _toPsrClassName(string fileName) 124 - { 125 - return "Unit_" + _toIdentifier(fileName); 126 - } 127 - 128 - string _validTestName(string richName) 129 - { 130 - // This used to be a simple Regex.Replace(testCase, @"[^a-zA-Z0-9_]", "_") 131 - // but in the move to .NET 3.1 + Mono.TextTemplate + dotnet local tools we somehow lost the 132 - // advanced capability of loading NuGet assemblies into T4 templates. 133 - // So... we grudgingly implement that Regex by hand. 134 - var cs = richName.ToCharArray(); 135 - for (var i = 0; i < cs.Length; i++) 136 - { 137 - if ((cs[i] < 'a' || 'z' < cs[i]) && (cs[i] < 'A' || 'Z' < cs[i]) && (cs[i] < '0' || '9' < cs[i]) && cs[i] != '_') 138 - { 139 - cs[i] = '_'; 140 - } 141 - } 142 - return new string(cs); 143 - } 144 - #>
-97
PrexoniteTests/Tests/Configurations/PsrUnitTests.tt
··· 1 - <#@ template debug="true" hostSpecific="true" #> 2 - <#@ output extension=".cs" #> 3 - <#@ import namespace="System.IO" #> 4 - <#@ import namespace="System.Collections.Generic" #> 5 - 6 - // ReSharper disable RedundantUsingDirective 7 - using System; 8 - using System.Reflection; 9 - using System.Collections.Generic; 10 - using System.CodeDom.Compiler; 11 - using Prexonite.Types; 12 - using Prexonite.Compiler.Cil; 13 - using NUnit.Framework; 14 - // ReSharper restore RedundantUsingDirective 15 - 16 - // ReSharper disable RedundantExplicitArrayCreation 17 - // ReSharper disable InconsistentNaming 18 - // ReSharper disable RedundantCommaInArrayInitializer 19 - 20 - namespace PrexoniteTests.Tests.Configurations 21 - { 22 - <# 23 - // Prexonite Script V1.0 Test Suites 24 - // [testFile :: (test_file_name, [under_test], [test_deps], [test_case])] 25 - foreach(var testFile in _getTestConfiguration()) 26 - { 27 - #> 28 - [GeneratedCode("PsrUnitTests.tt","0.0")] 29 - internal abstract class <#=_toPsrClassName(testFile.TestFileName)#> : ScriptedUnitTestContainer 30 - { 31 - [OneTimeSetUp] 32 - public void SetupTestFile() 33 - { 34 - var model = new TestModel 35 - { 36 - TestSuiteScript = @"<#=testFile.TestFileName#>", 37 - UnitsUnderTest = new TestDependency[]{ 38 - <# foreach(var dep in testFile.UnitsUnderTest) { #> 39 - new TestDependency { ScriptName = @"<#=dep.FileName#>", Dependencies = new string[] { 40 - <# foreach(var d in dep.Dependencies) { #> 41 - @"<#=d#>", 42 - <# } #> 43 - }}, 44 - <# } #> 45 - }, 46 - TestDependencies = new TestDependency[]{ 47 - <# foreach(var dep in testFile.TestDependencies) { #> 48 - new TestDependency { ScriptName = @"<#=dep.FileName#>", Dependencies = new string[] { 49 - <# foreach(var d in dep.Dependencies) { #> 50 - @"<#=d#>", 51 - <# } #> 52 - PrexoniteUnitTestFramework 53 - }}, 54 - <# } #> 55 - } 56 - }; 57 - Initialize(); 58 - Runner.Configure(model, this); 59 - } 60 - 61 - <# foreach(var testCase in testFile.TestCases) { #> 62 - [Test] 63 - public void <#=_validTestName(testCase)#>() 64 - { 65 - RunUnitTest(@"<#=testCase#>"); 66 - } 67 - <# } #> 68 - } 69 - <# } 70 - 71 - // Prexonite Script V2 Test Suites 72 - foreach (var configuration in _getTestConfigurationV2()) 73 - { 74 - var className = _toPsrClassName(configuration.TestFileName) + "V2"; 75 - #> 76 - [GeneratedCode("PsrUnitTests.tt","0.0")] 77 - internal abstract class <#=className#> : V2UnitTestContainer { 78 - protected <#=className#>(bool compileToCil) : base(@"<#=configuration.TestFileName#>", compileToCil) 79 - { 80 - } 81 - 82 - <# foreach (var testCase in configuration.TestCases) { #> 83 - [Test] 84 - public void <#=_validTestName(testCase)#>() 85 - { 86 - RunTestCase(@"<#=testCase#>"); 87 - } 88 - 89 - <# } #> 90 - } 91 - <# } #> 92 - } 93 - 94 - // ReSharper restore RedundantExplicitArrayCreation 95 - // ReSharper restore InconsistentNaming 96 - 97 - <#@ include file="LoadConfiguration.t4" #>
-133
PrexoniteTests/Tests/Configurations/VMTestConfigurations.tt
··· 1 - <#@ template debug="true" hostSpecific="true" #> 2 - <#@ output extension=".cs" #> 3 - //< Assembly Name="System.Core.dll" > 4 - 5 - // ReSharper disable RedundantUsingDirective 6 - // ReSharper disable RedundantNameQualifier 7 - using System; 8 - using System.CodeDom.Compiler; 9 - using System.Reflection; 10 - using System.Collections.Generic; 11 - using Prexonite.Types; 12 - using Prexonite.Compiler.Cil; 13 - using NUnit.Framework; 14 - // ReSharper restore RedundantUsingDirective 15 - 16 - // ReSharper disable RedundantExplicitArrayCreation 17 - // ReSharper disable InconsistentNaming 18 - 19 - namespace PrexoniteTests.Tests.Configurations 20 - { 21 - <# foreach(var testFile in _getTestConfiguration()) { 22 - var className = /* "PrexoniteTests.Tests.Configurations." + /* */ _toPsrClassName(testFile.TestFileName); 23 - var baseName = _toIdentifier(testFile.TestFileName); #> 24 - 25 - [TestFixture] 26 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 27 - internal class <#=baseName#>_Interpreted : <#=className#> 28 - { 29 - private readonly UnitTestConfiguration _runner = new UnitTestConfiguration.InMemory(); 30 - protected override UnitTestConfiguration Runner => _runner; 31 - } 32 - 33 - [TestFixture] 34 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 35 - internal class <#=baseName#>_CilStatic : <#=className#> 36 - { 37 - private readonly UnitTestConfiguration _runner = new UnitTestConfiguration.InMemory{CompileToCil=true}; 38 - protected override UnitTestConfiguration Runner => _runner; 39 - } 40 - 41 - [TestFixture] 42 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 43 - internal class <#=baseName#>_CilIsolated : <#=className#> 44 - { 45 - private readonly UnitTestConfiguration _runner = new UnitTestConfiguration.InMemory{ 46 - CompileToCil=true, 47 - Linking = FunctionLinking.FullyIsolated 48 - }; 49 - protected override UnitTestConfiguration Runner => _runner; 50 - } 51 - 52 - <# } #> 53 - 54 - <# foreach(var testFile in _getTestConfigurationV2()) { 55 - var className = _toPsrClassName(testFile.TestFileName) + "V2"; 56 - var baseName = _toIdentifier(testFile.TestFileName) + "V2"; #> 57 - 58 - [TestFixture] 59 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 60 - internal class <#=baseName#>_InterpretedV2 : <#=className#> 61 - { 62 - public <#=baseName#>_InterpretedV2() : base(false) 63 - { 64 - } 65 - } 66 - 67 - [TestFixture] 68 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 69 - internal class <#=baseName#>_CilIsolatedV2 : <#=className#> 70 - { 71 - public <#=baseName#>_CilIsolatedV2() : base(true) 72 - { 73 - } 74 - } 75 - <# } #> 76 - 77 - <# foreach(var vmClass in _getVMTestClasses()) { 78 - var baseName = vmClass.Substring(vmClass.LastIndexOf('.')+1); #> 79 - 80 - [TestFixture] 81 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 82 - internal class <#=baseName#>_Interpreted : <#=vmClass#> 83 - { 84 - public <#=baseName#>_Interpreted() 85 - { 86 - CompileToCil = false; 87 - } 88 - } 89 - 90 - [TestFixture] 91 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 92 - internal class <#=baseName#>_CilStatic : <#=vmClass#> 93 - { 94 - public <#=baseName#>_CilStatic() 95 - { 96 - CompileToCil = true; 97 - StaticLinking = FunctionLinking.FullyStatic; 98 - } 99 - } 100 - 101 - [TestFixture] 102 - [GeneratedCode("VMTestConfiguration.tt","0.0")] 103 - internal class <#=baseName#>_CilIsolated : <#=vmClass#> 104 - { 105 - public <#=baseName#>_CilIsolated() 106 - { 107 - CompileToCil = true; 108 - StaticLinking = FunctionLinking.FullyIsolated; 109 - } 110 - } 111 - 112 - <# } #> 113 - } 114 - 115 - // ReSharper enable RedundantExplicitArrayCreation 116 - // ReSharper enable InconsistentNaming 117 - 118 - <#@ include file="LoadConfiguration.t4" #><#@ import namespace="System.Collections.Generic" #><#@ import namespace="System.IO" #> 119 - 120 - <#+ 121 - IEnumerable<string> _getVMTestClasses() 122 - { 123 - return new []{ 124 - "PrexoniteTests.Tests.VMTests", 125 - "PrexoniteTests.Tests.PartialApplication", 126 - "PrexoniteTests.Tests.Lazy", 127 - "PrexoniteTests.Tests.Translation", 128 - "PrexoniteTests.Tests.BuiltInTypeTests", 129 - "PrexoniteTests.Tests.ShellExtensions", 130 - }; 131 - } 132 - 133 - #>
+75 -53
PrexoniteTests/psr-tests/_2/make_test_configuration.pxs
··· 13 13 function get_test_file_names(path,pattern)[import System::IO] = 14 14 ::Directory.EnumerateFiles(path ?? ".", pattern ?? "*.test.pxs", ::SearchOption.AllDirectories); 15 15 16 - function under_test = "under_test"; 17 - function test_dependencies = "test_dependencies"; 18 - function config_file_separator = "|"; 19 - function config_section_separator = ">"; 20 - function config_dependency_separator = "<"; 21 - 22 16 function create_config(testCases) 23 17 { 24 18 var s = new Structure; ··· 53 47 return new config(testCases); 54 48 } 55 49 56 - //write_config writes the configuration <config> to the supplied <textWriter>. 57 - // - textWriter, an object that behaves like a System.IO.TextWriter 58 - // - config, [test_file_name:{test_cases}] 59 - function write_config(textWriter, config) 50 + function write_json_string(writer, value)[import System] 60 51 { 61 - foreach(var kvp in config) 52 + var backslash = ::Convert.ToChar(92); 53 + var quote = ::Convert.ToChar(34); 54 + writer.Write(quote); 55 + foreach(var character in value.ToCharArray) 62 56 { 63 - textWriter.Write(kvp.Key); 64 - textWriter.Write(config_section_separator); 65 - foreach(var d in kvp.Value.test_cases) 66 - textWriter.Write("$(config_file_separator)$(d)"); 67 - textWriter.WriteLine; 57 + var code = ::Convert.ToInt32(character); 58 + if(code == 34 or code == 92) 59 + { 60 + writer.Write(backslash); 61 + writer.Write(character); 62 + } 63 + else if(code == 8 or code == 9 or code == 10 or code == 12 or code == 13) 64 + { 65 + writer.Write(backslash); 66 + writer.Write(::Convert.ToChar(if(code == 8) 98 67 + else if(code == 9) 116 68 + else if(code == 10) 110 69 + else if(code == 12) 102 70 + else 114)); 71 + } 72 + else if(code < 32) 73 + { 74 + writer.Write(backslash); 75 + writer.Write("u"); 76 + writer.Write(code.ToString("x4")); 77 + } 78 + else 79 + writer.Write(character); 68 80 } 81 + writer.Write(quote); 69 82 } 70 83 71 - function write_config_file(file_name, config)[import {System::IO, System::Text}] 84 + function write_property_name(writer, name) 72 85 { 73 - using(var tw = new ::StreamWriter( 74 - new ::FileStream(file_name,::FileMode.Create,::FileAccess.Write), 75 - ::Encoding.UTF8)) 76 - write_config(tw, config); 86 + write_json_string(writer, name); 87 + writer.Write(":"); 77 88 } 78 89 79 - function to_config(config) 90 + // write_config writes version 2 of the JSON test configuration. 91 + // - writer, an object that behaves like a System.IO.TextWriter 92 + // - config, [test_file_name:{test_cases}] 93 + function write_config(writer, config) 80 94 { 81 - var s; 82 - using(var sw = new ::StringWriter) 95 + writer.Write("{"); 96 + write_property_name(writer, "version"); 97 + writer.Write("2,"); 98 + write_property_name(writer, "suites"); 99 + writer.Write("["); 100 + var suiteSeparator = ""; 101 + foreach(var kvp in config) 83 102 { 84 - write_config(sw,config); 85 - s = sw.ToString; 103 + writer.Write(suiteSeparator); 104 + suiteSeparator = ","; 105 + writer.Write("{"); 106 + write_property_name(writer, "file"); 107 + write_json_string(writer, kvp.Key); 108 + writer.Write(","); 109 + write_property_name(writer, "tests"); 110 + writer.Write("["); 111 + var testSeparator = ""; 112 + foreach(var testCase in kvp.Value.test_cases) 113 + { 114 + writer.Write(testSeparator); 115 + testSeparator = ","; 116 + write_json_string(writer, testCase); 117 + } 118 + writer.Write("]}"); 86 119 } 87 - return s; 120 + writer.Write("]}"); 88 121 } 89 122 90 - function get_config(path) = 91 - get_test_file_names(path) >> map(tfn => tfn: read_metadata(tfn)); 92 - 93 - function main(path) 123 + function write_config_file(file_name, config)[import {System::IO, System::Text}] 94 124 { 95 - write_config_file("testconfig.txt",get_config(path)); 125 + using(var writer = new ::StreamWriter( 126 + new ::FileStream(file_name, ::FileMode.Create, ::FileAccess.Write), 127 + ::Encoding.UTF8)) 128 + write_config(writer, config); 96 129 } 97 130 98 - function read_config(textReader) 131 + function to_config(config)[import System::IO] 99 132 { 100 - var config = []; 101 - var line; 102 - for(; do line = textReader.ReadLine; while line is not null and line.Length > 0) 133 + using(var writer = new ::StringWriter) 103 134 { 104 - var fs = line.Split(config_section_separator); 105 - if(fs.Count != 2) 106 - throw "Unexpected number of sections (separated by '$(config_section_separator)'). Expected 2. Line: $line"; 107 - 108 - config[] = fs[0]: new config( 109 - (fs[1].Split(config_file_separator) >> skip(1) >> to_list), 110 - ); 135 + write_config(writer, config); 136 + return writer.ToString; 111 137 } 112 - return config; 113 138 } 114 139 115 - function read_config_file(fileName)[import {System::IO, System::Text}] 116 - { 117 - var c; 118 - using(var tr = new ::StreamReader( 119 - new ::FileStream(fileName, ::FileMode.Open, ::FileAccess.Read), 120 - ::Encoding.UTF8)) 121 - c = read_config(tr); 140 + function get_config(path) = 141 + get_test_file_names(path) >> map(tfn => tfn: read_metadata(tfn)); 122 142 123 - return c; 143 + function main(path) 144 + { 145 + write_config_file("testconfig.json",get_config(path)); 124 146 } 125 147 126 - } export(main, write_config_file, get_config, read_config, read_config_file, to_config, write_config, 148 + } export(main, write_config_file, get_config, to_config, write_config, 127 149 create_config, get_test_file_names, create_source_file); 128 150 129 151 Entry make_test_configuration\main;
+69
PrexoniteTests/psr-tests/_2/testconfig.json
··· 1 + { 2 + "version": 2, 3 + "suites": [ 4 + { 5 + "file": "./ast.test.pxs", 6 + "tests": [ 7 + "compiler_is_loaded", 8 + "test_ast_withpos_null", 9 + "test_ast_withpos_memcall", 10 + "test_ast_simple_memcall", 11 + "test_ast_memcall", 12 + "test_unique_id_counter", 13 + "test_is_member_access", 14 + "test_local_meta", 15 + "test_si_fields", 16 + "test_si_is_star", 17 + "test_si_make_star", 18 + "test_si_m_is_star", 19 + "test_sub_blocks" 20 + ] 21 + }, 22 + { 23 + "file": "./macro.test.pxs", 24 + "tests": [ 25 + "test_file", 26 + "test_pos", 27 + "test_is_in_macro", 28 + "test_establish_macro_context", 29 + "test_reports", 30 + "test_ast_is_expression", 31 + "test_ast_is_effect", 32 + "test_ast_is_partially_applicable", 33 + "test_ast_is_partial_application", 34 + "test_ast_is_CreateClosure", 35 + "test_ast_is_node", 36 + "test_temp", 37 + "test_optimize", 38 + "test_read", 39 + "test_macro_internal_id_static", 40 + "test_macro_internal_id", 41 + "test_macro_entity_static", 42 + "test_expand_macro", 43 + "test_ast_symbol", 44 + "test_ast_member", 45 + "test_ast_const", 46 + "test_ast_ret", 47 + "test_ast_with_args", 48 + "test_ast_new", 49 + "test_ast_null" 50 + ] 51 + }, 52 + { 53 + "file": "./pattern.test.pxs", 54 + "tests": [ 55 + "test_con", 56 + "test_dcon" 57 + ] 58 + }, 59 + { 60 + "file": "./prop.test.pxs", 61 + "tests": [ 62 + "test_prop_simple", 63 + "test_prop_proxy", 64 + "test_prop_complex", 65 + "test_prop_simple_glob" 66 + ] 67 + } 68 + ] 69 + }
-4
PrexoniteTests/psr-tests/_2/testconfig.txt
··· 1 - .\ast.test.pxs>|compiler_is_loaded|test_ast_withpos_null|test_ast_withpos_memcall|test_ast_simple_memcall|test_ast_memcall|test_unique_id_counter|test_is_member_access|test_local_meta|test_si_fields|test_si_is_star|test_si_make_star|test_si_m_is_star|test_sub_blocks 2 - .\macro.test.pxs>|test_file|test_pos|test_is_in_macro|test_establish_macro_context|test_reports|test_ast_is_expression|test_ast_is_effect|test_ast_is_partially_applicable|test_ast_is_partial_application|test_ast_is_CreateClosure|test_ast_is_node|test_temp|test_optimize|test_read|test_macro_internal_id_static|test_macro_internal_id|test_macro_entity_static|test_expand_macro|test_ast_symbol|test_ast_member|test_ast_const|test_ast_ret|test_ast_with_args|test_ast_new|test_ast_null 3 - .\pattern.test.pxs>|test_con|test_dcon 4 - .\prop.test.pxs>|test_prop_simple|test_prop_proxy|test_prop_complex|test_prop_simple_glob
+103 -75
PrexoniteTests/psr-tests/make_test_configuration.pxs
··· 11 11 12 12 function under_test = "under_test"; 13 13 function test_dependencies = "test_dependencies"; 14 - function config_file_separator = "|"; 15 - function config_section_separator = ">"; 16 - function config_dependency_separator = "<"; 17 - 18 14 function create_config(testUnits, deps, testCases) 19 15 { 20 16 var s = new Structure; ··· 67 63 return new config(testUnits, deps, testCases); 68 64 } 69 65 70 - function write_deplist(textWriter, deps /* ~String:[String] */) 66 + function write_json_string(writer, value)[import System] 71 67 { 72 - textWriter.Write(deps.Key); 73 - if(deps.Value.Count > 0){ 74 - foreach(var d in deps.Value) 75 - textWriter.Write("$(config_dependency_separator)$(d)"); 76 - } 77 - } 78 - 79 - //write_config writes the configuration <config> to the supplied <textWriter>. 80 - // - textWriter, an object that behaves like a System.IO.TextWriter 81 - // - config, [test_file_name:{under_test,deps,test_cases}] 82 - function write_config(textWriter, config) 83 - { 84 - foreach(var kvp in config) 68 + var backslash = ::Convert.ToChar(92); 69 + var quote = ::Convert.ToChar(34); 70 + writer.Write(quote); 71 + foreach(var character in value.ToCharArray) 85 72 { 86 - textWriter.Write(kvp.Key); 87 - textWriter.Write(config_section_separator); 88 - foreach(var d in kvp.Value.under_test){ 89 - textWriter.Write(config_file_separator); 90 - write_deplist(textWriter, d); 91 - } 92 - textWriter.Write(config_section_separator); 93 - foreach(var d in kvp.Value.deps){ 94 - textWriter.Write(config_file_separator); 95 - write_deplist(textWriter, d); 96 - } 97 - textWriter.Write(config_section_separator); 98 - foreach(var d in kvp.Value.test_cases) 99 - textWriter.Write("$(config_file_separator)$(d)"); 100 - textWriter.WriteLine; 73 + var code = ::Convert.ToInt32(character); 74 + if(code == 34 or code == 92) 75 + { 76 + writer.Write(backslash); 77 + writer.Write(character); 78 + } 79 + else if(code == 8 or code == 9 or code == 10 or code == 12 or code == 13) 80 + { 81 + writer.Write(backslash); 82 + writer.Write(::Convert.ToChar(if(code == 8) 98 83 + else if(code == 9) 116 84 + else if(code == 10) 110 85 + else if(code == 12) 102 86 + else 114)); 87 + } 88 + else if(code < 32) 89 + { 90 + writer.Write(backslash); 91 + writer.Write("u"); 92 + writer.Write(code.ToString("x4")); 93 + } 94 + else 95 + writer.Write(character); 101 96 } 97 + writer.Write(quote); 102 98 } 103 99 104 - function write_config_file(file_name, config)[import {System::IO, System::Text}] 100 + function write_property_name(writer, name) 105 101 { 106 - using(var tw = new ::StreamWriter( 107 - new ::FileStream(file_name,::FileMode.Create,::FileAccess.Write), 108 - ::Encoding.UTF8)) 109 - write_config(tw, config); 102 + write_json_string(writer, name); 103 + writer.Write(":"); 110 104 } 111 105 112 - function to_config(config) 106 + function write_dependencies(writer, name, dependencies) 113 107 { 114 - var s; 115 - using(var sw = new ::StringWriter) 108 + write_property_name(writer, name); 109 + writer.Write("["); 110 + var dependencySeparator = ""; 111 + foreach(var dependency in dependencies) 116 112 { 117 - write_config(sw,config); 118 - s = sw.ToString; 113 + writer.Write(dependencySeparator); 114 + dependencySeparator = ","; 115 + writer.Write("{"); 116 + write_property_name(writer, "file"); 117 + write_json_string(writer, dependency.Key); 118 + writer.Write(","); 119 + write_property_name(writer, "dependencies"); 120 + writer.Write("["); 121 + var fileSeparator = ""; 122 + foreach(var dependencyFile in dependency.Value) 123 + { 124 + writer.Write(fileSeparator); 125 + fileSeparator = ","; 126 + write_json_string(writer, dependencyFile); 127 + } 128 + writer.Write("]}"); 119 129 } 120 - return s; 130 + writer.Write("]"); 121 131 } 122 132 123 - function get_config(path) = 124 - get_test_file_names(path) >> map(tfn => tfn: read_metadata(tfn)); 125 - 126 - function main(path) 133 + // write_config writes version 1 of the JSON test configuration. 134 + // - writer, an object that behaves like a System.IO.TextWriter 135 + // - config, [test_file_name:{under_test,deps,test_cases}] 136 + function write_config(writer, config) 127 137 { 128 - write_config_file("testconfig.txt",get_config(path)); 138 + writer.Write("{"); 139 + write_property_name(writer, "version"); 140 + writer.Write("1,"); 141 + write_property_name(writer, "suites"); 142 + writer.Write("["); 143 + var suiteSeparator = ""; 144 + foreach(var kvp in config) 145 + { 146 + writer.Write(suiteSeparator); 147 + suiteSeparator = ","; 148 + writer.Write("{"); 149 + write_property_name(writer, "file"); 150 + write_json_string(writer, kvp.Key); 151 + writer.Write(","); 152 + write_dependencies(writer, "unitsUnderTest", kvp.Value.under_test); 153 + writer.Write(","); 154 + write_dependencies(writer, "testDependencies", kvp.Value.deps); 155 + writer.Write(","); 156 + write_property_name(writer, "tests"); 157 + writer.Write("["); 158 + var testSeparator = ""; 159 + foreach(var testCase in kvp.Value.test_cases) 160 + { 161 + writer.Write(testSeparator); 162 + testSeparator = ","; 163 + write_json_string(writer, testCase); 164 + } 165 + writer.Write("]}"); 166 + } 167 + writer.Write("]}"); 129 168 } 130 169 131 - function read_deps(s) 170 + function write_config_file(file_name, config)[import {System::IO, System::Text}] 132 171 { 133 - var ds = s.Split(config_dependency_separator); 134 - return ds[0]:(ds >> skip(1) >> all); 172 + using(var writer = new ::StreamWriter( 173 + new ::FileStream(file_name, ::FileMode.Create, ::FileAccess.Write), 174 + ::Encoding.UTF8)) 175 + write_config(writer, config); 135 176 } 136 177 137 - function read_config(textReader) 178 + function to_config(config)[import System::IO] 138 179 { 139 - var config = []; 140 - var line; 141 - for(; do line = textReader.ReadLine; while line is not null and line.Length > 0) 180 + using(var writer = new ::StringWriter) 142 181 { 143 - var fs = line.Split(config_section_separator); 144 - if(fs.Count != 3) 145 - continue; 146 - 147 - config[] = fs[0]: new config( 148 - (fs[1].Split(config_file_separator) >> skip(1) >> map(read_deps(?)) >> all), 149 - (fs[2].Split(config_file_separator) >> skip(1) >> map(read_deps(?)) >> all), 150 - (fs[3].Split(config_file_separator) >> skip(1) >> all), 151 - ); 182 + write_config(writer, config); 183 + return writer.ToString; 152 184 } 153 - return config; 154 185 } 155 186 156 - function read_config_file(fileName)[import {System::IO, System::Text}] 157 - { 158 - var c; 159 - using(var tr = new ::StreamReader( 160 - new ::FileStream(fileName, ::FileMode.Open, ::FileAccess.Read), 161 - ::Encoding.UTF8)) 162 - c = read_config(tr); 187 + function get_config(path) = 188 + get_test_file_names(path) >> map(tfn => tfn: read_metadata(tfn)); 163 189 164 - return c; 190 + function main(path) 191 + { 192 + write_config_file("testconfig.json",get_config(path)); 165 193 } 166 194 167 - } export(main, write_config_file, get_config, read_config, read_config_file, to_config, write_config, 195 + } export(main, write_config_file, get_config, to_config, write_config, 168 196 create_config, get_test_file_names); 169 197 170 198 function main = prx.test.make_test_configuration.main(*var args);
+236
PrexoniteTests/psr-tests/testconfig.json
··· 1 + { 2 + "version": 1, 3 + "suites": [ 4 + { 5 + "file": "./ast.test.pxs", 6 + "unitsUnderTest": [ 7 + { 8 + "file": "psr/impl/ast.pxs", 9 + "dependencies": [] 10 + } 11 + ], 12 + "testDependencies": [], 13 + "tests": [ 14 + "compiler_is_loaded", 15 + "test_ast_withpos_null", 16 + "test_ast_withpos_memcall", 17 + "test_ast_simple_memcall", 18 + "test_ast_memcall", 19 + "test_unique_id_counter", 20 + "test_is_member_access", 21 + "test_local_meta", 22 + "test_si_fields", 23 + "test_si_is_star", 24 + "test_si_make_star", 25 + "test_si_m_is_star", 26 + "test_sub_blocks" 27 + ] 28 + }, 29 + { 30 + "file": "./lang-ext.test.pxs", 31 + "unitsUnderTest": [ 32 + { 33 + "file": "psr/impl/ast.pxs", 34 + "dependencies": [] 35 + }, 36 + { 37 + "file": "psr/impl/macro.pxs", 38 + "dependencies": [ 39 + "psr/impl/ast.pxs" 40 + ] 41 + }, 42 + { 43 + "file": "psr/impl/struct.pxs", 44 + "dependencies": [ 45 + "psr/impl/ast.pxs", 46 + "psr/impl/macro.pxs" 47 + ] 48 + }, 49 + { 50 + "file": "psr/impl/pattern.pxs", 51 + "dependencies": [ 52 + "psr/impl/ast.pxs", 53 + "psr/impl/macro.pxs", 54 + "psr/impl/struct.pxs" 55 + ] 56 + }, 57 + { 58 + "file": "psr/impl/prop.pxs", 59 + "dependencies": [ 60 + "psr/impl/ast.pxs", 61 + "psr/impl/macro.pxs" 62 + ] 63 + } 64 + ], 65 + "testDependencies": [ 66 + { 67 + "file": "psr/test/meta_macro.pxs", 68 + "dependencies": [] 69 + } 70 + ], 71 + "tests": [ 72 + "test_con", 73 + "test_dcon", 74 + "test_prop_simple", 75 + "test_prop_proxy", 76 + "test_prop_complex", 77 + "test_prop_simple_glob" 78 + ] 79 + }, 80 + { 81 + "file": "./macro.test.pxs", 82 + "unitsUnderTest": [ 83 + { 84 + "file": "psr/impl/ast.pxs", 85 + "dependencies": [] 86 + }, 87 + { 88 + "file": "psr/impl/macro.pxs", 89 + "dependencies": [ 90 + "psr/impl/ast.pxs" 91 + ] 92 + } 93 + ], 94 + "testDependencies": [ 95 + { 96 + "file": "psr/test/meta_macro.pxs", 97 + "dependencies": [] 98 + } 99 + ], 100 + "tests": [ 101 + "test_file", 102 + "test_pos", 103 + "test_is_in_macro", 104 + "test_establish_macro_context", 105 + "test_reports", 106 + "test_ast_is_expression", 107 + "test_ast_is_effect", 108 + "test_ast_is_partially_applicable", 109 + "test_ast_is_partial_application", 110 + "test_ast_is_CreateClosure", 111 + "test_ast_is_node", 112 + "test_temp", 113 + "test_optimize", 114 + "test_read", 115 + "test_macro_internal_id_static", 116 + "test_macro_internal_id", 117 + "test_macro_entity_static", 118 + "test_expand_macro", 119 + "test_ast_symbol", 120 + "test_ast_member", 121 + "test_ast_const", 122 + "test_ast_ret", 123 + "test_ast\\with_arguments", 124 + "test_ast\\new", 125 + "test_ast\\null" 126 + ] 127 + }, 128 + { 129 + "file": "./misc.test.pxs", 130 + "unitsUnderTest": [ 131 + { 132 + "file": "psr/impl/ast.pxs", 133 + "dependencies": [] 134 + }, 135 + { 136 + "file": "psr/impl/macro.pxs", 137 + "dependencies": [ 138 + "psr/impl/ast.pxs" 139 + ] 140 + }, 141 + { 142 + "file": "psr/impl/struct.pxs", 143 + "dependencies": [ 144 + "psr/impl/ast.pxs", 145 + "psr/impl/macro.pxs" 146 + ] 147 + }, 148 + { 149 + "file": "psr/impl/misc.pxs", 150 + "dependencies": [ 151 + "psr/impl/struct.pxs", 152 + "psr/impl/ast.pxs", 153 + "psr/impl/macro.pxs" 154 + ] 155 + } 156 + ], 157 + "testDependencies": [], 158 + "tests": [ 159 + "test_cmp", 160 + "test_cmp_values", 161 + "test_cmp_keys", 162 + "test_cmp_with", 163 + "test_cmp_then", 164 + "test_cmpr", 165 + "test_ieq", 166 + "test_ieq_any", 167 + "test_ieq_all", 168 + "test_refeq", 169 + "test_nrefeq", 170 + "test_create_terminator", 171 + "test_swap" 172 + ] 173 + }, 174 + { 175 + "file": "./struct.test.pxs", 176 + "unitsUnderTest": [ 177 + { 178 + "file": "psr/impl/ast.pxs", 179 + "dependencies": [] 180 + }, 181 + { 182 + "file": "psr/impl/macro.pxs", 183 + "dependencies": [ 184 + "psr/impl/ast.pxs" 185 + ] 186 + }, 187 + { 188 + "file": "psr/impl/struct.pxs", 189 + "dependencies": [ 190 + "psr/impl/ast.pxs", 191 + "psr/impl/macro.pxs" 192 + ] 193 + }, 194 + { 195 + "file": "psr/impl/set.pxs", 196 + "dependencies": [ 197 + "psr/impl/struct.pxs" 198 + ] 199 + }, 200 + { 201 + "file": "psr/impl/queue.pxs", 202 + "dependencies": [ 203 + "psr/impl/struct.pxs" 204 + ] 205 + }, 206 + { 207 + "file": "psr/impl/stack.pxs", 208 + "dependencies": [ 209 + "psr/impl/struct.pxs" 210 + ] 211 + } 212 + ], 213 + "testDependencies": [], 214 + "tests": [ 215 + "test_struct", 216 + "tsm_create", 217 + "tsm_add_remove", 218 + "tsi_create", 219 + "tsi_add_remove", 220 + "tqm_count", 221 + "tqm_peek", 222 + "tqm_dequeue", 223 + "tqi_create", 224 + "tqi_enqueuedequeue", 225 + "tqi_nonserial", 226 + "tm_count", 227 + "tm_peek", 228 + "tm_pop", 229 + "tm_enumarte_pops", 230 + "ti_create", 231 + "ti_pushpop", 232 + "ti_nonserial" 233 + ] 234 + } 235 + ] 236 + }
-5
PrexoniteTests/psr-tests/testconfig.txt
··· 1 - .\ast.test.pxs>|psr\impl\ast.pxs>>|compiler_is_loaded|test_ast_withpos_null|test_ast_withpos_memcall|test_ast_simple_memcall|test_ast_memcall|test_unique_id_counter|test_is_member_access|test_local_meta|test_si_fields|test_si_is_star|test_si_make_star|test_si_m_is_star|test_sub_blocks 2 - .\lang-ext.test.pxs>|psr\impl\ast.pxs|psr\impl\macro.pxs<psr\impl\ast.pxs|psr\impl\struct.pxs<psr\impl\ast.pxs<psr\impl\macro.pxs|psr\impl\pattern.pxs<psr\impl\ast.pxs<psr\impl\macro.pxs<psr\impl\struct.pxs|psr\impl\prop.pxs<psr\impl\ast.pxs<psr\impl\macro.pxs>|psr\test\meta_macro.pxs>|test_con|test_dcon|test_prop_simple|test_prop_proxy|test_prop_complex|test_prop_simple_glob 3 - .\macro.test.pxs>|psr\impl\ast.pxs|psr\impl\macro.pxs<psr\impl\ast.pxs>|psr\test\meta_macro.pxs>|test_file|test_pos|test_is_in_macro|test_establish_macro_context|test_reports|test_ast_is_expression|test_ast_is_effect|test_ast_is_partially_applicable|test_ast_is_partial_application|test_ast_is_CreateClosure|test_ast_is_node|test_temp|test_optimize|test_read|test_macro_internal_id_static|test_macro_internal_id|test_macro_entity_static|test_expand_macro|test_ast_symbol|test_ast_member|test_ast_const|test_ast_ret|test_ast\with_arguments|test_ast\new|test_ast\null 4 - .\misc.test.pxs>|psr\impl\ast.pxs|psr\impl\macro.pxs<psr\impl\ast.pxs|psr\impl\struct.pxs<psr\impl\ast.pxs<psr\impl\macro.pxs|psr\impl\misc.pxs<psr\impl\struct.pxs<psr\impl\ast.pxs<psr\impl\macro.pxs>>|test_cmp|test_cmp_values|test_cmp_keys|test_cmp_with|test_cmp_then|test_cmpr|test_ieq|test_ieq_any|test_ieq_all|test_refeq|test_nrefeq|test_create_terminator|test_swap 5 - .\struct.test.pxs>|psr\impl\ast.pxs|psr\impl\macro.pxs<psr\impl\ast.pxs|psr\impl\struct.pxs<psr\impl\ast.pxs<psr\impl\macro.pxs|psr\impl\set.pxs<psr\impl\struct.pxs|psr\impl\queue.pxs<psr\impl\struct.pxs|psr\impl\stack.pxs<psr\impl\struct.pxs>>|test_struct|tsm_create|tsm_add_remove|tsi_create|tsi_add_remove|tqm_count|tqm_peek|tqm_dequeue|tqi_create|tqi_enqueuedequeue|tqi_nonserial|tm_count|tm_peek|tm_pop|tm_enumarte_pops|ti_create|ti_pushpop|ti_nonserial
+1
prx.slnx
··· 1 1 <Solution> 2 2 <Project Path="Prexonite/Prexonite.csproj" /> 3 3 <Project Path="PrexoniteTests/PrexoniteTests.csproj" /> 4 + <Project Path="PrexoniteTests.Generators/PrexoniteTests.Generators.csproj" /> 4 5 <Project Path="Prx/Prx.csproj" /> 5 6 </Solution>