Repository navigation
Expand file tree
/
Copy pathConvertRecordTests.cs
More file actions
212 lines (173 loc) · 7.61 KB
/
Copy pathConvertRecordTests.cs
File metadata and controls
212 lines (173 loc) · 7.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
namespace JavaToCSharp.Tests;
public class ConvertRecordTests
{
[Fact]
public void Record_Is_Converted_To_Positional_Record()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {}
""";
var parsed = Convert(javaCode);
Assert.Contains("public record Point(int x, int y)", parsed);
}
[Fact]
public void Record_Component_Names_Are_Preserved()
{
// Bodies reference components directly (`x + y`) and those references are not rewritten,
// so renaming the components would produce code that does not compile.
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public int sum() { return x + y; }
}
""";
var parsed = Convert(javaCode);
Assert.Contains("(int x, int y)", parsed);
Assert.Contains("return x + y;", parsed);
}
[Fact]
public void Record_Implements_Interface()
{
const string javaCode = """
package com.example;
public record Circle(int radius) implements Shape {}
""";
var parsed = Convert(javaCode);
Assert.Contains("public record Circle(int radius) : Shape", parsed);
}
[Fact]
public void Generic_Record_Emits_Type_Parameters()
{
const string javaCode = """
package com.example;
public record Labeled<T>(String label, T value) {}
""";
var parsed = Convert(javaCode);
Assert.Contains("public record Labeled<T>(string label, T value)", parsed);
}
[Fact]
public void Nested_Record_Is_Converted()
{
const string javaCode = """
package com.example;
public class Holder {
public record Inner(int a) {}
}
""";
var parsed = Convert(javaCode);
Assert.Contains("public record Inner(int a)", parsed);
}
[Fact]
public void Record_Static_Member_Is_Converted()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public static final Point ORIGIN = new Point(0, 0);
}
""";
var parsed = Convert(javaCode);
Assert.Contains("public static readonly Point ORIGIN", parsed);
}
[Fact]
public void Secondary_Constructor_Delegates_To_Primary_Constructor()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public Point(int v) { this(v, v); }
}
""";
var parsed = Convert(javaCode);
Assert.Contains("public Point(int v) : this(v, v)", parsed);
}
[Fact]
public void Compact_Constructor_Is_Lowered_To_An_Explicit_Constructor()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public Point {
if (x < 0) throw new IllegalArgumentException("neg");
}
}
""";
var warnings = new List<string>();
var parsed = Convert(javaCode, NewOptions(warnings));
Assert.Empty(warnings);
// The record drops its positional parameter list in favor of explicit properties.
Assert.DoesNotContain("record Point(", parsed);
Assert.Contains("public int x { get; init; }", parsed);
Assert.Contains("public Point(int x, int y)", parsed);
Assert.Contains("throw new ArgumentException(\"neg\")", parsed);
}
[Fact]
public void Compact_Constructor_Assigns_Components_After_Its_Body()
{
// Java runs the compact body against the parameters and assigns the fields afterwards, so
// a reassignment in the body must be reflected in the stored component value.
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public Point {
y = y * 2;
}
}
""";
var parsed = Convert(javaCode);
var bodyIndex = parsed.IndexOf("y = y * 2;", StringComparison.Ordinal);
var assignIndex = parsed.IndexOf("this.y = y;", StringComparison.Ordinal);
Assert.True(bodyIndex >= 0, "compact constructor body should be emitted");
Assert.True(assignIndex > bodyIndex, "component assignment should follow the compact body");
}
[Fact]
public void Canonical_Constructor_Is_Ported_Using_Explicit_Properties()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public Point(int x, int y) { this.x = x; this.y = y; }
}
""";
var warnings = new List<string>();
var parsed = Convert(javaCode, NewOptions(warnings));
Assert.Empty(warnings);
// A positional record would reject a same-signature constructor (CS0111).
Assert.DoesNotContain("record Point(", parsed);
Assert.Contains("public int x { get; init; }", parsed);
Assert.Contains("public Point(int x, int y)", parsed);
}
[Fact]
public void Explicit_Accessor_Warns_Because_It_Conflicts_With_Generated_Property()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {
public int x() { return Math.abs(x); }
}
""";
var warnings = new List<string>();
var parsed = Convert(javaCode, NewOptions(warnings));
Assert.Contains(warnings, w => w.Contains("Accessor `x()`"));
Assert.DoesNotContain("int X()", parsed);
}
[Fact]
public void Record_Without_Members_Ends_With_Semicolon()
{
const string javaCode = """
package com.example;
public record Point(int x, int y) {}
""";
var parsed = Convert(javaCode);
Assert.Contains("public record Point(int x, int y);", parsed);
}
private static JavaConversionOptions NewOptions(List<string>? warnings = null)
{
var options = new JavaConversionOptions { IncludeComments = false };
options.WarningEncountered += (_, eventArgs) => warnings?.Add(eventArgs.Message);
return options;
}
private static string Convert(string javaCode, JavaConversionOptions? options = null)
=> JavaToCSharpConverter.ConvertText(javaCode, options ?? NewOptions()) ?? "";
}