|
| 1 | +"""Tests for verifying translation completeness on Satellite. |
| 2 | +
|
| 3 | +:CaseAutomation: Automated |
| 4 | +
|
| 5 | +:CaseComponent: LocalizationInternationalization |
| 6 | +
|
| 7 | +:team: Dragonfly |
| 8 | +
|
| 9 | +""" |
| 10 | + |
| 11 | +import os |
| 12 | +import re |
| 13 | + |
| 14 | +import pytest |
| 15 | + |
| 16 | +from robottelo.constants import SUPPORTED_LANGUAGES |
| 17 | +from robottelo.utils.issue_handlers import is_open |
| 18 | + |
| 19 | +LOCALE_DIRS = ( |
| 20 | + '/usr/share/foreman/locale/', |
| 21 | + '/usr/share/gems/gems/{katello,foreman,hammer_cli}*/locale/', |
| 22 | +) |
| 23 | +FIND_POT = f"find {' '.join(LOCALE_DIRS)} -name '*.pot'" |
| 24 | + |
| 25 | + |
| 26 | +@pytest.fixture(params=SUPPORTED_LANGUAGES) |
| 27 | +def supported_language(request): |
| 28 | + return request.param |
| 29 | + |
| 30 | + |
| 31 | +def test_positive_check_missing_translations(target_sat, supported_language): |
| 32 | + """Verify that all translation files have 100% translated messages |
| 33 | + and are complete relative to their templates. |
| 34 | +
|
| 35 | + :id: f33c2ad0-02fd-4d50-b1a9-9b6c66a23700 |
| 36 | +
|
| 37 | + :parametrized: yes |
| 38 | +
|
| 39 | + :Verifies: SAT-32747 |
| 40 | +
|
| 41 | + :steps: |
| 42 | + 1. Find all .po translation files on the Satellite. |
| 43 | + 2. Run ``msgfmt --statistics`` on each file to detect untranslated messages. |
| 44 | + 3. For files with untranslated messages, run ``msgattrib --untranslated`` |
| 45 | + to list them. |
| 46 | + 4. Find all .pot template files and compare each .po file against its |
| 47 | + template using ``msgcmp`` to detect missing translations. |
| 48 | +
|
| 49 | + :expectedresults: |
| 50 | + 1. All translation files are found on the Satellite. |
| 51 | + 2. No translation file contains untranslated messages. |
| 52 | + 3. No translation file is missing strings defined in its template. |
| 53 | +
|
| 54 | + :CaseImportance: Medium |
| 55 | + """ |
| 56 | + # Step 1: Find all .po translation files for the given language |
| 57 | + po_search_dirs = ' '.join(os.path.join(dir_, supported_language, '') for dir_ in LOCALE_DIRS) |
| 58 | + find_po = f"find {po_search_dirs} -name '*.po'" |
| 59 | + result = target_sat.execute(find_po) |
| 60 | + assert result.status == 0, f'Failed to search for .po files: {result.stderr}' |
| 61 | + po_files = result.stdout.strip().splitlines() |
| 62 | + assert po_files, 'No .po translation files found on the Satellite' |
| 63 | + |
| 64 | + # Step 2 & 3: Check each .po file for untranslated messages |
| 65 | + untranslated_report = {} |
| 66 | + for po_file in po_files: |
| 67 | + stats = target_sat.execute(f'msgfmt -v --statistics -o /dev/null {po_file}') |
| 68 | + if 'untranslated' in stats.stderr: |
| 69 | + details = target_sat.execute(f'msgattrib --untranslated --indent --no-wrap {po_file}') |
| 70 | + untranslated_report[po_file] = { |
| 71 | + 'statistics': stats.stderr.strip(), |
| 72 | + 'messages': '\n'.join( |
| 73 | + [line for line in details.stdout.splitlines() if not line.startswith('#')] |
| 74 | + ), |
| 75 | + } |
| 76 | + |
| 77 | + # Step 4: Compare .po files against their .pot templates |
| 78 | + result = target_sat.execute(FIND_POT) |
| 79 | + assert result.status == 0, f'Failed to search for .pot files: {result.stderr}' |
| 80 | + pot_files = result.stdout.strip().splitlines() |
| 81 | + |
| 82 | + template_failures = {} |
| 83 | + for pot_file in pot_files: |
| 84 | + locale_dir = os.path.dirname(pot_file) |
| 85 | + pot_name = os.path.splitext(os.path.basename(pot_file))[0] |
| 86 | + |
| 87 | + related_pos = [po for po in po_files if po.startswith(locale_dir) and pot_name in po] |
| 88 | + for po_file in related_pos: |
| 89 | + cmp_result = target_sat.execute(f'msgcmp {po_file} {pot_file}') |
| 90 | + not_defined = [] |
| 91 | + for line in cmp_result.stderr.splitlines(): |
| 92 | + if f'not defined in {po_file}' in line: |
| 93 | + match = re.search(r':(\d+):', line) |
| 94 | + line_number = int(match.group(1)) if match else 0 |
| 95 | + pot_line = '' |
| 96 | + if line_number: |
| 97 | + get_line_result = target_sat.execute( |
| 98 | + f"sed -n '{line_number - 1}p' {pot_file}" |
| 99 | + ) |
| 100 | + pot_line = get_line_result.stdout.strip() |
| 101 | + not_defined.append((pot_line, line.strip())) |
| 102 | + if not_defined: |
| 103 | + template_failures[po_file] = not_defined |
| 104 | + |
| 105 | + errors = [] |
| 106 | + if untranslated_report: |
| 107 | + summary = '\n'.join( |
| 108 | + f"{info['statistics']}\n{info['messages']}\n" for info in untranslated_report.values() |
| 109 | + ) |
| 110 | + errors.append(f'Files with untranslated messages:\n{summary}') |
| 111 | + if template_failures: |
| 112 | + lines = [] |
| 113 | + for po_file, entries in template_failures.items(): |
| 114 | + lines.append(f'{po_file}:') |
| 115 | + for pot_line, msg in entries: |
| 116 | + lines.append(f' {msg}\n {pot_line}') |
| 117 | + errors.append('Files with missing translations relative to template:\n' + '\n'.join(lines)) |
| 118 | + |
| 119 | + # This code block can be removed once all translation issues are resolved. |
| 120 | + open_issues = {'fr': 'SAT-48297', 'ja': 'SAT-48299', 'ko': 'SAT-48300', 'zh_CN': 'SAT-48301'} |
| 121 | + if errors and open_issues.get(supported_language) and is_open(open_issues[supported_language]): |
| 122 | + pytest.xfail( |
| 123 | + f'Translation of language {supported_language} is still in progress ({open_issues[supported_language]}).\n' |
| 124 | + 'Missing translations:\n' + '\n\n'.join(errors) |
| 125 | + ) |
| 126 | + |
| 127 | + assert not errors, '\n\n'.join(errors) |
0 commit comments