|
30 | 30 | import local_cloudbuild |
31 | 31 |
|
32 | 32 |
|
| 33 | +# Matches script boilerplate |
| 34 | +STAGING_DIR_REGEX = re.compile( |
| 35 | + b'(?m)Copying source to staging directory (.+)$') |
| 36 | + |
33 | 37 | class ValidationUtilsTest(unittest.TestCase): |
34 | 38 |
|
35 | 39 | def test_get_field_value(self): |
@@ -163,6 +167,25 @@ def test_sub_and_quote(self): |
163 | 167 | used = set() |
164 | 168 | local_cloudbuild.sub_and_quote(s, subs, used) |
165 | 169 |
|
| 170 | + def check_call_with_capture(self, *args, **kw_args): |
| 171 | + """Act like subprocess.check_call but capture stdout""" |
| 172 | + try: |
| 173 | + self.check_call_output = subprocess.check_output(*args, **kw_args) |
| 174 | + print(self.check_call_output) |
| 175 | + except subprocess.CalledProcessError as e: |
| 176 | + self.check_call_output = e.output |
| 177 | + print(self.check_call_output) |
| 178 | + raise |
| 179 | + |
| 180 | + def have_docker(self): |
| 181 | + """Determine if the Docker daemon is present and usable""" |
| 182 | + if ((shutil.which('docker') is not None) and |
| 183 | + (subprocess.call(['docker', 'info'], |
| 184 | + stdout=subprocess.DEVNULL, |
| 185 | + stderr=subprocess.DEVNULL) == 0)): |
| 186 | + return True |
| 187 | + return False |
| 188 | + |
166 | 189 | def test_get_cloudbuild(self): |
167 | 190 | args = argparse.Namespace( |
168 | 191 | config='some_config_file', |
@@ -339,7 +362,7 @@ def test_generate_script_unused_user_substitution(self): |
339 | 362 | steps=[], |
340 | 363 | substitutions={'_FOO':'_foo'}, |
341 | 364 | ) |
342 | | - with self.assertRaisesRegexp(ValueError, 'User substitution variables'): |
| 365 | + with self.assertRaisesRegex(ValueError, 'User substitution variables'): |
343 | 366 | actual = local_cloudbuild.generate_script(cloudbuild) |
344 | 367 |
|
345 | 368 | def test_make_executable(self): |
@@ -369,58 +392,59 @@ def test_write_script(self): |
369 | 392 | self.assertEqual(actual, contents) |
370 | 393 |
|
371 | 394 | def test_local_cloudbuild(self): |
372 | | - # Actually run it if we can find a docker command. |
373 | | - should_run = False |
374 | | - if ((shutil.which('docker') is not None) and |
375 | | - (subprocess.call(['docker', 'info'], |
376 | | - stdout=subprocess.DEVNULL, |
377 | | - stderr=subprocess.DEVNULL) == 0)): |
378 | | - should_run = True |
| 395 | + if not self.have_docker(): |
| 396 | + self.fail('This test requires a working Docker daemon') |
379 | 397 |
|
380 | 398 | # Read cloudbuild.yaml from testdata file, write output to |
381 | 399 | # tempdir, and maybe try to run it |
382 | | - with tempfile.TemporaryDirectory( |
383 | | - prefix='local_cloudbuild_test_') as tempdir: |
384 | | - cases = ( |
385 | | - # Everything is ok |
386 | | - ('cloudbuild_ok.yaml', None, None), |
387 | | - # Builtin substitutions like $PROJECT_ID work |
388 | | - ('cloudbuild_builtin_substitutions.yaml', None, None), |
389 | | - # User substitutions like $_FOO work |
390 | | - ('cloudbuild_user_substitutions.yaml', |
391 | | - {'_FOO':'this is foo value'}, |
392 | | - None |
393 | | - ), |
394 | | - # User substitutions like $_FOO fails when undefined |
395 | | - ('cloudbuild_user_substitutions.yaml', None, ValueError), |
396 | | - # Exit code 1 (failure) |
397 | | - ('cloudbuild_err_rc1.yaml', None, subprocess.CalledProcessError), |
398 | | - # Command not found |
399 | | - ('cloudbuild_err_not_found.yaml', None, subprocess.CalledProcessError), |
| 400 | + cases = ( |
| 401 | + # Everything is ok |
| 402 | + ('cloudbuild_ok.yaml', None, None), |
| 403 | + # Builtin substitutions like $PROJECT_ID work |
| 404 | + ('cloudbuild_builtin_substitutions.yaml', None, None), |
| 405 | + # User substitutions like $_FOO work |
| 406 | + ('cloudbuild_user_substitutions.yaml', |
| 407 | + {'_FOO':'this is foo value'}, |
| 408 | + None |
| 409 | + ), |
| 410 | + # User substitutions like $_FOO fails when undefined |
| 411 | + ('cloudbuild_user_substitutions.yaml', None, ValueError), |
| 412 | + # Exit code 1 (failure) |
| 413 | + ('cloudbuild_err_rc1.yaml', None, subprocess.CalledProcessError), |
| 414 | + # Command not found |
| 415 | + ('cloudbuild_err_not_found.yaml', None, subprocess.CalledProcessError), |
| 416 | + # Cleaning up files owned by root |
| 417 | + ('cloudbuild_difficult_cleanup.yaml', None, None), |
| 418 | + ) |
| 419 | + for case in cases: |
| 420 | + with self.subTest(case=case), \ |
| 421 | + tempfile.TemporaryDirectory(prefix='local_cloudbuild_test_') as tempdir, \ |
| 422 | + unittest.mock.patch('subprocess.check_call', self.check_call_with_capture): |
| 423 | + config_name, substitutions, exception = case |
| 424 | + if substitutions is None: |
| 425 | + substitutions = local_cloudbuild.DEFAULT_SUBSTITUTIONS |
| 426 | + should_succeed = (exception is None) |
| 427 | + config = os.path.join(self.testdata_dir, config_name) |
| 428 | + actual_output_script = os.path.join( |
| 429 | + tempdir, config_name + '_local.sh') |
| 430 | + args = argparse.Namespace( |
| 431 | + config=config, |
| 432 | + output_script=actual_output_script, |
| 433 | + run=True, |
| 434 | + substitutions=substitutions, |
400 | 435 | ) |
401 | | - for case in cases: |
402 | | - with self.subTest(case=case): |
403 | | - config_name, substitutions, exception = case |
404 | | - if substitutions is None: |
405 | | - substitutions = local_cloudbuild.DEFAULT_SUBSTITUTIONS |
406 | | - should_succeed = (exception is None) |
407 | | - config = os.path.join(self.testdata_dir, config_name) |
408 | | - actual_output_script = os.path.join( |
409 | | - tempdir, config_name + '_local.sh') |
410 | | - args = argparse.Namespace( |
411 | | - config=config, |
412 | | - output_script=actual_output_script, |
413 | | - run=should_run, |
414 | | - substitutions=substitutions, |
415 | | - ) |
416 | | - if should_run: |
417 | | - print("Executing docker commands in {}".format(actual_output_script)) |
418 | | - if should_succeed: |
| 436 | + |
| 437 | + if should_succeed: |
| 438 | + local_cloudbuild.local_cloudbuild(args) |
| 439 | + else: |
| 440 | + with self.assertRaises(exception): |
419 | 441 | local_cloudbuild.local_cloudbuild(args) |
420 | | - else: |
421 | | - with self.assertRaises(exception): |
422 | | - local_cloudbuild.local_cloudbuild(args) |
423 | 442 |
|
| 443 | + # Check that staging dir was cleaned up |
| 444 | + match = re.search(STAGING_DIR_REGEX, self.check_call_output) |
| 445 | + self.assertTrue(match) |
| 446 | + staging_dir = match.group(1) |
| 447 | + self.assertFalse(os.path.isdir(staging_dir), staging_dir) |
424 | 448 |
|
425 | 449 | def test_parse_args(self): |
426 | 450 | # Test explicit output_script |
|
0 commit comments