__init__.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. # coding: utf-8
  2. import re
  3. RESTART_TEST_INDICATOR = '##restart-test##'
  4. INFRASTRUCTURE_ERROR_INDICATOR = '##infrastructure-error##'
  5. RESTART_TEST_INDICATORS = [
  6. RESTART_TEST_INDICATOR,
  7. "network error",
  8. ]
  9. UID_PREFIX_DELIMITER = '-'
  10. # testing
  11. BIN_DIRECTORY = 'bin'
  12. CANON_DATA_DIR_NAME = "canondata"
  13. CANON_RESULT_FILE_NAME = "result.json"
  14. CANONIZATION_RESULT_FILE_NAME = "canonization_res.json"
  15. COMMON_CONTEXT_FILE_NAME = "common_test.context"
  16. CONSOLE_SNIPPET_LIMIT = 5000
  17. FAKE_OUTPUT_EXTS = frozenset([".mf", ".fake", ".cpf", ".cpsf"])
  18. LIST_NODE_LOG_FILE = "test_list.log"
  19. LIST_NODE_RESULT_FILE = "test_list.json"
  20. LIST_RESULT_NODE_LOG_FILE = "list_result.log"
  21. LIST_TRACE_FILE_NAME = "ytest_list.report.trace"
  22. MAX_FILE_SIZE = 1024 * 1024 * 2 # 2 MB
  23. MAX_TEST_RESTART_COUNT = 3
  24. NO_LISTED_TESTS = "NO_LISTED_TESTS"
  25. REPORT_SNIPPET_LIMIT = 12000
  26. SANITIZER_ERROR_RC = 100
  27. SUITE_CONTEXT_FILE_NAME = "test.context"
  28. TEST_LIST_FILE = "test_names_list.json"
  29. TEST_SUBTEST_SEPARATOR = '::'
  30. TESTING_OUT_DIR_NAME = "testing_out_stuff"
  31. TESTING_OUT_RAM_DRIVE_DIR_NAME = "ram_drive_output"
  32. TESTING_OUT_TAR_NAME = TESTING_OUT_DIR_NAME + ".tar.zstd"
  33. TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
  34. TRACE_FILE_NAME = "ytest.report.trace"
  35. TRUNCATING_IGNORE_FILE_LIST = {TRACE_FILE_NAME, SUITE_CONTEXT_FILE_NAME, "run_test.log"}
  36. YT_RUN_TEST_DIR_NAME = "yt_run_test"
  37. YT_RUN_TEST_TAR_NAME = "yt_run_test.tar"
  38. COVERAGE_CFLAGS = ["-fprofile-instr-generate", "-fcoverage-mapping", "-DCLANG_COVERAGE"]
  39. COVERAGE_LDFLAGS = ["-fprofile-instr-generate", "-fcoverage-mapping"]
  40. CANON_BACKEND_KEY = "{canondata_backend}"
  41. DEFAULT_CANONIZATION_BACKEND = "storage.yandex-team.ru/get-devtools"
  42. MDS_URI_PREFIX = 'https://storage.yandex-team.ru/get-devtools/'
  43. BACKEND_URI_PREFIX = 'https://' + CANON_BACKEND_KEY + '/'
  44. MDS_SCHEME = 'mds'
  45. CANON_MDS_RESOURCE_REGEX = re.compile(re.escape(MDS_URI_PREFIX) + r'(.*?)($|#)')
  46. CANON_BACKEND_RESOURCE_REGEX = re.compile(re.escape(BACKEND_URI_PREFIX) + r'(.*?)($|#)')
  47. CANON_SBR_RESOURCE_REGEX = re.compile(r'(sbr:/?/?(\d+))')
  48. MANDATORY_ENV_VAR_NAME = 'YA_MANDATORY_ENV_VARS'
  49. BUILD_FLAGS_ALLOWED_IN_CONTEXT = {
  50. 'AUTOCHECK',
  51. # Required for local test runs
  52. 'TESTS_REQUESTED',
  53. 'USE_ARCADIA_PYTHON',
  54. 'USE_SYSTEM_PYTHON',
  55. }
  56. STYLE_TEST_TYPES = [
  57. "classpath.clash",
  58. "clang_tidy",
  59. "eslint",
  60. "gofmt",
  61. "govet",
  62. "java.style",
  63. "ktlint",
  64. "py2_flake8",
  65. "flake8",
  66. "black",
  67. ]
  68. REGULAR_TEST_TYPES = [
  69. "benchmark",
  70. "boost_test",
  71. "exectest",
  72. "fuzz",
  73. "g_benchmark",
  74. "go_bench",
  75. "go_test",
  76. "gtest",
  77. "hermione",
  78. "java",
  79. "jest",
  80. "py2test",
  81. "py3test",
  82. "pytest",
  83. "unittest",
  84. ]
  85. TEST_NODE_OUTPUT_RESULTS = [TESTING_OUT_TAR_NAME, YT_RUN_TEST_TAR_NAME]
  86. # kvm
  87. DEFAULT_RAM_REQUIREMENTS_FOR_KVM = 4
  88. MAX_RAM_REQUIREMENTS_FOR_KVM = 16
  89. # distbuild
  90. DISTBUILD_STATUS_REPORT_ENV_NAME = 'NODE_EXTENDED_STATUS_FILE_PATH'
  91. DEFAULT_TEST_NODE_TIMEOUT = 15 * 60
  92. TEST_NODE_FINISHING_TIME = 5 * 60
  93. # coverage
  94. COVERAGE_FUNCTION_ENTRIES_LIMIT = 2
  95. COVERAGE_PYTHON_EXTS = (".py", ".pyx", ".pxi", ".pxd")
  96. COVERAGE_RESOLVED_FILE_NAME_PATTERN = "coverage_resolved.{}.json"
  97. CPP_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("cpp")
  98. GO_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("go")
  99. JAVA_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("java")
  100. NLG_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("nlg")
  101. PYTHON2_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("py2")
  102. PYTHON3_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("py3")
  103. TS_COVERAGE_RESOLVED_FILE_NAME = COVERAGE_RESOLVED_FILE_NAME_PATTERN.format("ts")
  104. COVERAGE_CLANG_ENV_NAME = 'LLVM_PROFILE_FILE'
  105. COVERAGE_GCOV_ENV_NAME = 'GCOV_PREFIX'
  106. COVERAGE_GO_ENV_NAME = 'GO_COVERAGE_PREFIX'
  107. COVERAGE_PYTHON_ENV_NAME = 'PYTHON_COVERAGE_PREFIX'
  108. COVERAGE_TS_ENV_NAME = 'TS_COVERAGE_PREFIX'
  109. COVERAGE_NLG_ENV_NAME = 'NLG_COVERAGE_FILENAME'
  110. COVERAGE_ENV_VARS = (
  111. COVERAGE_CLANG_ENV_NAME,
  112. COVERAGE_GCOV_ENV_NAME,
  113. COVERAGE_GO_ENV_NAME,
  114. COVERAGE_NLG_ENV_NAME,
  115. COVERAGE_PYTHON_ENV_NAME,
  116. COVERAGE_TS_ENV_NAME,
  117. )
  118. PYTHON_COVERAGE_PREFIX_FILTER_ENV_NAME = 'PYTHON_COVERAGE_PREFIX_FILTER'
  119. PYTHON_COVERAGE_EXCLUDE_REGEXP_ENV_NAME = 'PYTHON_COVERAGE_EXCLUDE_REGEXP'
  120. # TODO get rid of this list - resolve nodes should be added automatically depending on the lang of the target module and their deps
  121. CLANG_COVERAGE_TEST_TYPES = (
  122. "boost_test",
  123. "coverage_extractor",
  124. "exectest",
  125. "fuzz",
  126. "gtest",
  127. "go_test",
  128. # java tests might use shared libraries
  129. "java",
  130. "py2test",
  131. "py3test",
  132. "pytest",
  133. "unittest",
  134. )
  135. COVERAGE_TABLE_CHUNKS = 20
  136. COVERAGE_TESTS_TIMEOUT_FACTOR = 1.5
  137. COVERAGE_YT_PROXY = "hahn.yt.yandex.net"
  138. COVERAGE_YT_ROOT_PATH = "//home/codecoverage"
  139. COVERAGE_YT_TABLE_PREFIX = "datatable"
  140. # fuzzing
  141. CORPUS_DATA_FILE_NAME = 'corpus.json'
  142. CORPUS_DATA_ROOT_DIR = 'fuzzing'
  143. CORPUS_DIR_NAME = 'corpus'
  144. FUZZING_COVERAGE_ARGS = ['--sanitize-coverage=trace-div,trace-gep']
  145. FUZZING_COMPRESSION_COEF = 1.1
  146. FUZZING_DEFAULT_TIMEOUT = 3600
  147. FUZZING_FINISHING_TIME = 600
  148. FUZZING_TIMEOUT_RE = re.compile(r'(^|\s)-max_total_time=(?P<max_time>\d+)')
  149. GENERATED_CORPUS_DIR_NAME = 'mined_corpus'
  150. MAX_CORPUS_RESOURCES_ALLOWED = 5
  151. # hermione
  152. HERMIONE_REPORT_DIR_NAME = "hermione-report"
  153. HERMIONE_REPORT_TAR_NAME = HERMIONE_REPORT_DIR_NAME + ".tar"
  154. HERMIONE_REPORT_INDEX_FILE_NAME = "index.html"
  155. HERMIONE_REPORT_DB_URLS_FILE_NAME = "databaseUrls.json"
  156. HERMIONE_TESTS_READ_FILE_NAME = "tests.json"
  157. HERMIONE_TESTS_READ_STDOUT_FILE_NAME = "read_tests.out"
  158. HERMIONE_TESTS_READ_STDERR_FILE_NAME = "read_tests.err"
  159. HERMIONE_TESTS_RUN_FILE_NAME = "test_results.jsonl"
  160. HERMIONE_TESTS_RUN_STDOUT_FILE_NAME = "run_tests.out"
  161. HERMIONE_TESTS_RUN_STDERR_FILE_NAME = "run_tests.err"
  162. # yt
  163. YT_OPERATION_ID_SUBSTITUTION = '$OPERATION_ID'
  164. YT_SANDBOX_ROOT_PREFIX = '$(YT_SANDBOX_ROOT)'
  165. # sandbox
  166. SANDBOX_RUN_TEST_YT_TOKEN_VALUE_NAME = 'YA_MAKE_SANDBOX_RUN_TEST_YT_TOKEN'
  167. # global resources
  168. ANDROID_AVD_ROOT = 'ANDROID_AVD_RESOURCE_GLOBAL'
  169. ANDROID_SDK_ROOT = 'ANDROID_SDK_RESOURCE_GLOBAL'
  170. COVERAGE_PUSH_TOOL_LOCAL = 'USE_SYSTEM_COVERAGE_PUSH_TOOL'
  171. COVERAGE_PUSH_TOOL_RESOURCE = 'COVERAGE_PUSH_TOOL_RESOURCE_GLOBAL'
  172. COVERAGE_PUSH_TOOL_LB_LOCAL = 'USE_SYSTEM_COVERAGE_PUSH_TOOL_LB'
  173. COVERAGE_PUSH_TOOL_LB_RESOURCE = 'COVERAGE_PUSH_TOOL_LB_RESOURCE_GLOBAL'
  174. FLAKE8_PY2_RESOURCE = 'FLAKE8_PY2_RESOURCE_GLOBAL'
  175. FLAKE8_PY3_RESOURCE = 'FLAKE8_PY3_RESOURCE_GLOBAL'
  176. GO_TOOLS_RESOURCE = 'GO_TOOLS_RESOURCE_GLOBAL'
  177. JSTYLE_RUNNER_LIB = 'JSTYLE_LIB_RESOURCE_GLOBAL'
  178. NODEJS_RESOURCE = 'NODEJS_RESOURCE_GLOBAL'
  179. NYC_RESOURCE = 'NYC_RESOURCE_GLOBAL'
  180. TEST_TOOL3_HOST = 'TEST_TOOL3_HOST_RESOURCE_GLOBAL'
  181. TEST_TOOL3_HOST_LOCAL = 'TEST_TOOL3_HOST_LOCAL'
  182. TEST_TOOL_HOST = 'TEST_TOOL_HOST_RESOURCE_GLOBAL'
  183. TEST_TOOL_HOST_LOCAL = 'TEST_TOOL_HOST_LOCAL'
  184. TEST_TOOL_TARGET = 'TEST_TOOL_TARGET_RESOURCE_GLOBAL'
  185. TEST_TOOL_TARGET_LOCAL = 'TEST_TOOL_TARGET_LOCAL'
  186. XCODE_TOOLS_RESOURCE = 'XCODE_TOOLS_ROOT_RESOURCE_GLOBAL'
  187. WINE_TOOL = 'WINE_TOOL_RESOURCE_GLOBAL'
  188. WINE32_TOOL = 'WINE32_TOOL_RESOURCE_GLOBAL'
  189. class Enum(object):
  190. @classmethod
  191. def enumerate(cls):
  192. return [v for k, v in cls.__dict__.items() if not k.startswith("_")]
  193. class TestRequirements(Enum):
  194. Container = 'container'
  195. Cpu = 'cpu'
  196. DiskUsage = 'disk_usage'
  197. Dns = 'dns'
  198. Kvm = 'kvm'
  199. Network = 'network'
  200. Ram = 'ram'
  201. RamDisk = 'ram_disk'
  202. SbVault = 'sb_vault'
  203. YavSecret = 'yav'
  204. class TestRequirementsConstants(Enum):
  205. All = 'all'
  206. AllCpuValue = 50
  207. AllRamDiskValue = 50
  208. MinCpu = 1
  209. MinRam = 1
  210. MinRamDisk = 0
  211. @classmethod
  212. def is_all_cpu(cls, value):
  213. return value == cls.All
  214. @classmethod
  215. def get_cpu_value(cls, value):
  216. return cls.AllCpuValue if cls.is_all_cpu(value) else value
  217. @classmethod
  218. def is_all_ram_disk(cls, value):
  219. return value == cls.All
  220. @classmethod
  221. def get_ram_disk_value(cls, value):
  222. return cls.AllRamDiskValue if cls.is_all_ram_disk(value) else value
  223. class TestSize(Enum):
  224. Small = 'small'
  225. Medium = 'medium'
  226. Large = 'large'
  227. DefaultTimeouts = {
  228. Small: 60,
  229. Medium: 600,
  230. Large: 3600,
  231. }
  232. DefaultPriorities = {
  233. Small: -1,
  234. Medium: -2,
  235. Large: -3,
  236. }
  237. DefaultRequirements = {
  238. Small: {
  239. TestRequirements.Cpu: 1,
  240. TestRequirements.Ram: 8,
  241. # TestRequirements.Ram: 2,
  242. TestRequirements.RamDisk: 0,
  243. },
  244. Medium: {
  245. TestRequirements.Cpu: 1,
  246. TestRequirements.Ram: 8,
  247. # TestRequirements.Ram: 4,
  248. TestRequirements.RamDisk: 0,
  249. },
  250. Large: {
  251. TestRequirements.Cpu: 1,
  252. TestRequirements.Ram: 8,
  253. # TestRequirements.Ram: 8,
  254. TestRequirements.RamDisk: 0,
  255. },
  256. }
  257. MaxRequirements = {
  258. Small: {
  259. TestRequirements.Cpu: 4,
  260. TestRequirements.Ram: 32,
  261. # TestRequirements.Ram: 4,
  262. TestRequirements.RamDisk: 32,
  263. },
  264. Medium: {
  265. TestRequirements.Cpu: 4,
  266. # TestRequirements.Cpu: 8,
  267. TestRequirements.Ram: 32,
  268. # TestRequirements.Ram: 16,
  269. TestRequirements.RamDisk: 32,
  270. },
  271. Large: {
  272. TestRequirements.Cpu: 4,
  273. TestRequirements.Ram: 32,
  274. TestRequirements.RamDisk: 32,
  275. },
  276. }
  277. LargeMarker = "TL"
  278. MediumMarker = "TM"
  279. SmallMarker = "TS"
  280. SizeMarkers = (LargeMarker, MediumMarker, SmallMarker)
  281. SizeShorthandMap = {
  282. Large: LargeMarker,
  283. Medium: MediumMarker,
  284. Small: SmallMarker,
  285. }
  286. @classmethod
  287. def sizes(cls):
  288. return cls.DefaultTimeouts.keys()
  289. @classmethod
  290. def get_shorthand(cls, size):
  291. return cls.SizeShorthandMap[size]
  292. @classmethod
  293. def is_test_shorthand(cls, name):
  294. return name in cls.SizeMarkers
  295. @classmethod
  296. def get_default_timeout(cls, size):
  297. if size in cls.DefaultTimeouts:
  298. return cls.DefaultTimeouts[size]
  299. raise Exception("Unknown test size '{}'".format(size))
  300. @classmethod
  301. def get_default_priorities(cls, size):
  302. if size in cls.DefaultPriorities:
  303. return cls.DefaultPriorities[size]
  304. raise Exception("Unknown test size '{}'".format(size))
  305. @classmethod
  306. def get_default_requirements(cls, size):
  307. if size in cls.DefaultRequirements:
  308. return cls.DefaultRequirements[size]
  309. raise Exception("Unknown test size '{}'".format(size))
  310. @classmethod
  311. def get_max_requirements(cls, size):
  312. if size in cls.MaxRequirements:
  313. return cls.MaxRequirements[size]
  314. raise Exception("Unknown test size '{}'".format(size))
  315. class TestRunExitCode(Enum):
  316. Skipped = 2
  317. Failed = 3
  318. TimeOut = 10
  319. InfrastructureError = 12
  320. class YaTestTags(Enum):
  321. AlwaysMinimize = "ya:always_minimize"
  322. Dirty = "ya:dirty"
  323. DumpNodeEnvironment = "ya:dump_node_env"
  324. DumpTestEnvironment = "ya:dump_test_env"
  325. ExoticPlatform = "ya:exotic_platform"
  326. External = "ya:external"
  327. Fat = "ya:fat"
  328. ForceDistbuild = "ya:force_distbuild"
  329. ForceSandbox = "ya:force_sandbox"
  330. GoNoSubtestReport = "ya:go_no_subtest_report"
  331. GoTotalReport = "ya:go_total_report"
  332. HugeLogs = "ya:huge_logs"
  333. Manual = "ya:manual"
  334. MapRootUser = "ya:map_root_user"
  335. NoFuse = "ya:nofuse"
  336. NoGracefulShutdown = "ya:no_graceful_shutdown"
  337. Norestart = "ya:norestart"
  338. Noretries = "ya:noretries"
  339. NotAutocheck = "ya:not_autocheck"
  340. Notags = "ya:notags"
  341. PerfTest = "ya:perftest"
  342. Privileged = "ya:privileged"
  343. ReportChunks = "ya:report_chunks"
  344. RunWithAsserts = "ya:relwithdebinfo"
  345. SandboxCoverage = "ya:sandbox_coverage"
  346. SequentialRun = "ya:sequential_run"
  347. TraceOutput = "ya:trace_output"
  348. YtRunner = "ya:yt"
  349. CopyData = "ya:copydata"
  350. CopyDataRO = "ya:copydataro"
  351. NoPstreeTrim = "ya:no_pstree_trim"
  352. class ServiceTags(Enum):
  353. AnyTag = "ya:anytag"
  354. class Status(object):
  355. GOOD, XFAIL, FAIL, XPASS, MISSING, CRASHED, TIMEOUT = range(1, 8)
  356. SKIPPED = -100
  357. NOT_LAUNCHED = -200
  358. CANON_DIFF = -300
  359. DESELECTED = -400
  360. INTERNAL = -int(2**31 - 1) # maxint
  361. FLAKY = -50
  362. # XFAILDIFF is internal status and should be replaced
  363. # with XFAIL or XPASS during verification stage of canon data
  364. XFAILDIFF = -90
  365. BY_NAME = {
  366. 'crashed': CRASHED,
  367. 'deselected': DESELECTED,
  368. 'diff': CANON_DIFF,
  369. 'fail': FAIL,
  370. 'flaky': FLAKY,
  371. 'good': GOOD,
  372. 'internal': INTERNAL,
  373. 'missing': MISSING,
  374. 'not_launched': NOT_LAUNCHED,
  375. 'skipped': SKIPPED,
  376. 'timeout': TIMEOUT,
  377. 'xfail': XFAIL,
  378. 'xfaildiff': XFAILDIFF,
  379. 'xpass': XPASS,
  380. }
  381. TO_STR = {
  382. CANON_DIFF: 'diff',
  383. CRASHED: 'crashed',
  384. DESELECTED: 'deselected',
  385. FAIL: 'fail',
  386. FLAKY: 'flaky',
  387. GOOD: 'good',
  388. INTERNAL: 'internal',
  389. MISSING: 'missing',
  390. NOT_LAUNCHED: 'not_launched',
  391. SKIPPED: 'skipped',
  392. TIMEOUT: 'timeout',
  393. XFAIL: 'xfail',
  394. XFAILDIFF: 'xfaildiff',
  395. XPASS: 'xpass',
  396. }
  397. class _Colors(object):
  398. _NAMES = [
  399. "blue",
  400. "cyan",
  401. "default",
  402. "green",
  403. "grey",
  404. "magenta",
  405. "red",
  406. "white",
  407. "yellow",
  408. ]
  409. _PREFIXES = ["", "light", "dark"]
  410. def __init__(self):
  411. self._table = {}
  412. for prefix in self._PREFIXES:
  413. for value in self._NAMES:
  414. name = value
  415. if prefix:
  416. name = "{}_{}".format(prefix, value)
  417. value = "{}-{}".format(prefix, value)
  418. self.__add_color(name.upper(), value)
  419. def __add_color(self, name, value):
  420. self._table[name] = value
  421. self.__setattr__(name, value)
  422. Colors = _Colors()
  423. class _Highlight(object):
  424. _MARKERS = {
  425. # special
  426. "RESET": "rst",
  427. "IMPORTANT": "imp",
  428. "UNIMPORTANT": "unimp",
  429. "BAD": "bad",
  430. "WARNING": "warn",
  431. "GOOD": "good",
  432. "PATH": "path",
  433. "ALTERNATIVE1": "alt1",
  434. "ALTERNATIVE2": "alt2",
  435. "ALTERNATIVE3": "alt3",
  436. }
  437. def __init__(self):
  438. # setting attributes because __getattr__ is much slower
  439. for attr, value in self._MARKERS.items():
  440. self.__setattr__(attr, value)
  441. Highlight = _Highlight()
  442. class _StatusColorMap(object):
  443. # There should be no XFAILDIFF, because it's internal status.
  444. # It should be replaced with XFAIL or XPASS during verification of canon data.
  445. _MAP = {
  446. 'crashed': Highlight.WARNING,
  447. 'deselected': Highlight.UNIMPORTANT,
  448. 'diff': Highlight.BAD,
  449. 'fail': Highlight.BAD,
  450. 'flaky': Highlight.ALTERNATIVE3,
  451. 'good': Highlight.GOOD,
  452. 'internal': Highlight.BAD,
  453. 'missing': Highlight.ALTERNATIVE1,
  454. 'not_launched': Highlight.BAD,
  455. 'skipped': Highlight.UNIMPORTANT,
  456. 'timeout': Highlight.BAD,
  457. 'xfail': Highlight.WARNING,
  458. 'xpass': Highlight.WARNING,
  459. }
  460. def __getitem__(self, item):
  461. return self._MAP[item]
  462. StatusColorMap = _StatusColorMap()