Skip to content

Commit 7a919dc

Browse files
committed
- F InlineApprovals works in kotlin without special configuration
this code is ugly and needs to be refactored next time
1 parent 0b11d8b commit 7a919dc

File tree

3 files changed

+203
-152
lines changed

3 files changed

+203
-152
lines changed

approvaltests-tests/src/test/kotlin/org/approvaltests/inline/KotlinInlineApprovalsTest.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class KotlinInlineApprovalsTest {
1313
val expected = """
1414
hello world
1515
""".trimIndent()
16-
Approvals.verify("hello world", Options().inline(expected)
17-
.withReporter(InlineKotlinReporter(BeyondCompareReporter(), null)))
16+
Approvals.verify("hello world", Options().inline(expected))
1817
}
1918
}

approvaltests/src/main/java/org/approvaltests/inline/InlineKotlinReporter.java

Lines changed: 188 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -10,167 +10,207 @@
1010
import org.lambda.functions.Function3;
1111

1212
import java.io.File;
13+
import java.nio.file.Files;
14+
import java.nio.file.Paths;
1315
import java.util.regex.Matcher;
1416
import java.util.regex.Pattern;
1517

16-
public class InlineKotlinReporter implements ApprovalFailureReporter, ApprovalReporterWithCleanUp {
17-
private final String sourceFilePath;
18-
private final StackTraceNamer stackTraceNamer;
19-
private final Function2<String, String, String> footerCreator;
20-
private final ApprovalFailureReporter reporter;
21-
private String additionalLines;
22-
private Function3<String, String, String, String> createNewReceivedFileText;
23-
24-
public InlineKotlinReporter(ApprovalFailureReporter reporter) {
25-
this(reporter, null);
26-
}
27-
28-
public InlineKotlinReporter(ApprovalFailureReporter reporter, Function2<String, String, String> footerCreator) {
29-
this.reporter = reporter;
30-
this.stackTraceNamer = new StackTraceNamer();
31-
this.sourceFilePath = stackTraceNamer.getSourceFilePath();
32-
this.createNewReceivedFileText = (kotlinSourceCode, actual, methodName) ->
33-
createNewReceivedFileText(kotlinSourceCode, actual, methodName);
34-
this.footerCreator = footerCreator != null ? footerCreator : (source, actual) -> "";
18+
public class InlineKotlinReporter implements ApprovalFailureReporter, ApprovalReporterWithCleanUp
19+
{
20+
private final String sourceFilePath;
21+
private final StackTraceNamer stackTraceNamer;
22+
private final Function2<String, String, String> footerCreator;
23+
private final ApprovalFailureReporter reporter;
24+
private String additionalLines;
25+
private Function3<String, String, String, String> createNewReceivedFileText;
26+
public InlineKotlinReporter(ApprovalFailureReporter reporter)
27+
{
28+
this(reporter, null);
29+
}
30+
public InlineKotlinReporter(ApprovalFailureReporter reporter, Function2<String, String, String> footerCreator)
31+
{
32+
this.reporter = reporter;
33+
this.footerCreator = footerCreator != null ? footerCreator : (source, actual) -> "";
34+
Result result = getResult();
35+
this.stackTraceNamer = result.stackTraceNamer;
36+
this.sourceFilePath = result.sourceFilePath;
37+
this.createNewReceivedFileText = result.createNewReceivedFileText;
38+
}
39+
public static Result getResult()
40+
{
41+
StackTraceNamer stackTraceNamer = new StackTraceNamer();
42+
String sourceFilePath = stackTraceNamer.getSourceFilePath();
43+
Function3<String, String, String, String> createNewReceivedFileText = (kotlinSourceCode, actual,
44+
methodName) -> createNewReceivedFileText(kotlinSourceCode, actual, methodName);
45+
return new Result(stackTraceNamer, sourceFilePath, createNewReceivedFileText);
46+
}
47+
public static class Result
48+
{
49+
public final StackTraceNamer stackTraceNamer;
50+
public final String sourceFilePath;
51+
public final Function3<String, String, String, String> createNewReceivedFileText;
52+
public Result(StackTraceNamer stackTraceNamer, String sourceFilePath,
53+
Function3<String, String, String, String> createNewReceivedFileText)
54+
{
55+
this.stackTraceNamer = stackTraceNamer;
56+
this.sourceFilePath = sourceFilePath;
57+
this.createNewReceivedFileText = createNewReceivedFileText;
3558
}
36-
37-
@Override
38-
public boolean report(String received, String approved) {
39-
additionalLines = footerCreator.call(received, approved);
40-
String sourceFile = sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".kt";
41-
String newSource = createReceived(FileUtils.readFile(received));
42-
return reporter.report(newSource, sourceFile);
43-
}
44-
45-
public String createReceived(String actual) {
46-
String file = sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".kt";
47-
String received = getReceivedFileName();
48-
String text = FileUtils.readFile(file);
49-
String fullText = this.createNewReceivedFileText.call(
50-
text,
51-
actual + additionalLines,
52-
this.stackTraceNamer.getInfo().getMethodName()
53-
);
54-
FileUtils.writeFile(new File(received), fullText);
55-
return received;
59+
public boolean isKotlin()
60+
{
61+
String sourceFile = sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".kt";
62+
return Files.exists(Paths.get(sourceFile));
5663
}
57-
58-
private String getReceivedFileName() {
59-
return sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".received.txt";
64+
}
65+
@Override
66+
public boolean report(String received, String approved)
67+
{
68+
additionalLines = footerCreator.call(received, approved);
69+
String sourceFile = sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".kt";
70+
String newSource = createReceived(FileUtils.readFile(received));
71+
return reporter.report(newSource, sourceFile);
72+
}
73+
public String createReceived(String actual)
74+
{
75+
String file = sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".kt";
76+
String received = getReceivedFileName();
77+
String text = FileUtils.readFile(file);
78+
String fullText = this.createNewReceivedFileText.call(text, actual + additionalLines,
79+
this.stackTraceNamer.getInfo().getMethodName());
80+
FileUtils.writeFile(new File(received), fullText);
81+
return received;
82+
}
83+
private String getReceivedFileName()
84+
{
85+
return sourceFilePath + stackTraceNamer.getInfo().getClassName() + ".received.txt";
86+
}
87+
@Override
88+
public void cleanUp(String received, String approved)
89+
{
90+
FileUtils.delete(getReceivedFileName());
91+
}
92+
public static String createNewReceivedFileText(String kotlinSourceCode, String actual, String methodName)
93+
{
94+
String normalizedSourceCode = kotlinSourceCode.replaceAll("\r\n", "\n");
95+
CodeParts codeParts = splitCode(normalizedSourceCode, methodName);
96+
if (codeParts.method != null && codeParts.method.contains("expected = \"\"\""))
97+
{
98+
replaceExpected(codeParts, actual);
6099
}
61-
62-
@Override
63-
public void cleanUp(String received, String approved) {
64-
FileUtils.delete(getReceivedFileName());
100+
else
101+
{
102+
addExpected(codeParts, actual);
65103
}
66-
67-
public static String createNewReceivedFileText(String kotlinSourceCode, String actual, String methodName) {
68-
String normalizedSourceCode = kotlinSourceCode.replaceAll("\r\n", "\n");
69-
CodeParts codeParts = splitCode(normalizedSourceCode, methodName);
70-
if (codeParts.method != null && codeParts.method.contains("expected = \"\"\"")) {
71-
replaceExpected(codeParts, actual);
72-
} else {
73-
addExpected(codeParts, actual);
74-
}
75-
return codeParts.getFullCode();
104+
return codeParts.getFullCode();
105+
}
106+
private static CodeParts splitCode(String text, String methodName)
107+
{
108+
CodeParts codeParts = new CodeParts();
109+
String[] lines = text.split("\n");
110+
// Remove empty trailing elements
111+
int actualLength = lines.length;
112+
while (actualLength > 0 && lines[actualLength - 1].isEmpty())
113+
{
114+
actualLength--;
76115
}
77-
78-
private static CodeParts splitCode(String text, String methodName) {
79-
CodeParts codeParts = new CodeParts();
80-
String[] lines = text.split("\n");
81-
// Remove empty trailing elements
82-
int actualLength = lines.length;
83-
while (actualLength > 0 && lines[actualLength - 1].isEmpty()) {
84-
actualLength--;
116+
String[] trimmedLines = new String[actualLength];
117+
System.arraycopy(lines, 0, trimmedLines, 0, actualLength);
118+
lines = trimmedLines;
119+
int start = 0;
120+
int end = 0;
121+
for (int i = 0; i < lines.length; i++)
122+
{
123+
String line = lines[i];
124+
if (start == 0)
125+
{
126+
// Do a regex search to check if a line contains the text "fun"
127+
if (line.matches(".*fun\\s+" + methodName + "\\s*\\(.*"))
128+
{
129+
start = i;
130+
codeParts.tab = extractLeadingWhitespace(line);
85131
}
86-
String[] trimmedLines = new String[actualLength];
87-
System.arraycopy(lines, 0, trimmedLines, 0, actualLength);
88-
lines = trimmedLines;
89-
90-
int start = 0;
91-
int end = 0;
92-
for (int i = 0; i < lines.length; i++) {
93-
String line = lines[i];
94-
if (start == 0) {
95-
// Do a regex search to check if a line contains the text "fun"
96-
if (line.matches(".*fun\\s+" + methodName + "\\s*\\(.*")) {
97-
start = i;
98-
codeParts.tab = extractLeadingWhitespace(line);
99-
}
100-
} else if (end == 0) {
101-
if (line.startsWith(codeParts.tab + "}")) {
102-
end = i + 1;
103-
break;
104-
}
105-
}
132+
}
133+
else if (end == 0)
134+
{
135+
if (line.startsWith(codeParts.tab + "}"))
136+
{
137+
end = i + 1;
138+
break;
106139
}
107-
codeParts.before = String.join("\n", ArrayUtils.getSubsection(lines, 0, start));
108-
codeParts.method = String.join("\n", ArrayUtils.getSubsection(lines, start, end));
109-
codeParts.after = String.join("\n", ArrayUtils.getSubsection(lines, end, lines.length));
110-
return codeParts;
140+
}
111141
}
112-
113-
private static String extractLeadingWhitespace(String text) {
114-
Pattern pattern = Pattern.compile("^\\s+");
115-
Matcher matcher = pattern.matcher(text);
116-
if (matcher.find()) {
117-
return matcher.group();
118-
}
119-
return "\t";
142+
codeParts.before = String.join("\n", ArrayUtils.getSubsection(lines, 0, start));
143+
codeParts.method = String.join("\n", ArrayUtils.getSubsection(lines, start, end));
144+
codeParts.after = String.join("\n", ArrayUtils.getSubsection(lines, end, lines.length));
145+
return codeParts;
146+
}
147+
private static String extractLeadingWhitespace(String text)
148+
{
149+
Pattern pattern = Pattern.compile("^\\s+");
150+
Matcher matcher = pattern.matcher(text);
151+
if (matcher.find())
152+
{ return matcher.group(); }
153+
return "\t";
154+
}
155+
private static void addExpected(CodeParts codeParts, String actual)
156+
{
157+
int start = codeParts.method.indexOf("{") + 2;
158+
String before = codeParts.method.substring(0, start);
159+
String after = codeParts.method.substring(start);
160+
codeParts.method = before + getExpected(actual, codeParts.tab) + after;
161+
}
162+
private static String getExpected(String actual, String tab)
163+
{
164+
return String.format("%s%sval expected = \"\"\"\n%s%s%s%s\"\"\".trimIndent()\n", tab, tab, indent(actual, tab),
165+
tab, tab, tab);
166+
}
167+
private static void replaceExpected(CodeParts codeParts, String actual)
168+
{
169+
int start = codeParts.method.indexOf("expected = \"\"\"");
170+
start = codeParts.method.substring(0, start).lastIndexOf("\n") + 1;
171+
// Find the closing """ by looking for it at the start of a line (after whitespace)
172+
int searchPos = start + "expected = \"\"\"".length();
173+
int end = -1;
174+
while (searchPos < codeParts.method.length())
175+
{
176+
int nextTripleQuote = codeParts.method.indexOf("\"\"\"", searchPos);
177+
if (nextTripleQuote == -1)
178+
break;
179+
// Check if this """ is at the start of a line (preceded only by whitespace)
180+
int lineStart = codeParts.method.lastIndexOf("\n", nextTripleQuote - 1) + 1;
181+
String textBeforeQuote = codeParts.method.substring(lineStart, nextTripleQuote);
182+
if (textBeforeQuote.trim().isEmpty())
183+
{
184+
end = nextTripleQuote;
185+
break;
186+
}
187+
searchPos = nextTripleQuote + 1;
120188
}
121-
122-
private static void addExpected(CodeParts codeParts, String actual) {
123-
int start = codeParts.method.indexOf("{") + 2;
124-
String before = codeParts.method.substring(0, start);
125-
String after = codeParts.method.substring(start);
126-
codeParts.method = before + getExpected(actual, codeParts.tab) + after;
189+
if (end == -1)
190+
{
191+
// Fallback to old behavior if we can't find it
192+
end = codeParts.method.indexOf("\"\"\"", start + "expected = \"\"\"".length());
127193
}
128-
129-
private static String getExpected(String actual, String tab) {
130-
return String.format("%s%sval expected = \"\"\"\n%s%s%s%s\"\"\".trimIndent()\n",
131-
tab, tab, indent(actual, tab), tab, tab, tab);
194+
end += 3; // Move past the closing """
195+
// Check if there's a .trimIndent() after the closing """
196+
String afterTripleQuote = codeParts.method.substring(end);
197+
if (afterTripleQuote.startsWith(".trimIndent()"))
198+
{
199+
end += ".trimIndent()".length();
132200
}
133-
134-
private static void replaceExpected(CodeParts codeParts, String actual) {
135-
int start = codeParts.method.indexOf("expected = \"\"\"");
136-
start = codeParts.method.substring(0, start).lastIndexOf("\n") + 1;
137-
// Find the closing """ by looking for it at the start of a line (after whitespace)
138-
int searchPos = start + "expected = \"\"\"".length();
139-
int end = -1;
140-
while (searchPos < codeParts.method.length()) {
141-
int nextTripleQuote = codeParts.method.indexOf("\"\"\"", searchPos);
142-
if (nextTripleQuote == -1) break;
143-
// Check if this """ is at the start of a line (preceded only by whitespace)
144-
int lineStart = codeParts.method.lastIndexOf("\n", nextTripleQuote - 1) + 1;
145-
String textBeforeQuote = codeParts.method.substring(lineStart, nextTripleQuote);
146-
if (textBeforeQuote.trim().isEmpty()) {
147-
end = nextTripleQuote;
148-
break;
149-
}
150-
searchPos = nextTripleQuote + 1;
151-
}
152-
if (end == -1) {
153-
// Fallback to old behavior if we can't find it
154-
end = codeParts.method.indexOf("\"\"\"", start + "expected = \"\"\"".length());
155-
}
156-
end += 3; // Move past the closing """
157-
// Check if there's a .trimIndent() after the closing """
158-
String afterTripleQuote = codeParts.method.substring(end);
159-
if (afterTripleQuote.startsWith(".trimIndent()")) {
160-
end += ".trimIndent()".length();
161-
}
162-
end = codeParts.method.indexOf("\n", end) + 1;
163-
String before = codeParts.method.substring(0, start);
164-
String after = codeParts.method.substring(end);
165-
codeParts.method = before + getExpected(actual, codeParts.tab) + after;
166-
}
167-
168-
private static String indent(String actual, String tab) {
169-
String[] split = StringUtils.split(actual, "\n");
170-
StringBuilder output = new StringBuilder();
171-
for (String line : split) {
172-
output.append(tab).append(tab).append(tab).append(line).append("\n");
173-
}
174-
return output.toString();
201+
end = codeParts.method.indexOf("\n", end) + 1;
202+
String before = codeParts.method.substring(0, start);
203+
String after = codeParts.method.substring(end);
204+
codeParts.method = before + getExpected(actual, codeParts.tab) + after;
205+
}
206+
private static String indent(String actual, String tab)
207+
{
208+
String[] split = StringUtils.split(actual, "\n");
209+
StringBuilder output = new StringBuilder();
210+
for (String line : split)
211+
{
212+
output.append(tab).append(tab).append(tab).append(line).append("\n");
175213
}
214+
return output.toString();
215+
}
176216
}

0 commit comments

Comments
 (0)