Skip to content

Commit 157e334

Browse files
marcin-golebiowskiCopilot
andcommitted
Add unit tests for LibProcessor, SpiceToken extensions, BaseWriter, CapacitorWriter, and ResistorWriter; improve string comparison and parameter handling
Co-authored-by: Copilot <copilot@github.com>
1 parent c677abf commit 157e334

10 files changed

Lines changed: 354 additions & 9 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using NSubstitute;
2+
using SpiceSharpParser.Common;
3+
using SpiceSharpParser.Common.FileSystem;
4+
using SpiceSharpParser.Common.Processors;
5+
using SpiceSharpParser.Common.Validation;
6+
using SpiceSharpParser.Lexers.Netlist.Spice;
7+
using SpiceSharpParser.Models.Netlist.Spice;
8+
using SpiceSharpParser.Models.Netlist.Spice.Objects;
9+
using SpiceSharpParser.Models.Netlist.Spice.Objects.Parameters;
10+
using SpiceSharpParser.Parsers.Netlist.Spice;
11+
using System.Collections.Generic;
12+
using System.IO;
13+
using Xunit;
14+
15+
namespace SpiceSharpParser.Tests.Common.Processors
16+
{
17+
public class LibProcessorTests
18+
{
19+
[Fact]
20+
public void When_SelectedLibHasNoEndl_Expect_ParserException()
21+
{
22+
using var fixture = CreateFixture(CreateLibStatements(includeEndl: false));
23+
24+
Assert.Throws<SpiceSharpParserException>(() => fixture.Processor.Process(CreateRequestStatements(fixture.LibPath)));
25+
}
26+
27+
[Fact]
28+
public void When_SelectedLibHasEndl_Expect_EndlIsNotIncludedInReplacement()
29+
{
30+
var includedComponent = CreateComponent("R1");
31+
using var fixture = CreateFixture(CreateLibStatements(includeEndl: true, includedComponent));
32+
var requestStatements = CreateRequestStatements(fixture.LibPath);
33+
34+
fixture.Processor.Process(requestStatements);
35+
36+
var statement = Assert.Single(requestStatements);
37+
Assert.Same(includedComponent, statement);
38+
}
39+
40+
private static ProcessorFixture CreateFixture(Statements includeStatements)
41+
{
42+
var libPath = Path.GetTempFileName();
43+
File.WriteAllText(libPath, "unused");
44+
45+
var fileReader = Substitute.For<IFileReader>();
46+
fileReader.ReadAll(Arg.Any<string>()).Returns("unused");
47+
48+
var tokens = new[] { new SpiceToken(SpiceTokenType.EOF, string.Empty) };
49+
var tokenProvider = Substitute.For<ISpiceTokenProvider>();
50+
tokenProvider.GetTokens("unused").Returns(tokens);
51+
52+
var tokenProviderPool = Substitute.For<ISpiceTokenProviderPool>();
53+
tokenProviderPool.GetSpiceTokenProvider(Arg.Any<SpiceLexerSettings>()).Returns(tokenProvider);
54+
55+
var parser = Substitute.For<ISingleSpiceNetlistParser>();
56+
parser.Parse(tokens).Returns(new SpiceNetlist(string.Empty, includeStatements));
57+
58+
var includesProcessor = Substitute.For<IProcessor>();
59+
var processor = new LibProcessor(
60+
fileReader,
61+
tokenProviderPool,
62+
parser,
63+
includesProcessor,
64+
() => Path.GetDirectoryName(libPath),
65+
new SpiceLexerSettings(false))
66+
{
67+
Validation = new ValidationEntryCollection(),
68+
};
69+
70+
return new ProcessorFixture(libPath, processor);
71+
}
72+
73+
private static Statements CreateRequestStatements(string libPath)
74+
{
75+
var statements = new Statements();
76+
statements.Add(new Control(
77+
"lib",
78+
new ParameterCollection(
79+
new List<Parameter>()
80+
{
81+
new WordParameter(libPath),
82+
new WordParameter("entry"),
83+
}),
84+
null));
85+
86+
return statements;
87+
}
88+
89+
private static Statements CreateLibStatements(bool includeEndl, Component includedComponent = null)
90+
{
91+
var statements = new Statements();
92+
statements.Add(new Control(
93+
"lib",
94+
new ParameterCollection(new List<Parameter>() { new WordParameter("entry") }),
95+
null));
96+
statements.Add(includedComponent ?? CreateComponent("R1"));
97+
98+
if (includeEndl)
99+
{
100+
statements.Add(new Control("endl", new ParameterCollection(), null));
101+
}
102+
103+
return statements;
104+
}
105+
106+
private static Component CreateComponent(string name)
107+
{
108+
return new Component(
109+
name,
110+
new ParameterCollection(
111+
new List<Parameter>()
112+
{
113+
new WordParameter("in"),
114+
new WordParameter("gnd"),
115+
new ValueParameter("1k"),
116+
}),
117+
null);
118+
}
119+
120+
private sealed class ProcessorFixture : System.IDisposable
121+
{
122+
public ProcessorFixture(string libPath, LibProcessor processor)
123+
{
124+
this.LibPath = libPath;
125+
this.Processor = processor;
126+
}
127+
128+
public string LibPath { get; }
129+
130+
public LibProcessor Processor { get; }
131+
132+
public void Dispose()
133+
{
134+
if (File.Exists(this.LibPath))
135+
{
136+
File.Delete(this.LibPath);
137+
}
138+
}
139+
}
140+
}
141+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using SpiceSharpParser.Lexers.Netlist.Spice;
2+
using System.Globalization;
3+
using System.Threading;
4+
using Xunit;
5+
6+
namespace SpiceSharpParser.Tests.Lexers.Spice
7+
{
8+
public class SpiceTokenExtensionsTests
9+
{
10+
[Fact]
11+
public void When_CurrentCultureIsTurkish_Expect_CaseInsensitiveComparisonIsOrdinal()
12+
{
13+
var previousCulture = Thread.CurrentThread.CurrentCulture;
14+
15+
try
16+
{
17+
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR");
18+
19+
var token = new SpiceToken(SpiceTokenType.WORD, "distribution");
20+
21+
Assert.True(token.Equal("DISTRIBUTION", false));
22+
}
23+
finally
24+
{
25+
Thread.CurrentThread.CurrentCulture = previousCulture;
26+
}
27+
}
28+
}
29+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using SpiceSharpParser.Common;
2+
using SpiceSharpParser.ModelWriters.CSharp;
3+
using SpiceSharpParser.Parsers.Expression;
4+
using System.Globalization;
5+
using System.Threading;
6+
using Xunit;
7+
8+
namespace SpiceSharpParser.Tests.ModelWriters
9+
{
10+
public class BaseWriterTests
11+
{
12+
[Fact]
13+
public void When_CurrentCultureUsesCommaDecimal_Expect_EvaluatedParameterUsesInvariantCulture()
14+
{
15+
var previousCulture = Thread.CurrentThread.CurrentCulture;
16+
17+
try
18+
{
19+
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("pl-PL");
20+
21+
var writer = new BaseWriter();
22+
var statement = Assert.IsType<CSharpCallStatement>(writer.SetParameter("r1", "resistance", "1.25", CreateContext()));
23+
24+
Assert.Equal(@"SetParameter(""resistance"", 1.25d)", statement.CallExpression);
25+
}
26+
finally
27+
{
28+
Thread.CurrentThread.CurrentCulture = previousCulture;
29+
}
30+
}
31+
32+
[Fact]
33+
public void When_CurrentCultureUsesCommaDecimal_Expect_DoubleParameterUsesInvariantCulture()
34+
{
35+
var previousCulture = Thread.CurrentThread.CurrentCulture;
36+
37+
try
38+
{
39+
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("pl-PL");
40+
41+
var writer = new BaseWriter();
42+
var statement = Assert.IsType<CSharpCallStatement>(writer.SetParameter("r1", "resistance", 1.25, null));
43+
44+
Assert.Equal(@"SetParameter(""resistance"", 1.25d)", statement.CallExpression);
45+
}
46+
finally
47+
{
48+
Thread.CurrentThread.CurrentCulture = previousCulture;
49+
}
50+
}
51+
52+
private static WriterContext CreateContext()
53+
{
54+
var parser = new ExpressionParser(
55+
new SpiceSharpBehavioral.Builders.Direct.RealBuilder(),
56+
false);
57+
58+
return new WriterContext()
59+
{
60+
CaseSettings = new SpiceNetlistCaseSensitivitySettings(),
61+
EvaluationContext = new EvaluationContext(parser),
62+
};
63+
}
64+
}
65+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using SpiceSharpParser.Common;
2+
using SpiceSharpParser.Models.Netlist.Spice.Objects;
3+
using SpiceSharpParser.Models.Netlist.Spice.Objects.Parameters;
4+
using SpiceSharpParser.ModelWriters.CSharp;
5+
using SpiceSharpParser.Parsers.Expression;
6+
using System.Linq;
7+
using Xunit;
8+
9+
namespace SpiceSharpParser.Tests.ModelWriters
10+
{
11+
public class CapacitorWriterTests
12+
{
13+
[Fact]
14+
public void When_ExpressionUsesCircuitQuantity_Expect_OnlyBehavioralCapacitor()
15+
{
16+
var component = new Component(
17+
"C1",
18+
new ParameterCollection(
19+
new System.Collections.Generic.List<Parameter>()
20+
{
21+
new WordParameter("in"),
22+
new WordParameter("gnd"),
23+
new ExpressionParameter("V(in)", null),
24+
}),
25+
lineInfo: null);
26+
27+
var writer = new SpiceSharpParser.ModelWriters.CSharp.Entities.Components.CapacitorWriter();
28+
29+
var lines = writer.Write(component, CreateContext());
30+
31+
var newStatement = Assert.Single(lines.OfType<CSharpNewStatement>());
32+
Assert.Contains("new BehavioralCapacitor", newStatement.NewExpression);
33+
}
34+
35+
private static WriterContext CreateContext()
36+
{
37+
var parser = new ExpressionParser(
38+
new SpiceSharpBehavioral.Builders.Direct.RealBuilder(),
39+
false);
40+
41+
return new WriterContext()
42+
{
43+
CaseSettings = new SpiceNetlistCaseSensitivitySettings(),
44+
EvaluationContext = new EvaluationContext(parser),
45+
};
46+
}
47+
}
48+
}

src/SpiceSharpParser.Tests/ModelWriters/ResistorWriterTests.cs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using SpiceSharpParser.Common;
2+
using SpiceSharpParser.Models.Netlist.Spice.Objects;
23
using SpiceSharpParser.Models.Netlist.Spice.Objects.Parameters;
34
using SpiceSharpParser.ModelWriters.CSharp;
4-
using SpiceSharpParser.ModelWriters.CSharp.Entities.Components;
5-
using System.Linq;
65
using SpiceSharpParser.Parsers.Expression;
6+
using System.Linq;
77
using Xunit;
88

99
namespace SpiceSharpParser.Tests.ModelWriters
@@ -25,7 +25,7 @@ public void Test01()
2525
}),
2626
lineInfo: null);
2727

