diff --git a/tests/lens_tests/test_canon_lenses.py b/tests/lens_tests/test_canon_lenses.py index 3217574d..37f5200a 100644 --- a/tests/lens_tests/test_canon_lenses.py +++ b/tests/lens_tests/test_canon_lenses.py @@ -4,7 +4,7 @@ import re import os import system_tests import math -from lens_tests.utils import extract_lenses_from_cpp +from lens_tests.utils import extract_lenses_from_cpp, make_test_cases # get directory of the current file file_dir = os.path.dirname(os.path.realpath(__file__)) @@ -14,38 +14,37 @@ canon_lens_file = os.path.abspath(os.path.join(file_dir, "./../../src/canonmn_in startpattern = "constexpr TagDetails canonCsLensType[] = {" # use utils function to extract all lenses lenses = extract_lenses_from_cpp(canon_lens_file, startpattern) +# use utils function to define test case data +test_cases = make_test_cases(lenses) +# see https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/Canon.pm#L9678 def aperture_to_raw_exif(aperture): - #see https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/Canon.pm#L9678 - aperture = float(aperture) # for apertures < 1 the below is negative - num = math.log(aperture)*2/math.log(2) + num = math.log(aperture) * 2 / math.log(2) # temporarily make the number positive - if (num < 0): + if num < 0: num = -num sign = -1 else: - sign = 1; + sign = 1 val = int(num) frac = num - val - if (abs(frac - 0.33) < 0.05): - frac = 0x0c - elif (abs(frac - 0.67) < 0.05): - frac = 0x14; + if abs(frac - 0.33) < 0.05: + frac = 0x0C + elif abs(frac - 0.67) < 0.05: + frac = 0x14 else: frac = int(frac * 0x20 + 0.5) - return sign * (val * 0x20 + frac); + return sign * (val * 0x20 + frac) -for (lens_id, lens_desc, meta) in lenses: +for lens_tc in test_cases: - tc = float(meta["tc"] or 1) - - testname = lens_id + "_" + lens_desc + testname = lens_tc["id"] + "_" + lens_tc["desc"] globals()[testname] = system_tests.CaseMeta( "canon_lenses." + testname, @@ -58,10 +57,10 @@ for (lens_id, lens_desc, meta) in lenses: "stderr": [""], "stdout": ["Exif.CanonCs.LensType Short 1 $lens_description\n"], "retval": [0], - "lens_id": lens_id, - "lens_description": lens_desc, - "aperture_max": aperture_to_raw_exif(meta["aperture_max_short"]), - "focal_length_min": int(int(meta["focal_length_min"]) * tc), - "focal_length_max": int(int(meta["focal_length_max"]) * tc), + "lens_id": lens_tc["id"], + "lens_description": lens_tc["target"], + "aperture_max": aperture_to_raw_exif(lens_tc["aperture_max_short"]), + "focal_length_min": int(lens_tc["focal_length_min"] * lens_tc["tc"]), + "focal_length_max": int(lens_tc["focal_length_max"] * lens_tc["tc"]), }, ) diff --git a/tests/lens_tests/utils.py b/tests/lens_tests/utils.py index 31e7afa6..fa03cf86 100644 --- a/tests/lens_tests/utils.py +++ b/tests/lens_tests/utils.py @@ -1,6 +1,7 @@ import re import os import logging +from itertools import groupby log = logging.getLogger(__name__) @@ -49,17 +50,54 @@ def extract_meta(text, pattern=LENS_META_DEFAULT_RE): """ result = pattern.match(text) - if(not result): + if not result: # didn't match return None ret = result.groupdict() # set min to max value if we didn't get a range but a single value - ret['focal_length_min'] = ret['focal_length_min'] or ret['focal_length_max'] - ret['aperture_max_short'] = ret['aperture_max_short'] or ret['aperture_max_tele'] + ret["focal_length_min"] = int(ret["focal_length_min"] or ret["focal_length_max"]) + ret["focal_length_max"] = int(ret["focal_length_max"]) + ret["aperture_max_short"] = float(ret["aperture_max_short"] or ret["aperture_max_tele"]) + ret["aperture_max_tele"] = float(ret["aperture_max_tele"]) + ret["tc"] = float(ret["tc"] or 1) return ret +# FIXME explain somwhere that lens_is_match(l1,l2) does not imply lens_is_match(l2,l1) +# becuse we don't have short and tele aperture values in exif +def lens_is_match(l1, l2): + """ + Test if lens l2 is compatible with lens l1, + assuming we write l1's metadata and apeture_max_short into exif + """ + return ( + all([l1[k] == l2[k] for k in ["tc", "focal_length_min", "focal_length_max"]]) + and l2["aperture_max_short"] <= l1["aperture_max_short"] <= l2["aperture_max_tele"] + ) + + +def make_test_cases(lenses): + """ + Creates a test case for each lens + Main job of this function is to collect all ambiguous lenses and define a test target + as the " *OR* " joined string of all ambiguous lens descriptions + """ + test_cases = [] + for lens_id, group in groupby(lenses, lambda x: x["id"]): + lens_group = list(group) + test_cases += [ + { + **lens["meta"], + "id": lens["id"], + "desc": lens["desc"], + "target": " *OR* ".join([l["desc"] for l in lens_group if lens_is_match(lens["meta"], l["meta"])]), + } + for lens in lens_group + ] + return test_cases + + def extract_lenses_from_cpp(filename, start_pattern): """ Extract lens information from the lens descritpions array in a maker note cpp file @@ -99,6 +137,5 @@ def extract_lenses_from_cpp(filename, start_pattern): log.error(f"Failure extracing metadata from lens description: {lens_entry[0]}: {lens_entry[1]}.") continue - lenses.append((*lens_entry, meta)) + lenses.append({"id": lens_entry[0], "desc": lens_entry[1], "meta": meta}) return lenses -