|
9 | 9 | from test.support.os_helper import TESTFN, unlink, rmtree |
10 | 10 | from textwrap import dedent |
11 | 11 | from unittest import TestCase |
| 12 | +import difflib |
12 | 13 | import inspect |
13 | 14 | import os.path |
14 | 15 | import re |
@@ -3042,6 +3043,140 @@ def test_cli_force(self): |
3042 | 3043 | generated = f.read() |
3043 | 3044 | self.assertEndsWith(generated, checksum) |
3044 | 3045 |
|
| 3046 | + DRY_RUN_CODE = dedent(""" |
| 3047 | + /*[clinic input] |
| 3048 | + func |
| 3049 | + a: int |
| 3050 | + / |
| 3051 | +
|
| 3052 | + Docstring. |
| 3053 | + [clinic start generated code]*/ |
| 3054 | + """) |
| 3055 | + |
| 3056 | + def make_dry_run_file(self, tmp_dir): |
| 3057 | + fn = os.path.join(tmp_dir, "test.c") |
| 3058 | + with open(fn, "w", encoding="utf-8") as f: |
| 3059 | + f.write(self.DRY_RUN_CODE) |
| 3060 | + return fn |
| 3061 | + |
| 3062 | + def check_unchanged(self, tmp_dir, fn, pre_mtime): |
| 3063 | + # Neither the source file nor the destination file |
| 3064 | + # nor its directory is created or modified. |
| 3065 | + with open(fn, encoding="utf-8") as f: |
| 3066 | + self.assertEqual(f.read(), self.DRY_RUN_CODE) |
| 3067 | + self.assertEqual(os.stat(fn).st_mtime_ns, pre_mtime) |
| 3068 | + self.assertEqual(os.listdir(tmp_dir), ["test.c"]) |
| 3069 | + |
| 3070 | + def test_cli_dry_run(self): |
| 3071 | + with os_helper.temp_dir() as tmp_dir: |
| 3072 | + fn = self.make_dry_run_file(tmp_dir) |
| 3073 | + pre_mtime = os.stat(fn).st_mtime_ns |
| 3074 | + out = self.expect_success("--dry-run", fn) |
| 3075 | + self.assertEqual(out.splitlines(), [ |
| 3076 | + f"would create {os.path.join(tmp_dir, 'clinic', 'test.c.h')}", |
| 3077 | + f"would update {fn}", |
| 3078 | + ]) |
| 3079 | + self.check_unchanged(tmp_dir, fn, pre_mtime) |
| 3080 | + |
| 3081 | + def test_cli_dry_run_no_change(self): |
| 3082 | + with os_helper.temp_dir() as tmp_dir: |
| 3083 | + fn = self.make_dry_run_file(tmp_dir) |
| 3084 | + self.expect_success(fn) |
| 3085 | + self.assertEqual(self.expect_success("--dry-run", fn), "") |
| 3086 | + self.assertEqual(self.expect_success("--diff", fn), "") |
| 3087 | + |
| 3088 | + def test_cli_dry_run_no_clinic_block(self): |
| 3089 | + with os_helper.temp_dir() as tmp_dir: |
| 3090 | + fn = os.path.join(tmp_dir, "test.c") |
| 3091 | + with open(fn, "w", encoding="utf-8") as f: |
| 3092 | + f.write("int x;\n") |
| 3093 | + self.assertEqual(self.expect_success("--dry-run", fn), "") |
| 3094 | + |
| 3095 | + def test_cli_dry_run_output(self): |
| 3096 | + with os_helper.temp_dir() as tmp_dir: |
| 3097 | + fn = self.make_dry_run_file(tmp_dir) |
| 3098 | + out_fn = os.path.join(tmp_dir, "output.c") |
| 3099 | + out = self.expect_success("--dry-run", "-o", out_fn, fn) |
| 3100 | + self.assertIn(f"would create {out_fn}", out) |
| 3101 | + self.assertNotIn(f"would update {fn}", out) |
| 3102 | + self.assertFalse(os.path.exists(out_fn)) |
| 3103 | + |
| 3104 | + def test_cli_dry_run_make(self): |
| 3105 | + with os_helper.temp_dir() as tmp_dir: |
| 3106 | + fn = self.make_dry_run_file(tmp_dir) |
| 3107 | + pre_mtime = os.stat(fn).st_mtime_ns |
| 3108 | + out = self.expect_success("--dry-run", "--make", "--srcdir", tmp_dir) |
| 3109 | + self.assertIn(f"would update {fn}", out) |
| 3110 | + self.check_unchanged(tmp_dir, fn, pre_mtime) |
| 3111 | + |
| 3112 | + def test_cli_dry_run_verbose(self): |
| 3113 | + with os_helper.temp_dir() as tmp_dir: |
| 3114 | + fn = self.make_dry_run_file(tmp_dir) |
| 3115 | + out, err, code = self.run_clinic("-v", "--dry-run", fn) |
| 3116 | + self.assertEqual(code, 0) |
| 3117 | + # The progress goes to stderr, so that the standard output |
| 3118 | + # contains only the report. |
| 3119 | + self.assertEqual(err.splitlines(), [fn]) |
| 3120 | + self.assertEqual(out.splitlines(), [ |
| 3121 | + f"would create {os.path.join(tmp_dir, 'clinic', 'test.c.h')}", |
| 3122 | + f"would update {fn}", |
| 3123 | + ]) |
| 3124 | + |
| 3125 | + def test_cli_dry_run_checksum_mismatch(self): |
| 3126 | + invalid_input = dedent(""" |
| 3127 | + /*[clinic input] |
| 3128 | + output preset block |
| 3129 | + module test |
| 3130 | + test.fn |
| 3131 | + a: int |
| 3132 | + [clinic start generated code]*/ |
| 3133 | + /*[clinic end generated code: output=bogus input=bogus]*/ |
| 3134 | + """) |
| 3135 | + with os_helper.temp_dir() as tmp_dir: |
| 3136 | + fn = os.path.join(tmp_dir, "test.c") |
| 3137 | + with open(fn, "w", encoding="utf-8") as f: |
| 3138 | + f.write(invalid_input) |
| 3139 | + pre_mtime = os.stat(fn).st_mtime_ns |
| 3140 | + # The dry run does not disable the checksum verification. |
| 3141 | + _, err = self.expect_failure("--dry-run", fn) |
| 3142 | + self.assertIn("Checksum mismatch!", err) |
| 3143 | + # With -f the change is reported, but still not written. |
| 3144 | + out = self.expect_success("--dry-run", "-f", fn) |
| 3145 | + self.assertIn(f"would update {fn}", out) |
| 3146 | + with open(fn, encoding="utf-8") as f: |
| 3147 | + self.assertEqual(f.read(), invalid_input) |
| 3148 | + self.assertEqual(os.stat(fn).st_mtime_ns, pre_mtime) |
| 3149 | + |
| 3150 | + def test_cli_diff(self): |
| 3151 | + with os_helper.temp_dir() as tmp_dir: |
| 3152 | + fn = self.make_dry_run_file(tmp_dir) |
| 3153 | + pre_mtime = os.stat(fn).st_mtime_ns |
| 3154 | + out = self.expect_success("--diff", fn) |
| 3155 | + self.check_unchanged(tmp_dir, fn, pre_mtime) |
| 3156 | + |
| 3157 | + # A new file is created by the patch. |
| 3158 | + dest_fn = os.path.join(tmp_dir, "clinic", "test.c.h") |
| 3159 | + self.assertStartsWith(out, f"--- /dev/null\n+++ {dest_fn}\n@@ -0,0 +1,") |
| 3160 | + self.assertIn(f"--- {fn}\n+++ {fn}\n", out) |
| 3161 | + self.assertIn("+/*[clinic end generated code:", out) |
| 3162 | + |
| 3163 | + # The patch is what clinic would have written. |
| 3164 | + self.expect_success(fn) |
| 3165 | + with open(fn, encoding="utf-8") as f: |
| 3166 | + new_contents = f.read() |
| 3167 | + expected = "".join(difflib.unified_diff( |
| 3168 | + self.DRY_RUN_CODE.splitlines(keepends=True), |
| 3169 | + new_contents.splitlines(keepends=True), |
| 3170 | + fromfile=fn, tofile=fn)) |
| 3171 | + self.assertEndsWith(out, expected) |
| 3172 | + |
| 3173 | + def test_cli_fail_converters_and_dry_run(self): |
| 3174 | + for opt in "--dry-run", "--diff": |
| 3175 | + with self.subTest(opt=opt): |
| 3176 | + _, err = self.expect_failure("--converters", opt) |
| 3177 | + msg = "can't use --dry-run or --diff with --converters" |
| 3178 | + self.assertIn(msg, err) |
| 3179 | + |
3045 | 3180 | def test_cli_make(self): |
3046 | 3181 | c_code = dedent(""" |
3047 | 3182 | /*[clinic input] |
|
0 commit comments