@@ -65,8 +65,8 @@ public class E2ETestContext implements AutoCloseable {
6565 */
6666 private static final String DEFAULT_GITHUB_TOKEN = "fake-token-for-e2e-tests" ;
6767 private static final Pattern SNAKE_CASE = Pattern .compile ("[^a-zA-Z0-9]" );
68- private static final Pattern USER_CONTENT_PATTERN = Pattern
69- .compile ("^\\ s+- \\ s+role: \\ s+user \\ s*$ \\ s+ content:\\ s*(.+? )$" , Pattern . MULTILINE );
68+ private static final Pattern USER_ROLE_PATTERN = Pattern . compile ( "^( \\ s*)- \\ s+role: \\ s+user \\ s*$" );
69+ private static final Pattern CONTENT_PATTERN = Pattern .compile ("^( \\ s*) content:\\ s*(.* )$" );
7070
7171 private final String cliPath ;
7272 private final Path homeDir ;
@@ -227,25 +227,109 @@ public List<String> getExpectedUserPrompts() {
227227 return List .of ();
228228 }
229229 try {
230- String content = Files .readString (currentSnapshotFile );
231- List <String > prompts = new ArrayList <>();
232- Matcher matcher = USER_CONTENT_PATTERN .matcher (content );
233- while (matcher .find ()) {
234- String prompt = matcher .group (1 ).trim ();
235- // Remove quotes if present
236- if ((prompt .startsWith ("\" " ) && prompt .endsWith ("\" " ))
237- || (prompt .startsWith ("'" ) && prompt .endsWith ("'" ))) {
238- prompt = prompt .substring (1 , prompt .length () - 1 );
230+ return parseExpectedUserPrompts (Files .readString (currentSnapshotFile ));
231+ } catch (IOException e ) {
232+ LOG .warning ("Failed to read snapshot file: " + e .getMessage ());
233+ return List .of ();
234+ }
235+ }
236+
237+ static List <String > parseExpectedUserPrompts (String yaml ) {
238+ String [] lines = yaml .split ("\\ R" , -1 );
239+ List <String > prompts = new ArrayList <>();
240+
241+ for (int i = 0 ; i < lines .length ; i ++) {
242+ Matcher roleMatcher = USER_ROLE_PATTERN .matcher (lines [i ]);
243+ if (!roleMatcher .matches ()) {
244+ continue ;
245+ }
246+
247+ int roleIndent = roleMatcher .group (1 ).length ();
248+ for (i ++; i < lines .length ; i ++) {
249+ String line = lines [i ];
250+ if (!line .isBlank () && leadingWhitespace (line ) <= roleIndent ) {
251+ i --;
252+ break ;
239253 }
240- if (!prompts .contains (prompt )) {
254+
255+ Matcher contentMatcher = CONTENT_PATTERN .matcher (line );
256+ if (!contentMatcher .matches ()) {
257+ continue ;
258+ }
259+
260+ int contentIndent = contentMatcher .group (1 ).length ();
261+ String value = contentMatcher .group (2 ).trim ();
262+ List <String > continuation = new ArrayList <>();
263+ while (i + 1 < lines .length
264+ && (lines [i + 1 ].isBlank () || leadingWhitespace (lines [i + 1 ]) > contentIndent )) {
265+ continuation .add (lines [++i ]);
266+ }
267+
268+ String prompt = isBlockScalar (value )
269+ ? parseBlockScalar (value .charAt (0 ), continuation )
270+ : parsePlainScalar (value , continuation );
271+ if (!prompt .isEmpty () && !prompts .contains (prompt )) {
241272 prompts .add (prompt );
242273 }
274+ break ;
243275 }
244- return prompts ;
245- } catch (IOException e ) {
246- LOG .warning ("Failed to read snapshot file: " + e .getMessage ());
247- return List .of ();
248276 }
277+
278+ return prompts ;
279+ }
280+
281+ private static String parsePlainScalar (String firstLine , List <String > continuation ) {
282+ StringBuilder value = new StringBuilder (unquote (firstLine ));
283+ for (String line : continuation ) {
284+ if (!line .isBlank ()) {
285+ if (!value .isEmpty ()) {
286+ value .append (' ' );
287+ }
288+ value .append (line .trim ());
289+ }
290+ }
291+ return value .toString ();
292+ }
293+
294+ private static String parseBlockScalar (char style , List <String > lines ) {
295+ int contentIndent = lines .stream ().filter (line -> !line .isBlank ()).mapToInt (E2ETestContext ::leadingWhitespace )
296+ .min ().orElse (0 );
297+ StringBuilder value = new StringBuilder ();
298+ boolean previousWasContent = false ;
299+ for (String line : lines ) {
300+ String text = line .isBlank () ? "" : line .substring (Math .min (contentIndent , line .length ()));
301+ if (text .isEmpty ()) {
302+ value .append ('\n' );
303+ previousWasContent = false ;
304+ } else {
305+ if (previousWasContent ) {
306+ value .append (style == '>' ? ' ' : '\n' );
307+ }
308+ value .append (text );
309+ previousWasContent = true ;
310+ }
311+ }
312+ return value .toString ().stripTrailing ();
313+ }
314+
315+ private static boolean isBlockScalar (String value ) {
316+ return value .matches ("[>|][+-]?" );
317+ }
318+
319+ private static String unquote (String value ) {
320+ if (value .length () >= 2 && ((value .startsWith ("\" " ) && value .endsWith ("\" " ))
321+ || (value .startsWith ("'" ) && value .endsWith ("'" )))) {
322+ return value .substring (1 , value .length () - 1 );
323+ }
324+ return value ;
325+ }
326+
327+ private static int leadingWhitespace (String value ) {
328+ int index = 0 ;
329+ while (index < value .length () && Character .isWhitespace (value .charAt (index ))) {
330+ index ++;
331+ }
332+ return index ;
249333 }
250334
251335 /**
0 commit comments