1717import org .elasticsearch .client .ResponseException ;
1818import org .elasticsearch .common .io .Streams ;
1919import org .elasticsearch .common .settings .Settings ;
20+ import org .elasticsearch .common .xcontent .XContentHelper ;
2021import org .elasticsearch .test .ListMatcher ;
2122import org .elasticsearch .test .MapMatcher ;
2223import org .elasticsearch .test .TestClustersThreadFilter ;
3031import org .junit .Assert ;
3132import org .junit .ClassRule ;
3233
34+ import java .io .ByteArrayInputStream ;
3335import java .io .ByteArrayOutputStream ;
3436import java .io .IOException ;
3537import java .io .InputStream ;
36- import java .nio .charset .Charset ;
3738import java .nio .charset .StandardCharsets ;
3839import java .util .ArrayList ;
3940import java .util .Arrays ;
4950import static org .hamcrest .Matchers .containsInAnyOrder ;
5051import static org .hamcrest .Matchers .containsString ;
5152import static org .hamcrest .Matchers .either ;
53+ import static org .hamcrest .Matchers .empty ;
5254import static org .hamcrest .Matchers .equalTo ;
5355import static org .hamcrest .Matchers .greaterThan ;
56+ import static org .hamcrest .Matchers .greaterThanOrEqualTo ;
5457import static org .hamcrest .Matchers .hasItem ;
5558import static org .hamcrest .Matchers .instanceOf ;
5659import static org .hamcrest .Matchers .not ;
60+ import static org .hamcrest .Matchers .oneOf ;
5761import static org .hamcrest .Matchers .startsWith ;
5862import static org .hamcrest .core .Is .is ;
5963
@@ -335,6 +339,7 @@ public void testProfile() throws IOException {
335339 }
336340 }
337341
342+ @ SuppressWarnings ("unchecked" )
338343 public void testProfileParsing () throws IOException {
339344 indexTimestampData (1 );
340345
@@ -346,9 +351,65 @@ public void testProfileParsing() throws IOException {
346351 try (XContentBuilder jsonOutputBuilder = new XContentBuilder (JsonXContent .jsonXContent , os )) {
347352 parseProfile (result , jsonOutputBuilder );
348353 }
349- String parsedProfile = os .toString (Charset .defaultCharset ());
350354
351- assertFalse (true );
355+ // Read the written JSON again into a map, so we can make assertions on it
356+ ByteArrayInputStream profileJson = new ByteArrayInputStream (os .toByteArray ());
357+ Map <String , Object > parsedProfile = XContentHelper .convertToMap (JsonXContent .jsonXContent , profileJson , true );
358+
359+ assertEquals (parsedProfile .get ("displayTimeUnit" ), "ns" );
360+ List <Map <String , Object >> events = (List <Map <String , Object >>) parsedProfile .get ("traceEvents" );
361+ // At least 1 metadata event to declare the node, and 2 events each for the data, node_reduce and final drivers, resp.
362+ assertThat (events .size (), greaterThanOrEqualTo (7 ));
363+
364+ // Declaration of each node as a "process" via a metadata event (phase `ph` is `M`)
365+ Map <String , Object > nodeMetadata = events .get (0 );
366+ assertEquals (nodeMetadata .get ("ph" ), "M" );
367+ assertEquals (nodeMetadata .get ("name" ), "process_name" );
368+ int nodeIndex = 0 ;
369+ assertEquals (nodeMetadata .get ("pid" ), nodeIndex );
370+ Map <String , Object > nodeMetadataArgs = (Map <String , Object >) nodeMetadata .get ("args" );
371+ // cluster:node should be the label for the "process" name
372+ assertEquals (nodeMetadataArgs .get ("name" ), "test-cluster:test-cluster-0" );
373+
374+ // The rest should be pairs of 2 events: first, a metadata event, declaring 1 "thread" per driver in the profile, then
375+ // a "complete" event (phase `ph` is `X`) with a timestamp, duration `dur`, thread duration `tdur` (cpu time) and additional
376+ // arguments obtained from the driver.
377+ for (int i = 1 ; i < events .size () - 1 ; i = i + 2 ) {
378+ Map <String , Object > driverMetadata = events .get (i );
379+
380+ assertEquals (driverMetadata .get ("ph" ), "M" );
381+ assertEquals (driverMetadata .get ("name" ), "thread_name" );
382+ assertEquals (driverMetadata .get ("pid" ), nodeIndex );
383+ int driverIndex = i / 2 ;
384+ assertEquals (driverMetadata .get ("tid" ), driverIndex );
385+ Map <String , Object > driverMetadataArgs = (Map <String , Object >) driverMetadata .get ("args" );
386+ String driverType = (String ) driverMetadataArgs .get ("name" );
387+ assertThat (driverType , oneOf ("data" , "node_reduce" , "final" ));
388+
389+ Map <String , Object > driverSlice = events .get (i + 1 );
390+ assertEquals (driverSlice .get ("ph" ), "X" );
391+ assertThat ((String ) driverSlice .get ("name" ), startsWith (driverType ));
392+ // Category used to implicitly colour-code and group drivers
393+ assertEquals (driverSlice .get ("cat" ), driverType );
394+ assertEquals (driverSlice .get ("pid" ), nodeIndex );
395+ assertEquals (driverSlice .get ("tid" ), driverIndex );
396+ long timestampMillis = (long ) driverSlice .get ("ts" );
397+ double durationMicros = (double ) driverSlice .get ("dur" );
398+ double cpuDurationMicros = (double ) driverSlice .get ("tdur" );
399+ assertTrue (timestampMillis >= 0 );
400+ assertTrue (durationMicros >= 0 );
401+ assertTrue (cpuDurationMicros >= 0 );
402+ assertTrue (durationMicros >= cpuDurationMicros );
403+
404+ // This should contain the essential information from a driver, like its operators, and will be just attached to the slice/
405+ // visible when clicking on it.
406+ Map <String , Object > driverSliceArgs = (Map <String , Object >) driverSlice .get ("args" );
407+ assertNotNull (driverSliceArgs .get ("cpu_nanos" ));
408+ assertNotNull (driverSliceArgs .get ("took_nanos" ));
409+ assertNotNull (driverSliceArgs .get ("iterations" ));
410+ assertNotNull (driverSliceArgs .get ("sleeps" ));
411+ assertThat (((List <String >) driverSliceArgs .get ("operators" )), not (empty ()));
412+ }
352413 }
353414
354415 public void testProfileOrdinalsGroupingOperator () throws IOException {
0 commit comments