diff options
Diffstat (limited to 'run_unittests.py')
-rwxr-xr-x | run_unittests.py | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/run_unittests.py b/run_unittests.py index 669853e..170df88 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4702,6 +4702,119 @@ recommended as it is not supported on some platforms''') self.assertRegex(contents, r'build main(\.exe)?.*: c_LINKER') self.assertRegex(contents, r'build (lib|cyg)?mylib.*: c_LINKER') + def test_commands_documented(self): + ''' + Test that all listed meson commands are documented in Commands.md. + ''' + help_usage_start_pattern = re.compile(r'^usage:[\t ]*[\r\n]*', re.MULTILINE) + help_positional_start_pattern = re.compile(r'^positional arguments:[\t ]*[\r\n]+', re.MULTILINE) + help_options_start_pattern = re.compile(r'^optional arguments:[\t ]*[\r\n]+', re.MULTILINE) + help_commands_start_pattern = re.compile(r'^[A-Za-z ]*[Cc]ommands:[\t ]*[\r\n]+', re.MULTILINE) + + def get_next_start(iterators, end): + return next((i.start() for i in iterators if i), end) + + def parse_help(help): + help_len = len(help) + usage = help_usage_start_pattern.search(help) + positionals = help_positional_start_pattern.search(help) + options = help_options_start_pattern.search(help) + commands = help_commands_start_pattern.search(help) + + arguments_start = get_next_start([positionals, options, commands], None) + self.assertIsNotNone(arguments_start, 'Cmd command is missing argument list') + + return { + 'usage': help[usage.end():arguments_start], + 'arguments': help[arguments_start:help_len], + } + + md_code_pattern = re.compile(r'^```[\r\n]*', re.MULTILINE) + md_usage_pattern = re.compile(r'^\$ ', re.MULTILINE) + + def parse_section(text, section_start, section_end): + matches = [i + for i in md_code_pattern.finditer(text, pos=section_start, endpos=section_end)] + self.assertGreaterEqual(len(matches), 4, '.md command is missing usage description and/or argument list') + + usage = md_usage_pattern.search(text, pos=matches[0].end(), endpos=matches[1].start()) + + return { + 'usage': text[usage.end():matches[1].start()], + 'arguments': text[matches[2].end():matches[3].start()], + } + + def normalize_text(text): + # clean up formatting + out = re.sub(r'( {2,}|\t+)', r' ', text, flags=re.MULTILINE) # replace whitespace chars with a single space + out = re.sub(r'\r\n+', r'\r', out, flags=re.MULTILINE) # replace newlines with a single linux EOL + out = re.sub(r'(^ +| +$)', '', out, flags=re.MULTILINE) # strip lines + out = re.sub(r'(^\n)', '', out, flags=re.MULTILINE) # remove empty lines + return out + + def clean_dir_arguments(text): + # Remove platform specific defaults + args = [ + 'prefix', + 'bindir', + 'datadir', + 'includedir', + 'infodir', + 'libdir', + 'libexecdir', + 'localedir', + 'localstatedir', + 'mandir', + 'sbindir', + 'sharedstatedir', + 'sysconfdir' + ] + out = text + for a in args: + out = re.sub(r'(--' + a + r' .+?)[ |\n]\(default:.+?\)(\.)?', r'\1\2', out, flags=re.MULTILINE|re.DOTALL) + return out + + ## Get command sections + + md = None + with open('docs/markdown/Commands.md', encoding='utf-8') as f: + md = f.read() + self.assertIsNotNone(md) + + section_pattern = re.compile(r'^### (.+)$', re.MULTILINE) + md_command_section_matches = [i for i in section_pattern.finditer(md)] + md_command_sections = dict() + for i, s in enumerate(md_command_section_matches): + section_end = len(md) if i == len(md_command_section_matches) - 1 else md_command_section_matches[i + 1].start() + md_command_sections[s.group(1)] = (s.start(), section_end) + + ## Validate commands + + md_commands = set(k for k,v in md_command_sections.items()) + + help_output = self._run(self.meson_command + ['--help']) + help_commands = set(c.strip() for c in re.findall(r'usage:(?:.+)?{((?:[a-z]+,*)+?)}', help_output, re.MULTILINE|re.DOTALL)[0].split(',')) + + self.assertEqual(md_commands | {'help'}, help_commands) + + ## Validate command options + + for command in md_commands: + print('Current command: {}'.format(command)) + + help_cmd_output = self._run(self.meson_command + [command, '--help'], override_envvars={'COLUMNS': '80'}) + + parsed_help = parse_help(help_cmd_output) + parsed_section = parse_section(md, *md_command_sections[command]) + + for p in [parsed_help, parsed_section]: + p['usage'] = normalize_text(p['usage']) + p['arguments'] = normalize_text(p['arguments']) + if command in ['setup', 'configure']: + parsed_help['arguments'] = clean_dir_arguments(parsed_help['arguments']) + + self.assertEqual(parsed_help['usage'], parsed_section['usage']) + self.assertEqual(parsed_help['arguments'], parsed_section['arguments']) class FailureTests(BasePlatformTests): ''' |