Skip to content

Commit ca83af1

Browse files
Improve code coverage (#2707)
Co-authored-by: codefactor-io <[email protected]>
1 parent 94f22cb commit ca83af1

File tree

4 files changed

+331
-0
lines changed

4 files changed

+331
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using ImperatorToCK3.CK3.Characters;
2+
using ImperatorToCK3.CommonUtils.Genes;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using Xunit;
7+
8+
namespace ImperatorToCK3.UnitTests.CK3.Characters;
9+
10+
public class DNATests {
11+
private static DNA CreateSampleDNA(
12+
out Dictionary<string, DNAColorGeneValue> colorDict,
13+
out Dictionary<string, DNAGeneValue> morphDict,
14+
out Dictionary<string, DNAAccessoryGeneValue> accessoryDict
15+
) {
16+
colorDict = new Dictionary<string, DNAColorGeneValue> {
17+
{
18+
"hair_color",
19+
new DNAColorGeneValue { X = 1, Y = 2, XRecessive = 3, YRecessive = 4 }
20+
}
21+
};
22+
morphDict = new Dictionary<string, DNAGeneValue> {
23+
{
24+
"gene_head_height",
25+
new DNAGeneValue { TemplateName = "tmpl_dom", IntSliderValue = 1, TemplateRecessiveName = "tmpl_rec", IntSliderValueRecessive = 2 }
26+
}
27+
};
28+
var wb = new WeightBlock();
29+
wb.AddObject("obj_a", 1);
30+
wb.AddObject("obj_b", 1);
31+
accessoryDict = new Dictionary<string, DNAAccessoryGeneValue> {
32+
{ "hairstyles", new DNAAccessoryGeneValue("tmpl_acc", "obj_a", wb) }
33+
};
34+
35+
return new DNA("test_dna", colorDict, morphDict, accessoryDict);
36+
}
37+
38+
[Fact]
39+
public void IdPropertyReturnsSuppliedId() {
40+
var dna = new DNA(
41+
"my_id",
42+
new Dictionary<string, DNAColorGeneValue>(),
43+
new Dictionary<string, DNAGeneValue>(),
44+
new Dictionary<string, DNAAccessoryGeneValue>()
45+
);
46+
Assert.Equal("my_id", dna.Id);
47+
}
48+
49+
[Fact]
50+
public void AccessoryDNAValuesReturnsExpectedEntries() {
51+
var dna = CreateSampleDNA(out _, out _, out var accessoryInput);
52+
53+
Assert.Single(dna.AccessoryDNAValues);
54+
Assert.True(dna.AccessoryDNAValues.ContainsKey("hairstyles"));
55+
var v = dna.AccessoryDNAValues["hairstyles"];
56+
Assert.Equal("tmpl_acc", v.TemplateName);
57+
Assert.Equal("obj_a", v.ObjectName);
58+
59+
// Mutate original dictionary; dna should not change (constructor copies).
60+
accessoryInput["beards"] = v;
61+
Assert.Single(dna.AccessoryDNAValues);
62+
Assert.DoesNotContain("beards", dna.AccessoryDNAValues.Keys);
63+
}
64+
65+
[Fact]
66+
public void DNALinesCombinesAllGeneTypes() {
67+
var dna = CreateSampleDNA(out var color, out var morph, out var accessory);
68+
69+
var lines = dna.DNALines.ToList();
70+
Assert.Equal(3, lines.Count);
71+
72+
Assert.Contains("hair_color={ 1 2 3 4 }", lines);
73+
Assert.Contains("gene_head_height={ \"tmpl_dom\" 1 \"tmpl_rec\" 2 }", lines);
74+
// For object "obj_a" being the first entry in weight block, slider=0.
75+
Assert.Contains("hairstyles={ \"tmpl_acc\" 0 \"tmpl_acc\" 0 }", lines);
76+
77+
// Mutate source dicts; dna should not reflect these changes.
78+
color["skin_color"] = new DNAColorGeneValue { X = 9, Y = 9, XRecessive = 9, YRecessive = 9 };
79+
morph["gene_age"] = new DNAGeneValue { TemplateName = "age", IntSliderValue = 5, TemplateRecessiveName = "age", IntSliderValueRecessive = 5 };
80+
accessory["beards"] = accessory["hairstyles"];
81+
82+
var linesAfter = dna.DNALines.ToList();
83+
Assert.Equal(3, linesAfter.Count);
84+
Assert.DoesNotContain(linesAfter, l => l.StartsWith("skin_color="));
85+
Assert.DoesNotContain(linesAfter, l => l.StartsWith("gene_age="));
86+
Assert.DoesNotContain(linesAfter, l => l.StartsWith("beards="));
87+
}
88+
89+
[Fact]
90+
public void ConstructorCopiesInputDictionaries() {
91+
var dna = CreateSampleDNA(out var color, out var morph, out var accessory);
92+
93+
// Replace entire dictionaries' contents after construction.
94+
color.Clear();
95+
morph.Clear();
96+
accessory.Clear();
97+
98+
// DNA should still expose original values.
99+
Assert.Single(dna.AccessoryDNAValues);
100+
Assert.Equal(3, dna.DNALines.Count());
101+
}
102+
103+
[Fact]
104+
public void WriteGenesProducesExpectedBlockWithEntries() {
105+
var dna = CreateSampleDNA(out _, out _, out _);
106+
var sb = new StringBuilder();
107+
dna.WriteGenes(sb);
108+
109+
var output = sb.ToString();
110+
// Normalize newlines for assertions.
111+
var lines = output.Replace("\r\n", "\n").Split('\n');
112+
// Opening and closing braces present
113+
Assert.Contains("\t\tgenes={", lines);
114+
Assert.Contains("\t\t}", lines);
115+
116+
// Three inner lines written, each indented once more
117+
var inner = lines.Where(l => l.StartsWith("\t\t\t")).ToList();
118+
Assert.Equal(3, inner.Count);
119+
Assert.Contains("\t\t\thair_color={ 1 2 3 4 }", inner);
120+
Assert.Contains("\t\t\tgene_head_height={ \"tmpl_dom\" 1 \"tmpl_rec\" 2 }", inner);
121+
Assert.Contains("\t\t\thairstyles={ \"tmpl_acc\" 0 \"tmpl_acc\" 0 }", inner);
122+
}
123+
124+
[Fact]
125+
public void WriteGenesWritesEmptyBlockWhenNoGenes() {
126+
var dna = new DNA(
127+
"empty",
128+
new Dictionary<string, DNAColorGeneValue>(),
129+
new Dictionary<string, DNAGeneValue>(),
130+
new Dictionary<string, DNAAccessoryGeneValue>()
131+
);
132+
var sb = new StringBuilder();
133+
dna.WriteGenes(sb);
134+
135+
var output = sb.ToString();
136+
var lines = output.Replace("\r\n", "\n").Split('\n');
137+
Assert.Contains("\t\tgenes={", lines);
138+
Assert.Contains("\t\t}", lines);
139+
Assert.DoesNotContain(lines, l => l.StartsWith("\t\t\t"));
140+
}
141+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using ImperatorToCK3.CommonUtils;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Xunit;
5+
6+
namespace ImperatorToCK3.UnitTests.CommonUtils;
7+
8+
public class EnumerableExtensionsTests {
9+
[Fact]
10+
public void LastOrNull_ReturnsLastMatching_WhenMatchesExist() {
11+
// Arrange
12+
IEnumerable<int> numbers = new[] { 1, 2, 3, 4, 5 };
13+
14+
// Act
15+
int? result = numbers.LastOrNull(n => n % 2 == 0);
16+
17+
// Assert
18+
Assert.Equal(4, result);
19+
}
20+
21+
[Fact]
22+
public void LastOrNull_ReturnsNull_WhenNoMatch() {
23+
// Arrange
24+
IEnumerable<int> numbers = new[] { 1, 3, 5, 7 };
25+
26+
// Act
27+
int? result = numbers.LastOrNull(n => n % 2 == 0);
28+
29+
// Assert
30+
Assert.Null(result);
31+
}
32+
33+
[Fact]
34+
public void LastOrNull_ReturnsNull_WhenSourceEmpty() {
35+
// Arrange
36+
IEnumerable<int> numbers = System.Array.Empty<int>();
37+
38+
// Act
39+
int? result = numbers.LastOrNull(n => n > 0);
40+
41+
// Assert
42+
Assert.Null(result);
43+
}
44+
45+
[Fact]
46+
public void LastOrNull_WorksWithNonMaterializedEnumerable() {
47+
// Arrange: use a generator to ensure the extension materializes correctly
48+
IEnumerable<int> numbers = Generate(1, 6); // yields 1..5
49+
50+
// Act
51+
int? result = numbers.LastOrNull(n => n > 2);
52+
53+
// Assert
54+
Assert.Equal(5, result);
55+
56+
static IEnumerable<int> Generate(int startInclusive, int endExclusive) {
57+
for (int i = startInclusive; i < endExclusive; ++i) {
58+
yield return i;
59+
}
60+
}
61+
}
62+
}

ImperatorToCK3.UnitTests/CommonUtils/Genes/AccessoryGeneTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using commonItems;
22
using ImperatorToCK3.CommonUtils.Genes;
3+
using System.Linq;
34
using Xunit;
45

56
namespace ImperatorToCK3.UnitTests.CommonUtils.Genes;
@@ -76,4 +77,42 @@ public void AccessoryGeneIsProperlyLoaded() {
7677
Assert.Equal(4, gene.GeneTemplates["nerdy_hairstyles"].AgeSexWeightBlocks.Count);
7778
Assert.Equal(3, gene.GeneTemplates["punk_hairstyles"].AgeSexWeightBlocks.Count);
7879
}
80+
81+
[Fact]
82+
public void GetGeneTemplateByIndexReturnsMatch() {
83+
var reader = new BufferedReader(
84+
" = {\n" +
85+
" hat_template = { index = 0 }\n" +
86+
" hood_template = { index = 3 }\n" +
87+
" }\n"
88+
);
89+
var gene = new AccessoryGene("headgear", reader);
90+
91+
var match = gene.GetGeneTemplateByIndex(3);
92+
Assert.Equal("hood_template", match.Id);
93+
}
94+
95+
[Fact]
96+
public void GetGeneTemplateByIndexReturnsFirstOnMiss() {
97+
var reader = new BufferedReader(
98+
" = {\n" +
99+
" a_template = { index = 0 }\n" +
100+
" b_template = { index = 1 }\n" +
101+
" }\n"
102+
);
103+
var gene = new AccessoryGene("some_accessory", reader);
104+
105+
var expectedFallback = gene.GeneTemplates.First();
106+
var result = gene.GetGeneTemplateByIndex(42);
107+
108+
Assert.Same(expectedFallback, result);
109+
}
110+
111+
[Fact]
112+
public void GetGeneTemplateByIndexThrowsWhenNoTemplates() {
113+
var reader = new BufferedReader(" = { index = 5 } ");
114+
var gene = new AccessoryGene("empty_accessory", reader);
115+
116+
Assert.Throws<System.InvalidOperationException>(() => gene.GetGeneTemplateByIndex(0));
117+
}
79118
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System.Linq;
2+
using commonItems;
3+
using ImperatorToCK3.CommonUtils.Genes;
4+
using Xunit;
5+
6+
namespace ImperatorToCK3.UnitTests.CommonUtils.Genes;
7+
8+
public class MorphGeneTests {
9+
[Fact]
10+
public void IndexCanBeSet() {
11+
var reader = new BufferedReader("= { index = 12 }");
12+
var gene = new MorphGene("test_morph_gene", reader);
13+
14+
Assert.Equal((uint)12, gene.Index);
15+
}
16+
17+
[Fact]
18+
public void IndexDefaultsToNull() {
19+
var reader = new BufferedReader("= {}");
20+
var gene = new MorphGene("test_morph_gene", reader);
21+
22+
Assert.Null(gene.Index);
23+
}
24+
25+
[Fact]
26+
public void GeneTemplatesDefaultToEmpty() {
27+
var reader = new BufferedReader("= {}");
28+
var gene = new MorphGene("test_morph_gene", reader);
29+
30+
Assert.Empty(gene.GeneTemplates);
31+
}
32+
33+
[Fact]
34+
public void MorphGeneIsProperlyLoaded() {
35+
var reader = new BufferedReader(
36+
" = {\n" +
37+
" index = 7\n" +
38+
" nose_morph_01 = { index = 0 visible = yes }\n" +
39+
" nose_morph_02 = { index = 3 visible = no }\n" +
40+
" }\n"
41+
);
42+
var gene = new MorphGene("nose_shape", reader);
43+
44+
Assert.Equal((uint)7, gene.Index);
45+
Assert.Equal(2, gene.GeneTemplates.Count);
46+
Assert.Equal((uint)0, gene.GeneTemplates["nose_morph_01"].Index);
47+
Assert.Equal((uint)3, gene.GeneTemplates["nose_morph_02"].Index);
48+
}
49+
50+
[Fact]
51+
public void GetGeneTemplateByIndexReturnsMatch() {
52+
var reader = new BufferedReader(
53+
" = {\n" +
54+
" nose_morph_01 = { index = 0 }\n" +
55+
" nose_morph_02 = { index = 3 }\n" +
56+
" }\n"
57+
);
58+
var gene = new MorphGene("nose_shape", reader);
59+
60+
var match = gene.GetGeneTemplateByIndex(3);
61+
Assert.NotNull(match);
62+
Assert.Equal("nose_morph_02", match!.Id);
63+
}
64+
65+
[Fact]
66+
public void GetGeneTemplateByIndexReturnsFirstOnMiss() {
67+
var reader = new BufferedReader(
68+
" = {\n" +
69+
" a_template = { index = 0 }\n" +
70+
" b_template = { index = 1 }\n" +
71+
" }\n"
72+
);
73+
var gene = new MorphGene("some_morph", reader);
74+
75+
var expectedFallback = gene.GeneTemplates.First();
76+
var result = gene.GetGeneTemplateByIndex(42);
77+
78+
Assert.Same(expectedFallback, result);
79+
}
80+
81+
[Fact]
82+
public void GetGeneTemplateByIndexReturnsNullWhenNoTemplates() {
83+
var reader = new BufferedReader(" = { index = 5 } ");
84+
var gene = new MorphGene("empty_morph", reader);
85+
86+
var result = gene.GetGeneTemplateByIndex(0);
87+
Assert.Null(result);
88+
}
89+
}

0 commit comments

Comments
 (0)