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-exception 6# 7# ===----------------------------------------------------------------------===## 8 9# With picolibc, test_program_stderr_is_not_conflated_with_stdout fails 10# because stdout & stderr are treated as the same. 11# XFAIL: LIBCXX-PICOLIBC-FIXME 12 13# Note: We prepend arguments with 'x' to avoid thinking there are too few 14# arguments in case an argument is an empty string. 15# RUN: %{python} %s x%S x%T x%{substitutions} 16 17import base64 18import copy 19import os 20import pickle 21import platform 22import subprocess 23import sys 24import unittest 25from os.path import dirname 26 27# Allow importing 'lit' and the 'libcxx' module. Make sure we put the lit 28# path first so we don't find any system-installed version. 29monorepoRoot = dirname(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.path 34import libcxx.test.dsl as dsl 35import lit.LitConfig 36import lit.util 37 38# Steal some parameters from the config running this test so that we can 39# bootstrap our own TestingConfig. 40args = list(map(lambda s: s[1:], sys.argv[1:8])) # Remove the leading 'x' 41SOURCE_ROOT, EXEC_PATH, SUBSTITUTIONS = args 42sys.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 for 58 the purpose of running unit tests below. We pre-populate it with the 59 minimum required substitutions. 60 """ 61 self.litConfig = lit.LitConfig.LitConfig( 62 progname="lit", 63 path=[], 64 quiet=False, 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_ROOT 78 self.config.test_exec_root = EXEC_PATH 79 self.config.recursiveExpansionLimit = 10 80 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 if 85 there is no such substitution. 86 """ 87 found = [x for (s, x) in self.config.substitutions if s == substitution] 88 assert len(found) == 1 89 return found[0] 90 91 92def findIndex(list, pred): 93 """Finds the index of the first element satisfying 'pred' in a list, or 94 'len(list)' if there is no such element.""" 95 index = 0 96 for x in list: 97 if pred(x): 98 break 99 else: 100 index += 1 101 return index 102 103 104class TestHasCompileFlag(SetupConfigs): 105 """ 106 Tests for libcxx.test.dsl.hasCompileFlag 107 """ 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.sourceBuilds 127 """ 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.programOutput 146 """ 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 error 170 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 compile 180 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 result 205 # is given, to ensure we're not incorrectly caching the result of the 206 # 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, making 235 # 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.programSucceeds 250 """ 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 compile 267 """ 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.hasLocale 277 """ 278 279 def test_doesnt_explode(self): 280 # It's really hard to test that a system has a given locale, so at least 281 # 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.compilerMacros 307 """ 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.featureTestMacros 340 """ 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.Feature 354 """ 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.Parameter 421 """ 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"] = True 552 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 exceptions 570 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"] = False 578 param = dsl.Parameter( 579 name="enable_exceptions", 580 choices=[True, False], 581 type=bool, 582 help="", 583 actions=lambda exceptions: [] 584 if exceptions 585 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: f 595 ) 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: f 602 ) 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: f 611 ) 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: f 621 ) 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