630 lines · python
1# ===----------------------------------------------------------------------===##2#3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4# See https://llvm.org/LICENSE.txt for license information.5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6#7# ===----------------------------------------------------------------------===##8 9# With picolibc, test_program_stderr_is_not_conflated_with_stdout fails10# because stdout & stderr are treated as the same.11# XFAIL: LIBCXX-PICOLIBC-FIXME12 13# Note: We prepend arguments with 'x' to avoid thinking there are too few14# arguments in case an argument is an empty string.15# RUN: %{python} %s x%S x%{temp} x%{substitutions}16 17import base6418import copy19import os20import pickle21import platform22import subprocess23import sys24import unittest25from os.path import dirname26 27# Allow importing 'lit' and the 'libcxx' module. Make sure we put the lit28# path first so we don't find any system-installed version.29monorepoRoot = dirname(dirname(dirname(dirname(dirname(__file__)))))30sys.path = [31 os.path.join(monorepoRoot, "libcxx", "utils"),32 os.path.join(monorepoRoot, "llvm", "utils", "lit"),33] + sys.path34import libcxx.test.dsl as dsl35import lit.LitConfig36import lit.util37 38# Steal some parameters from the config running this test so that we can39# bootstrap our own TestingConfig.40args = list(map(lambda s: s[1:], sys.argv[1:8])) # Remove the leading 'x'41SOURCE_ROOT, EXEC_PATH, SUBSTITUTIONS = args42sys.argv[1:8] = []43 44# Decode the substitutions.45SUBSTITUTIONS = pickle.loads(base64.b64decode(SUBSTITUTIONS))46for s, sub in SUBSTITUTIONS:47 print("Substitution '{}' is '{}'".format(s, sub))48 49 50class SetupConfigs(unittest.TestCase):51 """52 Base class for the tests below -- it creates a fake TestingConfig.53 """54 55 def setUp(self):56 """57 Create a fake TestingConfig that can be populated however we wish for58 the purpose of running unit tests below. We pre-populate it with the59 minimum required substitutions.60 """61 self.litConfig = lit.LitConfig.LitConfig(62 progname="lit",63 path=[],64 diagnostic_level="note",65 useValgrind=False,66 valgrindLeakCheck=False,67 valgrindArgs=[],68 noExecute=False,69 debug=False,70 isWindows=platform.system() == "Windows",71 order="smart",72 params={},73 )74 75 self.config = lit.TestingConfig.TestingConfig.fromdefaults(self.litConfig)76 self.config.environment = dict(os.environ)77 self.config.test_source_root = SOURCE_ROOT78 self.config.test_exec_root = EXEC_PATH79 self.config.recursiveExpansionLimit = 1080 self.config.substitutions = copy.deepcopy(SUBSTITUTIONS)81 82 def getSubstitution(self, substitution):83 """84 Return a given substitution from the TestingConfig. It is an error if85 there is no such substitution.86 """87 found = [x for (s, x) in self.config.substitutions if s == substitution]88 assert len(found) == 189 return found[0]90 91 92def findIndex(list, pred):93 """Finds the index of the first element satisfying 'pred' in a list, or94 'len(list)' if there is no such element."""95 index = 096 for x in list:97 if pred(x):98 break99 else:100 index += 1101 return index102 103 104class TestHasCompileFlag(SetupConfigs):105 """106 Tests for libcxx.test.dsl.hasCompileFlag107 """108 109 def test_no_flag_should_work(self):110 self.assertTrue(dsl.hasCompileFlag(self.config, ""))111 112 def test_flag_exists(self):113 self.assertTrue(dsl.hasCompileFlag(self.config, "-O1"))114 115 def test_nonexistent_flag(self):116 self.assertFalse(117 dsl.hasCompileFlag(self.config, "-this_is_not_a_flag_any_compiler_has")118 )119 120 def test_multiple_flags(self):121 self.assertTrue(dsl.hasCompileFlag(self.config, "-O1 -Dhello"))122 123 124class TestSourceBuilds(SetupConfigs):125 """126 Tests for libcxx.test.dsl.sourceBuilds127 """128 129 def test_valid_program_builds(self):130 source = """int main(int, char**) { return 0; }"""131 self.assertTrue(dsl.sourceBuilds(self.config, source))132 133 def test_compilation_error_fails(self):134 source = """int main(int, char**) { this does not compile }"""135 self.assertFalse(dsl.sourceBuilds(self.config, source))136 137 def test_link_error_fails(self):138 source = """extern void this_isnt_defined_anywhere();139 int main(int, char**) { this_isnt_defined_anywhere(); return 0; }"""140 self.assertFalse(dsl.sourceBuilds(self.config, source))141 142 143class TestProgramOutput(SetupConfigs):144 """145 Tests for libcxx.test.dsl.programOutput146 """147 148 def test_valid_program_returns_output(self):149 source = """150 #include <cstdio>151 int main(int, char**) { std::printf("FOOBAR"); return 0; }152 """153 self.assertEqual(dsl.programOutput(self.config, source), "FOOBAR")154 155 def test_valid_program_returns_output_newline_handling(self):156 source = """157 #include <cstdio>158 int main(int, char**) { std::printf("FOOBAR\\n"); return 0; }159 """160 self.assertEqual(dsl.programOutput(self.config, source), "FOOBAR\n")161 162 def test_valid_program_returns_no_output(self):163 source = """164 int main(int, char**) { return 0; }165 """166 self.assertEqual(dsl.programOutput(self.config, source), "")167 168 def test_program_that_fails_to_run_raises_runtime_error(self):169 # The program compiles, but exits with an error170 source = """171 int main(int, char**) { return 1; }172 """173 self.assertRaises(174 dsl.ConfigurationRuntimeError,175 lambda: dsl.programOutput(self.config, source),176 )177 178 def test_program_that_fails_to_compile_raises_compilation_error(self):179 # The program doesn't compile180 source = """181 int main(int, char**) { this doesnt compile }182 """183 self.assertRaises(184 dsl.ConfigurationCompilationError,185 lambda: dsl.programOutput(self.config, source),186 )187 188 def test_pass_arguments_to_program(self):189 source = """190 #include <cassert>191 #include <string>192 int main(int argc, char** argv) {193 assert(argc == 3);194 assert(argv[1] == std::string("first-argument"));195 assert(argv[2] == std::string("second-argument"));196 return 0;197 }198 """199 args = ["first-argument", "second-argument"]200 self.assertEqual(dsl.programOutput(self.config, source, args=args), "")201 202 def test_caching_is_not_too_aggressive(self):203 # Run a program, then change the substitutions and run it again.204 # Make sure the program is run the second time and the right result205 # is given, to ensure we're not incorrectly caching the result of the206 # first program run.207 source = """208 #include <cstdio>209 int main(int, char**) {210 std::printf("MACRO=%u\\n", MACRO);211 return 0;212 }213 """214 compileFlagsIndex = findIndex(215 self.config.substitutions, lambda x: x[0] == "%{compile_flags}"216 )217 compileFlags = self.config.substitutions[compileFlagsIndex][1]218 219 self.config.substitutions[compileFlagsIndex] = (220 "%{compile_flags}",221 compileFlags + " -DMACRO=1",222 )223 output1 = dsl.programOutput(self.config, source)224 self.assertEqual(output1, "MACRO=1\n")225 226 self.config.substitutions[compileFlagsIndex] = (227 "%{compile_flags}",228 compileFlags + " -DMACRO=2",229 )230 output2 = dsl.programOutput(self.config, source)231 self.assertEqual(output2, "MACRO=2\n")232 233 def test_program_stderr_is_not_conflated_with_stdout(self):234 # Run a program that produces stdout output and stderr output too, making235 # sure the stderr output does not pollute the stdout output.236 source = """237 #include <cstdio>238 int main(int, char**) {239 std::fprintf(stdout, "STDOUT-OUTPUT");240 std::fprintf(stderr, "STDERR-OUTPUT");241 return 0;242 }243 """244 self.assertEqual(dsl.programOutput(self.config, source), "STDOUT-OUTPUT")245 246 247class TestProgramSucceeds(SetupConfigs):248 """249 Tests for libcxx.test.dsl.programSucceeds250 """251 252 def test_success(self):253 source = """254 int main(int, char**) { return 0; }255 """256 self.assertTrue(dsl.programSucceeds(self.config, source))257 258 def test_failure(self):259 source = """260 int main(int, char**) { return 1; }261 """262 self.assertFalse(dsl.programSucceeds(self.config, source))263 264 def test_compile_failure(self):265 source = """266 this does not compile267 """268 self.assertRaises(269 dsl.ConfigurationCompilationError,270 lambda: dsl.programSucceeds(self.config, source),271 )272 273 274class TestHasLocale(SetupConfigs):275 """276 Tests for libcxx.test.dsl.hasLocale277 """278 279 def test_doesnt_explode(self):280 # It's really hard to test that a system has a given locale, so at least281 # make sure we don't explode when we try to check it.282 try:283 dsl.hasAnyLocale(self.config, ["en_US.UTF-8"])284 except subprocess.CalledProcessError:285 self.fail("checking for hasLocale should not explode")286 287 def test_nonexistent_locale(self):288 self.assertFalse(289 dsl.hasAnyLocale(self.config, ["forsurethisisnotanexistinglocale"])290 )291 292 def test_localization_program_doesnt_compile(self):293 compilerIndex = findIndex(self.config.substitutions, lambda x: x[0] == "%{cxx}")294 self.config.substitutions[compilerIndex] = (295 "%{cxx}",296 "this-is-certainly-not-a-valid-compiler!!",297 )298 self.assertRaises(299 dsl.ConfigurationCompilationError,300 lambda: dsl.hasAnyLocale(self.config, ["en_US.UTF-8"]),301 )302 303 304class TestCompilerMacros(SetupConfigs):305 """306 Tests for libcxx.test.dsl.compilerMacros307 """308 309 def test_basic(self):310 macros = dsl.compilerMacros(self.config)311 self.assertIsInstance(macros, dict)312 self.assertGreater(len(macros), 0)313 for (k, v) in macros.items():314 self.assertIsInstance(k, str)315 self.assertIsInstance(v, str)316 317 def test_no_flag(self):318 macros = dsl.compilerMacros(self.config)319 self.assertIn("__cplusplus", macros.keys())320 321 def test_empty_flag(self):322 macros = dsl.compilerMacros(self.config, "")323 self.assertIn("__cplusplus", macros.keys())324 325 def test_with_flag(self):326 macros = dsl.compilerMacros(self.config, "-DFOO=3")327 self.assertIn("__cplusplus", macros.keys())328 self.assertEqual(macros["FOO"], "3")329 330 def test_with_flags(self):331 macros = dsl.compilerMacros(self.config, "-DFOO=3 -DBAR=hello")332 self.assertIn("__cplusplus", macros.keys())333 self.assertEqual(macros["FOO"], "3")334 self.assertEqual(macros["BAR"], "hello")335 336 337class TestFeatureTestMacros(SetupConfigs):338 """339 Tests for libcxx.test.dsl.featureTestMacros340 """341 342 def test_basic(self):343 macros = dsl.featureTestMacros(self.config)344 self.assertIsInstance(macros, dict)345 self.assertGreater(len(macros), 0)346 for (k, v) in macros.items():347 self.assertIsInstance(k, str)348 self.assertIsInstance(v, int)349 350 351class TestFeature(SetupConfigs):352 """353 Tests for libcxx.test.dsl.Feature354 """355 356 def test_trivial(self):357 feature = dsl.Feature(name="name")358 origSubstitutions = copy.deepcopy(self.config.substitutions)359 actions = feature.getActions(self.config)360 self.assertTrue(len(actions) == 1)361 for a in actions:362 a.applyTo(self.config)363 self.assertEqual(origSubstitutions, self.config.substitutions)364 self.assertIn("name", self.config.available_features)365 366 def test_name_can_be_a_callable(self):367 feature = dsl.Feature(name=lambda cfg: "name")368 for a in feature.getActions(self.config):369 a.applyTo(self.config)370 self.assertIn("name", self.config.available_features)371 372 def test_name_is_not_a_string_1(self):373 feature = dsl.Feature(name=None)374 self.assertRaises(ValueError, lambda: feature.getActions(self.config))375 self.assertRaises(ValueError, lambda: feature.pretty(self.config))376 377 def test_name_is_not_a_string_2(self):378 feature = dsl.Feature(name=lambda cfg: None)379 self.assertRaises(ValueError, lambda: feature.getActions(self.config))380 self.assertRaises(ValueError, lambda: feature.pretty(self.config))381 382 def test_adding_action(self):383 feature = dsl.Feature(name="name", actions=[dsl.AddCompileFlag("-std=c++03")])384 origLinkFlags = copy.deepcopy(self.getSubstitution("%{link_flags}"))385 for a in feature.getActions(self.config):386 a.applyTo(self.config)387 self.assertIn("name", self.config.available_features)388 self.assertIn("-std=c++03", self.getSubstitution("%{compile_flags}"))389 self.assertEqual(origLinkFlags, self.getSubstitution("%{link_flags}"))390 391 def test_actions_can_be_a_callable(self):392 feature = dsl.Feature(393 name="name",394 actions=lambda cfg: (395 self.assertIs(self.config, cfg),396 [dsl.AddCompileFlag("-std=c++03")],397 )[1],398 )399 for a in feature.getActions(self.config):400 a.applyTo(self.config)401 self.assertIn("-std=c++03", self.getSubstitution("%{compile_flags}"))402 403 def test_unsupported_feature(self):404 feature = dsl.Feature(name="name", when=lambda _: False)405 self.assertEqual(feature.getActions(self.config), [])406 407 def test_is_supported_gets_passed_the_config(self):408 feature = dsl.Feature(409 name="name", when=lambda cfg: (self.assertIs(self.config, cfg), True)[1]410 )411 self.assertEqual(len(feature.getActions(self.config)), 1)412 413 414def _throw():415 raise ValueError()416 417 418class TestParameter(SetupConfigs):419 """420 Tests for libcxx.test.dsl.Parameter421 """422 423 def test_empty_name_should_blow_up(self):424 self.assertRaises(425 ValueError,426 lambda: dsl.Parameter(427 name="", choices=["c++03"], type=str, help="", actions=lambda _: []428 ),429 )430 431 def test_empty_choices_should_blow_up(self):432 self.assertRaises(433 ValueError,434 lambda: dsl.Parameter(435 name="std", choices=[], type=str, help="", actions=lambda _: []436 ),437 )438 439 def test_no_choices_is_ok(self):440 param = dsl.Parameter(name="triple", type=str, help="", actions=lambda _: [])441 self.assertEqual(param.name, "triple")442 443 def test_name_is_set_correctly(self):444 param = dsl.Parameter(445 name="std", choices=["c++03"], type=str, help="", actions=lambda _: []446 )447 self.assertEqual(param.name, "std")448 449 def test_no_value_provided_and_no_default_value(self):450 param = dsl.Parameter(451 name="std", choices=["c++03"], type=str, help="", actions=lambda _: []452 )453 self.assertRaises(454 ValueError, lambda: param.getActions(self.config, self.litConfig.params)455 )456 457 def test_no_value_provided_and_default_value(self):458 param = dsl.Parameter(459 name="std",460 choices=["c++03"],461 type=str,462 help="",463 default="c++03",464 actions=lambda std: [dsl.AddFeature(std)],465 )466 for a in param.getActions(self.config, self.litConfig.params):467 a.applyTo(self.config)468 self.assertIn("c++03", self.config.available_features)469 470 def test_value_provided_on_command_line_and_no_default_value(self):471 self.litConfig.params["std"] = "c++03"472 param = dsl.Parameter(473 name="std",474 choices=["c++03"],475 type=str,476 help="",477 actions=lambda std: [dsl.AddFeature(std)],478 )479 for a in param.getActions(self.config, self.litConfig.params):480 a.applyTo(self.config)481 self.assertIn("c++03", self.config.available_features)482 483 def test_value_provided_on_command_line_and_default_value(self):484 """The value provided on the command line should override the default value"""485 self.litConfig.params["std"] = "c++11"486 param = dsl.Parameter(487 name="std",488 choices=["c++03", "c++11"],489 type=str,490 default="c++03",491 help="",492 actions=lambda std: [dsl.AddFeature(std)],493 )494 for a in param.getActions(self.config, self.litConfig.params):495 a.applyTo(self.config)496 self.assertIn("c++11", self.config.available_features)497 self.assertNotIn("c++03", self.config.available_features)498 499 def test_value_provided_in_config_and_default_value(self):500 """The value provided in the config should override the default value"""501 self.config.std = "c++11"502 param = dsl.Parameter(503 name="std",504 choices=["c++03", "c++11"],505 type=str,506 default="c++03",507 help="",508 actions=lambda std: [dsl.AddFeature(std)],509 )510 for a in param.getActions(self.config, self.litConfig.params):511 a.applyTo(self.config)512 self.assertIn("c++11", self.config.available_features)513 self.assertNotIn("c++03", self.config.available_features)514 515 def test_value_provided_in_config_and_on_command_line(self):516 """The value on the command line should override the one in the config"""517 self.config.std = "c++11"518 self.litConfig.params["std"] = "c++03"519 param = dsl.Parameter(520 name="std",521 choices=["c++03", "c++11"],522 type=str,523 help="",524 actions=lambda std: [dsl.AddFeature(std)],525 )526 for a in param.getActions(self.config, self.litConfig.params):527 a.applyTo(self.config)528 self.assertIn("c++03", self.config.available_features)529 self.assertNotIn("c++11", self.config.available_features)530 531 def test_no_actions(self):532 self.litConfig.params["std"] = "c++03"533 param = dsl.Parameter(534 name="std", choices=["c++03"], type=str, help="", actions=lambda _: []535 )536 actions = param.getActions(self.config, self.litConfig.params)537 self.assertEqual(actions, [])538 539 def test_boolean_value_parsed_from_trueish_string_parameter(self):540 self.litConfig.params["enable_exceptions"] = "True"541 param = dsl.Parameter(542 name="enable_exceptions",543 choices=[True, False],544 type=bool,545 help="",546 actions=lambda exceptions: [] if exceptions else _throw(),547 )548 self.assertEqual(param.getActions(self.config, self.litConfig.params), [])549 550 def test_boolean_value_from_true_boolean_parameter(self):551 self.litConfig.params["enable_exceptions"] = True552 param = dsl.Parameter(553 name="enable_exceptions",554 choices=[True, False],555 type=bool,556 help="",557 actions=lambda exceptions: [] if exceptions else _throw(),558 )559 self.assertEqual(param.getActions(self.config, self.litConfig.params), [])560 561 def test_boolean_value_parsed_from_falseish_string_parameter(self):562 self.litConfig.params["enable_exceptions"] = "False"563 param = dsl.Parameter(564 name="enable_exceptions",565 choices=[True, False],566 type=bool,567 help="",568 actions=lambda exceptions: []569 if exceptions570 else [dsl.AddFeature("-fno-exceptions")],571 )572 for a in param.getActions(self.config, self.litConfig.params):573 a.applyTo(self.config)574 self.assertIn("-fno-exceptions", self.config.available_features)575 576 def test_boolean_value_from_false_boolean_parameter(self):577 self.litConfig.params["enable_exceptions"] = False578 param = dsl.Parameter(579 name="enable_exceptions",580 choices=[True, False],581 type=bool,582 help="",583 actions=lambda exceptions: []584 if exceptions585 else [dsl.AddFeature("-fno-exceptions")],586 )587 for a in param.getActions(self.config, self.litConfig.params):588 a.applyTo(self.config)589 self.assertIn("-fno-exceptions", self.config.available_features)590 591 def test_list_parsed_from_comma_delimited_string_empty(self):592 self.litConfig.params["additional_features"] = ""593 param = dsl.Parameter(594 name="additional_features", type=list, help="", actions=lambda f: f595 )596 self.assertEqual(param.getActions(self.config, self.litConfig.params), [])597 598 def test_list_parsed_from_comma_delimited_string_1(self):599 self.litConfig.params["additional_features"] = "feature1"600 param = dsl.Parameter(601 name="additional_features", type=list, help="", actions=lambda f: f602 )603 self.assertEqual(604 param.getActions(self.config, self.litConfig.params), ["feature1"]605 )606 607 def test_list_parsed_from_comma_delimited_string_2(self):608 self.litConfig.params["additional_features"] = "feature1,feature2"609 param = dsl.Parameter(610 name="additional_features", type=list, help="", actions=lambda f: f611 )612 self.assertEqual(613 param.getActions(self.config, self.litConfig.params),614 ["feature1", "feature2"],615 )616 617 def test_list_parsed_from_comma_delimited_string_3(self):618 self.litConfig.params["additional_features"] = "feature1,feature2, feature3"619 param = dsl.Parameter(620 name="additional_features", type=list, help="", actions=lambda f: f621 )622 self.assertEqual(623 param.getActions(self.config, self.litConfig.params),624 ["feature1", "feature2", "feature3"],625 )626 627 628if __name__ == "__main__":629 unittest.main(verbosity=2)630