|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Tests for `codeql_test_run.py`. |
| 3 | +
|
| 4 | +Sorting arguments is the whole of what this file decides, and it decides it by looking |
| 5 | +at each one: a word is a test, a `-` is a flag, `NAME=value` is an environment |
| 6 | +assignment. Several of these pin down that an argument arrives whole, spaces and all, |
| 7 | +which is what taking them as a list rather than re-splitting a string bought. |
| 8 | +""" |
| 9 | + |
| 10 | +import os |
| 11 | +import unittest |
| 12 | +from unittest import mock |
| 13 | + |
| 14 | +import codeql_test_run |
| 15 | + |
| 16 | + |
| 17 | +def empty_args(): |
| 18 | + """What `main` builds before sorting, limited to what `parse_args` fills in.""" |
| 19 | + return { |
| 20 | + "tests": [], |
| 21 | + "flags": [], |
| 22 | + "env": [], |
| 23 | + "all_checks": [], |
| 24 | + "codeql": "host", |
| 25 | + "all": False, |
| 26 | + } |
| 27 | + |
| 28 | + |
| 29 | +def sorted_args(*argv): |
| 30 | + args = empty_args() |
| 31 | + codeql_test_run.parse_args(args, list(argv)) |
| 32 | + return args |
| 33 | + |
| 34 | + |
| 35 | +class TestParseArgs(unittest.TestCase): |
| 36 | + def test_a_plain_word_is_a_test(self): |
| 37 | + self.assertEqual(sorted_args("ql/test/Foo")["tests"], ["ql/test/Foo"]) |
| 38 | + |
| 39 | + def test_a_dash_is_a_flag(self): |
| 40 | + self.assertEqual(sorted_args("--fail-on-trap-errors")["flags"], ["--fail-on-trap-errors"]) |
| 41 | + |
| 42 | + def test_an_uppercase_assignment_is_an_environment_variable(self): |
| 43 | + self.assertEqual(sorted_args("CPUS=4")["env"], ["CPUS=4"]) |
| 44 | + |
| 45 | + def test_a_lowercase_assignment_is_a_test(self): |
| 46 | + # Only shouting counts, so a path that happens to contain `=` stays a path. |
| 47 | + self.assertEqual(sorted_args("dir/a=b")["tests"], ["dir/a=b"]) |
| 48 | + |
| 49 | + def test_codeql_selects_the_executable(self): |
| 50 | + self.assertEqual(sorted_args("--codeql=built")["codeql"], "built") |
| 51 | + |
| 52 | + def test_the_last_codeql_wins(self): |
| 53 | + self.assertEqual(sorted_args("--codeql=host", "--codeql=built")["codeql"], "built") |
| 54 | + |
| 55 | + def test_all_checks_is_asked_for_by_either_spelling(self): |
| 56 | + self.assertTrue(sorted_args("--all-checks")["all"]) |
| 57 | + self.assertTrue(sorted_args("+")["all"]) |
| 58 | + |
| 59 | + def test_an_extra_check_is_held_back_until_it_is_asked_for(self): |
| 60 | + held = sorted_args("--all-checks=--check-databases") |
| 61 | + self.assertEqual(held["all_checks"], ["--check-databases"]) |
| 62 | + # Held back means held back: it is not a flag until `--all-checks` arrives. |
| 63 | + self.assertEqual(held["flags"], []) |
| 64 | + self.assertFalse(held["all"]) |
| 65 | + |
| 66 | + def test_an_empty_argument_is_ignored(self): |
| 67 | + # One of these comes of a caller interpolating a variable that was never set. |
| 68 | + self.assertEqual(sorted_args("", "test")["tests"], ["test"]) |
| 69 | + |
| 70 | + def test_a_test_path_containing_a_space_stays_one_argument(self): |
| 71 | + self.assertEqual(sorted_args("some dir/test")["tests"], ["some dir/test"]) |
| 72 | + |
| 73 | + def test_an_assignment_whose_value_contains_a_space_stays_whole(self): |
| 74 | + """The value is the point, and a split one used to arrive as three characters. |
| 75 | +
|
| 76 | + An argument list carries this; a whitespace-separated string cannot, as there |
| 77 | + is nothing left in it to tell a separator from part of a value. |
| 78 | + """ |
| 79 | + self.assertEqual(sorted_args("EXTRA=a b")["env"], ["EXTRA=a b"]) |
| 80 | + |
| 81 | + def test_sorts_a_whole_command_line_at_once(self): |
| 82 | + args = sorted_args("-j2", "CPUS=4", "ql/test", "+", "--all-checks=--check-diff") |
| 83 | + self.assertEqual(args["flags"], ["-j2"]) |
| 84 | + self.assertEqual(args["env"], ["CPUS=4"]) |
| 85 | + self.assertEqual(args["tests"], ["ql/test"]) |
| 86 | + self.assertEqual(args["all_checks"], ["--check-diff"]) |
| 87 | + self.assertTrue(args["all"]) |
| 88 | + |
| 89 | + |
| 90 | +class TestEnvValue(unittest.TestCase): |
| 91 | + def test_prefers_a_test_argument(self): |
| 92 | + args = sorted_args("CPUS=4") |
| 93 | + with mock.patch.dict(os.environ, {"CPUS": "8"}): |
| 94 | + self.assertEqual(codeql_test_run.env_value(args, "CPUS", "1"), "4") |
| 95 | + |
| 96 | + def test_falls_back_to_the_environment(self): |
| 97 | + with mock.patch.dict(os.environ, {"CPUS": "8"}): |
| 98 | + self.assertEqual(codeql_test_run.env_value(empty_args(), "CPUS", "1"), "8") |
| 99 | + |
| 100 | + def test_falls_back_to_the_default(self): |
| 101 | + with mock.patch.dict(os.environ, {}, clear=True): |
| 102 | + self.assertEqual(codeql_test_run.env_value(empty_args(), "CPUS", "1"), "1") |
| 103 | + |
| 104 | + def test_the_last_assignment_wins(self): |
| 105 | + args = sorted_args("CPUS=4", "CPUS=2") |
| 106 | + self.assertEqual(codeql_test_run.env_value(args, "CPUS", "1"), "2") |
| 107 | + |
| 108 | + def test_an_empty_value_does_not_count_as_a_setting(self): |
| 109 | + args = sorted_args("CPUS=") |
| 110 | + with mock.patch.dict(os.environ, {"CPUS": "8"}): |
| 111 | + self.assertEqual(codeql_test_run.env_value(args, "CPUS", "1"), "8") |
| 112 | + |
| 113 | + def test_a_value_containing_a_space_survives(self): |
| 114 | + args = sorted_args("EXTRA=a b") |
| 115 | + self.assertEqual(codeql_test_run.env_value(args, "EXTRA", "none"), "a b") |
| 116 | + |
| 117 | + |
| 118 | +if __name__ == "__main__": |
| 119 | + unittest.main() |
0 commit comments