1*760c253cSXin Li# Copyright 2013 The ChromiumOS Authors 2*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 3*760c253cSXin Li# found in the LICENSE file. 4*760c253cSXin Li"""A reproducing entity. 5*760c253cSXin Li 6*760c253cSXin LiPart of the Chrome build flags optimization. 7*760c253cSXin Li 8*760c253cSXin LiThe Task class is used by different modules. Each module fills in the 9*760c253cSXin Licorresponding information into a Task instance. Class Task contains the bit set 10*760c253cSXin Lirepresenting the flags selection. The builder module is responsible for filling 11*760c253cSXin Lithe image and the checksum field of a Task. The executor module will put the 12*760c253cSXin Liexecution output to the execution field. 13*760c253cSXin Li""" 14*760c253cSXin Li 15*760c253cSXin Li__author__ = "[email protected] (Yuheng Long)" 16*760c253cSXin Li 17*760c253cSXin Liimport os 18*760c253cSXin Liimport subprocess 19*760c253cSXin Liimport sys 20*760c253cSXin Lifrom uuid import uuid4 21*760c253cSXin Li 22*760c253cSXin Li 23*760c253cSXin LiBUILD_STAGE = 1 24*760c253cSXin LiTEST_STAGE = 2 25*760c253cSXin Li 26*760c253cSXin Li# Message indicating that the build or test failed. 27*760c253cSXin LiERROR_STRING = "error" 28*760c253cSXin Li 29*760c253cSXin Li# The maximum number of tries a build can have. Some compilations may fail due 30*760c253cSXin Li# to unexpected environment circumstance. This variable defines how many tries 31*760c253cSXin Li# the build should attempt before giving up. 32*760c253cSXin LiBUILD_TRIES = 3 33*760c253cSXin Li 34*760c253cSXin Li# The maximum number of tries a test can have. Some tests may fail due to 35*760c253cSXin Li# unexpected environment circumstance. This variable defines how many tries the 36*760c253cSXin Li# test should attempt before giving up. 37*760c253cSXin LiTEST_TRIES = 3 38*760c253cSXin Li 39*760c253cSXin Li 40*760c253cSXin Li# Create the file/directory if it does not already exist. 41*760c253cSXin Lidef _CreateDirectory(file_name): 42*760c253cSXin Li directory = os.path.dirname(file_name) 43*760c253cSXin Li if not os.path.exists(directory): 44*760c253cSXin Li os.makedirs(directory) 45*760c253cSXin Li 46*760c253cSXin Li 47*760c253cSXin Liclass Task(object): 48*760c253cSXin Li """A single reproducing entity. 49*760c253cSXin Li 50*760c253cSXin Li A single test of performance with a particular set of flags. It records the 51*760c253cSXin Li flag set, the image, the check sum of the image and the cost. 52*760c253cSXin Li """ 53*760c253cSXin Li 54*760c253cSXin Li # The command that will be used in the build stage to compile the tasks. 55*760c253cSXin Li BUILD_COMMAND = None 56*760c253cSXin Li # The command that will be used in the test stage to test the tasks. 57*760c253cSXin Li TEST_COMMAND = None 58*760c253cSXin Li # The directory to log the compilation and test results. 59*760c253cSXin Li LOG_DIRECTORY = None 60*760c253cSXin Li 61*760c253cSXin Li @staticmethod 62*760c253cSXin Li def InitLogCommand(build_command, test_command, log_directory): 63*760c253cSXin Li """Set up the build and test command for the task and the log directory. 64*760c253cSXin Li 65*760c253cSXin Li This framework is generic. It lets the client specify application specific 66*760c253cSXin Li compile and test methods by passing different build_command and 67*760c253cSXin Li test_command. 68*760c253cSXin Li 69*760c253cSXin Li Args: 70*760c253cSXin Li build_command: The command that will be used in the build stage to compile 71*760c253cSXin Li this task. 72*760c253cSXin Li test_command: The command that will be used in the test stage to test this 73*760c253cSXin Li task. 74*760c253cSXin Li log_directory: The directory to log the compilation and test results. 75*760c253cSXin Li """ 76*760c253cSXin Li 77*760c253cSXin Li Task.BUILD_COMMAND = build_command 78*760c253cSXin Li Task.TEST_COMMAND = test_command 79*760c253cSXin Li Task.LOG_DIRECTORY = log_directory 80*760c253cSXin Li 81*760c253cSXin Li def __init__(self, flag_set): 82*760c253cSXin Li """Set up the optimization flag selection for this task. 83*760c253cSXin Li 84*760c253cSXin Li Args: 85*760c253cSXin Li flag_set: The optimization flag set that is encapsulated by this task. 86*760c253cSXin Li """ 87*760c253cSXin Li 88*760c253cSXin Li self._flag_set = flag_set 89*760c253cSXin Li 90*760c253cSXin Li # A unique identifier that distinguishes this task from other tasks. 91*760c253cSXin Li self._task_identifier = uuid4() 92*760c253cSXin Li 93*760c253cSXin Li self._log_path = (Task.LOG_DIRECTORY, self._task_identifier) 94*760c253cSXin Li 95*760c253cSXin Li # Initiate the hash value. The hash value is used so as not to recompute it 96*760c253cSXin Li # every time the hash method is called. 97*760c253cSXin Li self._hash_value = None 98*760c253cSXin Li 99*760c253cSXin Li # Indicate that the task has not been compiled/tested. 100*760c253cSXin Li self._build_cost = None 101*760c253cSXin Li self._exe_cost = None 102*760c253cSXin Li self._checksum = None 103*760c253cSXin Li self._image = None 104*760c253cSXin Li self._file_length = None 105*760c253cSXin Li self._text_length = None 106*760c253cSXin Li 107*760c253cSXin Li def __eq__(self, other): 108*760c253cSXin Li """Test whether two tasks are equal. 109*760c253cSXin Li 110*760c253cSXin Li Two tasks are equal if their flag_set are equal. 111*760c253cSXin Li 112*760c253cSXin Li Args: 113*760c253cSXin Li other: The other task with which this task is tested equality. 114*760c253cSXin Li Returns: 115*760c253cSXin Li True if the encapsulated flag sets are equal. 116*760c253cSXin Li """ 117*760c253cSXin Li if isinstance(other, Task): 118*760c253cSXin Li return self.GetFlags() == other.GetFlags() 119*760c253cSXin Li return False 120*760c253cSXin Li 121*760c253cSXin Li def __hash__(self): 122*760c253cSXin Li if self._hash_value is None: 123*760c253cSXin Li # Cache the hash value of the flags, so as not to recompute them. 124*760c253cSXin Li self._hash_value = hash(self._flag_set) 125*760c253cSXin Li return self._hash_value 126*760c253cSXin Li 127*760c253cSXin Li def GetIdentifier(self, stage): 128*760c253cSXin Li """Get the identifier of the task in the stage. 129*760c253cSXin Li 130*760c253cSXin Li The flag set uniquely identifies a task in the build stage. The checksum of 131*760c253cSXin Li the image of the task uniquely identifies the task in the test stage. 132*760c253cSXin Li 133*760c253cSXin Li Args: 134*760c253cSXin Li stage: The stage (build/test) in which this method is called. 135*760c253cSXin Li Returns: 136*760c253cSXin Li Return the flag set in build stage and return the checksum in test stage. 137*760c253cSXin Li """ 138*760c253cSXin Li 139*760c253cSXin Li # Define the dictionary for different stage function lookup. 140*760c253cSXin Li get_identifier_functions = { 141*760c253cSXin Li BUILD_STAGE: self.FormattedFlags, 142*760c253cSXin Li TEST_STAGE: self.__GetCheckSum, 143*760c253cSXin Li } 144*760c253cSXin Li 145*760c253cSXin Li assert stage in get_identifier_functions 146*760c253cSXin Li return get_identifier_functions[stage]() 147*760c253cSXin Li 148*760c253cSXin Li def GetResult(self, stage): 149*760c253cSXin Li """Get the performance results of the task in the stage. 150*760c253cSXin Li 151*760c253cSXin Li Args: 152*760c253cSXin Li stage: The stage (build/test) in which this method is called. 153*760c253cSXin Li Returns: 154*760c253cSXin Li Performance results. 155*760c253cSXin Li """ 156*760c253cSXin Li 157*760c253cSXin Li # Define the dictionary for different stage function lookup. 158*760c253cSXin Li get_result_functions = { 159*760c253cSXin Li BUILD_STAGE: self.__GetBuildResult, 160*760c253cSXin Li TEST_STAGE: self.GetTestResult, 161*760c253cSXin Li } 162*760c253cSXin Li 163*760c253cSXin Li assert stage in get_result_functions 164*760c253cSXin Li 165*760c253cSXin Li return get_result_functions[stage]() 166*760c253cSXin Li 167*760c253cSXin Li def SetResult(self, stage, result): 168*760c253cSXin Li """Set the performance results of the task in the stage. 169*760c253cSXin Li 170*760c253cSXin Li This method is called by the pipeling_worker to set the results for 171*760c253cSXin Li duplicated tasks. 172*760c253cSXin Li 173*760c253cSXin Li Args: 174*760c253cSXin Li stage: The stage (build/test) in which this method is called. 175*760c253cSXin Li result: The performance results of the stage. 176*760c253cSXin Li """ 177*760c253cSXin Li 178*760c253cSXin Li # Define the dictionary for different stage function lookup. 179*760c253cSXin Li set_result_functions = { 180*760c253cSXin Li BUILD_STAGE: self.__SetBuildResult, 181*760c253cSXin Li TEST_STAGE: self.__SetTestResult, 182*760c253cSXin Li } 183*760c253cSXin Li 184*760c253cSXin Li assert stage in set_result_functions 185*760c253cSXin Li 186*760c253cSXin Li set_result_functions[stage](result) 187*760c253cSXin Li 188*760c253cSXin Li def Done(self, stage): 189*760c253cSXin Li """Check whether the stage is done. 190*760c253cSXin Li 191*760c253cSXin Li Args: 192*760c253cSXin Li stage: The stage to be checked, build or test. 193*760c253cSXin Li Returns: 194*760c253cSXin Li True if the stage is done. 195*760c253cSXin Li """ 196*760c253cSXin Li 197*760c253cSXin Li # Define the dictionary for different result string lookup. 198*760c253cSXin Li done_string = { 199*760c253cSXin Li BUILD_STAGE: self._build_cost, 200*760c253cSXin Li TEST_STAGE: self._exe_cost, 201*760c253cSXin Li } 202*760c253cSXin Li 203*760c253cSXin Li assert stage in done_string 204*760c253cSXin Li 205*760c253cSXin Li return done_string[stage] is not None 206*760c253cSXin Li 207*760c253cSXin Li def Work(self, stage): 208*760c253cSXin Li """Perform the task. 209*760c253cSXin Li 210*760c253cSXin Li Args: 211*760c253cSXin Li stage: The stage in which the task is performed, compile or test. 212*760c253cSXin Li """ 213*760c253cSXin Li 214*760c253cSXin Li # Define the dictionary for different stage function lookup. 215*760c253cSXin Li work_functions = {BUILD_STAGE: self.__Compile, TEST_STAGE: self.__Test} 216*760c253cSXin Li 217*760c253cSXin Li assert stage in work_functions 218*760c253cSXin Li 219*760c253cSXin Li work_functions[stage]() 220*760c253cSXin Li 221*760c253cSXin Li def FormattedFlags(self): 222*760c253cSXin Li """Format the optimization flag set of this task. 223*760c253cSXin Li 224*760c253cSXin Li Returns: 225*760c253cSXin Li The formatted optimization flag set that is encapsulated by this task. 226*760c253cSXin Li """ 227*760c253cSXin Li return str(self._flag_set.FormattedForUse()) 228*760c253cSXin Li 229*760c253cSXin Li def GetFlags(self): 230*760c253cSXin Li """Get the optimization flag set of this task. 231*760c253cSXin Li 232*760c253cSXin Li Returns: 233*760c253cSXin Li The optimization flag set that is encapsulated by this task. 234*760c253cSXin Li """ 235*760c253cSXin Li 236*760c253cSXin Li return self._flag_set 237*760c253cSXin Li 238*760c253cSXin Li def __GetCheckSum(self): 239*760c253cSXin Li """Get the compilation image checksum of this task. 240*760c253cSXin Li 241*760c253cSXin Li Returns: 242*760c253cSXin Li The compilation image checksum of this task. 243*760c253cSXin Li """ 244*760c253cSXin Li 245*760c253cSXin Li # The checksum should be computed before this method is called. 246*760c253cSXin Li assert self._checksum is not None 247*760c253cSXin Li return self._checksum 248*760c253cSXin Li 249*760c253cSXin Li def __Compile(self): 250*760c253cSXin Li """Run a compile. 251*760c253cSXin Li 252*760c253cSXin Li This method compile an image using the present flags, get the image, 253*760c253cSXin Li test the existent of the image and gathers monitoring information, and sets 254*760c253cSXin Li the internal cost (fitness) for this set of flags. 255*760c253cSXin Li """ 256*760c253cSXin Li 257*760c253cSXin Li # Format the flags as a string as input to compile command. The unique 258*760c253cSXin Li # identifier is passed to the compile command. If concurrent processes are 259*760c253cSXin Li # used to compile different tasks, these processes can use the identifier to 260*760c253cSXin Li # write to different file. 261*760c253cSXin Li flags = self._flag_set.FormattedForUse() 262*760c253cSXin Li command = "%s %s %s" % ( 263*760c253cSXin Li Task.BUILD_COMMAND, 264*760c253cSXin Li " ".join(flags), 265*760c253cSXin Li self._task_identifier, 266*760c253cSXin Li ) 267*760c253cSXin Li 268*760c253cSXin Li # Try BUILD_TRIES number of times before confirming that the build fails. 269*760c253cSXin Li for _ in range(BUILD_TRIES): 270*760c253cSXin Li try: 271*760c253cSXin Li # Execute the command and get the execution status/results. 272*760c253cSXin Li p = subprocess.Popen( 273*760c253cSXin Li command.split(), 274*760c253cSXin Li stdout=subprocess.PIPE, 275*760c253cSXin Li stderr=subprocess.PIPE, 276*760c253cSXin Li ) 277*760c253cSXin Li (out, err) = p.communicate() 278*760c253cSXin Li 279*760c253cSXin Li if out: 280*760c253cSXin Li out = out.strip() 281*760c253cSXin Li if out != ERROR_STRING: 282*760c253cSXin Li # Each build results contains the checksum of the result image, the 283*760c253cSXin Li # performance cost of the build, the compilation image, the length 284*760c253cSXin Li # of the build, and the length of the text section of the build. 285*760c253cSXin Li ( 286*760c253cSXin Li checksum, 287*760c253cSXin Li cost, 288*760c253cSXin Li image, 289*760c253cSXin Li file_length, 290*760c253cSXin Li text_length, 291*760c253cSXin Li ) = out.split() 292*760c253cSXin Li # Build successfully. 293*760c253cSXin Li break 294*760c253cSXin Li 295*760c253cSXin Li # Build failed. 296*760c253cSXin Li cost = ERROR_STRING 297*760c253cSXin Li except _: 298*760c253cSXin Li # If there is exception getting the cost information of the build, the 299*760c253cSXin Li # build failed. 300*760c253cSXin Li cost = ERROR_STRING 301*760c253cSXin Li 302*760c253cSXin Li # Convert the build cost from String to integer. The build cost is used to 303*760c253cSXin Li # compare a task with another task. Set the build cost of the failing task 304*760c253cSXin Li # to the max integer. The for loop will keep trying until either there is a 305*760c253cSXin Li # success or BUILD_TRIES number of tries have been conducted. 306*760c253cSXin Li self._build_cost = sys.maxint if cost == ERROR_STRING else float(cost) 307*760c253cSXin Li 308*760c253cSXin Li self._checksum = checksum 309*760c253cSXin Li self._file_length = file_length 310*760c253cSXin Li self._text_length = text_length 311*760c253cSXin Li self._image = image 312*760c253cSXin Li 313*760c253cSXin Li self.__LogBuildCost(err) 314*760c253cSXin Li 315*760c253cSXin Li def __Test(self): 316*760c253cSXin Li """__Test the task against benchmark(s) using the input test command.""" 317*760c253cSXin Li 318*760c253cSXin Li # Ensure that the task is compiled before being tested. 319*760c253cSXin Li assert self._image is not None 320*760c253cSXin Li 321*760c253cSXin Li # If the task does not compile, no need to test. 322*760c253cSXin Li if self._image == ERROR_STRING: 323*760c253cSXin Li self._exe_cost = ERROR_STRING 324*760c253cSXin Li return 325*760c253cSXin Li 326*760c253cSXin Li # The unique identifier is passed to the test command. If concurrent 327*760c253cSXin Li # processes are used to compile different tasks, these processes can use the 328*760c253cSXin Li # identifier to write to different file. 329*760c253cSXin Li command = "%s %s %s" % ( 330*760c253cSXin Li Task.TEST_COMMAND, 331*760c253cSXin Li self._image, 332*760c253cSXin Li self._task_identifier, 333*760c253cSXin Li ) 334*760c253cSXin Li 335*760c253cSXin Li # Try TEST_TRIES number of times before confirming that the build fails. 336*760c253cSXin Li for _ in range(TEST_TRIES): 337*760c253cSXin Li try: 338*760c253cSXin Li p = subprocess.Popen( 339*760c253cSXin Li command.split(), 340*760c253cSXin Li stdout=subprocess.PIPE, 341*760c253cSXin Li stderr=subprocess.PIPE, 342*760c253cSXin Li ) 343*760c253cSXin Li (out, err) = p.communicate() 344*760c253cSXin Li 345*760c253cSXin Li if out: 346*760c253cSXin Li out = out.strip() 347*760c253cSXin Li if out != ERROR_STRING: 348*760c253cSXin Li # The test results contains the performance cost of the test. 349*760c253cSXin Li cost = out 350*760c253cSXin Li # Test successfully. 351*760c253cSXin Li break 352*760c253cSXin Li 353*760c253cSXin Li # Test failed. 354*760c253cSXin Li cost = ERROR_STRING 355*760c253cSXin Li except _: 356*760c253cSXin Li # If there is exception getting the cost information of the test, the 357*760c253cSXin Li # test failed. The for loop will keep trying until either there is a 358*760c253cSXin Li # success or TEST_TRIES number of tries have been conducted. 359*760c253cSXin Li cost = ERROR_STRING 360*760c253cSXin Li 361*760c253cSXin Li self._exe_cost = sys.maxint if (cost == ERROR_STRING) else float(cost) 362*760c253cSXin Li 363*760c253cSXin Li self.__LogTestCost(err) 364*760c253cSXin Li 365*760c253cSXin Li def __SetBuildResult( 366*760c253cSXin Li self, (checksum, build_cost, image, file_length, text_length) 367*760c253cSXin Li ): 368*760c253cSXin Li self._checksum = checksum 369*760c253cSXin Li self._build_cost = build_cost 370*760c253cSXin Li self._image = image 371*760c253cSXin Li self._file_length = file_length 372*760c253cSXin Li self._text_length = text_length 373*760c253cSXin Li 374*760c253cSXin Li def __GetBuildResult(self): 375*760c253cSXin Li return ( 376*760c253cSXin Li self._checksum, 377*760c253cSXin Li self._build_cost, 378*760c253cSXin Li self._image, 379*760c253cSXin Li self._file_length, 380*760c253cSXin Li self._text_length, 381*760c253cSXin Li ) 382*760c253cSXin Li 383*760c253cSXin Li def GetTestResult(self): 384*760c253cSXin Li return self._exe_cost 385*760c253cSXin Li 386*760c253cSXin Li def __SetTestResult(self, exe_cost): 387*760c253cSXin Li self._exe_cost = exe_cost 388*760c253cSXin Li 389*760c253cSXin Li def LogSteeringCost(self): 390*760c253cSXin Li """Log the performance results for the task. 391*760c253cSXin Li 392*760c253cSXin Li This method is called by the steering stage and this method writes the 393*760c253cSXin Li results out to a file. The results include the build and the test results. 394*760c253cSXin Li """ 395*760c253cSXin Li 396*760c253cSXin Li steering_log = "%s/%s/steering.txt" % self._log_path 397*760c253cSXin Li 398*760c253cSXin Li _CreateDirectory(steering_log) 399*760c253cSXin Li 400*760c253cSXin Li with open(steering_log, "w") as out_file: 401*760c253cSXin Li # Include the build and the test results. 402*760c253cSXin Li steering_result = ( 403*760c253cSXin Li self._flag_set, 404*760c253cSXin Li self._checksum, 405*760c253cSXin Li self._build_cost, 406*760c253cSXin Li self._image, 407*760c253cSXin Li self._file_length, 408*760c253cSXin Li self._text_length, 409*760c253cSXin Li self._exe_cost, 410*760c253cSXin Li ) 411*760c253cSXin Li 412*760c253cSXin Li # Write out the result in the comma-separated format (CSV). 413*760c253cSXin Li out_file.write("%s,%s,%s,%s,%s,%s,%s\n" % steering_result) 414*760c253cSXin Li 415*760c253cSXin Li def __LogBuildCost(self, log): 416*760c253cSXin Li """Log the build results for the task. 417*760c253cSXin Li 418*760c253cSXin Li The build results include the compilation time of the build, the result 419*760c253cSXin Li image, the checksum, the file length and the text length of the image. 420*760c253cSXin Li The file length of the image includes the length of the file of the image. 421*760c253cSXin Li The text length only includes the length of the text section of the image. 422*760c253cSXin Li 423*760c253cSXin Li Args: 424*760c253cSXin Li log: The build log of this task. 425*760c253cSXin Li """ 426*760c253cSXin Li 427*760c253cSXin Li build_result_log = "%s/%s/build.txt" % self._log_path 428*760c253cSXin Li 429*760c253cSXin Li _CreateDirectory(build_result_log) 430*760c253cSXin Li 431*760c253cSXin Li with open(build_result_log, "w") as out_file: 432*760c253cSXin Li build_result = ( 433*760c253cSXin Li self._flag_set, 434*760c253cSXin Li self._build_cost, 435*760c253cSXin Li self._image, 436*760c253cSXin Li self._checksum, 437*760c253cSXin Li self._file_length, 438*760c253cSXin Li self._text_length, 439*760c253cSXin Li ) 440*760c253cSXin Li 441*760c253cSXin Li # Write out the result in the comma-separated format (CSV). 442*760c253cSXin Li out_file.write("%s,%s,%s,%s,%s,%s\n" % build_result) 443*760c253cSXin Li 444*760c253cSXin Li # The build information about running the build. 445*760c253cSXin Li build_run_log = "%s/%s/build_log.txt" % self._log_path 446*760c253cSXin Li _CreateDirectory(build_run_log) 447*760c253cSXin Li 448*760c253cSXin Li with open(build_run_log, "w") as out_log_file: 449*760c253cSXin Li # Write out the execution information. 450*760c253cSXin Li out_log_file.write("%s" % log) 451*760c253cSXin Li 452*760c253cSXin Li def __LogTestCost(self, log): 453*760c253cSXin Li """Log the test results for the task. 454*760c253cSXin Li 455*760c253cSXin Li The test results include the runtime execution time of the test. 456*760c253cSXin Li 457*760c253cSXin Li Args: 458*760c253cSXin Li log: The test log of this task. 459*760c253cSXin Li """ 460*760c253cSXin Li 461*760c253cSXin Li test_log = "%s/%s/test.txt" % self._log_path 462*760c253cSXin Li 463*760c253cSXin Li _CreateDirectory(test_log) 464*760c253cSXin Li 465*760c253cSXin Li with open(test_log, "w") as out_file: 466*760c253cSXin Li test_result = (self._flag_set, self._checksum, self._exe_cost) 467*760c253cSXin Li 468*760c253cSXin Li # Write out the result in the comma-separated format (CSV). 469*760c253cSXin Li out_file.write("%s,%s,%s\n" % test_result) 470*760c253cSXin Li 471*760c253cSXin Li # The execution information about running the test. 472*760c253cSXin Li test_run_log = "%s/%s/test_log.txt" % self._log_path 473*760c253cSXin Li 474*760c253cSXin Li _CreateDirectory(test_run_log) 475*760c253cSXin Li 476*760c253cSXin Li with open(test_run_log, "w") as out_log_file: 477*760c253cSXin Li # Append the test log information. 478*760c253cSXin Li out_log_file.write("%s" % log) 479*760c253cSXin Li 480*760c253cSXin Li def IsImproved(self, other): 481*760c253cSXin Li """Compare the current task with another task. 482*760c253cSXin Li 483*760c253cSXin Li Args: 484*760c253cSXin Li other: The other task against which the current task is compared. 485*760c253cSXin Li 486*760c253cSXin Li Returns: 487*760c253cSXin Li True if this task has improvement upon the other task. 488*760c253cSXin Li """ 489*760c253cSXin Li 490*760c253cSXin Li # The execution costs must have been initiated. 491*760c253cSXin Li assert self._exe_cost is not None 492*760c253cSXin Li assert other.GetTestResult() is not None 493*760c253cSXin Li 494*760c253cSXin Li return self._exe_cost < other.GetTestResult() 495