xref: /aosp_15_r20/platform_testing/libraries/screenshot/update_goldens2.py (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1*dd0948b3SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*dd0948b3SAndroid Build Coastguard Worker
3*dd0948b3SAndroid Build Coastguard Workerimport argparse
4*dd0948b3SAndroid Build Coastguard Workerimport os
5*dd0948b3SAndroid Build Coastguard Workerimport shutil
6*dd0948b3SAndroid Build Coastguard Worker
7*dd0948b3SAndroid Build Coastguard Worker
8*dd0948b3SAndroid Build Coastguard Workerdef parse_arguments():
9*dd0948b3SAndroid Build Coastguard Worker    """Parses command-line arguments and returns the parsed arguments object."""
10*dd0948b3SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description="Validate directories and golden files.")
11*dd0948b3SAndroid Build Coastguard Worker    parser.add_argument("--source-directory", required=True, help="Path to the source directory.")
12*dd0948b3SAndroid Build Coastguard Worker    parser.add_argument("--android-build-top", required=True,
13*dd0948b3SAndroid Build Coastguard Worker                        help="Path to the Android build directory.")
14*dd0948b3SAndroid Build Coastguard Worker    return parser.parse_args()
15*dd0948b3SAndroid Build Coastguard Worker
16*dd0948b3SAndroid Build Coastguard Worker
17*dd0948b3SAndroid Build Coastguard Workerdef validate_directories(args):
18*dd0948b3SAndroid Build Coastguard Worker    """Validates the provided source and Android directory arguments and returns their paths if valid."""
19*dd0948b3SAndroid Build Coastguard Worker    if not args.source_directory or not args.android_build_top:
20*dd0948b3SAndroid Build Coastguard Worker        print("Error: Both --source-directory and --android-build-top arguments are required.")
21*dd0948b3SAndroid Build Coastguard Worker        return None
22*dd0948b3SAndroid Build Coastguard Worker
23*dd0948b3SAndroid Build Coastguard Worker    source_dir = args.source_directory
24*dd0948b3SAndroid Build Coastguard Worker    android_dir = args.android_build_top
25*dd0948b3SAndroid Build Coastguard Worker
26*dd0948b3SAndroid Build Coastguard Worker    is_source_dir_valid = os.path.isdir(source_dir)
27*dd0948b3SAndroid Build Coastguard Worker    is_android_dir_valid = os.path.isdir(android_dir)
28*dd0948b3SAndroid Build Coastguard Worker
29*dd0948b3SAndroid Build Coastguard Worker    if not is_source_dir_valid:
30*dd0948b3SAndroid Build Coastguard Worker        print(f"Error: Source directory does not exist: {source_dir}")
31*dd0948b3SAndroid Build Coastguard Worker
32*dd0948b3SAndroid Build Coastguard Worker    if not is_android_dir_valid:
33*dd0948b3SAndroid Build Coastguard Worker        print(f"Error: Android build directory does not exist: {android_dir}")
34*dd0948b3SAndroid Build Coastguard Worker
35*dd0948b3SAndroid Build Coastguard Worker    if not is_source_dir_valid or not is_android_dir_valid:
36*dd0948b3SAndroid Build Coastguard Worker        return None
37*dd0948b3SAndroid Build Coastguard Worker
38*dd0948b3SAndroid Build Coastguard Worker    return [source_dir, android_dir]
39*dd0948b3SAndroid Build Coastguard Worker
40*dd0948b3SAndroid Build Coastguard Worker
41*dd0948b3SAndroid Build Coastguard Workerdef find_golden_files(source_dir):
42*dd0948b3SAndroid Build Coastguard Worker    """Finds golden files within the source directory and returns their filenames or handles errors."""
43*dd0948b3SAndroid Build Coastguard Worker    golden_files = []
44*dd0948b3SAndroid Build Coastguard Worker    for root, _, files in os.walk(source_dir):
45*dd0948b3SAndroid Build Coastguard Worker        for file in files:
46*dd0948b3SAndroid Build Coastguard Worker            if "_goldResult_" in file and file.endswith(".textproto"):
47*dd0948b3SAndroid Build Coastguard Worker                golden_files.append(file)
48*dd0948b3SAndroid Build Coastguard Worker
49*dd0948b3SAndroid Build Coastguard Worker    if not golden_files:
50*dd0948b3SAndroid Build Coastguard Worker        print("Error: No golden files found in the source directory.")
51*dd0948b3SAndroid Build Coastguard Worker        return None
52*dd0948b3SAndroid Build Coastguard Worker
53*dd0948b3SAndroid Build Coastguard Worker    return golden_files
54*dd0948b3SAndroid Build Coastguard Worker
55*dd0948b3SAndroid Build Coastguard Worker
56*dd0948b3SAndroid Build Coastguard Workerdef validate_protos(source_dir, proto_files):
57*dd0948b3SAndroid Build Coastguard Worker    """Validates proto files, extracts image locations, and handles errors."""
58*dd0948b3SAndroid Build Coastguard Worker    all_image_locations = []
59*dd0948b3SAndroid Build Coastguard Worker    for filename in proto_files:
60*dd0948b3SAndroid Build Coastguard Worker        required_image_locations = {"image_location_diff": False, "image_location_golden": False,
61*dd0948b3SAndroid Build Coastguard Worker                                    "image_location_reference": False, "image_location_test": False}
62*dd0948b3SAndroid Build Coastguard Worker        found_image_locations = {}
63*dd0948b3SAndroid Build Coastguard Worker
64*dd0948b3SAndroid Build Coastguard Worker        with open(os.path.join(source_dir, filename), 'r') as file:
65*dd0948b3SAndroid Build Coastguard Worker            for line in file:
66*dd0948b3SAndroid Build Coastguard Worker                for location in required_image_locations:
67*dd0948b3SAndroid Build Coastguard Worker                    if line.startswith(location):
68*dd0948b3SAndroid Build Coastguard Worker                        if required_image_locations[location]:
69*dd0948b3SAndroid Build Coastguard Worker                            print(f"Error: Duplicate '{location}' entry found in {filename}.")
70*dd0948b3SAndroid Build Coastguard Worker                            return None
71*dd0948b3SAndroid Build Coastguard Worker                        required_image_locations[location] = True
72*dd0948b3SAndroid Build Coastguard Worker                        found_image_locations[location] = line.split(": ")[1].strip().strip('"')
73*dd0948b3SAndroid Build Coastguard Worker                        break
74*dd0948b3SAndroid Build Coastguard Worker
75*dd0948b3SAndroid Build Coastguard Worker        if not all(required_image_locations.values()):
76*dd0948b3SAndroid Build Coastguard Worker            missing_keys = [key for key, found in required_image_locations.items() if not found]
77*dd0948b3SAndroid Build Coastguard Worker            print(f"Error: Missing image location(s) in {filename}: {', '.join(missing_keys)}")
78*dd0948b3SAndroid Build Coastguard Worker            return None
79*dd0948b3SAndroid Build Coastguard Worker        else:
80*dd0948b3SAndroid Build Coastguard Worker            print(f"Proto file {filename} is valid.")
81*dd0948b3SAndroid Build Coastguard Worker            all_image_locations.append(found_image_locations)
82*dd0948b3SAndroid Build Coastguard Worker    return all_image_locations
83*dd0948b3SAndroid Build Coastguard Worker
84*dd0948b3SAndroid Build Coastguard Worker
85*dd0948b3SAndroid Build Coastguard Workerdef validate_test_images(source_dir, all_image_locations):
86*dd0948b3SAndroid Build Coastguard Worker    """Validates if PNG files exist in the source directory based on provided image locations."""
87*dd0948b3SAndroid Build Coastguard Worker    for image_locations in all_image_locations:
88*dd0948b3SAndroid Build Coastguard Worker        for location, path in image_locations.items():
89*dd0948b3SAndroid Build Coastguard Worker            if location != "image_location_golden":
90*dd0948b3SAndroid Build Coastguard Worker                base_name = os.path.splitext(os.path.basename(path))[0]
91*dd0948b3SAndroid Build Coastguard Worker                for root, _, files in os.walk(source_dir):
92*dd0948b3SAndroid Build Coastguard Worker                    for file in files:
93*dd0948b3SAndroid Build Coastguard Worker                        if file.startswith(base_name) and file.endswith(".png"):
94*dd0948b3SAndroid Build Coastguard Worker                            image_locations[location] = os.path.join(root, file)
95*dd0948b3SAndroid Build Coastguard Worker                            break
96*dd0948b3SAndroid Build Coastguard Worker                    else:
97*dd0948b3SAndroid Build Coastguard Worker                        print(f"Error: No PNG file found matching {path} in {source_dir}")
98*dd0948b3SAndroid Build Coastguard Worker                        return None
99*dd0948b3SAndroid Build Coastguard Worker        filename_without_ext = \
100*dd0948b3SAndroid Build Coastguard Worker        os.path.splitext(os.path.basename(image_locations["image_location_golden"]))[0]
101*dd0948b3SAndroid Build Coastguard Worker        print(f"Golden {filename_without_ext} is valid.")
102*dd0948b3SAndroid Build Coastguard Worker    return all_image_locations
103*dd0948b3SAndroid Build Coastguard Worker
104*dd0948b3SAndroid Build Coastguard Worker
105*dd0948b3SAndroid Build Coastguard Workerdef update_goldens(android_dir, updated_image_locations_list):
106*dd0948b3SAndroid Build Coastguard Worker    """Copies updated 'image_location_test' images to their 'image_location_golden' paths in the android_build_top directory."""
107*dd0948b3SAndroid Build Coastguard Worker    for image_locations in updated_image_locations_list:
108*dd0948b3SAndroid Build Coastguard Worker        test_image_path = image_locations["image_location_test"]
109*dd0948b3SAndroid Build Coastguard Worker        golden_image_path = os.path.join(android_dir, image_locations["image_location_golden"])
110*dd0948b3SAndroid Build Coastguard Worker
111*dd0948b3SAndroid Build Coastguard Worker        try:
112*dd0948b3SAndroid Build Coastguard Worker            shutil.copy2(test_image_path, golden_image_path)
113*dd0948b3SAndroid Build Coastguard Worker            print(f"Updated golden image: {golden_image_path}")
114*dd0948b3SAndroid Build Coastguard Worker        except IOError as e:
115*dd0948b3SAndroid Build Coastguard Worker            print(f"Error updating golden image: {e}")
116*dd0948b3SAndroid Build Coastguard Worker
117*dd0948b3SAndroid Build Coastguard Worker
118*dd0948b3SAndroid Build Coastguard Workerdef main():
119*dd0948b3SAndroid Build Coastguard Worker    args = parse_arguments()
120*dd0948b3SAndroid Build Coastguard Worker
121*dd0948b3SAndroid Build Coastguard Worker    directories = validate_directories(args)
122*dd0948b3SAndroid Build Coastguard Worker    if directories is None:
123*dd0948b3SAndroid Build Coastguard Worker        return
124*dd0948b3SAndroid Build Coastguard Worker
125*dd0948b3SAndroid Build Coastguard Worker    source_dir, android_dir = directories
126*dd0948b3SAndroid Build Coastguard Worker
127*dd0948b3SAndroid Build Coastguard Worker    proto_files = find_golden_files(source_dir)
128*dd0948b3SAndroid Build Coastguard Worker    if proto_files is None:
129*dd0948b3SAndroid Build Coastguard Worker        return
130*dd0948b3SAndroid Build Coastguard Worker
131*dd0948b3SAndroid Build Coastguard Worker    all_image_locations = validate_protos(source_dir, proto_files)
132*dd0948b3SAndroid Build Coastguard Worker    if all_image_locations is None:
133*dd0948b3SAndroid Build Coastguard Worker        return
134*dd0948b3SAndroid Build Coastguard Worker
135*dd0948b3SAndroid Build Coastguard Worker    updated_image_locations_list = validate_test_images(source_dir, all_image_locations)
136*dd0948b3SAndroid Build Coastguard Worker    if updated_image_locations_list is None:
137*dd0948b3SAndroid Build Coastguard Worker        return
138*dd0948b3SAndroid Build Coastguard Worker
139*dd0948b3SAndroid Build Coastguard Worker    update_goldens(android_dir, updated_image_locations_list)
140*dd0948b3SAndroid Build Coastguard Worker
141*dd0948b3SAndroid Build Coastguard Worker
142*dd0948b3SAndroid Build Coastguard Workerif __name__ == "__main__":
143*dd0948b3SAndroid Build Coastguard Worker    main()
144