From 00072d81c31b6a9a5fbc9d10e8dc5facf764e7b2 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 29 Mar 2026 11:25:52 +0200 Subject: [PATCH 1/4] fix: Allow tracing string variables with "$" in the name --- ZXBStudio/BuildSystem/ZXBasicMap.cs | 40 +++++++++++--------------- ZXBStudio/BuildSystem/ZXVariableMap.cs | 13 ++++----- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/ZXBStudio/BuildSystem/ZXBasicMap.cs b/ZXBStudio/BuildSystem/ZXBasicMap.cs index a17a828..7ac172f 100644 --- a/ZXBStudio/BuildSystem/ZXBasicMap.cs +++ b/ZXBStudio/BuildSystem/ZXBasicMap.cs @@ -179,7 +179,7 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable AllFiles, string ParseInputParameters(funcMatch.Groups[5].Value, currentFunction.InputParameters); if (funcMatch.Groups[7].Success) - currentFunction.ReturnType = StorageFromString(funcMatch.Groups[5].Value, currentFunction.Name); + currentFunction.ReturnType = StorageFromString(funcMatch.Groups[7].Value, currentFunction.Name); else currentFunction.ReturnType = ZXVariableStorage.F; @@ -202,8 +202,7 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable AllFiles, string if (varNameDef.Contains("(")) //array { string varName = varNameDef.Substring(0, varNameDef.IndexOf("(")).Trim(); - - if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline))) + if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline))) continue; string[] dims = varNameDef.Substring(varNameDef.IndexOf("(") + 1).Replace(")", "").Split(",", StringSplitOptions.RemoveEmptyEntries); @@ -219,8 +218,7 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable AllFiles, string foreach (var vName in varNames) { string varName = vName.Trim(); - - if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline))) + if (!jointLines.Skip(buc + 1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline))) continue; var storage = StorageFromString(dimMatch.Groups[5].Value, varName); @@ -291,15 +289,15 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable AllFiles, string //Search for the var in the sub/function that the location points to if (location.LocationType == ZXBasicLocationType.Sub) { - var sub = subs.Where(s => s.Name == location.Name).FirstOrDefault(); + var sub = subs.FirstOrDefault(s => string.Equals(s.Name, location.Name, StringComparison.OrdinalIgnoreCase)); if(sub != null) - foundVar = sub.LocalVariables.Where(v => v.Name == varName).FirstOrDefault(); + foundVar = sub.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase)); } else { - var func = functions.Where(f => f.Name == location.Name).FirstOrDefault(); + var func = functions.FirstOrDefault(f => string.Equals(f.Name, location.Name, StringComparison.OrdinalIgnoreCase)); if (func != null) - foundVar = func.LocalVariables.Where(v => v.Name == varName).FirstOrDefault(); + foundVar = func.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase)); } } @@ -322,15 +320,15 @@ public ZXBasicMap(ZXCodeFile MainFile, IEnumerable AllFiles, string //(to avoid the very unprobable case where the same var is defined in different files in locations that match the same range) if (possibleLocation.LocationType == ZXBasicLocationType.Sub) { - var sub = subs.Where(s => s.Name == possibleLocation.Name).FirstOrDefault(); + var sub = subs.FirstOrDefault(s => string.Equals(s.Name, possibleLocation.Name, StringComparison.OrdinalIgnoreCase)); if (sub != null) - foundVar = sub.LocalVariables.Where(v => v.Name == varName && !v.Unused).FirstOrDefault(); + foundVar = sub.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase) && !v.Unused); } else { - var func = functions.Where(f => f.Name == possibleLocation.Name).FirstOrDefault(); + var func = functions.FirstOrDefault(f => string.Equals(f.Name, possibleLocation.Name, StringComparison.OrdinalIgnoreCase)); if (func != null) - foundVar = func.LocalVariables.Where(v => v.Name == varName && !v.Unused).FirstOrDefault(); + foundVar = func.LocalVariables.FirstOrDefault(v => string.Equals(v.Name, varName, StringComparison.OrdinalIgnoreCase) && !v.Unused); } //If the criteria finds a var, return it @@ -359,11 +357,7 @@ void GetSubVars(ZXBasicSub Sub, string[] Lines) if (varNameDef.Contains("(")) //array { string varName = varNameDef.Substring(0, varNameDef.IndexOf("(")).Trim(); - - //Ignore unused vars (vars that are found only on its dim line, there may be the improbable - //case where a var is defined and used in the same line using a colon and not used - //anywhere else, but that would be an awful code :) ) - if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline))) + if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline))) continue; string[] dims = varNameDef.Substring(varNameDef.IndexOf("(") + 1).Replace(")", "").Split(",", StringSplitOptions.RemoveEmptyEntries); @@ -379,9 +373,7 @@ void GetSubVars(ZXBasicSub Sub, string[] Lines) foreach (var vName in varNames) { string varName = vName.Trim(); - - //Ignore unused vars - if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_]){varName}($|[^a-zA-Z0-9_])", RegexOptions.Multiline))) + if (!Lines.Skip(buc+1).Any(l => Regex.IsMatch(l, $"(^|[^a-zA-Z0-9_$]){Regex.Escape(varName)}($|[^a-zA-Z0-9_$])", RegexOptions.Multiline))) continue; var storage = StorageFromString(dimMatch.Groups[5].Value, varName); @@ -415,7 +407,7 @@ public List GetBuildLocations(ZXCodeFile CodeFile) if (subMatch != null && subMatch.Success) { - loc = new ZXBasicLocation { Name = subMatch.Groups[2].Value.Trim(), LocationType = ZXBasicLocationType.Sub, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) }; + loc = new ZXBasicLocation { Name = subMatch.Groups[4].Value.Trim(), LocationType = ZXBasicLocationType.Sub, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) }; continue; } @@ -423,7 +415,7 @@ public List GetBuildLocations(ZXCodeFile CodeFile) if (funcMatch != null && funcMatch.Success) { - loc = new ZXBasicLocation { Name = funcMatch.Groups[2].Value.Trim(), LocationType = ZXBasicLocationType.Function, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) }; + loc = new ZXBasicLocation { Name = funcMatch.Groups[4].Value.Trim(), LocationType = ZXBasicLocationType.Function, FirstLine = buc, File = Path.Combine(CodeFile.Directory, CodeFile.TempFileName) }; continue; } } @@ -465,7 +457,7 @@ public bool ContainsBuildDim(ZXCodeFile CodeFile, string VarName, int LineNumber if (LineNumber >= lines.Length) return false; - return Regex.IsMatch(lines[LineNumber], $"(\\s|,){VarName}(\\s|,|\\(|$)", RegexOptions.Multiline); + return Regex.IsMatch(lines[LineNumber], $"(\\s|,){Regex.Escape(VarName)}(\\s|,|\\(|$)", RegexOptions.Multiline); } private static void ParseInputParameters(string ParameterString, List Storage) diff --git a/ZXBStudio/BuildSystem/ZXVariableMap.cs b/ZXBStudio/BuildSystem/ZXVariableMap.cs index 4ac7227..2953a09 100644 --- a/ZXBStudio/BuildSystem/ZXVariableMap.cs +++ b/ZXBStudio/BuildSystem/ZXVariableMap.cs @@ -60,7 +60,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic string varName = m.Groups[1].Value; string bVarName = varName.Substring(1); - var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => v.Name == bVarName); + var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => string.Equals(v.Name.TrimEnd('$'), bVarName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase)); if (basicVar == null) continue; @@ -78,7 +78,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic ZXVariable newVar = new ZXVariable { - Name = bVarName, + Name = basicVar.Name, Address = new ZXVariableAddress { AddressType = ZXVariableAddressType.Absolute, AddressValue = addr }, Scope = ZXVariableScope.GlobalScope, VariableType = ZXVariableType.Flat, @@ -98,7 +98,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic string bVarName = varName.Substring(1); - var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => v.Name == bVarName); + var basicVar = BasicMap.GlobalVariables.FirstOrDefault(v => string.Equals(v.Name.TrimEnd('$'), bVarName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase)); if (basicVar == null) continue; @@ -133,7 +133,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic ZXVariable newVar = new ZXVariable { - Name = bVarName, + Name = basicVar.Name, Address = new ZXVariableAddress { AddressType = ZXVariableAddressType.Absolute, AddressValue = addr }, Scope = ZXVariableScope.GlobalScope, VariableType = ZXVariableType.Array, @@ -179,10 +179,9 @@ private void ProcessLocalVariables(string icContent, string mapContent, ZXBasicM ZXVariableScope currentScope = new ZXVariableScope { ScopeName = locName, StartAddress = startAddr, EndAddress = endAddr }; - ZXBasicSub? sub = BasicMap.Subs.Where(m => m.Name == locName).FirstOrDefault(); - + ZXBasicSub? sub = BasicMap.Subs.FirstOrDefault(m => string.Equals(m.Name.TrimEnd('$'), locName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase)); if (sub == null) - sub = BasicMap.Functions.Where(m => m.Name == locName).FirstOrDefault(); + sub = BasicMap.Functions.FirstOrDefault(m => string.Equals(m.Name.TrimEnd('$'), locName.TrimEnd('$'), StringComparison.OrdinalIgnoreCase)); //Function params if (sub != null) From ff2972e3e0d16a11968042295790d90d3977cc0c Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 29 Mar 2026 11:28:00 +0200 Subject: [PATCH 2/4] build: add tmp/ to .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9491a2f..322ad2a 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,7 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +# tmp/ dir +tmp/ From 9dd517cd36ad8fe7fce2ac7e4c421f0449ea4d7e Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 29 Mar 2026 11:49:53 +0200 Subject: [PATCH 3/4] tests: add tests for ZXBStudio --- ZXBStudio/BuildSystem/ZXVariableMap.cs | 4 +- ZXBStudio/ZXBasicStudio.csproj | 4 + ZXBasicStudioTest/SigilMappingTests.cs | 123 +++++++++++++++++++++ ZXBasicStudioTest/UnitTest1.cs | 14 --- ZXBasicStudioTest/UsageDetectionTests.cs | 64 +++++++++++ ZXBasicStudioTest/ZXBasicStudioTest.csproj | 6 + 6 files changed, 199 insertions(+), 16 deletions(-) create mode 100644 ZXBasicStudioTest/SigilMappingTests.cs delete mode 100644 ZXBasicStudioTest/UnitTest1.cs create mode 100644 ZXBasicStudioTest/UsageDetectionTests.cs diff --git a/ZXBStudio/BuildSystem/ZXVariableMap.cs b/ZXBStudio/BuildSystem/ZXVariableMap.cs index 2953a09..8af652c 100644 --- a/ZXBStudio/BuildSystem/ZXVariableMap.cs +++ b/ZXBStudio/BuildSystem/ZXVariableMap.cs @@ -42,7 +42,7 @@ public ZXVariableMap(string ICFile, string MapFile, ZXBasicMap BasicMap) } - private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasicMap BasicMap) + internal void ProcessGlobalVariables(string icContent, string mapContent, ZXBasicMap BasicMap) { int splitIndex = icContent.IndexOf("--- end of user code ---"); @@ -144,7 +144,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic } } - private void ProcessLocalVariables(string icContent, string mapContent, ZXBasicMap BasicMap) + internal void ProcessLocalVariables(string icContent, string mapContent, ZXBasicMap BasicMap) { int splitIndex = icContent.IndexOf("--- end of user code ---"); diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index 0edf91e..96fcd5e 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -15,6 +15,10 @@ False 1.7.0.0 + + + + diff --git a/ZXBasicStudioTest/SigilMappingTests.cs b/ZXBasicStudioTest/SigilMappingTests.cs new file mode 100644 index 0000000..225ff79 --- /dev/null +++ b/ZXBasicStudioTest/SigilMappingTests.cs @@ -0,0 +1,123 @@ +using Xunit; +using FluentAssertions; +using ZXBasicStudio.BuildSystem; +using System.Runtime.Serialization; +using System.Collections.Generic; +using System.Linq; + +namespace ZXBasicStudioTest +{ + public class SigilMappingTests + { + [Fact] + public void GetVariables_ShouldMatchSigilVariable() + { + // Arrange + // We use GetUninitializedObject to skip the constructor which depends on files + var basicMap = (ZXBasicMap)FormatterServices.GetUninitializedObject(typeof(ZXBasicMap)); + + basicMap.GlobalVariables = new[] + { + new ZXBasicVariable { Name = "a$" } + }; + basicMap.Subs = new ZXBasicSub[0]; + basicMap.Functions = new ZXBasicFunction[0]; + basicMap.BuildLocations = new ZXBasicLocation[0]; + + // IC Content mimicking a global variable '_a' + string icContent = "--- end of user code ---\n('var', '_a', '0')"; + // Map content mimicking the same variable + string mapContent = "8000: ._a"; + + // Act + var variableMap = (ZXVariableMap)FormatterServices.GetUninitializedObject(typeof(ZXVariableMap)); + // We need to initialize the private 'vars' list since we used GetUninitializedObject + var varsField = typeof(ZXVariableMap).GetField("vars", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + varsField!.SetValue(variableMap, new List()); + + variableMap.ProcessGlobalVariables(icContent, mapContent, basicMap); + var variables = variableMap.Variables; + + // Assert + variables.Should().NotBeNull(); + var variable = variables.FirstOrDefault(v => v.Name == "a$"); + variable.Should().NotBeNull("Variable 'a$' should be found even if IC uses '_a'"); + variable!.Address.AddressValue.Should().Be(0x8000); + } + + [Fact] + public void GetVariables_ShouldBeCaseInsensitive() + { + // Arrange + var basicMap = (ZXBasicMap)FormatterServices.GetUninitializedObject(typeof(ZXBasicMap)); + + basicMap.GlobalVariables = new[] + { + new ZXBasicVariable { Name = "MyVar$" } + }; + basicMap.Subs = new ZXBasicSub[0]; + basicMap.Functions = new ZXBasicFunction[0]; + basicMap.BuildLocations = new ZXBasicLocation[0]; + + // IC Content uses lowercase '_myvar' + string icContent = "--- end of user code ---\n('var', '_myvar', '0')"; + string mapContent = "9000: ._myvar"; + + // Act + var variableMap = (ZXVariableMap)FormatterServices.GetUninitializedObject(typeof(ZXVariableMap)); + var varsField = typeof(ZXVariableMap).GetField("vars", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + varsField!.SetValue(variableMap, new List()); + + variableMap.ProcessGlobalVariables(icContent, mapContent, basicMap); + var variables = variableMap.Variables; + + // Assert + var variable = variables.FirstOrDefault(v => v.Name == "MyVar$"); + variable.Should().NotBeNull("Variable 'MyVar$' should be matched case-insensitively"); + variable!.Address.AddressValue.Should().Be(0x9000); + } + + [Fact] + public void ProcessLocalVariables_ShouldMatchSubNameWithSigil() + { + // Arrange + var basicMap = (ZXBasicMap)FormatterServices.GetUninitializedObject(typeof(ZXBasicMap)); + + var sub = new ZXBasicSub { Name = "MySub$" }; + sub.LocalVariables = new List(); + sub.InputParameters = new List + { + new ZXBasicParameter { Name = "param1", Offset = -2, Storage = ZXVariableStorage.U16 } + }; + + basicMap.GlobalVariables = new ZXBasicVariable[0]; + basicMap.Subs = new[] { sub }; + basicMap.Functions = new ZXBasicFunction[0]; + basicMap.BuildLocations = new[] + { + new ZXBasicLocation { Name = "MySub$", LocationType = ZXBasicLocationType.Sub, FirstLine = 0, LastLine = 10, File = "main.bas" } + }; + + // IC Content showing start and end of MySub (sigil is stripped in label usually: _MySub) + // Note: ProcessLocalVariables extracts locName = label.Substring(1) from '_MySub' -> 'MySub' + string icContent = "('label', '_MySub')\n('label', '_MySub__leave')\n--- end of user code ---"; + // Map content showing start and end addresses + string mapContent = "8000: ._MySub\n8010: ._MySub__leave"; + + // Act + var variableMap = (ZXVariableMap)FormatterServices.GetUninitializedObject(typeof(ZXVariableMap)); + var varsField = typeof(ZXVariableMap).GetField("vars", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + varsField!.SetValue(variableMap, new List()); + + variableMap.ProcessLocalVariables(icContent, mapContent, basicMap); + var variables = variableMap.Variables; + + // Assert + variables.Should().NotBeNull(); + // If MySub$ was matched correctly, its parameters should be added + variables.Should().Contain(v => v.Name == "param1", "Sub MySub$ should be matched to label _MySub and its parameters processed"); + var param = variables.First(v => v.Name == "param1"); + param.Scope.ScopeName.Should().Be("MySub"); + } + } +} diff --git a/ZXBasicStudioTest/UnitTest1.cs b/ZXBasicStudioTest/UnitTest1.cs deleted file mode 100644 index e6a0eb5..0000000 --- a/ZXBasicStudioTest/UnitTest1.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace ZXBasicStudioTest -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - int a = 10; - int b = 20; - - Assert.NotEqual(a, b); - } - } -} \ No newline at end of file diff --git a/ZXBasicStudioTest/UsageDetectionTests.cs b/ZXBasicStudioTest/UsageDetectionTests.cs new file mode 100644 index 0000000..96fb24c --- /dev/null +++ b/ZXBasicStudioTest/UsageDetectionTests.cs @@ -0,0 +1,64 @@ +using Xunit; +using FluentAssertions; +using ZXBasicStudio.BuildSystem; +using System.IO; +using System.Collections.Generic; +using System.Linq; + +namespace ZXBasicStudioTest +{ + public class UsageDetectionTests + { + [Fact] + public void ZXBasicMap_ShouldDetectSigilVariableUsage() + { + // Arrange + string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + string mainFileContent = "dim a$ as string\na$ = \"Hello\"\nprint a$"; + string mainPath = Path.Combine(tempDir, "main.bas"); + File.WriteAllText(mainPath, mainFileContent); + + var mainCodeFile = new ZXCodeFile(mainPath); + var allFiles = new List { mainCodeFile }; + string buildLog = ""; // No unused warnings for now + + // Act + var basicMap = new ZXBasicMap(mainCodeFile, allFiles, buildLog); + + // Assert + basicMap.GlobalVariables.Should().Contain(v => v.Name == "a$"); + var varA = basicMap.GlobalVariables.First(v => v.Name == "a$"); + varA.Unused.Should().BeFalse("a$ is used later in the code"); + + // Cleanup + Directory.Delete(tempDir, true); + } + + [Fact] + public void ZXBasicMap_ShouldDistinguishSigilAndNonSigilVariables() + { + // Arrange + string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + // a is used, a$ is NOT used (only defined) + string mainFileContent = "dim a as integer\ndim a$ as string\na = 10\nprint a"; + string mainPath = Path.Combine(tempDir, "main.bas"); + File.WriteAllText(mainPath, mainFileContent); + + var mainCodeFile = new ZXCodeFile(mainPath); + var allFiles = new List { mainCodeFile }; + string buildLog = ""; + + // Act + var basicMap = new ZXBasicMap(mainCodeFile, allFiles, buildLog); + + // Assert + basicMap.GlobalVariables.Should().Contain(v => v.Name == "a"); + basicMap.GlobalVariables.Should().NotContain(v => v.Name == "a$", "a$ is NOT used, so it should be skipped (if it's not marked as unused in buildLog, the local regex check should mark it as unused and it's skipped in GlobalVariables list generation)"); + + // Cleanup + Directory.Delete(tempDir, true); + } + } +} diff --git a/ZXBasicStudioTest/ZXBasicStudioTest.csproj b/ZXBasicStudioTest/ZXBasicStudioTest.csproj index 9b37b01..8e57e39 100644 --- a/ZXBasicStudioTest/ZXBasicStudioTest.csproj +++ b/ZXBasicStudioTest/ZXBasicStudioTest.csproj @@ -16,12 +16,18 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + From 674631b512a4bd0c59e7ea5709c7a949724b86f4 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sun, 29 Mar 2026 12:21:24 +0200 Subject: [PATCH 4/4] build: add tests --- .github/workflows/build-zxbstudio.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-zxbstudio.yml b/.github/workflows/build-zxbstudio.yml index 34ec5d1..8b9d49e 100644 --- a/.github/workflows/build-zxbstudio.yml +++ b/.github/workflows/build-zxbstudio.yml @@ -29,6 +29,9 @@ jobs: - name: Build project run: dotnet build ZXBasicStudio.sln --configuration Release --no-restore + + - name: Run tests + run: dotnet test - name: Publish for Linux run: |