1# Copyright 2024 The ChromiumOS Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4# noqa: E402 pylint: disable=invalid-name 5 6"""Contains all the pre-upload and pre-commit validation checks for SuiteSets""" 7 8# Optional but recommended 9PRESUBMIT_VERSION = "2.0.0" 10 11# Mandatory: run under Python 3 12USE_PYTHON3 = True 13 14 15def CheckProtosAreUpToDate(input_api, output_api): 16 """CheckProtosAreUpToDate validates the generated protos are up-to-date.""" 17 suite_set_root = input_api.PresubmitLocalPath() 18 err = _generate_protos(input_api, suite_set_root) 19 if err: 20 return [output_api.PresubmitError(err)] 21 22 proto_dir = input_api.os_path.join(suite_set_root, "generated") 23 proto_diff, err = _git_diff(input_api, proto_dir) 24 if err: 25 return [output_api.PresubmitError(err)] 26 if proto_diff: 27 err_msg = ( 28 "The generated proto files are not up-to-date.\n\n" 29 "Amend the commit to include the unstaged changes\n" 30 "to the generated proto files." 31 ) 32 return [output_api.PresubmitError(err_msg)] 33 return [] 34 35 36def CheckSuiteSetsAreWellFormed(input_api, output_api): 37 """CheckSuiteSetsAreWellFormed checks the wellformedness of the protos.""" 38 script_path = input_api.os_path.join( 39 input_api.PresubmitLocalPath(), "presubmit/validate_suite_sets.py" 40 ) 41 suite_sets_proto_file = input_api.os_path.join( 42 input_api.PresubmitLocalPath(), "generated/suite_sets.jsonpb" 43 ) 44 suites_proto_file = input_api.os_path.join( 45 input_api.PresubmitLocalPath(), "generated/suites.jsonpb" 46 ) 47 any_file_affected = ( 48 _is_affected_file(input_api, script_path) 49 or _is_affected_file(input_api, suite_sets_proto_file) 50 or _is_affected_file(input_api, suites_proto_file) 51 ) 52 if not any_file_affected: 53 return [] 54 55 cmd_name = "validate_suite_sets" 56 cmd = [ 57 input_api.python3_executable, 58 script_path, 59 "--suite_set_file", 60 suite_sets_proto_file, 61 "--suite_file", 62 suites_proto_file, 63 ] 64 presubmit_cmd = input_api.Command( 65 name=cmd_name, 66 cmd=cmd, 67 kwargs={}, 68 message=output_api.PresubmitPromptWarning, 69 ) 70 print("Running " + cmd_name) 71 return input_api.RunTests([presubmit_cmd]) 72 73 74def _generate_protos(input_api, suite_set_root): 75 """_generate_protos calls the given script.""" 76 generate_script = input_api.os_path.join(suite_set_root, "generate.sh") 77 cmd = [generate_script] 78 proc = input_api.subprocess.Popen( 79 args=cmd, 80 stdout=input_api.subprocess.PIPE, 81 stderr=input_api.subprocess.PIPE, 82 universal_newlines=True, 83 ) 84 _, err = proc.communicate() 85 if proc.returncode and err: 86 return f"Error when calling {generate_script}: {str(err)}" 87 return None 88 89 90def _git_diff(input_api, path): 91 """_git_diff returns the string generated by git diff for the given path.""" 92 cmd = ["git", "diff", path] 93 proc = input_api.subprocess.Popen( 94 args=cmd, 95 stdout=input_api.subprocess.PIPE, 96 stderr=input_api.subprocess.PIPE, 97 universal_newlines=True, 98 ) 99 out, err = proc.communicate() 100 if proc.returncode and err: 101 return None, f"Error when calling git diff: {str(err)}" 102 return out.strip(), None 103 104 105def _is_affected_file(input_api, file_to_check): 106 """_is_affected_file returns True if given file in the affected files.""" 107 108 def is_file_to_check(file): 109 norm_f_path = input_api.os_path.normpath(file.AbsoluteLocalPath()) 110 norm_f_to_check_path = input_api.os_path.normpath(file_to_check) 111 return norm_f_path == norm_f_to_check_path 112 113 affected_files = input_api.AffectedFiles(file_filter=is_file_to_check) 114 return len(affected_files) > 0 115