#!/usr/bin/env python """VisualizeTestTimes.py Visualize CTest test times with VTK. Run from the top of the build tree after the ctest has been run at least once. Pass the --modular-grouping flag to group by module.""" import os import pprint import subprocess import sys import vtk vtk_major_version = vtk.vtkVersion.GetVTKMajorVersion() if len(sys.argv) > 1 and sys.argv[1] == "-h": print("Usage: VisualizeTestTimes.py [--modular-grouping]") sys.exit(1) modular = False if len(sys.argv) > 1 and sys.argv[1] == "--modular-grouping": modular = True # Sanity check to ensure we are in the build tree test_cost_data_file = os.path.join("Testing", "Temporary", "CTestCostData.txt") if not os.path.exists(test_cost_data_file): print("Run this script from the build tree after running ctest " + "at least once.") sys.exit(1) # Read the input data with open(test_cost_data_file) as fp: test_cost_data_lines = fp.readlines() failed_tests_index = test_cost_data_lines.index("---\n") # Import the data into a vtkTable table = vtk.vtkTable() id_array = vtk.vtkUnsignedIntArray() id_array.SetName("Pedigree Id") table.AddColumn(id_array) attributes = table.GetAttributes(vtk.vtkDataObject.ROW) attributes.SetActivePedigreeIds("Pedigree Id") test_name_array = vtk.vtkStringArray() test_name_array.SetName("Test Name") table.AddColumn(test_name_array) number_of_runs_array = vtk.vtkUnsignedIntArray() number_of_runs_array.SetName("Number of Runs") table.AddColumn(number_of_runs_array) test_time_array = vtk.vtkFloatArray() test_time_array.SetName("Test Time") table.AddColumn(test_time_array) runs_long_array = vtk.vtkStringArray() runs_long_array.SetName("RUNS_LONG Label") table.AddColumn(runs_long_array) other_labels_array = vtk.vtkStringArray() other_labels_array.SetName("Other Labels") table.AddColumn(other_labels_array) table.SetNumberOfRows(failed_tests_index) ctest_exe = "ctest" runs_long = subprocess.check_output([ctest_exe, "-L", "RUNS_LONG", "-N"]) runs_long = runs_long.split("\n")[1:-3] runs_long = [ii.split()[2] for ii in runs_long] has_runs_long_time = 0.0 no_runs_long_time = 0.0 for ii in range(failed_tests_index): split = test_cost_data_lines[ii].strip().split() table.SetValue(ii, 0, ii) name = split[0] table.SetValue(ii, 1, name) table.SetValue(ii, 2, int(split[1])) time = float(split[2]) table.SetValue(ii, 3, time) if name in runs_long: table.SetValue(ii, 4, "Has RUNS_LONG Label") has_runs_long_time += time else: table.SetValue(ii, 4, "No RUNS_LONG Label") no_runs_long_time += time table.SetValue(ii, 5, "None") labels = subprocess.check_output([ctest_exe, "--print-labels"]) labels = labels.split("\n")[2:-1] labels = [ii.strip() for ii in labels] if "RUNS_LONG" in labels: labels.pop(labels.index("RUNS_LONG")) # Assuming tests will only have RUNS_LONG and up to only one other label if modular: for label in labels: tests = subprocess.check_output([ctest_exe, "-L", label, "-N"]) tests = tests.split("\n")[2:-3] tests = [ii.split()[2] for ii in tests] for test in tests: index = test_name_array.LookupValue(test) other_labels_array.SetValue(index, label) print("RUNS_LONG tests:") pprint.pprint(runs_long) print( "RUNS_LONG time percentage: {:.4}%".format( str(has_runs_long_time / (has_runs_long_time + no_runs_long_time) * 100) ) ) print( "RUNS_LONG test percentage: {:.4}%".format( str((len(runs_long)) / (failed_tests_index - 1.0) * 100) ) ) # Convert the vtkTable to a vtkTree table_to_tree = vtk.vtkTableToTreeFilter() if vtk_major_version == 5: table_to_tree.SetInput(table) else: table_to_tree.SetInputData(table) group_runs_long = vtk.vtkGroupLeafVertices() group_runs_long.SetInputConnection(table_to_tree.GetOutputPort()) group_runs_long.SetInputArrayToProcess( 0, 0, 0, vtk.vtkDataObject.VERTEX, "RUNS_LONG Label" ) group_runs_long.SetInputArrayToProcess(1, 0, 0, vtk.vtkDataObject.VERTEX, "Test Name") group_other_label = vtk.vtkGroupLeafVertices() group_other_label.SetInputConnection(group_runs_long.GetOutputPort()) group_other_label.SetInputArrayToProcess( 0, 0, 0, vtk.vtkDataObject.VERTEX, "Other Labels" ) group_other_label.SetInputArrayToProcess(1, 0, 0, vtk.vtkDataObject.VERTEX, "Test Name") # Visualize with a tree map view tree_map_view = vtk.vtkTreeMapView() if modular: tree_map_view.SetTreeFromInputConnection(group_other_label.GetOutputPort()) else: tree_map_view.SetTreeFromInputConnection(group_runs_long.GetOutputPort()) tree_map_view.SetAreaLabelArrayName("Test Name") tree_map_view.SetAreaHoverArrayName("Test Name") tree_map_view.SetAreaLabelVisibility(True) tree_map_view.SetAreaSizeArrayName("Test Time") tree_map_view.DisplayHoverTextOn() tree_map_view.SetLayoutStrategyToSquarify() # Pretty preference: Mellow, Neon, Ocean theme = vtk.vtkViewTheme.CreateNeonTheme() tree_map_view.ApplyViewTheme(theme) tree_map_view.Update() tree_map_view.ResetCamera() interactor = tree_map_view.GetInteractor() interactor.Initialize() interactor.Start()