28-
var writer = new ResistorWriter();
28+
var writer = new SpiceSharpParser.ModelWriters.CSharp.Entities.Components.ResistorWriter();
2929

3030
var parser = new ExpressionParser(
3131
new SpiceSharpBehavioral.Builders.Direct.RealBuilder(),
@@ -39,5 +39,65 @@ public void Test01()
3939

4040
Assert.True(lines.Any());
4141
}
42+
43+
[Fact]
44+
public void When_ExpressionUsesCircuitQuantity_Expect_OnlyBehavioralResistor()
45+
{
46+
var component = new Component(
47+
"R1",
48+
new ParameterCollection(
49+
new System.Collections.Generic.List<Parameter>()
50+
{
51+
new WordParameter("in"),
52+
new WordParameter("gnd"),
53+
new ExpressionParameter("V(in)", null),
54+
}),
55+
lineInfo: null);
56+
57+
var writer = new SpiceSharpParser.ModelWriters.CSharp.Entities.Components.ResistorWriter();
58+
59+
var lines = writer.Write(component, CreateContext());
60+
61+
var newStatement = Assert.Single(lines.OfType<CSharpNewStatement>());
62+
Assert.Contains("new BehavioralResistor", newStatement.NewExpression);
63+
}
64+
65+
[Fact]
66+
public void When_ResistorUsesModel_Expect_ModelNameWithoutExtraParenthesis()
67+
{
68+
var component = new Component(
69+
"R1",
70+
new ParameterCollection(
71+
new System.Collections.Generic.List<Parameter>()
72+
{
73+
new WordParameter("in"),
74+
new WordParameter("gnd"),
75+
new WordParameter("RMOD"),
76+
}),
77+
lineInfo: null);
78+
79+
var context = CreateContext();
80+
context.RegisterModelType("RMOD", "R");
81+
82+
var writer = new SpiceSharpParser.ModelWriters.CSharp.Entities.Components.ResistorWriter();
83+
84+
var lines = writer.Write(component, context);
85+
86+
var newStatement = Assert.Single(lines.OfType<CSharpNewStatement>());
87+
Assert.Equal(@"new Resistor(""R1"", ""in"", ""gnd"", ""RMOD"")", newStatement.NewExpression);
88+
}
89+
90+
private static WriterContext CreateContext()
91+
{
92+
var parser = new ExpressionParser(
93+
new SpiceSharpBehavioral.Builders.Direct.RealBuilder(),
94+
false);
95+
96+
return new WriterContext()
97+
{
98+
CaseSettings = new SpiceNetlistCaseSensitivitySettings(),
99+
EvaluationContext = new EvaluationContext(parser),
100+
};
101+
}
42102
}
43103
}

src/SpiceSharpParser/Common/Processors/LibProcessor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ private static void ReadSingleLibWithTwoArguments(Statements statements, Control
102102
int position = allStatements.IndexOf(libEntry);
103103
int libPosition = position;
104104

105-
for (; !(allStatements[position] is Control c && c.Name.ToLower() == "endl") && position < allStatements.Count; position++)
105+
for (; position < allStatements.Count && !(allStatements[position] is Control c && c.Name.Equals("endl", StringComparison.OrdinalIgnoreCase)); position++)
106106
{
107107
// iterate to find position
108108
}
@@ -113,7 +113,7 @@ private static void ReadSingleLibWithTwoArguments(Statements statements, Control
113113
}
114114
else
115115
{
116-
var libStatements = allStatements.Skip(libPosition + 1).Take(position - libPosition);
116+
var libStatements = allStatements.Skip(libPosition + 1).Take(position - libPosition - 1);
117117
statements.Replace(lib, libStatements);
118118
}
119119
}

0 commit comments

Comments
 (0)