Skip to content

Commit 8b6ebb6

Browse files
committed
tests: add tests for ZXBStudio
1 parent 61feaef commit 8b6ebb6

6 files changed

Lines changed: 199 additions & 16 deletions

File tree

ZXBStudio/BuildSystem/ZXVariableMap.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public ZXVariableMap(string ICFile, string MapFile, ZXBasicMap BasicMap)
4242
}
4343

4444

45-
private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
45+
internal void ProcessGlobalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
4646
{
4747
int splitIndex = icContent.IndexOf("--- end of user code ---");
4848

@@ -144,7 +144,7 @@ private void ProcessGlobalVariables(string icContent, string mapContent, ZXBasic
144144
}
145145
}
146146

147-
private void ProcessLocalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
147+
internal void ProcessLocalVariables(string icContent, string mapContent, ZXBasicMap BasicMap)
148148
{
149149
int splitIndex = icContent.IndexOf("--- end of user code ---");
150150

ZXBStudio/ZXBasicStudio.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
<Deterministic>False</Deterministic>
1616
<Version>1.7.0.0</Version>
1717
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<InternalsVisibleTo Include="ZXBasicStudioTest" />
21+
</ItemGroup>
1822
<ItemGroup>
1923
<AvaloniaXaml Remove="LanguageDefinitions\**" />
2024
<Compile Remove="LanguageDefinitions\**" />
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using Xunit;
2+
using FluentAssertions;
3+
using ZXBasicStudio.BuildSystem;
4+
using System.Runtime.Serialization;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
8+
namespace ZXBasicStudioTest
9+
{
10+
public class SigilMappingTests
11+
{
12+
[Fact]
13+
public void GetVariables_ShouldMatchSigilVariable()
14+
{
15+
// Arrange
16+
// We use GetUninitializedObject to skip the constructor which depends on files
17+
var basicMap = (ZXBasicMap)FormatterServices.GetUninitializedObject(typeof(ZXBasicMap));
18+
19+
basicMap.GlobalVariables = new[]
20+
{
21+
new ZXBasicVariable { Name = "a$" }
22+
};
23+
basicMap.Subs = new ZXBasicSub[0];
24+
basicMap.Functions = new ZXBasicFunction[0];
25+
basicMap.BuildLocations = new ZXBasicLocation[0];
26+
27+
// IC Content mimicking a global variable '_a'
28+
string icContent = "--- end of user code ---\n('var', '_a', '0')";
29+
// Map content mimicking the same variable
30+
string mapContent = "8000: ._a";
31+
32+
// Act
33+
var variableMap = (ZXVariableMap)FormatterServices.GetUninitializedObject(typeof(ZXVariableMap));
34+
// We need to initialize the private 'vars' list since we used GetUninitializedObject
35+
var varsField = typeof(ZXVariableMap).GetField("vars", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
36+
varsField!.SetValue(variableMap, new List<ZXVariable>());
37+
38+
variableMap.ProcessGlobalVariables(icContent, mapContent, basicMap);
39+
var variables = variableMap.Variables;
40+
41+
// Assert
42+
variables.Should().NotBeNull();
43+
var variable = variables.FirstOrDefault(v => v.Name == "a$");
44+
variable.Should().NotBeNull("Variable 'a$' should be found even if IC uses '_a'");
45+
variable!.Address.AddressValue.Should().Be(0x8000);
46+
}
47+
48+
[Fact]
49+
public void GetVariables_ShouldBeCaseInsensitive()
50+
{
51+
// Arrange
52+
var basicMap = (ZXBasicMap)FormatterServices.GetUninitializedObject(typeof(ZXBasicMap));
53+
54+
basicMap.GlobalVariables = new[]
55+
{
56+
new ZXBasicVariable { Name = "MyVar$" }
57+
};
58+
basicMap.Subs = new ZXBasicSub[0];
59+
basicMap.Functions = new ZXBasicFunction[0];
60+
basicMap.BuildLocations = new ZXBasicLocation[0];
61+
62+
// IC Content uses lowercase '_myvar'
63+
string icContent = "--- end of user code ---\n('var', '_myvar', '0')";
64+
string mapContent = "9000: ._myvar";
65+
66+
// Act
67+
var variableMap = (ZXVariableMap)FormatterServices.GetUninitializedObject(typeof(ZXVariableMap));
68+
var varsField = typeof(ZXVariableMap).GetField("vars", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
69+
varsField!.SetValue(variableMap, new List<ZXVariable>());
70+
71+
variableMap.ProcessGlobalVariables(icContent, mapContent, basicMap);
72+
var variables = variableMap.Variables;
73+
74+
// Assert
75+
var variable = variables.FirstOrDefault(v => v.Name == "MyVar$");
76+
variable.Should().NotBeNull("Variable 'MyVar$' should be matched case-insensitively");
77+
variable!.Address.AddressValue.Should().Be(0x9000);
78+
}
79+
80+
[Fact]
81+
public void ProcessLocalVariables_ShouldMatchSubNameWithSigil()
82+
{
83+
// Arrange
84+
var basicMap = (ZXBasicMap)FormatterServices.GetUninitializedObject(typeof(ZXBasicMap));
85+
86+
var sub = new ZXBasicSub { Name = "MySub$" };
87+
sub.LocalVariables = new List<ZXBasicVariable>();
88+
sub.InputParameters = new List<ZXBasicParameter>
89+
{
90+
new ZXBasicParameter { Name = "param1", Offset = -2, Storage = ZXVariableStorage.U16 }
91+
};
92+
93+
basicMap.GlobalVariables = new ZXBasicVariable[0];
94+
basicMap.Subs = new[] { sub };
95+
basicMap.Functions = new ZXBasicFunction[0];
96+
basicMap.BuildLocations = new[]
97+
{
98+
new ZXBasicLocation { Name = "MySub$", LocationType = ZXBasicLocationType.Sub, FirstLine = 0, LastLine = 10, File = "main.bas" }
99+
};
100+
101+
// IC Content showing start and end of MySub (sigil is stripped in label usually: _MySub)
102+
// Note: ProcessLocalVariables extracts locName = label.Substring(1) from '_MySub' -> 'MySub'
103+
string icContent = "('label', '_MySub')\n('label', '_MySub__leave')\n--- end of user code ---";
104+
// Map content showing start and end addresses
105+
string mapContent = "8000: ._MySub\n8010: ._MySub__leave";
106+
107+
// Act
108+
var variableMap = (ZXVariableMap)FormatterServices.GetUninitializedObject(typeof(ZXVariableMap));
109+
var varsField = typeof(ZXVariableMap).GetField("vars", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
110+
varsField!.SetValue(variableMap, new List<ZXVariable>());
111+
112+
variableMap.ProcessLocalVariables(icContent, mapContent, basicMap);
113+
var variables = variableMap.Variables;
114+
115+
// Assert
116+
variables.Should().NotBeNull();
117+
// If MySub$ was matched correctly, its parameters should be added
118+
variables.Should().Contain(v => v.Name == "param1", "Sub MySub$ should be matched to label _MySub and its parameters processed");
119+
var param = variables.First(v => v.Name == "param1");
120+
param.Scope.ScopeName.Should().Be("MySub");
121+
}
122+
}
123+
}

ZXBasicStudioTest/UnitTest1.cs

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Xunit;
2+
using FluentAssertions;
3+
using ZXBasicStudio.BuildSystem;
4+
using System.IO;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
8+
namespace ZXBasicStudioTest
9+
{
10+
public class UsageDetectionTests
11+
{
12+
[Fact]
13+
public void ZXBasicMap_ShouldDetectSigilVariableUsage()
14+
{
15+
// Arrange
16+
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
17+
Directory.CreateDirectory(tempDir);
18+
string mainFileContent = "dim a$ as string\na$ = \"Hello\"\nprint a$";
19+
string mainPath = Path.Combine(tempDir, "main.bas");
20+
File.WriteAllText(mainPath, mainFileContent);
21+
22+
var mainCodeFile = new ZXCodeFile(mainPath);
23+
var allFiles = new List<ZXCodeFile> { mainCodeFile };
24+
string buildLog = ""; // No unused warnings for now
25+
26+
// Act
27+
var basicMap = new ZXBasicMap(mainCodeFile, allFiles, buildLog);
28+
29+
// Assert
30+
basicMap.GlobalVariables.Should().Contain(v => v.Name == "a$");
31+
var varA = basicMap.GlobalVariables.First(v => v.Name == "a$");
32+
varA.Unused.Should().BeFalse("a$ is used later in the code");
33+
34+
// Cleanup
35+
Directory.Delete(tempDir, true);
36+
}
37+
38+
[Fact]
39+
public void ZXBasicMap_ShouldDistinguishSigilAndNonSigilVariables()
40+
{
41+
// Arrange
42+
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
43+
Directory.CreateDirectory(tempDir);
44+
// a is used, a$ is NOT used (only defined)
45+
string mainFileContent = "dim a as integer\ndim a$ as string\na = 10\nprint a";
46+
string mainPath = Path.Combine(tempDir, "main.bas");
47+
File.WriteAllText(mainPath, mainFileContent);
48+
49+
var mainCodeFile = new ZXCodeFile(mainPath);
50+
var allFiles = new List<ZXCodeFile> { mainCodeFile };
51+
string buildLog = "";
52+
53+
// Act
54+
var basicMap = new ZXBasicMap(mainCodeFile, allFiles, buildLog);
55+
56+
// Assert
57+
basicMap.GlobalVariables.Should().Contain(v => v.Name == "a");
58+
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)");
59+
60+
// Cleanup
61+
Directory.Delete(tempDir, true);
62+
}
63+
}
64+
}

ZXBasicStudioTest/ZXBasicStudioTest.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@
1616
</PackageReference>
1717
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
1818
<PackageReference Include="xunit" Version="2.9.3" />
19+
<PackageReference Include="Moq" Version="4.20.72" />
20+
<PackageReference Include="FluentAssertions" Version="8.0.1" />
1921
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
2022
<PrivateAssets>all</PrivateAssets>
2123
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2224
</PackageReference>
2325
</ItemGroup>
2426

27+
<ItemGroup>
28+
<ProjectReference Include="..\ZXBStudio\ZXBasicStudio.csproj" />
29+
</ItemGroup>
30+
2531
<ItemGroup>
2632
<Using Include="Xunit" />
2733
</ItemGroup>

0 commit comments

Comments
 (0)