Skip to content

Commit 53fd865

Browse files
Copilotbrunoborges
andcommitted
Harden documentation sample API guard test
Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
1 parent 4c7f1fd commit 53fd865

File tree

1 file changed

+109
-13
lines changed

1 file changed

+109
-13
lines changed

src/test/java/com/github/copilot/sdk/DocumentationSamplesTest.java

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,129 @@
77
import java.nio.file.Path;
88
import java.util.ArrayList;
99
import java.util.List;
10-
import java.util.regex.Pattern;
1110
import java.util.stream.Stream;
1211

1312
import org.junit.jupiter.api.Test;
1413

1514
class DocumentationSamplesTest {
1615

17-
private static final Pattern CREATE_SESSION_WITHOUT_PERMISSION = Pattern
18-
.compile("createSession\\(\\s*new SessionConfig\\(\\)(?!\\.setOnPermissionRequest\\()", Pattern.DOTALL);
19-
private static final Pattern RESUME_CONFIG_WITHOUT_PERMISSION = Pattern.compile(
20-
"new ResumeSessionConfig\\(\\)(?!\\.setOnPermissionRequest\\()", Pattern.DOTALL);
21-
private static final Pattern REMOVED_RESUME_OVERLOAD = Pattern
22-
.compile("resumeSession\\([^,\\n\\)]*\\)\\.get\\(\\)");
23-
2416
@Test
2517
void docsAndJbangSamplesUseRequiredPermissionHandler() throws IOException {
2618
for (Path path : documentationFiles()) {
27-
String content = Files.readString(path);
28-
assertFalse(CREATE_SESSION_WITHOUT_PERMISSION.matcher(content).find(),
29-
() -> path + " contains createSession sample without setOnPermissionRequest");
30-
assertFalse(RESUME_CONFIG_WITHOUT_PERMISSION.matcher(content).find(),
19+
String content = stripStringsAndComments(Files.readString(path));
20+
assertFalse(hasConfigWithoutPermissionHandler(content, "SessionConfig"),
21+
() -> path + " contains SessionConfig sample without setOnPermissionRequest");
22+
assertFalse(hasConfigWithoutPermissionHandler(content, "ResumeSessionConfig"),
3123
() -> path + " contains ResumeSessionConfig sample without setOnPermissionRequest");
32-
assertFalse(REMOVED_RESUME_OVERLOAD.matcher(content).find(),
24+
assertFalse(hasSingleArgumentResumeSessionCall(content),
3325
() -> path + " contains removed resumeSession(String) overload");
3426
}
3527
}
3628

29+
private static boolean hasConfigWithoutPermissionHandler(String content, String configType) {
30+
String constructor = "new " + configType + "()";
31+
int fromIndex = 0;
32+
while (true) {
33+
int start = content.indexOf(constructor, fromIndex);
34+
if (start < 0) {
35+
return false;
36+
}
37+
int end = content.indexOf(';', start);
38+
if (end < 0) {
39+
end = content.length();
40+
}
41+
if (!content.substring(start, end).contains("setOnPermissionRequest(")) {
42+
return true;
43+
}
44+
fromIndex = start + constructor.length();
45+
}
46+
}
47+
48+
private static boolean hasSingleArgumentResumeSessionCall(String content) {
49+
int fromIndex = 0;
50+
while (true) {
51+
int callStart = content.indexOf("resumeSession(", fromIndex);
52+
if (callStart < 0) {
53+
return false;
54+
}
55+
int index = callStart + "resumeSession(".length();
56+
int depth = 1;
57+
int topLevelCommaCount = 0;
58+
while (index < content.length() && depth > 0) {
59+
char c = content.charAt(index);
60+
if (c == '(') {
61+
depth++;
62+
} else if (c == ')') {
63+
depth--;
64+
} else if (c == ',' && depth == 1) {
65+
topLevelCommaCount++;
66+
}
67+
index++;
68+
}
69+
70+
if (depth == 0 && topLevelCommaCount == 0) {
71+
return true;
72+
}
73+
fromIndex = callStart + 1;
74+
}
75+
}
76+
77+
private static String stripStringsAndComments(String input) {
78+
StringBuilder out = new StringBuilder(input.length());
79+
int i = 0;
80+
while (i < input.length()) {
81+
char c = input.charAt(i);
82+
if (c == '"' || c == '\'') {
83+
char quote = c;
84+
out.append(' ');
85+
i++;
86+
while (i < input.length()) {
87+
char current = input.charAt(i);
88+
out.append(' ');
89+
if (current == '\\') {
90+
i++;
91+
if (i < input.length()) {
92+
out.append(' ');
93+
}
94+
} else if (current == quote) {
95+
i++;
96+
break;
97+
}
98+
i++;
99+
}
100+
continue;
101+
}
102+
if (c == '/' && i + 1 < input.length()) {
103+
char next = input.charAt(i + 1);
104+
if (next == '/') {
105+
out.append(' ').append(' ');
106+
i += 2;
107+
while (i < input.length() && input.charAt(i) != '\n') {
108+
out.append(' ');
109+
i++;
110+
}
111+
continue;
112+
}
113+
if (next == '*') {
114+
out.append(' ').append(' ');
115+
i += 2;
116+
while (i + 1 < input.length() && !(input.charAt(i) == '*' && input.charAt(i + 1) == '/')) {
117+
out.append(' ');
118+
i++;
119+
}
120+
if (i + 1 < input.length()) {
121+
out.append(' ').append(' ');
122+
i += 2;
123+
}
124+
continue;
125+
}
126+
}
127+
out.append(c);
128+
i++;
129+
}
130+
return out.toString();
131+
}
132+
37133
private static List<Path> documentationFiles() throws IOException {
38134
Path root = Path.of("").toAbsolutePath();
39135
List<Path> files = new ArrayList<>();

0 commit comments

Comments
 (0)