From e1e2c94ec6908e3c462ca4af617c225c2171908c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Corr=C3=AAa=20da=20Silva=20Sanches?= Date: Wed, 9 Sep 2020 02:49:56 -0300 Subject: [PATCH] Porting a large portion of the remaining code-tests to the new style Using the TestingContext helper class. (follow-up to PR #3005) --- CHANGELOG.md | 8 +- Lib/fontbakery/codetesting.py | 20 +- .../cabincondensed/CabinCondensed-Bold.ttf | Bin 0 -> 98012 bytes .../cabincondensed/CabinCondensed-Medium.ttf | Bin 0 -> 98348 bytes .../cabincondensed/CabinCondensed-Regular.ttf | Bin 0 -> 99348 bytes .../CabinCondensed-SemiBold.ttf | Bin 0 -> 99044 bytes tests/profiles/adobefonts_test.py | 25 +- tests/profiles/cff_test.py | 18 +- tests/profiles/cmap_test.py | 16 +- tests/profiles/dsig_test.py | 12 +- tests/profiles/fontval_test.py | 6 +- tests/profiles/fvar_test.py | 197 ++-- tests/profiles/gdef_test.py | 63 +- tests/profiles/glyf_test.py | 77 +- tests/profiles/googlefonts_test.py | 988 ++++++++---------- tests/profiles/gpos_test.py | 11 +- tests/profiles/head_test.py | 21 +- tests/profiles/hhea_test.py | 36 +- tests/profiles/hmtx_test.py | 13 +- tests/profiles/kern_test.py | 16 +- tests/profiles/loca_test.py | 23 +- tests/profiles/name_test.py | 40 +- tests/profiles/os2_test.py | 50 +- tests/profiles/post_test.py | 17 +- tests/profiles/stat_test.py | 16 +- tests/profiles/universal_test.py | 307 +++--- 26 files changed, 937 insertions(+), 1043 deletions(-) create mode 100644 data/test/cabincondensed/CabinCondensed-Bold.ttf create mode 100644 data/test/cabincondensed/CabinCondensed-Medium.ttf create mode 100644 data/test/cabincondensed/CabinCondensed-Regular.ttf create mode 100644 data/test/cabincondensed/CabinCondensed-SemiBold.ttf diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eca550990..adec1ea6e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ Below are the most important changes from each release. A more detailed list of changes is available in the corresponding milestones for each release in the Github issue tracker (https://github.com/googlefonts/fontbakery/milestones?state=closed). -## 0.7.30 (2020-Sept-someday) + +## 0.7.31 (2020-Oct-??) + - ... + + +## 0.7.30 (2020-Sept-24) ### Note-worthy code changes + - The vast majority of code-tests now use our new style which is less errorprone, using the helper CheckTester class. (PR #3035) - Adopted 4-spaces indentation. We're changing our codestyle to facilitate collaboration from people who also work with the fontTools and AFDKO codebases. (issue 2997) - All rationale text needs to have 8 indentation spaces (because this indentation on the source should not show up on the user-interface when rationale text is printed on the text terminal) - Remove PriorityLevel class as it makes classifying checks by priority more complicated then necessary! (issue #2981) diff --git a/Lib/fontbakery/codetesting.py b/Lib/fontbakery/codetesting.py index 6f26b4a4d8..484d298249 100644 --- a/Lib/fontbakery/codetesting.py +++ b/Lib/fontbakery/codetesting.py @@ -17,8 +17,12 @@ from fontbakery.checkrunner import CheckRunner, Profile, get_module_profile -class TestingContext: - """ CAUTION: this uses a lot of "private" methods and properties +class CheckTester: + """ + This class offers a bit of automation to aid in the implementation of + code-tests to validade the proper behaviour of FontBakery checks. + + !!!CAUTION: this uses a lot of "private" methods and properties of CheckRunner, in order to make unit testing different cases simpler. This is not intended to run in production. However, if that is desired @@ -33,18 +37,6 @@ class TestingContext: An initial run can be with unaltered arguments, as CheckRunner would produce them by itself. And subsequent calls can reuse some of them. - - Example: - > import fontbakery.profiles.googlefonts as googlefonts_profile - > check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/some_id') - > first_result = check(initialTTFont) # can be called with overrides - > modifiedTTFont = check['ttFont'] - > mofifiedTTFont[opentype_table].some_field = some_value - > second_result = check(modifiedTTFont) - > overriden_value = check['some_dependency'] - > overriden_value.change_something() - > another_result = check(modifiedTTFont, {'some_dependency': overriden_value}) """ def __init__(self, module_or_profile, check_id): self.profile = module_or_profile \ diff --git a/data/test/cabincondensed/CabinCondensed-Bold.ttf b/data/test/cabincondensed/CabinCondensed-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..af85fbe550b8514877a7938d143373ad7522d637 GIT binary patch literal 98012 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

Fklcxui6zp0fU%& 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 literal 0 HcmV?d00001 diff --git a/data/test/cabincondensed/CabinCondensed-Regular.ttf b/data/test/cabincondensed/CabinCondensed-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a848bb4ba1e245bb87e6265f113112e8a6b41104 GIT binary patch literal 99348 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 literal 0 HcmV?d00001 diff --git a/data/test/cabincondensed/CabinCondensed-SemiBold.ttf b/data/test/cabincondensed/CabinCondensed-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6191e72310e94514330f589ad60a12fd10d23289 GIT binary patch literal 99044 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


^Vj{n6m@qP0H0TzT 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 literal 0 HcmV?d00001 diff --git a/tests/profiles/adobefonts_test.py b/tests/profiles/adobefonts_test.py index 1c593c6f2a..c9ab961ac2 100644 --- a/tests/profiles/adobefonts_test.py +++ b/tests/profiles/adobefonts_test.py @@ -2,16 +2,16 @@ from fontTools.ttLib import TTFont from fontbakery.checkrunner import (PASS, WARN, FAIL) -from fontbakery.codetesting import (TestingContext, +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, portable_path, - TEST_FILE, - assert_PASS, - assert_results_contain) + TEST_FILE) from fontbakery.profiles import adobefonts as adobefonts_profile def test_check_family_consistent_upm(): - check = TestingContext(adobefonts_profile, - "com.adobe.fonts/check/family/consistent_upm") + check = CheckTester(adobefonts_profile, + "com.adobe.fonts/check/family/consistent_upm") base_path = TEST_FILE("source-sans-pro/OTF") @@ -35,9 +35,8 @@ def test_check_family_consistent_upm(): def test_check_family_consistent_upm_new_style(): # these fonts have a consistent unitsPerEm of 1000: - from fontbakery.codetesting import TestingContext - check = TestingContext(adobefonts_profile, - 'com.adobe.fonts/check/family/consistent_upm') + check = CheckTester(adobefonts_profile, + "com.adobe.fonts/check/family/consistent_upm") filenames = ['SourceSansPro-Regular.otf', 'SourceSansPro-Bold.otf', @@ -78,8 +77,8 @@ def test_get_family_checks(): def test_check_find_empty_letters(): - check = TestingContext(adobefonts_profile, - "com.adobe.fonts/check/find_empty_letters") + check = CheckTester(adobefonts_profile, + "com.adobe.fonts/check/find_empty_letters") # this font has inked glyphs for all letters font = TEST_FILE('source-sans-pro/OTF/SourceSansPro-Regular.otf') @@ -96,8 +95,8 @@ def test_check_find_empty_letters(): def test_check_missing_whitespace(): """ Check that overridden test for nbsp yields WARN rather than FAIL. """ - check = TestingContext(adobefonts_profile, - "com.google.fonts/check/whitespace_glyphs:adobefonts") + check = CheckTester(adobefonts_profile, + "com.google.fonts/check/whitespace_glyphs:adobefonts") font = TEST_FILE('source-sans-pro/OTF/SourceSansPro-Regular.otf') ttFont = TTFont(font) diff --git a/tests/profiles/cff_test.py b/tests/profiles/cff_test.py index 2de5c40869..cc45040157 100644 --- a/tests/profiles/cff_test.py +++ b/tests/profiles/cff_test.py @@ -1,9 +1,9 @@ from fontTools.ttLib import TTFont -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, +from fontbakery.codetesting import (assert_PASS, assert_results_contain, - TestingContext) + CheckTester, + TEST_FILE) from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) from fontbakery.profiles import cff as cff_profile @@ -11,8 +11,8 @@ def test_check_cff_call_depth(): - check = TestingContext(cff_profile, - "com.adobe.fonts/check/cff_call_depth") + check = CheckTester(cff_profile, + "com.adobe.fonts/check/cff_call_depth") # this font's CFF subr call depths should all be <= 10: font = TEST_FILE('source-sans-pro/OTF/SourceSansPro-Regular.otf') @@ -38,8 +38,8 @@ def test_check_cff_call_depth(): def test_check_cff2_call_depth(): - check = TestingContext(cff_profile, - "com.adobe.fonts/check/cff2_call_depth") + check = CheckTester(cff_profile, + "com.adobe.fonts/check/cff2_call_depth") # this font's CFF subr call depths should all be <= 10: font = TEST_FILE('source-sans-pro/VAR/SourceSansVariable-Roman.otf') @@ -65,8 +65,8 @@ def test_check_cff2_call_depth(): def test_check_cff_deprecated_operators(): - check = TestingContext(cff_profile, - "com.adobe.fonts/check/cff_deprecated_operators") + check = CheckTester(cff_profile, + "com.adobe.fonts/check/cff_deprecated_operators") # this font uses the deprecated 'dotsection' operator font = TEST_FILE('deprecated_operators/cff1_dotsection.otf') diff --git a/tests/profiles/cmap_test.py b/tests/profiles/cmap_test.py index b8a496fd69..22549d9a25 100644 --- a/tests/profiles/cmap_test.py +++ b/tests/profiles/cmap_test.py @@ -1,10 +1,10 @@ import pytest from fontTools.ttLib import TTFont -from fontbakery.codetesting import (TestingContext, - TEST_FILE, - assert_PASS, - assert_results_contain) +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) from fontbakery.profiles import opentype as opentype_profile @@ -27,8 +27,8 @@ def mada_ttFonts(): def test_check_family_equal_unicode_encodings(mada_ttFonts): """ Fonts have equal unicode encodings ? """ - check = TestingContext(opentype_profile, - "com.google.fonts/check/family/equal_unicode_encodings") + check = CheckTester(opentype_profile, + "com.google.fonts/check/family/equal_unicode_encodings") from fontbakery.constants import WindowsEncodingID @@ -52,8 +52,8 @@ def test_check_family_equal_unicode_encodings(mada_ttFonts): # Note: I am not aware of any real-case of a font that FAILs this check. def test_check_all_glyphs_have_codepoints(): """ Check all glyphs have codepoints assigned. """ - check = TestingContext(opentype_profile, - "com.google.fonts/check/all_glyphs_have_codepoints") + check = CheckTester(opentype_profile, + "com.google.fonts/check/all_glyphs_have_codepoints") # our reference Mada SemiBold is know to be good here. ttFont = TTFont(TEST_FILE("mada/Mada-SemiBold.ttf")) diff --git a/tests/profiles/dsig_test.py b/tests/profiles/dsig_test.py index 3d20066b63..fe8b586e02 100644 --- a/tests/profiles/dsig_test.py +++ b/tests/profiles/dsig_test.py @@ -1,7 +1,7 @@ -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) -from fontbakery.codetesting import TestingContext +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) from fontbakery.checkrunner import FAIL from fontbakery.profiles import opentype as opentype_profile @@ -10,8 +10,8 @@ def test_check_dsig(): """ Does the font have a DSIG table ? """ - check = TestingContext(opentype_profile, - "com.google.fonts/check/dsig") + check = CheckTester(opentype_profile, + "com.google.fonts/check/dsig") # Our reference Cabin Regular font is good (theres a DSIG table declared): ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) diff --git a/tests/profiles/fontval_test.py b/tests/profiles/fontval_test.py index 85a48f53ce..6bda41164a 100644 --- a/tests/profiles/fontval_test.py +++ b/tests/profiles/fontval_test.py @@ -1,4 +1,3 @@ -import os import pytest from fontbakery.codetesting import (TEST_FILE, @@ -9,7 +8,9 @@ def test_check_fontvalidator(): """ MS Font Validator checks """ - from fontbakery.profiles.fontval import com_google_fonts_check_fontvalidator as check + # check = CheckTester(fontval_profile, + # "com.google.fonts/check/fontvalidator") + check = fontval_profile.com_google_fonts_check_fontvalidator font = TEST_FILE("mada/Mada-Regular.ttf") @@ -19,6 +20,7 @@ def test_check_fontvalidator(): assert status != ERROR # Simulate FontVal missing. + import os old_path = os.environ["PATH"] os.environ["PATH"] = "" with pytest.raises(OSError) as _: diff --git a/tests/profiles/fvar_test.py b/tests/profiles/fvar_test.py index cf85e63d22..5a5afa9559 100644 --- a/tests/profiles/fvar_test.py +++ b/tests/profiles/fvar_test.py @@ -1,36 +1,28 @@ -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, - SKIP, PASS, FAIL) +from fontTools.ttLib import TTFont + +from fontbakery.checkrunner import (FAIL, WARN) from fontbakery.codetesting import (assert_PASS, - assert_results_contain) + assert_results_contain, + CheckTester) +from fontbakery.profiles import opentype as opentype_profile -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) - -from fontTools.ttLib import TTFont def test_check_varfont_regular_wght_coord(): """ The variable font 'wght' (Weight) axis coordinate must be 400 on the 'Regular' instance. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_wght_coord as check - from fontbakery.profiles.shared_conditions import regular_wght_coord + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/regular_wght_coord") - # Our reference varfont, CabinVFBeta.ttf, has - # a good Regular:wght coordinate + # Our reference varfont CabinVFBeta.ttf + # has a good Regular:wght coordinate ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") - regular_weight_coord = regular_wght_coord(ttFont) - - # So it must PASS the test - assert_PASS(check(ttFont, regular_weight_coord), + assert_PASS(check(ttFont), 'with a good Regular:wght coordinate...') - # We then change the value so it must FAIL: + # We then ensure the check detects it when we + # introduce the problem by setting a bad value: ttFont["fvar"].instances[0].coordinates["wght"] = 500 - - # Then re-read the coord: - regular_weight_coord = regular_wght_coord(ttFont) - - # and now this should FAIL the test: - assert_results_contain(check(ttFont, - regular_weight_coord), + assert_results_contain(check(ttFont), FAIL, 'not-400', 'with a bad Regular:wght coordinate (500)...') @@ -38,28 +30,19 @@ def test_check_varfont_regular_wght_coord(): def test_check_varfont_regular_wdth_coord(): """ The variable font 'wdth' (Width) axis coordinate must be 100 on the 'Regular' instance. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_wdth_coord as check - from fontbakery.profiles.shared_conditions import regular_wdth_coord + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/regular_wdth_coord") - # Our reference varfont, CabinVFBeta.ttf, has - # a good Regular:wdth coordinate + # Our reference varfont CabinVFBeta.ttf + # has a good Regular:wdth coordinate ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") - regular_width_coord = regular_wdth_coord(ttFont) - - # So it must PASS the test - assert_PASS(check(ttFont, - regular_width_coord), + assert_PASS(check(ttFont), 'with a good Regular:wdth coordinate...') - # We then change the value so it must FAIL: + # We then ensure the check detects it when we + # introduce the problem by setting a bad value: ttFont["fvar"].instances[0].coordinates["wdth"] = 0 - - # Then re-read the coord: - regular_width_coord = regular_wdth_coord(ttFont) - - # and now this should FAIL the test: - assert_results_contain(check(ttFont, - regular_width_coord), + assert_results_contain(check(ttFont), FAIL, 'not-100', 'with a bad Regular:wdth coordinate (0)...') @@ -67,14 +50,14 @@ def test_check_varfont_regular_wdth_coord(): def test_check_varfont_regular_slnt_coord(): """ The variable font 'slnt' (Slant) axis coordinate must be zero on the 'Regular' instance. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_slnt_coord as check - from fontbakery.profiles.shared_conditions import regular_slnt_coord - from fontTools.ttLib.tables._f_v_a_r import Axis + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/regular_slnt_coord") # Our reference varfont, CabinVFBeta.ttf, lacks a 'slnt' variation axis. ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") # So we add one: + from fontTools.ttLib.tables._f_v_a_r import Axis new_axis = Axis() new_axis.axisTag = "slnt" ttFont["fvar"].axes.append(new_axis) @@ -84,29 +67,21 @@ def test_check_varfont_regular_slnt_coord(): # Note: I know the correct instance index for this hotfix because # I inspected our reference CabinVF using ttx - # then we test the code of the regular_slnt_coord condition: - regular_slant_coord = regular_slnt_coord(ttFont) - - # And with this the test must FAIL - assert_results_contain(check(ttFont, - regular_slant_coord), + # And with this the check must detect the problem: + assert_results_contain(check(ttFont), FAIL, 'non-zero', 'with a bad Regular:slnt coordinate (12)...') - # We then fix the Regular:slnt coordinate value: - regular_slant_coord = 0 - - # and now this should PASS the test: - assert_PASS(check(ttFont, - regular_slant_coord), + # We then fix the Regular:slnt coordinate value in order to PASS: + assert_PASS(check(ttFont, {"regular_slnt_coord": 0}), 'with a good Regular:slnt coordinate (zero)...') def test_check_varfont_regular_ital_coord(): """ The variable font 'ital' (Italic) axis coordinate must be zero on the 'Regular' instance. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_ital_coord as check - from fontbakery.profiles.shared_conditions import regular_ital_coord + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/regular_ital_coord") from fontTools.ttLib.tables._f_v_a_r import Axis # Our reference varfont, CabinVFBeta.ttf, lacks an 'ital' variation axis. @@ -122,29 +97,21 @@ def test_check_varfont_regular_ital_coord(): # Note: I know the correct instance index for this hotfix because # I inspected the our reference CabinVF using ttx - # then we test the code of the regular_ital_coord condition: - regular_italic_coord = regular_ital_coord(ttFont) - - # So it must FAIL the test - assert_results_contain(check(ttFont, - regular_italic_coord), + # And with this the check must detect the problem: + assert_results_contain(check(ttFont), FAIL, 'non-zero', 'with a bad Regular:ital coordinate (123)...') - # We then fix the Regular:ital coordinate: - regular_italic_coord = 0 - - # and now this should PASS the test: - assert_PASS(check(ttFont, - regular_italic_coord), + # but with zero it must PASS the check: + assert_PASS(check(ttFont, {"regular_ital_coord": 0}), 'with a good Regular:ital coordinate (zero)...') def test_check_varfont_regular_opsz_coord(): """ The variable font 'opsz' (Optical Size) axis coordinate should be between 9 and 13 on the 'Regular' instance. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_regular_opsz_coord as check - from fontbakery.profiles.shared_conditions import regular_opsz_coord + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/regular_opsz_coord") from fontTools.ttLib.tables._f_v_a_r import Axis # Our reference varfont, CabinVFBeta.ttf, lacks an 'opsz' variation axis. @@ -160,58 +127,38 @@ def test_check_varfont_regular_opsz_coord(): # Note: I know the correct instance index for this hotfix because # I inspected the our reference CabinVF using ttx - # then we test the regular_opsz_coord condition: - regular_opticalsize_coord = regular_opsz_coord(ttFont) - - # And it must WARN the test - assert_results_contain(check(ttFont, - regular_opticalsize_coord), + # Then we ensure the problem is detected: + assert_results_contain(check(ttFont), WARN, 'out-of-range', 'with a bad Regular:opsz coordinate (8)...') # We try yet another bad value - regular_opticalsize_coord = 14 - - # And it must also WARN the test - assert_results_contain(check(ttFont, regular_opticalsize_coord), + # and the check should detect the problem: + assert_results_contain(check(ttFont, {"regular_opsz_coord": 14}), WARN, 'out-of-range', 'with another bad Regular:opsz value (14)...') # We then test with good default opsz values: for value in [9, 10, 11, 12, 13]: - regular_opticalsize_coord = value - - # and now this should PASS the test: - assert_PASS(check(ttFont, - regular_opticalsize_coord), + assert_PASS(check(ttFont, {"regular_opsz_coord": value}), f'with a good Regular:opsz coordinate ({value})...') def test_check_varfont_bold_wght_coord(): """ The variable font 'wght' (Weight) axis coordinate must be 700 on the 'Bold' instance. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_bold_wght_coord as check - from fontbakery.profiles.shared_conditions import bold_wght_coord + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/bold_wght_coord") - # Our reference varfont, CabinVFBeta.ttf, has - # a good Bold:wght coordinate + # Our reference varfont CabinVFBeta.ttf + # has a good Bold:wght coordinate ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") - bold_weight_coord = bold_wght_coord(ttFont) - - # So it must PASS the test - assert_PASS(check(ttFont, - bold_weight_coord), - 'with a bad Bold:wght coordinate...') + assert_PASS(check(ttFont), + 'with a good Bold:wght coordinate...') - # We then change the value so it must FAIL: + # We then change the value to ensure the problem is properly detected by the check: ttFont["fvar"].instances[3].coordinates["wght"] = 600 - - # Then re-read the coord: - bold_weight_coord = bold_wght_coord(ttFont) - - # and now this should FAIL the test: - assert_results_contain(check(ttFont, - bold_weight_coord), + assert_results_contain(check(ttFont), FAIL, 'not-700', 'with a bad Bold:wght coordinage (600)...') @@ -219,28 +166,23 @@ def test_check_varfont_bold_wght_coord(): def test_check_varfont_wght_valid_range(): """ The variable font 'wght' (Weight) axis coordinate must be within spec range of 1 to 1000 on all instances. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_wght_valid_range as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/wght_valid_range") - # Our reference varfont, CabinVFBeta.ttf, has - # all instances within the 1-1000 range + # Our reference varfont CabinVFBeta.ttf + # has all instances within the 1-1000 range ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") - - # so it must PASS the test: assert_PASS(check(ttFont), 'with a good varfont...') - # We then introduce a bad value: + # We then introduce the problem by setting a bad value: ttFont["fvar"].instances[0].coordinates["wght"] = 0 - - # And it must FAIL the test assert_results_contain(check(ttFont), FAIL, 'out-of-range', 'with wght=0...') # And yet another bad value: ttFont["fvar"].instances[0].coordinates["wght"] = 1001 - - # Should also FAIL: assert_results_contain(check(ttFont), FAIL, 'out-of-range', 'with wght=1001...') @@ -249,28 +191,23 @@ def test_check_varfont_wght_valid_range(): def test_check_varfont_wdth_valid_range(): """ The variable font 'wdth' (Width) axis coordinate must be within spec range of 1 to 1000 on all instances. """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_wdth_valid_range as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/wdth_valid_range") - # Our reference varfont, CabinVFBeta.ttf, has - # all instances within the 1-1000 range + # Our reference varfont CabinVFBeta.ttf + # has all instances within the 1-1000 range ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") - - # so it must PASS the test: assert_PASS(check(ttFont), 'with a good varfont...') - # We then introduce a bad value: + # We then introduce the problem by setting a bad value: ttFont["fvar"].instances[0].coordinates["wdth"] = 0 - - # And it must FAIL the test assert_results_contain(check(ttFont), FAIL, 'out-of-range', 'with wght=0...') # And yet another bad value: ttFont["fvar"].instances[0].coordinates["wdth"] = 1001 - - # Should also FAIL: assert_results_contain(check(ttFont), FAIL, 'out-of-range', 'with wght=1001...') @@ -279,15 +216,14 @@ def test_check_varfont_wdth_valid_range(): def test_check_varfont_slnt_range(): """ The variable font 'slnt' (Slant) axis coordinate specifies positive values in its range? """ - from fontbakery.profiles.fvar import com_google_fonts_check_varfont_slnt_range as check - from fontbakery.profiles.shared_conditions import slnt_axis + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/slnt_range") # Our reference Inter varfont has a bad slnt range ttFont = TTFont("data/test/varfont/inter/Inter[slnt,wght].ttf") - assert_results_contain(check(ttFont, - slnt_axis(ttFont)), + assert_results_contain(check(ttFont), WARN, 'unusual-range', - 'with a varfont that has a bad "slnt" range.') + 'with a varfont that has an unusual "slnt" range.') # We then fix the font-bug by flipping the slnt axis range: for i, axis in enumerate(ttFont["fvar"].axes): @@ -296,7 +232,6 @@ def test_check_varfont_slnt_range(): ttFont["fvar"].axes[i].minValue = -maxValue ttFont["fvar"].axes[i].maxValue = -minValue - # And it must now PASS - assert_PASS(check(ttFont, - slnt_axis(ttFont))) + # And it must now be good ;-) + assert_PASS(check(ttFont)) diff --git a/tests/profiles/gdef_test.py b/tests/profiles/gdef_test.py index e29303307f..e424f16023 100644 --- a/tests/profiles/gdef_test.py +++ b/tests/profiles/gdef_test.py @@ -1,10 +1,13 @@ from fontTools.ttLib import TTFont, newTable from fontTools.ttLib.tables import otTables -from fontbakery.checkrunner import (WARN, PASS, SKIP) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, + +from fontbakery.checkrunner import WARN +from fontbakery.codetesting import (assert_PASS, assert_SKIP, - assert_results_contain) + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile def get_test_font(): @@ -16,6 +19,9 @@ def get_test_font(): glyph = test_ufo.newGlyph("acutecomb") glyph.unicode = 0x0301 test_ttf = ufo2ft.compileTTF(test_ufo) + + # Make the CheckTester class happy... :-P + test_ttf.reader.file.name = "in-memory-data.ttf" return test_ttf @@ -29,62 +35,65 @@ def add_gdef_table(font, class_defs): def test_check_gdef_spacing_marks(): """ Are some spacing glyphs in GDEF mark glyph class? """ - from fontbakery.profiles.gdef import com_google_fonts_check_gdef_spacing_marks as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/gdef_spacing_marks") - test_font = get_test_font() - assert_SKIP(check(test_font), + ttFont = get_test_font() + assert_SKIP(check(ttFont), 'if a font lacks a GDEF table...') - add_gdef_table(test_font, {}) - assert_PASS(check(test_font), + add_gdef_table(ttFont, {}) + assert_PASS(check(ttFont), 'with an empty GDEF table...') # Add a table with 'A' defined as a mark glyph: - add_gdef_table(test_font, {'A': 3}) - assert_results_contain(check(test_font), + add_gdef_table(ttFont, {'A': 3}) + assert_results_contain(check(ttFont), WARN, 'spacing-mark-glyphs', 'if a mark glyph has non-zero width...') def test_check_gdef_mark_chars(): """ Are some mark characters not in in GDEF mark glyph class? """ - from fontbakery.profiles.gdef import com_google_fonts_check_gdef_mark_chars as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/gdef_mark_chars") - test_font = get_test_font() - assert_SKIP(check(test_font), + ttFont = get_test_font() + assert_SKIP(check(ttFont), 'if a font lacks a GDEF table...') # Add a GDEF table not including `acutecomb` (U+0301) as a mark char: - add_gdef_table(test_font, {}) - message = assert_results_contain(check(test_font), + add_gdef_table(ttFont, {}) + message = assert_results_contain(check(ttFont), WARN, 'mark-chars', 'if a mark-char is not listed...') assert 'U+0301' in message # Include it in the table to see the check PASS: - add_gdef_table(test_font, {'acutecomb': 3}) - assert_PASS(check(test_font), + add_gdef_table(ttFont, {'acutecomb': 3}) + assert_PASS(check(ttFont), 'when properly declared...') def test_check_gdef_non_mark_chars(): """ Are some non-mark characters in GDEF mark glyph class spacing? """ - from fontbakery.profiles.gdef import com_google_fonts_check_gdef_non_mark_chars as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/gdef_non_mark_chars") - test_font = get_test_font() - assert_SKIP(check(test_font), + ttFont = get_test_font() + assert_SKIP(check(ttFont), 'if a font lacks a GDEF table...') - add_gdef_table(test_font, {}) - assert_PASS(check(test_font), + add_gdef_table(ttFont, {}) + assert_PASS(check(ttFont), 'with an empty GDEF table.') - add_gdef_table(test_font, {'acutecomb': 3}) - assert_PASS(check(test_font), + add_gdef_table(ttFont, {'acutecomb': 3}) + assert_PASS(check(ttFont), 'with an GDEF with only properly declared mark chars.') - add_gdef_table(test_font, {'acute': 3, 'acutecomb': 3}) - message = assert_results_contain(check(test_font), + add_gdef_table(ttFont, {'acute': 3, 'acutecomb': 3}) + message = assert_results_contain(check(ttFont), WARN, 'non-mark-chars', 'with an GDEF with a non-mark char (U+00B4, "acute") misdeclared') assert 'U+00B4' in message diff --git a/tests/profiles/glyf_test.py b/tests/profiles/glyf_test.py index 493ff5c1e8..6cde582852 100644 --- a/tests/profiles/glyf_test.py +++ b/tests/profiles/glyf_test.py @@ -1,69 +1,72 @@ import io -import os from fontTools.ttLib import TTFont -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) +from fontbakery.checkrunner import (WARN, FAIL) +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile def test_check_glyf_unused_data(): """ Is there any unused data at the end of the glyf table? """ - from fontbakery.profiles.glyf import com_google_fonts_check_glyf_unused_data as check - - test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") + check = CheckTester(opentype_profile, + "com.google.fonts/check/glyf_unused_data") - test_font = TTFont(test_font_path) - assert_PASS(check(test_font)) + font = TEST_FILE("nunito/Nunito-Regular.ttf") + ttFont = TTFont(font) + assert_PASS(check(ttFont)) # Always start with a fresh copy, as fT works lazily. Accessing certain data # can prevent the test from working because we rely on uninitialized # behavior. - test_font = TTFont(test_font_path) - test_font["loca"].locations.pop() - test_file = io.BytesIO() - test_font.save(test_file) - test_font = TTFont(test_file) - assert_results_contain(check(test_font), + ttFont = TTFont(font) + ttFont["loca"].locations.pop() + _file = io.BytesIO() + ttFont.save(_file) + ttFont = TTFont(_file) + ttFont.reader.file.name = font + assert_results_contain(check(ttFont), FAIL, 'unreachable-data') - test_font = TTFont(test_font_path) - test_font["loca"].locations.append(50000) - test_file = io.BytesIO() - test_font.save(test_file) - test_font = TTFont(test_file) - assert_results_contain(check(test_font), + ttFont = TTFont(font) + ttFont["loca"].locations.append(50000) + _file = io.BytesIO() + ttFont.save(_file) + ttFont = TTFont(_file) + ttFont.reader.file.name = font + assert_results_contain(check(ttFont), FAIL, 'missing-data') def test_check_points_out_of_bounds(): """ Check for points out of bounds. """ - from fontbakery.profiles.glyf import com_google_fonts_check_points_out_of_bounds as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/points_out_of_bounds") - test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) - assert_results_contain(check(test_font), + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + assert_results_contain(check(ttFont), WARN, 'points-out-of-bounds') - test_font2 = TTFont(TEST_FILE("familysans/FamilySans-Regular.ttf")) - assert_PASS(check(test_font2)) + ttFont = TTFont(TEST_FILE("familysans/FamilySans-Regular.ttf")) + assert_PASS(check(ttFont)) def test_check_glyf_non_transformed_duplicate_components(): """Check glyphs do not have duplicate components which have the same x,y coordinates.""" - from fontbakery.profiles.glyf import com_google_fonts_check_glyf_non_transformed_duplicate_components as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/glyf_non_transformed_duplicate_components") - test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) - assert_PASS(check(test_font)) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + assert_PASS(check(ttFont)) # Set qutodbl's components to have the same x,y values - test_font['glyf']['quotedbl'].components[0].x = 0 - test_font['glyf']['quotedbl'].components[1].x = 0 - test_font['glyf']['quotedbl'].components[0].y = 0 - test_font['glyf']['quotedbl'].components[1].y = 0 - assert_results_contain(check(test_font), + ttFont['glyf']['quotedbl'].components[0].x = 0 + ttFont['glyf']['quotedbl'].components[1].x = 0 + ttFont['glyf']['quotedbl'].components[0].y = 0 + ttFont['glyf']['quotedbl'].components[1].y = 0 + assert_results_contain(check(ttFont), FAIL, 'found-duplicates') diff --git a/tests/profiles/googlefonts_test.py b/tests/profiles/googlefonts_test.py index 49ea3eb1f2..9e093f2507 100644 --- a/tests/profiles/googlefonts_test.py +++ b/tests/profiles/googlefonts_test.py @@ -8,7 +8,7 @@ assert_PASS, portable_path, TEST_FILE, - TestingContext) + CheckTester) from fontbakery.constants import (NameID, PlatformID, WindowsEncodingID, @@ -45,10 +45,10 @@ def mada_ttFonts(): ] cabin_condensed_fonts = [ - TEST_FILE("cabin/CabinCondensed-Regular.ttf"), - TEST_FILE("cabin/CabinCondensed-Medium.ttf"), - TEST_FILE("cabin/CabinCondensed-Bold.ttf"), - TEST_FILE("cabin/CabinCondensed-SemiBold.ttf") + TEST_FILE("cabincondensed/CabinCondensed-Regular.ttf"), + TEST_FILE("cabincondensed/CabinCondensed-Medium.ttf"), + TEST_FILE("cabincondensed/CabinCondensed-Bold.ttf"), + TEST_FILE("cabincondensed/CabinCondensed-SemiBold.ttf") ] montserrat_fonts = [ @@ -153,8 +153,8 @@ def test_example_checkrunner_based(cabin_regular_path): def test_check_canonical_filename(): """ Files are named canonically. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/canonical_filename") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/canonical_filename") static_canonical_names = [ TEST_FILE("montserrat/Montserrat-Thin.ttf"), @@ -211,8 +211,8 @@ def test_check_canonical_filename(): def test_check_description_broken_links(): """ Does DESCRIPTION file contain broken links ? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/description/broken_links") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/description/broken_links") font = TEST_FILE("cabin/Cabin-Regular.ttf") assert_PASS(check(font), @@ -242,8 +242,8 @@ def test_check_description_broken_links(): def test_check_description_git_url(): """ Does DESCRIPTION file contain an upstream Git repo URL? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/description/git_url") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/description/git_url") # TODO: test INFO 'url-found' @@ -265,8 +265,8 @@ def test_check_description_git_url(): def test_check_description_valid_html(): """ DESCRIPTION file is a propper HTML snippet ? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/description/valid_html") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/description/valid_html") font = TEST_FILE("nunito/Nunito-Regular.ttf") assert_PASS(check(font), @@ -293,8 +293,8 @@ def test_check_description_valid_html(): def test_check_description_min_length(): """ DESCRIPTION.en_us.html must have more than 200 bytes. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/description/min_length") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/description/min_length") font = TEST_FILE("nunito/Nunito-Regular.ttf") @@ -315,8 +315,8 @@ def test_check_description_min_length(): def test_check_description_max_length(): """ DESCRIPTION.en_us.html must have less than 1000 bytes. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/description/max_length") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/description/max_length") font = TEST_FILE("nunito/Nunito-Regular.ttf") @@ -337,8 +337,8 @@ def test_check_description_max_length(): def test_check_description_eof_linebreak(): """ DESCRIPTION.en_us.html should end in a linebreak. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/description/eof_linebreak") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/description/eof_linebreak") font = TEST_FILE("nunito/Nunito-Regular.ttf") @@ -355,13 +355,13 @@ def test_check_description_eof_linebreak(): 'when we add one...') -def test_check_name_family_and_style_max_length(): +def test_check_name_family_and_style_max_length(): """ Combined length of family and style must not exceed 27 characters. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/family_and_style_max_length") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/family_and_style_max_length") # Our reference Cabin Regular is known to be good - ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) # So it must PASS the check: assert_PASS(check(ttFont), @@ -373,26 +373,26 @@ def test_check_name_family_and_style_max_length(): # but later we increased a bit the max allowed length. # First we expect a WARN with a bad FAMILY NAME - for index, name in enumerate(ttFont["name"].names): + for index, name in enumerate(ttFont["name"].names): if name.nameID == NameID.FONT_FAMILY_NAME: # This has 28 chars, while the max currently allowed is 27. bad = "AnAbsurdlyLongFamilyNameFont" assert len(bad) == 28 - ttFont["name"].names[index].string = bad.encode(name.getEncoding()) + ttFont["name"].names[index].string = bad.encode(name.getEncoding()) break assert_results_contain(check(ttFont), WARN, 'too-long', 'with a bad font...') # Now let's restore the good Cabin Regular... - ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) # ...and break the check again with a bad SUBFAMILY NAME: - for index, name in enumerate(ttFont["name"].names): + for index, name in enumerate(ttFont["name"].names): if name.nameID == NameID.FONT_SUBFAMILY_NAME: bad = "WithAVeryLongAndBadStyleName" assert len(bad) == 28 - ttFont["name"].names[index].string = bad.encode(name.getEncoding()) + ttFont["name"].names[index].string = bad.encode(name.getEncoding()) break assert_results_contain(check(ttFont), WARN, 'too-long', @@ -401,8 +401,8 @@ def test_check_name_family_and_style_max_length(): def test_check_name_line_breaks(): """ Name table entries should not contain line-breaks. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/line_breaks") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/line_breaks") # Our reference Mada Regular font is good here: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -423,8 +423,8 @@ def test_check_name_line_breaks(): def test_check_name_rfn(): """ Name table strings must not contain 'Reserved Font Name'. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/rfn") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/rfn") ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) assert_PASS(check(ttFont)) @@ -437,8 +437,8 @@ def test_check_name_rfn(): def test_check_metadata_parses(): """ Check METADATA.pb parse correctly. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/parses") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/parses") good = TEST_FILE("merriweather/Merriweather-Regular.ttf") assert_PASS(check(good), @@ -457,8 +457,8 @@ def test_check_metadata_parses(): def test_check_metadata_unknown_designer(): """ Font designer field in METADATA.pb must not be 'unknown'. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/unknown_designer") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/unknown_designer") font = TEST_FILE("merriweather/Merriweather.ttf") assert_PASS(check(font), @@ -474,9 +474,9 @@ def test_check_metadata_unknown_designer(): def test_check_metadata_designer_values(): """ Multiple values in font designer field in METADATA.pb must be separated by commas. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/designer_values") - + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/designer_values") + font = TEST_FILE("merriweather/Merriweather.ttf") assert_PASS(check(font), 'with a good METADATA.pb file...') @@ -496,8 +496,8 @@ def test_check_metadata_designer_values(): def test_check_metadata_broken_links(): """ Does DESCRIPTION file contain broken links? """ - #check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/metadata/broken_links") + #check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/metadata/broken_links") # TODO: Implement-me! # INFO, "email" # WARN, "timeout" @@ -506,8 +506,8 @@ def test_check_metadata_broken_links(): def test_check_metadata_undeclared_fonts(): """ Ensure METADATA.pb lists all font binaries. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/undeclared_fonts") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/undeclared_fonts") # Our reference Nunito family is know to be good here. font = TEST_FILE("nunito/Nunito-Regular.ttf") @@ -537,8 +537,8 @@ def test_check_metadata_undeclared_fonts(): # TODO: re-enable after addressing issue #1998 def DISABLED_test_check_family_equal_numbers_of_glyphs(mada_ttFonts, cabin_ttFonts): """ Fonts have equal numbers of glyphs? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/family/equal_numbers_of_glyphs") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/family/equal_numbers_of_glyphs") # our reference Cabin family is know to be good here. assert_PASS(check(cabin_ttFonts), @@ -554,8 +554,8 @@ def DISABLED_test_check_family_equal_numbers_of_glyphs(mada_ttFonts, cabin_ttFon # TODO: re-enable after addressing issue #1998 def DISABLED_test_check_family_equal_glyph_names(mada_ttFonts, cabin_ttFonts): """ Fonts have equal glyph names? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/family/equal_glyph_names") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/family/equal_glyph_names") # our reference Cabin family is know to be good here. assert_PASS(check(cabin_ttFonts), @@ -570,8 +570,8 @@ def DISABLED_test_check_family_equal_glyph_names(mada_ttFonts, cabin_ttFonts): def test_check_fstype(): """ Checking OS/2 fsType """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/fstype") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/fstype") # our reference Cabin family is know to be good here. ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) @@ -633,8 +633,8 @@ def test_condition__registered_vendor_ids(): def test_check_vendor_id(): """ Checking OS/2 achVendID """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/vendor_id") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/vendor_id") # Let's start with our reference Merriweather Regular ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Regular.ttf")) @@ -664,8 +664,8 @@ def test_check_vendor_id(): def NOT_IMPLEMENTED__test_check_glyph_coverage(): """ Check glyph coverage. """ - #check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/glyph_coverage") + #check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/glyph_coverage") #TODO: Implement-me! ## Our reference Mada Regular is know to be bad here. @@ -683,8 +683,8 @@ def NOT_IMPLEMENTED__test_check_glyph_coverage(): def test_check_name_unwanted_chars(): """ Substitute copyright, registered and trademark symbols in name table entries. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/unwanted_chars") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/unwanted_chars") # Our reference Mada Regular is know to be bad here. font = TEST_FILE("mada/Mada-Regular.ttf") @@ -700,8 +700,8 @@ def test_check_name_unwanted_chars(): def test_check_usweightclass(): """ Checking OS/2 usWeightClass. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/usweightclass") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/usweightclass") # Our reference Mada Regular is know to be bad here. font = TEST_FILE("mada/Mada-Regular.ttf") @@ -810,8 +810,8 @@ def test_check_name_license(mada_ttFonts): def NOT_IMPLEMENTED_test_check_name_license_url(): """ License URL matches License text on name table? """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/name/license_url") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/name/license_url") # TODO: Implement-me! # # code-paths: @@ -828,13 +828,13 @@ def test_check_name_description_max_length(): """ Description strings in the name table must not exceed 200 characters. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/description_max_length") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/description_max_length") # Our reference Mada Regular is know to be good here. ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) assert_PASS(check(ttFont), - 'with a good font...') + 'with a good font...') # Here we add strings to NameID.DESCRIPTION with exactly 100 chars, # so it should still PASS: @@ -856,8 +856,8 @@ def test_check_name_description_max_length(): def test_check_hinting_impact(): """ Show hinting filesize impact. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/hinting_impact") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/hinting_impact") font = TEST_FILE("mada/Mada-Regular.ttf") assert_results_contain(check(font), @@ -868,8 +868,8 @@ def test_check_hinting_impact(): def test_check_name_version_format(): """ Version format is correct in 'name' table ? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/version_format") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/version_format") # Our reference Mada Regular font is good here: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -898,8 +898,8 @@ def test_check_name_version_format(): def NOT_IMPLEMENTED_test_check_old_ttfautohint(): """ Font has old ttfautohint applied? """ - # check = TestingContext(googlefonts_profile, - # 'com.google.fonts/check/old_ttfautohint') + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/old_ttfautohint") # TODO: Implement-me! # # code-paths: @@ -927,8 +927,8 @@ def NOT_IMPLEMENTED_test_check_old_ttfautohint(): ]) def test_check_has_ttfautohint_params(expected_status, expected_keyword, reason, font): """ Font has ttfautohint params? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/has_ttfautohint_params") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/has_ttfautohint_params") assert_results_contain(check(font), expected_status, expected_keyword, @@ -937,8 +937,8 @@ def test_check_has_ttfautohint_params(expected_status, expected_keyword, reason, def test_check_epar(): """ EPAR table present in font? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/epar") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/epar") # Our reference Mada Regular lacks an EPAR table: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -957,8 +957,8 @@ def test_check_epar(): def NOT_IMPLEMENTED_test_check_gasp(): """ Is GASP table correctly set? """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/gasp") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/gasp") # TODO: Implement-me! # # code-paths: @@ -973,8 +973,8 @@ def NOT_IMPLEMENTED_test_check_gasp(): def test_check_name_familyname_first_char(): """ Make sure family name does not begin with a digit. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/familyname_first_char") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/familyname_first_char") # Our reference Mada Regular is known to be good ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -996,8 +996,8 @@ def test_check_name_familyname_first_char(): def test_check_name_ascii_only_entries(): """ Are there non-ASCII characters in ASCII-only NAME table entries? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/name/ascii_only_entries") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/ascii_only_entries") # Our reference Merriweather Regular is known to be good ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Regular.ttf")) @@ -1066,9 +1066,9 @@ def test_split_camel_case_condition(): def test_check_metadata_listed_on_gfonts(): """ METADATA.pb: Fontfamily is listed on Google Fonts API? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/listed_on_gfonts") - + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/listed_on_gfonts") + font = TEST_FILE("familysans/FamilySans-Regular.ttf") # Our reference FamilySans family is a just a generic example # and thus is not really hosted (nor will ever be hosted) at Google Fonts servers: @@ -1099,8 +1099,8 @@ def test_check_metadata_listed_on_gfonts(): # - Implement the test. def NOT_IMPLEMENTED_test_check_metadata_profiles_csv(): """ METADATA.pb: Designer exists in Google Fonts profiles.csv? """ - # check = TestingContext(googlefonts_profile, - # 'com.google.fonts/check/metadata/profiles_csv') + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/metadata/profiles_csv") # TODO: Implement-me! # # code-paths: @@ -1112,8 +1112,8 @@ def NOT_IMPLEMENTED_test_check_metadata_profiles_csv(): def test_check_metadata_unique_full_name_values(): """ METADATA.pb: check if fonts field only has unique "full_name" values. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/unique_full_name_values') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/unique_full_name_values") # Our reference FamilySans family is good: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1130,8 +1130,8 @@ def test_check_metadata_unique_full_name_values(): def test_check_metadata_unique_weight_style_pairs(): """ METADATA.pb: check if fonts field only contains unique style:weight pairs. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/unique_weight_style_pairs') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/unique_weight_style_pairs") # Our reference FamilySans family is good: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1149,8 +1149,8 @@ def test_check_metadata_unique_weight_style_pairs(): def test_check_metadata_license(): """ METADATA.pb license is "APACHE2", "UFL" or "OFL"? """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/license') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/license") # Let's start with our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1174,8 +1174,8 @@ def test_check_metadata_license(): def test_check_metadata_menu_and_latin(): """ METADATA.pb should contain at least "menu" and "latin" subsets. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/menu_and_latin') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/menu_and_latin") # Let's start with our reference FamilySans family: fonts = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1211,8 +1211,8 @@ def test_check_metadata_menu_and_latin(): def test_check_metadata_subsets_order(): """ METADATA.pb subsets should be alphabetically ordered. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/subsets_order') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/subsets_order") # Let's start with our reference FamilySans family: fonts = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1248,8 +1248,8 @@ def test_check_metadata_subsets_order(): def test_check_metadata_includes_production_subsets(): """Check METADATA.pb has production subsets.""" - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/includes_production_subsets') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/includes_production_subsets") # We need to use a family that is already in production # Our reference Cabin is known to be good @@ -1269,8 +1269,8 @@ def test_check_metadata_includes_production_subsets(): def test_check_metadata_copyright(): """ METADATA.pb: Copyright notice is the same in all fonts? """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/copyright') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/copyright") # Let's start with our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1291,8 +1291,8 @@ def test_check_metadata_copyright(): def test_check_metadata_familyname(): """ Check that METADATA.pb family values are all the same. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/familyname') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/familyname") # Let's start with our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1313,8 +1313,8 @@ def test_check_metadata_familyname(): def test_check_metadata_has_regular(): """ METADATA.pb: According Google Fonts standards, families should have a Regular style. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/has_regular') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/has_regular") # Let's start with our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1338,8 +1338,8 @@ def test_check_metadata_has_regular(): def test_check_metadata_regular_is_400(): """ METADATA.pb: Regular should be 400. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/regular_is_400') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/regular_is_400") # Let's start with the METADATA.pb file from our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1363,8 +1363,8 @@ def test_check_metadata_regular_is_400(): def test_check_metadata_nameid_family_name(): """ Checks METADATA.pb font.name field matches family name declared on the name table. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/nameid/family_name') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/nameid/family_name") # Let's start with the METADATA.pb file from our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1388,8 +1388,8 @@ def test_check_metadata_nameid_family_name(): def test_check_metadata_nameid_post_script_name(): """ Checks METADATA.pb font.post_script_name matches postscript name declared on the name table. """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/nameid/post_script_name') + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/nameid/post_script_name") # Let's start with the METADATA.pb file from our reference FamilySans family: font = TEST_FILE("familysans/FamilySans-Regular.ttf") @@ -1412,9 +1412,9 @@ def test_check_metadata_nameid_post_script_name(): def test_check_metadata_nameid_full_name(): """ METADATA.pb font.fullname value matches fullname declared on the name table ? """ - check = TestingContext(googlefonts_profile, - 'com.google.fonts/check/metadata/nameid/full_name') - + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/nameid/full_name") + font = TEST_FILE("merriweather/Merriweather-Regular.ttf") assert_PASS(check(font), @@ -1446,8 +1446,8 @@ def test_check_metadata_nameid_full_name(): def test_check_metadata_nameid_font_name(): """ METADATA.pb font.name value should be same as the family name declared on the name table. """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/nameid/font_name") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/nameid/font_name") # Our reference Merriweather-Regular is know to have good fullname metadata font = TEST_FILE("merriweather/Merriweather-Regular.ttf") @@ -1471,8 +1471,8 @@ def test_check_metadata_nameid_font_name(): def test_check_metadata_match_fullname_postscript(): """ METADATA.pb family.full_name and family.post_script_name fields have equivalent values ? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/match_fullname_postscript") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/match_fullname_postscript") regular_font = TEST_FILE("merriweather/Merriweather-Regular.ttf") lightitalic_font = TEST_FILE("merriweather/Merriweather-LightItalic.ttf") @@ -1521,8 +1521,8 @@ def test_check_metadata_match_fullname_postscript(): def NOT_IMPLEMENTED_test_check_match_filename_postscript(): """ METADATA.pb family.filename and family.post_script_name fields have equivalent values? """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/match_filename_postscript") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/match_filename_postscript") # TODO: Implement-me! # # code-paths: @@ -1555,8 +1555,8 @@ def NOT_IMPLEMENTED_test_check_match_filename_postscript(): def test_check_metadata_valid_name_values(): """ METADATA.pb font.name field contains font name in right format? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/valid_name_values") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/valid_name_values") # Our reference Montserrat family is a good 18-styles family: for font in MONTSERRAT_RIBBI: @@ -1584,8 +1584,8 @@ def test_check_metadata_valid_name_values(): def test_check_metadata_valid_full_name_values(): """ METADATA.pb font.full_name field contains font name in right format? """ - check = TestingContext(googlefonts_profile, - "com.google.fonts/check/metadata/valid_full_name_values") + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/valid_full_name_values") # Our reference Montserrat family is a good 18-styles family: # properly described in its METADATA.pb file: @@ -1616,6 +1616,7 @@ def test_check_metadata_valid_full_name_values(): def test_check_metadata_valid_filename_values(): """ METADATA.pb font.filename field contains font name in right format? """ + # FIXME: CheckTester from fontbakery.profiles.googlefonts \ import (com_google_fonts_check_metadata_valid_filename_values as check, family_metadata) @@ -1639,47 +1640,32 @@ def test_check_metadata_valid_filename_values(): def test_check_metadata_valid_post_script_name_values(): """ METADATA.pb font.post_script_name field contains font name in right format? """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_valid_post_script_name_values as check, - family_metadata, - font_metadata, - font_familynames) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/valid_post_script_name_values") # Our reference Montserrat family is a good 18-styles family: for fontfile in MONTSERRAT_RIBBI + MONTSERRAT_NON_RIBBI: - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - ttFont = TTFont(fontfile) - font_fnames = font_familynames(ttFont) - # So it must PASS the check: - assert_PASS(check(font_meta, font_fnames), + assert_PASS(check(fontfile), f"with a good font ({fontfile})...") # And fail if it finds a bad filename: - font_meta.post_script_name = "WrongPSName" - assert_results_contain(check(font_meta, font_fnames), + md = check["font_metadata"] + md.post_script_name = "WrongPSName" + assert_results_contain(check(fontfile, {"font_metadata": md}), FAIL, 'mismatch', f'with a bad font ({fontfile})...') def test_check_metadata_valid_copyright(): """ Copyright notice on METADATA.pb matches canonical pattern ? """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_valid_copyright as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/valid_copyright") # Our reference Cabin Regular is known to be bad # Since it provides an email instead of a git URL: - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must FAIL the check: - assert_results_contain(check(font_meta), + font = TEST_FILE("cabin/Cabin-Regular.ttf") + assert_results_contain(check(font), FAIL, 'bad-notice-format', 'with a bad copyright notice string...') @@ -1688,34 +1674,34 @@ def test_check_metadata_valid_copyright(): # It only focuses on the string format. good_string = ("Copyright 2017 The Archivo Black Project Authors" " (https://github.com/Omnibus-Type/ArchivoBlack)") - font_meta.copyright = good_string - assert_PASS(check(font_meta), + md = check["font_metadata"] + md.copyright = good_string + assert_PASS(check(font, {"font_metadata": md}), 'with a good copyright notice string...') # We also ignore case, so these should also PASS: - font_meta.copyright = good_string.upper() - assert_PASS(check(font_meta), + md.copyright = good_string.upper() + assert_PASS(check(font, {"font_metadata": md}), 'with all uppercase...') - font_meta.copyright = good_string.lower() - assert_PASS(check(font_meta), + md.copyright = good_string.lower() + assert_PASS(check(font, {"font_metadata": md}), 'with all lowercase...') def test_check_font_copyright(): """Copyright notices match canonical pattern in fonts""" - from fontbakery.profiles.googlefonts import com_google_fonts_check_font_copyright as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/font_copyright") + # Our reference Cabin Regular is known to be bad # Since it provides an email instead of a git URL: - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(fontfile) - - # So it must FAIL the check: + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) assert_results_contain(check(ttFont), FAIL, 'bad-notice-format', 'with a bad copyright notice string...') - # Then we change it into a good string (example extracted from Archivo Black): + # We change it into a good string (example extracted from Archivo Black): # note: the check does not actually verify that the project name is correct. # It only focuses on the string format. good_string = ("Copyright 2017 The Archivo Black Project Authors" @@ -1729,45 +1715,36 @@ def test_check_font_copyright(): def test_check_metadata_reserved_font_name(): """ Copyright notice on METADATA.pb should not contain Reserved Font Name. """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_reserved_font_name as check, - family_metadata, - font_metadata) - - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/reserved_font_name") - assert_PASS(check(font_meta), + font = TEST_FILE("cabin/Cabin-Regular.ttf") + assert_PASS(check(font), 'with a good copyright notice string...') # Then we make it bad: - font_meta.copyright += "Reserved Font Name" - - assert_results_contain(check(font_meta), + md = check["font_metadata"] + md.copyright += "Reserved Font Name" + assert_results_contain(check(font, {"font_metadata": md}), WARN, 'rfn', 'with a notice containing "Reserved Font Name"...') def test_check_metadata_copyright_max_length(): """ METADATA.pb: Copyright notice shouldn't exceed 500 chars. """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_copyright_max_length as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/copyright_max_length") - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) + font = TEST_FILE("cabin/Cabin-Regular.ttf") + check(font) + md = check["font_metadata"] - font_meta.copyright = 500 * "x" - assert_PASS(check(font_meta), + md.copyright = 500 * "x" + assert_PASS(check(font, {"font_metadata": md}), 'with a 500-char copyright notice string...') - font_meta.copyright = 501 * "x" - assert_results_contain(check(font_meta), + md.copyright = 501 * "x" + assert_results_contain(check(font, {"font_metadata": md}), FAIL, 'max-length', 'with a 501-char copyright notice string...') @@ -1810,18 +1787,12 @@ def test_check_metadata_filenames(): def test_check_metadata_italic_style(): """ METADATA.pb font.style "italic" matches font internals ? """ from fontbakery.constants import MacStyle - from fontbakery.profiles.googlefonts import (com_google_fonts_check_metadata_italic_style as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/italic_style") + # Our reference Merriweather Italic is known to good - fontfile = TEST_FILE("merriweather/Merriweather-Italic.ttf") - ttFont = TTFont(fontfile) - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS: - assert_PASS(check(ttFont, font_meta), + ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Italic.ttf")) + assert_PASS(check(ttFont), 'with a good font...') # now let's introduce issues on the FULL_FONT_NAME entries @@ -1830,7 +1801,7 @@ def test_check_metadata_italic_style(): if name.nameID == NameID.FULL_FONT_NAME: backup = name.string ttFont['name'].names[i].string = "BAD VALUE".encode(name.getEncoding()) - assert_results_contain(check(ttFont, font_meta), + assert_results_contain(check(ttFont), FAIL, 'bad-fullfont-name', 'with a bad NameID.FULL_FONT_NAME entry...') # and restore the good value: @@ -1839,29 +1810,23 @@ def test_check_metadata_italic_style(): # And, finally, let's flip off that italic bit # and get a "bad-macstyle" FAIL (so much fun!): ttFont['head'].macStyle &= ~MacStyle.ITALIC - assert_results_contain(check(ttFont, font_meta), + assert_results_contain(check(ttFont), FAIL, 'bad-macstyle', 'with bad macstyle bit value...') def test_check_metadata_normal_style(): """ METADATA.pb font.style "normal" matches font internals ? """ + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/normal_style") from fontbakery.constants import MacStyle - from fontbakery.profiles.googlefonts import (com_google_fonts_check_metadata_normal_style as check, - family_metadata, - font_metadata) + # This one is pretty similar to check/metadata/italic_style # You may want to take a quick look above... # Our reference Merriweather Regular is known to be good here. - fontfile = TEST_FILE("merriweather/Merriweather-Regular.ttf") - ttFont = TTFont(fontfile) - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(ttFont, font_meta), + ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Regular.ttf")) + assert_PASS(check(ttFont), 'with a good font...') # now we sadically insert brokenness into @@ -1870,7 +1835,7 @@ def test_check_metadata_normal_style(): if name.nameID == NameID.FONT_FAMILY_NAME: backup = name.string ttFont['name'].names[i].string = "Merriweather-Italic".encode(name.getEncoding()) - assert_results_contain(check(ttFont, font_meta), + assert_results_contain(check(ttFont), FAIL, 'familyname-italic', 'with a non-italic font that has a "-Italic" in FONT_FAMILY_NAME...') # and restore the good value: @@ -1882,7 +1847,7 @@ def test_check_metadata_normal_style(): if name.nameID == NameID.FULL_FONT_NAME: backup = name.string ttFont['name'].names[i].string = "Merriweather-Italic".encode(name.getEncoding()) - assert_results_contain(check(ttFont, font_meta), + assert_results_contain(check(ttFont), FAIL, 'fullfont-italic', 'with a non-italic font that has a "-Italic" in FULL_FONT_NAME...') # and restore the good value: @@ -1894,37 +1859,28 @@ def test_check_metadata_normal_style(): # to the test for com.google.fonts/check/metadata/italic_style above. # Here we have to set the bit back to 1 to get a wrongful "this font is an italic" setting: ttFont['head'].macStyle |= MacStyle.ITALIC - assert_results_contain(check(ttFont, font_meta), + assert_results_contain(check(ttFont), FAIL, 'bad-macstyle', 'with bad macstyle bit value...') def test_check_metadata_nameid_family_and_full_names(): """ METADATA.pb font.name and font.full_name fields match the values declared on the name table? """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_nameid_family_and_full_names as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/nameid/family_and_full_names") # Our reference Merriweather Regular is known to be good here. - fontfile = TEST_FILE("merriweather/Merriweather-Regular.ttf") - ttFont = TTFont(fontfile) - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(ttFont, font_meta), + ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Regular.ttf")) + assert_PASS(check(ttFont), 'with a good font...') - # There we go again: - # breaking FULL_FONT_NAME entries - # one by one: + # There we go again! + # Breaking FULL_FONT_NAME entries one by one: for i, name in enumerate(ttFont['name'].names): if name.nameID == NameID.FULL_FONT_NAME: backup = name.string ttFont['name'].names[i].string = "This is utterly wrong!".encode(name.getEncoding()) - assert_results_contain(check(ttFont, font_meta), + assert_results_contain(check(ttFont), FAIL, 'fullname-mismatch', 'with a METADATA.pb / FULL_FONT_NAME mismatch...') # and restore the good value: @@ -1935,8 +1891,8 @@ def test_check_metadata_nameid_family_and_full_names(): if name.nameID == NameID.FONT_FAMILY_NAME: backup = name.string ttFont['name'].names[i].string = ("I'm listening to" - " Wes Montgomery live 1965").encode(name.getEncoding()) - assert_results_contain(check(ttFont, font_meta), + " The Players with Hiromasa Suzuki - Galaxy (1979)").encode(name.getEncoding()) + assert_results_contain(check(ttFont), FAIL, 'familyname-mismatch', 'with a METADATA.pb / FONT_FAMILY_NAME mismatch...') # and restore the good value: @@ -1945,158 +1901,133 @@ def test_check_metadata_nameid_family_and_full_names(): def test_check_metadata_fontname_not_camel_cased(): """ METADATA.pb: Check if fontname is not camel cased. """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_fontname_not_camel_cased as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/fontname_not_camel_cased") # Our reference Cabin Regular is known to be good - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(font_meta), + font = TEST_FILE("cabin/Cabin-Regular.ttf") + assert_PASS(check(font), 'with a good font...') # Then we FAIL with a CamelCased name: - font_meta.name = "GollyGhost" - assert_results_contain(check(font_meta), + md = check["font_metadata"] + md.name = "GollyGhost" + assert_results_contain(check(font, {"font_metadata": md}), FAIL, 'camelcase', - 'with a bad font (CamelCased font name)...') + 'with a bad font name (CamelCased)...') # And we also make sure the check PASSes with a few known good names: - good_names = ["VT323", "PT Sans", "Amatic SC"] - for good_name in good_names: - font_meta.name = good_name - assert_PASS(check(font_meta), + for good_name in ["VT323", + "PT Sans", + "Amatic SC"]: + md.name = good_name + assert_PASS(check(font, {"font_metadata": md}), f'with a good font name "{good_name}"...') def test_check_metadata_match_name_familyname(): """ METADATA.pb: Check font name is the same as family name. """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_match_name_familyname as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/match_name_familyname") # Our reference Cabin Regular is known to be good - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(family_meta, font_meta), + font = TEST_FILE("cabin/Cabin-Regular.ttf") + assert_PASS(check(font), 'with a good font...') # Then we FAIL with mismatching names: - family_meta.name = "Some Fontname" - font_meta.name = "Something Else" - assert_results_contain(check(family_meta, font_meta), + family_md = check["family_metadata"] + font_md = check["font_metadata"] + family_md.name = "Some Fontname" + font_md.name = "Something Else" + assert_results_contain(check(font, {"family_metadata": family_md, + "font_metadata": font_md}), FAIL, 'mismatch', - 'with a bad font...') + 'with bad font/family name metadata...') def test_check_check_metadata_canonical_weight_value(): """ METADATA.pb: Check that font weight has a canonical value. """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_canonical_weight_value as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/canonical_weight_value") - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) + font = TEST_FILE("cabin/Cabin-Regular.ttf") + check(font) + md = check["font_metadata"] for w in [100, 200, 300, 400, 500, 600, 700, 800, 900]: - font_meta.weight = w - assert_PASS(check(font_meta), + md.weight = w + assert_PASS(check(font, {"font_metadata": md}), f'with a good weight value ({w})...') for w in [150, 250, 350, 450, 550, 650, 750, 850]: - font_meta.weight = w - assert_results_contain(check(font_meta), + md.weight = w + assert_results_contain(check(font, {"font_metadata": md}), FAIL, 'bad-weight', 'with a bad weight value ({w})...') def test_check_metadata_os2_weightclass(): """ Checking OS/2 usWeightClass matches weight specified at METADATA.pb """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_os2_weightclass as check, - family_metadata, - font_metadata) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/os2_weightclass") - # VF + # === test cases for Variable Fonts === # Our reference Jura is known to be good - fontfile = portable_path("data/test/varfont/jura/Jura[wght].ttf") - ttFont = TTFont(fontfile) - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(ttFont, font_meta), - f'with a good font ({fontfile})...') + ttFont = TTFont(TEST_FILE("varfont/jura/Jura[wght].ttf")) + assert_PASS(check(ttFont), + f'with a good metadata...') - # And fail if it finds a bad weight value: - good_value = font_meta.weight + # Should report if a bad weight value is ifound though: + md = check["font_metadata"] + good_value = md.weight bad_value = good_value + 100 - font_meta.weight = bad_value - assert_results_contain(check(ttFont, font_meta), + md.weight = bad_value + assert_results_contain(check(ttFont, {"font_metadata": md}), FAIL, 'mismatch', - f'with a bad font ({fontfile})...') + f'with a bad metadata...') - # Static + # === test cases for Static Fonts === # Our reference Montserrat family is a good 18-styles family: for fontfile in MONTSERRAT_RIBBI + MONTSERRAT_NON_RIBBI: ttFont = TTFont(fontfile) - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(ttFont, font_meta), + assert_PASS(check(ttFont), f'with a good font ({fontfile})...') - # And fail if it finds a bad weight value: - good_value = font_meta.weight + # but should report bad weight values: + md = check["font_metadata"] + good_value = md.weight bad_value = good_value + 50 - font_meta.weight = bad_value - assert_results_contain(check(ttFont, font_meta), + md.weight = bad_value + assert_results_contain(check(ttFont, {"font_metadata": md}), FAIL, 'mismatch', - f'with a bad font ({fontfile})...') + f'with bad metadata for {fontfile}...') # If font is Thin or ExtraLight, ensure that this check can # accept both 100, 250 for Thin and 200, 275 for ExtraLight - font_meta.weight = good_value - font_meta = font_metadata(family_meta, fontfile) if "Thin" in fontfile: ttFont["OS/2"].usWeightClass = 100 - assert_PASS(check(ttFont, font_meta), - f'with a good font ({fontfile})...') + assert_PASS(check(ttFont), + f'with weightclass 100 on ({fontfile})...') ttFont["OS/2"].usWeightClass = 250 - assert_PASS(check(ttFont, font_meta), - f'with a good font ({fontfile})...') + assert_PASS(check(ttFont), + f'with weightclass 250 on ({fontfile})...') if "ExtraLight" in fontfile: ttFont["OS/2"].usWeightClass = 200 - assert_PASS(check(ttFont, font_meta), - f'with a good font ({fontfile})...') + assert_PASS(check(ttFont), + f'with weightClass 200 on ({fontfile})...') ttFont["OS/2"].usWeightClass = 275 - assert_PASS(check(ttFont, font_meta), - f'with a good font ({fontfile})...') + assert_PASS(check(ttFont), + f'with weightClass 275 on ({fontfile})...') def NOT_IMPLEMENTED_test_check_metadata_match_weight_postscript(): """ METADATA.pb: Metadata weight matches postScriptName. """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/metadata/match_weight_postscript") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/metadata/match_weight_postscript") # TODO: Implement-me! # # code-paths: @@ -2107,8 +2038,8 @@ def NOT_IMPLEMENTED_test_check_metadata_match_weight_postscript(): def NOT_IMPLEMENTED_test_check_metadata_canonical_style_names(): """ METADATA.pb: Font styles are named canonically? """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/metadata/canonical_style_names") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/metadata/canonical_style_names") # TODO: Implement-me! # # code-paths: @@ -2120,10 +2051,10 @@ def NOT_IMPLEMENTED_test_check_metadata_canonical_style_names(): def test_check_unitsperem_strict(): """ Stricter unitsPerEm criteria for Google Fonts. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_unitsperem_strict as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/unitsperem_strict") - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(fontfile) + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) PASS_VALUES = [16, 32, 64, 128, 256, 512, 1024] # Good for better performance on legacy renderers PASS_VALUES.extend([500, 1000]) # or common typical values @@ -2161,8 +2092,8 @@ def test_check_unitsperem_strict(): def NOT_IMPLEMENTED_test_check_version_bump(): """ Version number has increased since previous release on Google Fonts? """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/version_bump") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/version_bump") # TODO: Implement-me! # # code-paths: @@ -2175,8 +2106,8 @@ def NOT_IMPLEMENTED_test_check_version_bump(): def NOT_IMPLEMENTED_test_check_production_glyphs_similarity(): """ Glyphs are similiar to Google Fonts version? """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/production_glyphs_similarity") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/production_glyphs_similarity") # TODO: Implement-me! # # code-paths: @@ -2186,8 +2117,8 @@ def NOT_IMPLEMENTED_test_check_production_glyphs_similarity(): def NOT_IMPLEMENTED_test_check_fsselection(): """ Checking OS/2 fsSelection value. """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/fsselection") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/fsselection") # TODO: Implement-me! # # code-paths: @@ -2196,10 +2127,10 @@ def NOT_IMPLEMENTED_test_check_fsselection(): def test_check_italic_angle(): """ Checking post.italicAngle value. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_italic_angle as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/italic_angle") - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(fontfile) + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) # italic-angle, style, fail_message test_cases = [ @@ -2216,22 +2147,22 @@ def test_check_italic_angle(): ttFont["post"].italicAngle = value if expected_result != PASS: - assert_results_contain(check(ttFont, style), + assert_results_contain(check(ttFont, {"style": style}), expected_result, expected_msg, f"with italic-angle:{value} style:{style}...") else: - assert_PASS(check(ttFont, style), + assert_PASS(check(ttFont, {"style": style}), f'with italic-angle:{value} style:{style}...') def test_check_mac_style(): """ Checking head.macStyle value. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_mac_style as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/mac_style") from fontbakery.constants import MacStyle - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(fontfile) + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) # macStyle-value, style, expected test_cases = [ @@ -2247,24 +2178,22 @@ def test_check_mac_style(): for macStyle_value, style, expected in test_cases: ttFont["head"].macStyle = macStyle_value - results = check(ttFont, style) if expected == PASS: - assert_PASS(results, + assert_PASS(check(ttFont, {"style": style}), 'with macStyle:{macStyle_value} style:{style}...') else: - assert_results_contain(results, + assert_results_contain(check(ttFont, {"style": style}), FAIL, expected, f"with macStyle:{macStyle_value} style:{style}...") def test_check_contour_count(montserrat_ttFonts): """Check glyphs contain the recommended contour count""" - from fontbakery.profiles.googlefonts import com_google_fonts_check_contour_count as check - + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/contour_count") # TODO: FAIL, "lacks-cmap" - for ttFont in montserrat_ttFonts: assert_PASS(check(ttFont), 'Montserrat which was used to assemble the glyph data...') @@ -2281,6 +2210,9 @@ def test_check_contour_count(montserrat_ttFonts): # More info at https://github.com/googlefonts/fontbakery/issues/2581 def DISABLED_test_check_production_encoded_glyphs(cabin_ttFonts): """Check glyphs are not missing when compared to version on fonts.google.com""" + # FIXME: + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/production_encoded_glyphs") from fontbakery.profiles.shared_conditions import family_directory from fontbakery.profiles.googlefonts \ import (com_google_fonts_check_production_encoded_glyphs as check, @@ -2313,52 +2245,44 @@ def DISABLED_test_check_production_encoded_glyphs(cabin_ttFonts): def test_check_metadata_nameid_copyright(): """ Copyright field for this font on METADATA.pb matches all copyright notice entries on the name table? """ + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/nameid/copyright") from fontbakery.utils import get_name_entry_strings - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_metadata_nameid_copyright as check, - family_metadata, - font_metadata) # Our reference Cabin Regular is known to be good - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(fontfile) - family_directory = os.path.dirname(fontfile) - family_meta = family_metadata(family_directory) - font_meta = font_metadata(family_meta, fontfile) - - # So it must PASS the check: - assert_PASS(check(ttFont, font_meta), + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) + assert_PASS(check(ttFont), "with a good METADATA.pb for this font...") - # Then we FAIL with mismatching names: + # But the check must report when mismatching names are found: good_value = get_name_entry_strings(ttFont, NameID.COPYRIGHT_NOTICE)[0] - font_meta.copyright = good_value + "something bad" - assert_results_contain(check(ttFont, font_meta), + md = check["font_metadata"] + md.copyright = good_value + "something bad" + assert_results_contain(check(ttFont, {"font_metadata": md}), FAIL, 'mismatch', 'with a bad METADATA.pb (with a copyright string not matching this font)...') def test_check_metadata_category(): """ Category field for this font on METADATA.pb is valid? """ - from fontbakery.profiles.googlefonts import (com_google_fonts_check_metadata_category as check, - family_metadata) - # Our reference Cabin family... - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - family_directory = os.path.dirname(fontfile) - metadata = family_metadata(family_directory) - assert metadata.category == "SANS_SERIF" # ...is known to be good ;-) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/metadata/category") - # So it must PASS the check: - assert_PASS(check(metadata), + # Our reference Cabin family... + font = TEST_FILE("cabin/Cabin-Regular.ttf") + check(font) + md = check["family_metadata"] + assert md.category == "SANS_SERIF" # ...is known to be good ;-) + assert_PASS(check(font), "with a good METADATA.pb...") - # Then we report a problem with this sample of bad values: + # We then report a problem with this sample of bad values: for bad_value in ["SAN_SERIF", "MONO_SPACE", "sans_serif", "monospace"]: - metadata.category = bad_value - assert_results_contain(check(metadata), + md.category = bad_value + assert_results_contain(check(font, {"family_metadata": md}), FAIL, 'bad-value', f'with a bad category "{bad_value}"...') @@ -2368,25 +2292,23 @@ def test_check_metadata_category(): "SERIF", "DISPLAY", "HANDWRITING"]: - metadata.category = good_value - assert_PASS(check(metadata), + md.category = good_value + assert_PASS(check(font, {"family_metadata": md}), f'with "{good_value}"...') def test_check_name_mandatory_entries(): """ Font has all mandatory 'name' table entries ? """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_name_mandatory_entries as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/mandatory_entries") # We'll check both RIBBI and non-RIBBI fonts # so that we cover both cases for FAIL/PASS scenarios - #First with a RIBBI font: + # === First with a RIBBI font: === # Our reference Cabin Regular is known to be good ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) - style = "Regular" - - # So it must PASS the check: - assert_PASS(check(ttFont, style), + assert_PASS(check(ttFont), "with a good RIBBI font...") mandatory_entries = [NameID.FONT_FAMILY_NAME, @@ -2402,17 +2324,14 @@ def test_check_name_mandatory_entries(): ttFont['name'].names[i].nameID = 0 # not really removing it, but replacing it # by something else completely irrelevant # for the purposes of this specific check - assert_results_contain(check(ttFont, style), + assert_results_contain(check(ttFont), FAIL, 'missing-entry', f'with a missing madatory (RIBBI) name entry (id={mandatory})...') - #And now a non-RIBBI font: + # === And now a non-RIBBI font: === # Our reference Merriweather Black is known to be good ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Black.ttf")) - style = "Black" - - # So it must PASS the check: - assert_PASS(check(ttFont, style), + assert_PASS(check(ttFont), "with a good non-RIBBI font...") mandatory_entries = [NameID.FONT_FAMILY_NAME, @@ -2430,7 +2349,7 @@ def test_check_name_mandatory_entries(): ttFont['name'].names[i].nameID = 0 # not really removing it, but replacing it # by something else completely irrelevant # for the purposes of this specific check - assert_results_contain(check(ttFont, style), + assert_results_contain(check(ttFont), FAIL, 'missing-entry', 'with a missing madatory (non-RIBBI) name entry (id={mandatory})...') @@ -2443,10 +2362,9 @@ def test_condition_familyname_with_spaces(): def test_check_name_familyname(): """ Check name table: FONT_FAMILY_NAME entries. """ - from fontbakery.profiles.googlefonts import (com_google_fonts_check_name_familyname as check, - familyname, - familyname_with_spaces, - style) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/familyname") + # TODO: FAIL, "lacks-name" test_cases = [ @@ -2472,18 +2390,16 @@ def test_check_name_familyname(): if name.nameID == NameID.FONT_FAMILY_NAME: ttFont['name'].names[i].string = value.encode(name.getEncoding()) - assert_results_contain(check(ttFont, - style(filename), - familyname_with_spaces(familyname(filename))), + assert_results_contain(check(ttFont), expected, keyword, f'with filename="{filename}",' - f' value="{value}", style="{style(filename)}"...') + f' value="{value}", style="{check["style"]}"...') def test_check_name_subfamilyname(): """ Check name table: FONT_SUBFAMILY_NAME entries. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_name_subfamilyname as check - from fontbakery.profiles.googlefonts_conditions import expected_style + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/subfamilyname") PASS_test_cases = [ # filename mac_value win_value @@ -2523,8 +2439,9 @@ def test_check_name_subfamilyname(): if name.nameID == NameID.FONT_SUBFAMILY_NAME: ttFont['name'].names[i].string = value.encode(name.getEncoding()) - style = expected_style(ttFont) - assert_PASS(check(ttFont, expected_style(ttFont)), + results = check(ttFont) + style = check["expected_style"] + assert_PASS(results, f"with filename='{filename}', value='{value}', " f"style_win='{style.win_style_name}', " f"style_mac='{style.mac_style_name}'...") @@ -2540,8 +2457,7 @@ def test_check_name_subfamilyname(): MacintoshLanguageID.ENGLISH) # And this should now FAIL: - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'bad-familyname') # Repeat this for a Win subfamily name @@ -2551,20 +2467,18 @@ def test_check_name_subfamilyname(): PlatformID.WINDOWS, WindowsEncodingID.UNICODE_BMP, WindowsLanguageID.ENGLISH_USA) - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'bad-familyname') def test_check_name_fullfontname(): """ Check name table: FULL_FONT_NAME entries. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_name_fullfontname as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/fullfontname") # Our reference Cabin Regular is known to be good ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) - - # So it must PASS the check: - assert_PASS(check(ttFont, "Regular", "Cabin"), + assert_PASS(check(ttFont), "with a good Regular font...") # Let's now test the Regular exception @@ -2573,7 +2487,7 @@ def test_check_name_fullfontname(): if name.nameID == NameID.FULL_FONT_NAME: backup = name.string ttFont["name"].names[index].string = "Cabin".encode(name.getEncoding()) - assert_results_contain(check(ttFont, "Regular", "Cabin"), + assert_results_contain(check(ttFont), WARN, 'lacks-regular', 'with a good Regular font that omits "Regular" on FULL_FONT_NAME...') # restore it: @@ -2582,9 +2496,7 @@ def test_check_name_fullfontname(): # Let's also make sure our good reference Cabin BoldItalic PASSes the check. # This also tests the splitting of filename infered style with a space char ttFont = TTFont(TEST_FILE("cabin/Cabin-BoldItalic.ttf")) - - # So it must PASS the check: - assert_PASS(check(ttFont, "Bold Italic", "Cabin"), + assert_PASS(check(ttFont), "with a good Bold Italic font...") # And here we test the FAIL codepath: @@ -2592,7 +2504,7 @@ def test_check_name_fullfontname(): if name.nameID == NameID.FULL_FONT_NAME: backup = name.string ttFont["name"].names[index].string = "MAKE IT FAIL".encode(name.getEncoding()) - assert_results_contain(check(ttFont, "Bold Italic", "Cabin"), + assert_results_contain(check(ttFont), FAIL, 'bad-entry', 'with a bad FULL_FONT_NAME entry...') # restore it: @@ -2601,8 +2513,8 @@ def test_check_name_fullfontname(): def NOT_IMPLEMENTED_test_check_name_postscriptname(): """ Check name table: POSTSCRIPT_NAME entries. """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/name/postscriptname") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/name/postscriptname") # TODO: Implement-me! # # code-paths: @@ -2612,34 +2524,23 @@ def NOT_IMPLEMENTED_test_check_name_postscriptname(): def test_check_name_typographicfamilyname(): """ Check name table: TYPOGRAPHIC_FAMILY_NAME entries. """ - from fontbakery.profiles.googlefonts \ - import (com_google_fonts_check_name_typographicfamilyname as check, - style, - familyname, - familyname_with_spaces) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/typographicfamilyname") # RIBBI fonts must not have a TYPOGRAPHIC_FAMILY_NAME entry - font = TEST_FILE("montserrat/Montserrat-BoldItalic.ttf") - ttFont = TTFont(font) - assert_PASS(check(ttFont, - style(font), - familyname_with_spaces(familyname(font))), + ttFont = TTFont(TEST_FILE("montserrat/Montserrat-BoldItalic.ttf")) + assert_PASS(check(ttFont), f"with a RIBBI without nameid={NameID.TYPOGRAPHIC_FAMILY_NAME} entry...") - # so we add one and make sure is emits a FAIL: + # so we add one and make sure the check reports the problem: ttFont['name'].names[5].nameID = NameID.TYPOGRAPHIC_FAMILY_NAME # 5 is arbitrary here - assert_results_contain(check(ttFont, - style(font), - familyname_with_spaces(familyname(font))), + assert_results_contain(check(ttFont), FAIL, 'ribbi', f'with a RIBBI that has got a nameid={NameID.TYPOGRAPHIC_FAMILY_NAME} entry...') # non-RIBBI fonts must have a TYPOGRAPHIC_FAMILY_NAME entry - font = TEST_FILE("montserrat/Montserrat-ExtraLight.ttf") - ttFont = TTFont(font) - assert_PASS(check(ttFont, - style(font), - familyname_with_spaces(familyname(font))), + ttFont = TTFont(TEST_FILE("montserrat/Montserrat-ExtraLight.ttf")) + assert_PASS(check(ttFont), f"with a non-RIBBI containing a nameid={NameID.TYPOGRAPHIC_FAMILY_NAME} entry...") # set bad values on all TYPOGRAPHIC_FAMILY_NAME entries: @@ -2647,9 +2548,7 @@ def test_check_name_typographicfamilyname(): if name.nameID == NameID.TYPOGRAPHIC_FAMILY_NAME: ttFont['name'].names[i].string = "foo".encode(name.getEncoding()) - assert_results_contain(check(ttFont, - style(font), - familyname_with_spaces(familyname(font))), + assert_results_contain(check(ttFont), FAIL, 'non-ribbi-bad-value', 'with a non-RIBBI with bad nameid={NameID.TYPOGRAPHIC_FAMILY_NAME} entries...') @@ -2659,18 +2558,15 @@ def test_check_name_typographicfamilyname(): if name.nameID == NameID.TYPOGRAPHIC_FAMILY_NAME: ttFont['name'].names[i].nameID = 255 # blah! :-) - assert_results_contain(check(ttFont, - style(font), - familyname_with_spaces(familyname(font))), + assert_results_contain(check(ttFont), FAIL, 'non-ribbi-lacks-entry', f'with a non-RIBBI lacking a nameid={NameID.TYPOGRAPHIC_FAMILY_NAME} entry...') def test_check_name_typographicsubfamilyname(): """ Check name table: TYPOGRAPHIC_SUBFAMILY_NAME entries. """ - from fontbakery.profiles.googlefonts_conditions import expected_style - from fontbakery.profiles.googlefonts \ - import com_google_fonts_check_name_typographicsubfamilyname as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/typographicsubfamilyname") RIBBI = "montserrat/Montserrat-BoldItalic.ttf" NON_RIBBI = "montserrat/Montserrat-ExtraLight.ttf" @@ -2687,22 +2583,18 @@ def test_check_name_typographicsubfamilyname(): PlatformID.MACINTOSH, MacintoshEncodingID.ROMAN, MacintoshLanguageID.ENGLISH) - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'mismatch', f'with a RIBBI that has got incorrect' f' nameid={NameID.TYPOGRAPHIC_SUBFAMILY_NAME} entries...') - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'bad-win-name') - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'bad-mac-name') # non-RIBBI fonts must have a TYPOGRAPHIC_SUBFAMILY_NAME entry ttFont = TTFont(TEST_FILE(NON_RIBBI)) - assert_PASS(check(ttFont, - expected_style(ttFont)), + assert_PASS(check(ttFont), f'with a non-RIBBI containing a nameid={NameID.TYPOGRAPHIC_SUBFAMILY_NAME} entry...') # set bad values on the win TYPOGRAPHIC_SUBFAMILY_NAME entry: @@ -2712,8 +2604,7 @@ def test_check_name_typographicsubfamilyname(): PlatformID.WINDOWS, WindowsEncodingID.UNICODE_BMP, WindowsLanguageID.ENGLISH_USA) - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'bad-typo-win', f'with a non-RIBBI with bad nameid={NameID.TYPOGRAPHIC_SUBFAMILY_NAME} entries...') @@ -2724,8 +2615,7 @@ def test_check_name_typographicsubfamilyname(): PlatformID.MACINTOSH, MacintoshEncodingID.ROMAN, MacintoshLanguageID.ENGLISH) - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'bad-typo-mac', f'with a non-RIBBI with bad nameid={NameID.TYPOGRAPHIC_SUBFAMILY_NAME} entries...') @@ -2743,8 +2633,7 @@ def test_check_name_typographicsubfamilyname(): win_name.nameID = 254 if mac_name: mac_name.nameID = 255 - assert_results_contain(check(ttFont, - expected_style(ttFont)), + assert_results_contain(check(ttFont), FAIL, 'missing-typo-win', f'with a non-RIBBI lacking a nameid={NameID.TYPOGRAPHIC_SUBFAMILY_NAME} entry...') # note: the check must not complain @@ -2753,7 +2642,8 @@ def test_check_name_typographicsubfamilyname(): def test_check_name_copyright_length(): """ Length of copyright notice must not exceed 500 characters. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_name_copyright_length as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/name/copyright_length") ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) @@ -2782,31 +2672,29 @@ def test_check_name_copyright_length(): def test_check_fontdata_namecheck(): """ Familyname is unique according to namecheck.fontdata.com """ - from fontbakery.profiles.googlefonts import (com_google_fonts_check_fontdata_namecheck as check, - familyname) + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/fontdata_namecheck") # We dont FAIL because this is meant as a merely informative check # There may be frequent cases when fonts are being updated and thus # already have a public family name registered on the # namecheck.fontdata.com database. font = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(font) - assert_results_contain(check(ttFont, familyname(font)), + assert_results_contain(check(font), INFO, 'name-collision', 'with an already used name...') # Here we know that FamilySans has not been (and will not be) # registered as a real family. font = TEST_FILE("familysans/FamilySans-Regular.ttf") - ttFont = TTFont(font) - assert_PASS(check(ttFont, familyname(font)), + assert_PASS(check(font), 'with a unique family name...') def test_check_fontv(): """ Check for font-v versioning """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_fontv as check - from fontv.libfv import FontVersion + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/fontv") ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) assert_results_contain(check(ttFont), @@ -2814,6 +2702,7 @@ def test_check_fontv(): 'with a font that does not follow' ' the suggested font-v versioning scheme ...') + from fontv.libfv import FontVersion fv = FontVersion(ttFont) fv.set_state_git_commit_sha1(development=True) version_string = fv.get_name_id5_version_string() @@ -2832,7 +2721,8 @@ def test_check_fontv(): # https://github.com/googlefonts/fontbakery/issues/1727 def disabled_test_check_negative_advance_width(): """ Check that advance widths cannot be inferred as negative. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_negative_advance_width as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/negative_advance_width") # Our reference Cabin Regular is good ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) @@ -2866,7 +2756,8 @@ def disabled_test_check_negative_advance_width(): def test_check_varfont_generate_static(): """ Check a static ttf can be generated from a variable font. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_generate_static as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont/generate_static") ttFont = TTFont(TEST_FILE("cabinvfbeta/CabinVFBeta.ttf")) assert_PASS(check(ttFont)) @@ -2879,7 +2770,8 @@ def test_check_varfont_generate_static(): def test_check_varfont_has_HVAR(): """ Check that variable fonts have an HVAR table. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_has_HVAR as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont/has_HVAR") # Our reference Cabin Variable Font contains an HVAR table. ttFont = TTFont(TEST_FILE("cabinvfbeta/CabinVFBeta.ttf")) @@ -2897,7 +2789,8 @@ def test_check_varfont_has_HVAR(): # See: https://github.com/googlefonts/fontbakery/issues/2118#issuecomment-432283698 def DISABLED_test_check_varfont_has_MVAR(): """ Check that variable fonts have an MVAR table. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_has_MVAR as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont/has_MVAR") # Our reference Cabin Variable Font contains an MVAR table. ttFont = TTFont(TEST_FILE("cabinvfbeta/CabinVFBeta.ttf")) @@ -2913,38 +2806,40 @@ def DISABLED_test_check_varfont_has_MVAR(): def test_check_smart_dropout(): """ Font enables smart dropout control in "prep" table instructions? """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_smart_dropout as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/smart_dropout") - test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) - # - PASS, "Program at 'prep' table contains instructions enabling smart dropout control." - test_font = TTFont(test_font_path) - assert_PASS(check(test_font)) + # "Program at 'prep' table contains + # instructions enabling smart dropout control." + assert_PASS(check(ttFont)) - # - FAIL, "Font does not contain TrueType instructions enabling - # smart dropout control in the 'prep' table program." + # "Font does not contain TrueType instructions enabling + # smart dropout control in the 'prep' table program." import array - test_font["prep"].program.bytecode = array.array('B', [0]) - assert_results_contain(check(test_font), + ttFont["prep"].program.bytecode = array.array('B', [0]) + assert_results_contain(check(ttFont), FAIL, 'lacks-smart-dropout') -def test_check_vtt_clean(): +def test_check_vttclean(): """ There must not be VTT Talk sources in the font. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_vtt_clean as check - from fontbakery.profiles.shared_conditions import vtt_talk_sources + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/vttclean") - good_font = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) - assert_PASS(check(good_font, vtt_talk_sources(good_font))) + good_font = TEST_FILE("mada/Mada-Regular.ttf") + assert_PASS(check(good_font)) - bad_font = TTFont(TEST_FILE("hinting/Roboto-VF.ttf")) - assert_results_contain(check(bad_font, vtt_talk_sources(bad_font)), + bad_font = TEST_FILE("hinting/Roboto-VF.ttf") + assert_results_contain(check(bad_font), FAIL, 'has-vtt-sources') def test_check_aat(): """ Are there unwanted Apple tables ? """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_aat as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/aat") unwanted_tables = [ 'EBSC', 'Zaph', 'acnt', 'ankr', 'bdat', 'bhed', 'bloc', @@ -2970,7 +2865,8 @@ def test_check_aat(): def test_check_fvar_name_entries(): """ All name entries referenced by fvar instances exist on the name table? """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_fvar_name_entries as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/fvar_name_entries") # This broken version of the Expletus variable font, was where this kind of problem was first observed: ttFont = TTFont(TEST_FILE("broken_expletus_vf/ExpletusSansBeta-VF.ttf")) @@ -2991,7 +2887,8 @@ def test_check_fvar_name_entries(): def test_check_varfont_has_instances(): """ A variable font must have named instances. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_has_instances as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont_has_instances") # ExpletusVF does have instances. # Note: The "broken" in the path name refers to something else. @@ -3013,7 +2910,8 @@ def test_check_varfont_has_instances(): def test_check_varfont_weight_instances(): """ Variable font weight coordinates must be multiples of 100. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_weight_instances as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont_weight_instances") # This copy of Markazi Text has an instance with # a 491 'wght' coordinate instead of 500. @@ -3034,8 +2932,8 @@ def test_check_varfont_weight_instances(): def NOT_IMPLEMENTED_test_check_family_tnum_horizontal_metrics(): """ All tabular figures must have the same width across the RIBBI-family. """ - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/family/tnum_horizontal_metrics") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/family/tnum_horizontal_metrics") # TODO: Implement-me! # # code-paths: @@ -3045,7 +2943,8 @@ def NOT_IMPLEMENTED_test_check_family_tnum_horizontal_metrics(): def test_check_integer_ppem_if_hinted(): """ PPEM must be an integer on hinted fonts. """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_integer_ppem_if_hinted as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/integer_ppem_if_hinted") # Our reference Merriweather Regular is hinted, but does not set # the "rounded PPEM" flag (bit 3 on the head table flags) as @@ -3066,24 +2965,18 @@ def test_check_integer_ppem_if_hinted(): def test_check_ligature_carets(): """ Is there a caret position declared for every ligature? """ - from fontbakery.profiles.googlefonts import com_google_fonts_check_ligature_carets as check - from fontbakery.profiles.shared_conditions import ligatures + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/ligature_carets") # Our reference Mada Medium is known to be bad ttFont = TTFont(TEST_FILE("mada/Mada-Medium.ttf")) - lig = ligatures(ttFont) - - # So it must emit a WARN: - assert_results_contain(check(ttFont, lig), + assert_results_contain(check(ttFont), WARN, 'lacks-caret-pos', 'with a bad font...') - # And FamilySans Regular is known to be bad + # And FamilySans Regular is also bad ttFont = TTFont("data/test/familysans/FamilySans-Regular.ttf") - lig = ligatures(ttFont) - - # So it must emit a WARN: - assert_results_contain(check(ttFont, lig), + assert_results_contain(check(ttFont), WARN, 'GDEF-missing', 'with a bad font...') @@ -3095,76 +2988,68 @@ def test_check_ligature_carets(): def test_check_kerning_for_non_ligated_sequences(): """ Is there kerning info for non-ligated sequences ? """ - from fontbakery.profiles.googlefonts \ - import com_google_fonts_check_kerning_for_non_ligated_sequences as check - from fontbakery.profiles.gpos import has_kerning_info - from fontbakery.profiles.shared_conditions import ligatures + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/kerning_for_non_ligated_sequences") # Our reference Mada Medium is known to be good ttFont = TTFont(TEST_FILE("mada/Mada-Medium.ttf")) - lig = ligatures(ttFont) - has_kinfo = has_kerning_info(ttFont) - - # So it must PASS the check: - assert_PASS(check(ttFont, lig, has_kinfo), + assert_PASS(check(ttFont), 'with a good font...') # And Merriweather Regular is known to be bad ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Regular.ttf")) - lig = ligatures(ttFont) - has_kinfo = has_kerning_info(ttFont) - - # So the check must emit a WARN in this testcase: - assert_results_contain(check(ttFont, lig, has_kinfo), + assert_results_contain(check(ttFont), WARN, 'lacks-kern-info', 'with a bad font...') def test_check_family_control_chars(): """Are any unacceptable control characters present in font files?""" - from fontbakery.profiles.googlefonts import com_google_fonts_check_family_control_chars as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/family/control_chars") - passing_file = TEST_FILE("bad_character_set/control_chars/" - "FontbakeryTesterCCGood-Regular.ttf") - error_onebad_cc_file = TEST_FILE("bad_character_set/control_chars/" - "FontbakeryTesterCCOneBad-Regular.ttf") - error_multibad_cc_file = TEST_FILE("bad_character_set/control_chars/" - "FontbakeryTesterCCMultiBad-Regular.ttf") + good_font = TEST_FILE("bad_character_set/control_chars/" + "FontbakeryTesterCCGood-Regular.ttf") + onebad_cc_font = TEST_FILE("bad_character_set/control_chars/" + "FontbakeryTesterCCOneBad-Regular.ttf") + multibad_cc_font = TEST_FILE("bad_character_set/control_chars/" + "FontbakeryTesterCCMultiBad-Regular.ttf") # No unacceptable control characters should pass with one file - tt = TTFont(passing_file) - assert_PASS(check([tt]), + fonts = [good_font] + assert_PASS(check(fonts), 'with one good font...') # No unacceptable control characters should pass with multiple good files - tt = TTFont(passing_file) - assert_PASS(check([tt, tt]), + fonts = [good_font, + good_font] + assert_PASS(check(fonts), 'with multiple good fonts...') # Unacceptable control chars should fail with one file x one bad char in font - tt = TTFont(error_onebad_cc_file) - assert_results_contain(check([tt]), + fonts = [onebad_cc_font] + assert_results_contain(check(fonts), FAIL, 'unacceptable', 'with one bad font that has one bad char...') # Unacceptable control chars should fail with one file x multiple bad char in font - tt = TTFont(error_multibad_cc_file) - assert_results_contain(check([tt]), + fonts = [multibad_cc_font] + assert_results_contain(check(fonts), FAIL, 'unacceptable', 'with one bad font that has multiple bad char...') # Unacceptable control chars should fail with multiple files x multiple bad chars in fonts - tt1 = TTFont(error_onebad_cc_file) - tt2 = TTFont(error_multibad_cc_file) - assert_results_contain(check([tt1, tt2]), + fonts = [onebad_cc_font, + multibad_cc_font] + assert_results_contain(check(fonts), FAIL, 'unacceptable', 'with multiple bad fonts that have multiple bad chars...') def NOT_IMPLEMENTED__test_com_google_fonts_check_repo_dirname_match_nameid_1(): """Are any unacceptable control characters present in font files?""" - # check = TestingContext(googlefonts_profile, - # "com.google.fonts/check/repo_dirname_match_nameid_1") + # check = CheckTester(googlefonts_profile, + # "com.google.fonts/check/repo_dirname_match_nameid_1") # TODO: Implement-me! # # PASS @@ -3172,8 +3057,8 @@ def NOT_IMPLEMENTED__test_com_google_fonts_check_repo_dirname_match_nameid_1(): # FAIL, "mismatch" # # passing_file = TEST_FILE(".../.ttf") - # ttFont = TTFont(passing_file) - # assert_PASS(check([ttFont]), + # fonts = [passing_file] + # assert_PASS(check(fonts), # 'with one good font...') @@ -3182,8 +3067,8 @@ def test_check_repo_vf_has_static_fonts(): from fontbakery.profiles.googlefonts import com_google_fonts_check_repo_vf_has_static_fonts as check import tempfile import shutil - # in order for this check to work, we need to mimmic the folder structure of - # the Google Fonts repository + # in order for this check to work, we need to + # mimic the folder structure of the Google Fonts repository with tempfile.TemporaryDirectory() as tmp_gf_dir: family_dir = portable_path(tmp_gf_dir + "/ofl/testfamily") src_family = portable_path("data/test/varfont") @@ -3331,7 +3216,8 @@ def test_check_vertical_metrics_regressions(cabin_ttFonts): def test_check_cjk_vertical_metrics(): - from fontbakery.profiles.googlefonts import com_google_fonts_check_cjk_vertical_metrics as check + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/cjk_vertical_metrics") ttFont = TTFont(cjk_font) assert_PASS(check(ttFont), @@ -3388,14 +3274,14 @@ def test_check_cjk_vertical_metrics(): def test_check_varfont_instance_coordinates(vf_ttFont): - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_instance_coordinates as check - from fontbakery.parse import instance_parse - from copy import copy + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont_instance_coordinates") # OpenSans-Roman-VF is correct assert_PASS(check(vf_ttFont), 'with a variable font which has correct instance coordinates.') + from copy import copy vf_ttFont2 = copy(vf_ttFont) for instance in vf_ttFont2['fvar'].instances: for axis in instance.coordinates.keys(): @@ -3407,13 +3293,13 @@ def test_check_varfont_instance_coordinates(vf_ttFont): def test_check_varfont_instance_names(vf_ttFont): - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_instance_names as check - from fontbakery.parse import instance_parse - from copy import copy + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont_instance_names") assert_PASS(check(vf_ttFont), 'with a variable font which has correct instance names.') + from copy import copy vf_ttFont2 = copy(vf_ttFont) for instance in vf_ttFont2['fvar'].instances: instance.subfamilyNameID = 300 @@ -3434,13 +3320,13 @@ def test_check_varfont_instance_names(vf_ttFont): def test_check_varfont_duplicate_instance_names(vf_ttFont): - from fontbakery.profiles.googlefonts \ - import com_google_fonts_check_varfont_duplicate_instance_names as check - from copy import copy + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont_duplicate_instance_names") assert_PASS(check(vf_ttFont), 'with a variable font which has unique instance names.') + from copy import copy vf_ttFont2 = copy(vf_ttFont) duplicate_instance_name = vf_ttFont2['name'].getName( vf_ttFont2['fvar'].instances[0].subfamilyNameID, @@ -3448,7 +3334,7 @@ def test_check_varfont_duplicate_instance_names(vf_ttFont): WindowsEncodingID.UNICODE_BMP, WindowsLanguageID.ENGLISH_USA ).toUnicode() - vf_ttFont2['name'].setName(string=duplicate_instance_name, + vf_ttFont2['name'].setName(string=duplicate_instance_name, nameID=vf_ttFont2['fvar'].instances[1].subfamilyNameID, platformID=PlatformID.WINDOWS, platEncID=WindowsEncodingID.UNICODE_BMP, @@ -3459,16 +3345,16 @@ def test_check_varfont_duplicate_instance_names(vf_ttFont): def test_check_varfont_unsupported_axes(): """Ensure VFs do not contain opsz or ital axes.""" - from fontbakery.profiles.googlefonts import com_google_fonts_check_varfont_unsupported_axes as check - from fontbakery.profiles.shared_conditions import slnt_axis, ital_axis - from fontTools.ttLib.tables._f_v_a_r import Axis + check = CheckTester(googlefonts_profile, + "com.google.fonts/check/varfont/unsupported_axes") # Our reference varfont, CabinVFBeta.ttf, lacks 'ital' and 'slnt' variation axes. # So, should pass the check: - ttFont = TTFont("data/test/cabinvfbeta/CabinVFBeta.ttf") + ttFont = TTFont(TEST_FILE("cabinvfbeta/CabinVFBeta.ttf")) assert_PASS(check(ttFont)) # If we add 'ital' it must FAIL: + from fontTools.ttLib.tables._f_v_a_r import Axis new_axis = Axis() new_axis.axisTag = "ital" ttFont["fvar"].axes.append(new_axis) diff --git a/tests/profiles/gpos_test.py b/tests/profiles/gpos_test.py index cfa52d6a86..53686c844f 100644 --- a/tests/profiles/gpos_test.py +++ b/tests/profiles/gpos_test.py @@ -2,15 +2,16 @@ from fontbakery.codetesting import (TEST_FILE, assert_PASS, - assert_results_contain) -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) + assert_results_contain, + CheckTester) +from fontbakery.checkrunner import WARN +from fontbakery.profiles import opentype as opentype_profile def test_check_gpos_kerning_info(): """ Does GPOS table have kerning information? """ - from fontbakery.profiles.gpos import com_google_fonts_check_gpos_kerning_info as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/gpos_kerning_info") # Our reference Mada Regular is known to have kerning-info # exclusively on an extension subtable diff --git a/tests/profiles/head_test.py b/tests/profiles/head_test.py index e76df42880..0d98d1ff96 100644 --- a/tests/profiles/head_test.py +++ b/tests/profiles/head_test.py @@ -4,12 +4,12 @@ import pytest from fontTools.ttLib import TTFont -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) +from fontbakery.checkrunner import (WARN, FAIL) +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile mada_fonts = [ @@ -29,7 +29,8 @@ def mada_ttFonts(): def test_check_family_equal_font_versions(mada_ttFonts): """ Make sure all font files have the same version value. """ - from fontbakery.profiles.head import com_google_fonts_check_family_equal_font_versions as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/family/equal_font_versions") # our reference Mada family is know to be good here. assert_PASS(check(mada_ttFonts), @@ -47,7 +48,8 @@ def test_check_family_equal_font_versions(mada_ttFonts): def test_check_unitsperem(): """ Checking unitsPerEm value is reasonable. """ - from fontbakery.profiles.head import com_google_fonts_check_unitsperem as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/unitsperem") # In this test we'll forge several known-good and known-bad values. # We'll use Mada Regular to start with: @@ -104,7 +106,8 @@ def test_parse_version_string(): def test_check_font_version(): """ Checking font version fields. """ - from fontbakery.profiles.head import com_google_fonts_check_font_version as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/font_version") test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") test_font = TTFont(test_font_path) diff --git a/tests/profiles/hhea_test.py b/tests/profiles/hhea_test.py index d37b7d296e..88234a0a7f 100644 --- a/tests/profiles/hhea_test.py +++ b/tests/profiles/hhea_test.py @@ -1,20 +1,18 @@ -import os - from fontTools.ttLib import TTFont -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) +from fontbakery.checkrunner import WARN, FAIL +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile def test_check_linegaps(): """ Checking Vertical Metric Linegaps. """ - from fontbakery.profiles.hhea import com_google_fonts_check_linegaps as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/linegaps") - print('Test FAIL with non-zero hhea.lineGap...') # Our reference Mada Regular is know to be bad here. ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -23,13 +21,15 @@ def test_check_linegaps(): ttFont['hhea'].lineGap = 1 ttFont['OS/2'].sTypoLineGap = 0 assert_results_contain(check(ttFont), - WARN, 'hhea') + WARN, 'hhea', + 'with non-zero hhea.lineGap...') # Then we run the check with a non-zero OS/2.sTypoLineGap: ttFont['hhea'].lineGap = 0 ttFont['OS/2'].sTypoLineGap = 1 assert_results_contain(check(ttFont), - WARN, 'OS/2') + WARN, 'OS/2', + 'with non-zero OS/2.sTypoLineGap...') # And finaly we fix it by making both values equal to zero: ttFont['hhea'].lineGap = 0 @@ -39,13 +39,13 @@ def test_check_linegaps(): def test_check_maxadvancewidth(): """ MaxAdvanceWidth is consistent with values in the Hmtx and Hhea tables? """ - from fontbakery.profiles.hhea import com_google_fonts_check_maxadvancewidth as check - - test_font = TTFont(TEST_FILE("familysans/FamilySans-Regular.ttf")) + check = CheckTester(opentype_profile, + "com.google.fonts/check/maxadvancewidth") - assert_PASS(check(test_font)) + ttFont = TTFont(TEST_FILE("familysans/FamilySans-Regular.ttf")) + assert_PASS(check(ttFont)) - test_font["hmtx"].metrics["A"] = (1234567, 1234567) - assert_results_contain(check(test_font), + ttFont["hmtx"].metrics["A"] = (1234567, 1234567) + assert_results_contain(check(ttFont), FAIL, 'mismatch') diff --git a/tests/profiles/hmtx_test.py b/tests/profiles/hmtx_test.py index 27cc1c0dfc..375c2839e2 100644 --- a/tests/profiles/hmtx_test.py +++ b/tests/profiles/hmtx_test.py @@ -1,18 +1,17 @@ -import os - from fontTools.ttLib import TTFont -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) +from fontbakery.checkrunner import FAIL from fontbakery.codetesting import (TEST_FILE, assert_PASS, - assert_results_contain) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) + assert_results_contain, + CheckTester) +from fontbakery.profiles import opentype as opentype_profile def test_check_whitespace_widths(): """ Whitespace glyphs have coherent widths? """ - from fontbakery.profiles.hmtx import com_google_fonts_check_whitespace_widths as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/whitespace_widths") ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) assert_PASS(check(ttFont)) diff --git a/tests/profiles/kern_test.py b/tests/profiles/kern_test.py index 268823131a..b2bca54b35 100644 --- a/tests/profiles/kern_test.py +++ b/tests/profiles/kern_test.py @@ -1,16 +1,18 @@ from fontTools.ttLib import TTFont -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) +from fontbakery.checkrunner import INFO +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) +from fontbakery.profiles import opentype as opentype_profile def test_check_kern_table(): - """ Is there a "kern" table declared in the font ? """ - from fontbakery.profiles.kern import com_google_fonts_check_kern_table as check + """ Is there a "kern" table declared in the font? """ + check = CheckTester(opentype_profile, + "com.google.fonts/check/kern_table") # Our reference Mada Regular is known to be good # (does not have a 'kern' table): diff --git a/tests/profiles/loca_test.py b/tests/profiles/loca_test.py index da4e9ddc03..773f7c64b4 100644 --- a/tests/profiles/loca_test.py +++ b/tests/profiles/loca_test.py @@ -1,26 +1,27 @@ import io -import os - from fontTools.ttLib import TTFont from fontbakery.checkrunner import FAIL -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile def test_check_loca_maxp_num_glyphs(): """Does the number of glyphs in the loca table match the maxp table?""" - from fontbakery.profiles.loca import com_google_fonts_check_loca_maxp_num_glyphs as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/loca/maxp_num_glyphs") - font = TEST_FILE("nunito/Nunito-Regular.ttf") - ttFont = TTFont(font) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) assert_PASS(check(ttFont)) ttFont["loca"].locations.pop() - test_file = io.BytesIO() - ttFont.save(test_file) - ttFont = TTFont(test_file) + _file = io.BytesIO() + ttFont.save(_file) + ttFont = TTFont(_file) + ttFont.reader.file.name = "foo" # Make CheckTester class happy... :-P assert_results_contain(check(ttFont), FAIL, 'corrupt') diff --git a/tests/profiles/name_test.py b/tests/profiles/name_test.py index a8727baaf5..b6c8116c74 100644 --- a/tests/profiles/name_test.py +++ b/tests/profiles/name_test.py @@ -9,17 +9,18 @@ WindowsLanguageID, MacintoshEncodingID, MacintoshLanguageID) -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, +from fontbakery.checkrunner import (INFO, WARN, PASS, FAIL) +from fontbakery.codetesting import (assert_PASS, assert_results_contain, - portable_path) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) + CheckTester, + portable_path, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile def test_check_name_empty_records(): - from fontbakery.profiles.name import com_adobe_fonts_check_name_empty_records as check + check = CheckTester(opentype_profile, + "com.adobe.fonts/check/name/empty_records") font_path = TEST_FILE("source-sans-pro/OTF/SourceSansPro-Regular.otf") test_font = TTFont(font_path) @@ -42,7 +43,8 @@ def test_check_name_no_copyright_on_description(): """ Description strings in the name table must not contain copyright info. """ - from fontbakery.profiles.name import com_google_fonts_check_name_no_copyright_on_description as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/name/no_copyright_on_description") # Our reference Mada Regular is know to be good here. ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -61,8 +63,8 @@ def test_check_name_no_copyright_on_description(): def test_check_monospace(): """ Checking correctness of monospaced metadata. """ - from fontbakery.profiles.name import com_google_fonts_check_monospace as check - from fontbakery.profiles.shared_conditions import glyph_metrics_stats + check = CheckTester(opentype_profile, + "com.google.fonts/check/monospace") from fontbakery.constants import (PANOSE_Proportion, IsFixedWidth) @@ -76,14 +78,13 @@ def test_check_monospace(): # Our reference Mada Regular is a non-monospace font # know to have good metadata for this check. ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) - stats = glyph_metrics_stats(ttFont) - assert_results_contain(check(ttFont, stats), + assert_results_contain(check(ttFont), PASS, "good", 'with a good non-monospace font...') # We'll mark it as monospaced on the post table and make sure it fails: ttFont["post"].isFixedPitch = 42 # *any* non-zero value means monospaced - assert_results_contain(check(ttFont, stats), + assert_results_contain(check(ttFont), FAIL, 'bad-post-isFixedPitch', 'with a non-monospaced font with bad' ' post.isFixedPitch value ...') @@ -93,7 +94,7 @@ def test_check_monospace(): # Now we mark it as monospaced on the OS/2 and it should also fail: ttFont["OS/2"].panose.bProportion = PANOSE_Proportion.MONOSPACED - assert_results_contain(check(ttFont, stats), + assert_results_contain(check(ttFont), FAIL, 'bad-panose-proportion', 'with a non-monospaced font with bad' ' OS/2.panose.bProportion value (MONOSPACED) ...') @@ -107,9 +108,7 @@ def test_check_monospace(): # a monospaced font with good metadata here. ttFont = TTFont(TEST_FILE("overpassmono/OverpassMono-Regular.ttf")) - stats = glyph_metrics_stats(ttFont) - assert stats['most_common_width'] == 616 - status, message = list(check(ttFont, stats))[-1] + status, message = check(ttFont)[-1] # WARN is emitted when there's at least one outlier. # I don't see a good reason to be picky and also test that one separately here... assert (status == WARN and message.code == "mono-outliers") or \ @@ -120,7 +119,7 @@ def test_check_monospace(): # here we search for the expected FAIL among all results # instead of simply looking at the last one # because we may also get an outliers WARN in some cases: - assert_results_contain(check(ttFont, stats), + assert_results_contain(check(ttFont), FAIL, 'mono-bad-post-isFixedPitch', 'with a monospaced font with' ' bad post.isFixedPitch value ...') @@ -142,7 +141,7 @@ def test_check_monospace(): for bad_value in bad_monospaced_panose_values: ttFont["OS/2"].panose.bProportion = bad_value # again, we search the expected FAIL because we may algo get an outliers WARN here: - assert_results_contain(check(ttFont, stats), + assert_results_contain(check(ttFont), FAIL, 'mono-bad-panose-proportion', f'Test FAIL with a monospaced font with bad' f' OS/2.panose.bProportion value ({bad_value}) ...') @@ -150,7 +149,8 @@ def test_check_monospace(): def test_check_name_match_familyname_fullfont(): """ Does full font name begin with the font family name? """ - from fontbakery.profiles.name import com_google_fonts_check_name_match_familyname_fullfont as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/name/match_familyname_fullfont") # Our reference Mada Regular is known to be good ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) diff --git a/tests/profiles/os2_test.py b/tests/profiles/os2_test.py index 14d4cc2991..dc42e619de 100644 --- a/tests/profiles/os2_test.py +++ b/tests/profiles/os2_test.py @@ -1,20 +1,19 @@ import io import os - import pytest -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain, - portable_path) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) - import fontTools.ttLib from fontTools.ttLib import TTFont import fontTools.subset +from fontbakery.checkrunner import (INFO, WARN, FAIL) +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + portable_path, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile + mada_fonts = [ TEST_FILE("mada/Mada-Black.ttf"), @@ -44,7 +43,8 @@ def mada_ttFonts(): def test_check_family_panose_proportion(mada_ttFonts): """ Fonts have consistent PANOSE proportion ? """ - from fontbakery.profiles.os2 import com_google_fonts_check_family_panose_proportion as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/family/panose_proportion") assert_PASS(check(mada_ttFonts), 'with good family.') @@ -61,7 +61,8 @@ def test_check_family_panose_proportion(mada_ttFonts): def test_check_family_panose_familytype(mada_ttFonts): """ Fonts have consistent PANOSE family type ? """ - from fontbakery.profiles.os2 import com_google_fonts_check_family_panose_familytype as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/family/panose_familytype") assert_PASS(check(mada_ttFonts), 'with good family.') @@ -78,7 +79,8 @@ def test_check_family_panose_familytype(mada_ttFonts): def test_check_xavgcharwidth(): """ Check if OS/2 xAvgCharWidth is correct. """ - from fontbakery.profiles.os2 import com_google_fonts_check_xavgcharwidth as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/xavgcharwidth") test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") @@ -93,7 +95,9 @@ def test_check_xavgcharwidth(): assert_results_contain(check(test_font), WARN, None) # FIXME: This needs a message keyword - test_font = TTFont() + del test_font['OS/2'] + del test_font['glyf'] + del test_font['hmtx'] test_font['OS/2'] = fontTools.ttLib.newTable('OS/2') test_font['OS/2'].version = 4 test_font['OS/2'].xAvgCharWidth = 1000 @@ -116,6 +120,7 @@ def test_check_xavgcharwidth(): temp_file = io.BytesIO() test_font.save(temp_file) test_font = TTFont(temp_file) + test_font.reader.file.name = "foo.ttf" assert_PASS(check(test_font)) test_font['OS/2'].xAvgCharWidth = 450 @@ -127,6 +132,7 @@ def test_check_xavgcharwidth(): WARN, None) # FIXME: This needs a message keyword test_font = TTFont(temp_file) + test_font.reader.file.name = "foo.ttf" subsetter = fontTools.subset.Subsetter() subsetter.populate(glyphs=['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', @@ -139,8 +145,8 @@ def test_check_xavgcharwidth(): def test_check_fsselection_matches_macstyle(): """Check if OS/2 fsSelection matches head macStyle bold and italic bits.""" - from fontbakery.profiles.os2 import \ - com_adobe_fonts_check_fsselection_matches_macstyle as check + check = CheckTester(opentype_profile, + "com.adobe.fonts/check/fsselection_matches_macstyle") from fontbakery.constants import FsSelection test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") @@ -168,6 +174,9 @@ def test_check_fsselection_matches_macstyle(): def test_check_family_bold_italic_unique_for_nameid1(): """Check that OS/2.fsSelection bold/italic settings are unique within each Compatible Family group (i.e. group of up to 4 with same NameID1)""" + # FIXME: This should work: + # check = CheckTester(opentype_profile, + # "com.adobe.fonts/check/family/bold_italic_unique_for_nameid1") from fontbakery.profiles.os2 import \ com_adobe_fonts_check_family_bold_italic_unique_for_nameid1 as check from fontbakery.constants import FsSelection @@ -181,16 +190,16 @@ def test_check_family_bold_italic_unique_for_nameid1(): 'SourceSansPro-BoldIt.otf'] font_paths = [os.path.join(base_path, n) for n in font_names] - test_fonts = [TTFont(x) for x in font_paths] + ttFonts = [TTFont(x) for x in font_paths] # the family should be correctly constructed - assert_PASS(check(test_fonts)) + assert_PASS(check(ttFonts)) # now hack the italic font to also have the bold bit set - test_fonts[2]['OS/2'].fsSelection |= FsSelection.BOLD + ttFonts[2]['OS/2'].fsSelection |= FsSelection.BOLD # we should get a failure due to two fonts with both bold & italic set - message = assert_results_contain(check(test_fonts), + message = assert_results_contain(check(ttFonts), FAIL, None) # FIXME: This needs a message keyword! assert message == ("Family 'Source Sans Pro' has 2 fonts (should be no" " more than 1) with the same OS/2.fsSelection" @@ -199,7 +208,8 @@ def test_check_family_bold_italic_unique_for_nameid1(): def test_check_code_pages(): """ Check code page character ranges """ - from fontbakery.profiles.os2 import com_google_fonts_check_code_pages as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/code_pages") ttFont = TTFont(TEST_FILE("merriweather/Merriweather-Regular.ttf")) assert(ttFont['OS/2'].ulCodePageRange1 != 0 or diff --git a/tests/profiles/post_test.py b/tests/profiles/post_test.py index 0f7dbc0d0f..81f1f23638 100644 --- a/tests/profiles/post_test.py +++ b/tests/profiles/post_test.py @@ -2,12 +2,12 @@ from fontTools.ttLib import TTFont -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, SKIP, PASS, FAIL) -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) +from fontbakery.checkrunner import WARN, FAIL +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile mada_fonts = [ @@ -27,7 +27,8 @@ def mada_ttFonts(): def test_check_family_underline_thickness(mada_ttFonts): """ Fonts have consistent underline thickness ? """ - from fontbakery.profiles.post import com_google_fonts_check_family_underline_thickness as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/family/underline_thickness") # We start with our reference Mada font family, # which we know has the same value of post.underlineThickness @@ -54,6 +55,8 @@ def test_check_family_underline_thickness(mada_ttFonts): def test_check_post_table_version(): """ Font has correct post table version (2 for TTF, 3 for OTF)? """ + check = CheckTester(opentype_profile, + "com.google.fonts/check/post_table_version") from fontbakery.profiles.post import com_google_fonts_check_post_table_version as check # our reference Mada family is know to be good here. diff --git a/tests/profiles/stat_test.py b/tests/profiles/stat_test.py index 4694ff8e9d..f423a18fd4 100644 --- a/tests/profiles/stat_test.py +++ b/tests/profiles/stat_test.py @@ -1,15 +1,17 @@ -from fontbakery.checkrunner import PASS, FAIL -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, - assert_results_contain) - from fontTools.ttLib import TTFont +from fontbakery.checkrunner import FAIL +from fontbakery.codetesting import (assert_PASS, + assert_results_contain, + CheckTester, + TEST_FILE) +from fontbakery.profiles import opentype as opentype_profile + def test_check_varfont_stat_axis_record_for_each_axis(): """ Check the STAT table has an Axis Record for every axis in the font. """ - from fontbakery.profiles.stat \ - import com_google_fonts_check_varfont_stat_axis_record_for_each_axis as check + check = CheckTester(opentype_profile, + "com.google.fonts/check/varfont/stat_axis_record_for_each_axis") # Our reference Cabin[wdth,wght].ttf variable font # has all necessary Axis Records diff --git a/tests/profiles/universal_test.py b/tests/profiles/universal_test.py index 0e5e900e73..0d8eea6278 100644 --- a/tests/profiles/universal_test.py +++ b/tests/profiles/universal_test.py @@ -3,16 +3,14 @@ import os from fontTools.ttLib import TTFont -from fontbakery.constants import NameID -from fontbakery.codetesting import (TEST_FILE, - assert_PASS, +from fontbakery.checkrunner import (INFO, WARN, FAIL) +from fontbakery.codetesting import (assert_PASS, assert_SKIP, assert_results_contain, - portable_path) -from fontbakery.checkrunner import (DEBUG, INFO, WARN, ERROR, - SKIP, PASS, FAIL, ENDCHECK) - -check_statuses = (ERROR, FAIL, SKIP, PASS, WARN, INFO, DEBUG) + CheckTester, + TEST_FILE) +from fontbakery.constants import NameID +from fontbakery.profiles import universal as universal_profile @pytest.fixture @@ -52,10 +50,10 @@ def montserrat_ttFonts(): ] cabin_condensed_fonts = [ - TEST_FILE("cabin/CabinCondensed-Regular.ttf"), - TEST_FILE("cabin/CabinCondensed-Medium.ttf"), - TEST_FILE("cabin/CabinCondensed-Bold.ttf"), - TEST_FILE("cabin/CabinCondensed-SemiBold.ttf") + TEST_FILE("cabincondensed/CabinCondensed-Regular.ttf"), + TEST_FILE("cabincondensed/CabinCondensed-Medium.ttf"), + TEST_FILE("cabincondensed/CabinCondensed-Bold.ttf"), + TEST_FILE("cabincondensed/CabinCondensed-SemiBold.ttf") ] @pytest.fixture @@ -69,23 +67,22 @@ def cabin_condensed_ttFonts(): def test_check_valid_glyphnames(): """ Glyph names are all valid? """ - import io - from fontbakery.profiles.universal import com_google_fonts_check_valid_glyphnames as check + check = CheckTester(universal_profile, + "com.google.fonts/check/valid_glyphnames") # We start with a good font file: - test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") - test_font = TTFont(test_font_path) - assert_PASS(check(test_font)) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + assert_PASS(check(ttFont)) # There used to be a 31 char max-length limit. - # This was good: - test_font.glyphOrder[2] = "a" * 31 - assert_PASS(check(test_font)) + # This was considered good: + ttFont.glyphOrder[2] = "a" * 31 + assert_PASS(check(ttFont)) - # And this was bad: + # And this was considered bad: legacy_too_long = "a" * 32 - test_font.glyphOrder[2] = legacy_too_long - message = assert_results_contain(check(test_font), + ttFont.glyphOrder[2] = legacy_too_long + message = assert_results_contain(check(ttFont), WARN, 'legacy-long-names') assert legacy_too_long in message @@ -95,11 +92,11 @@ def test_check_valid_glyphnames(): bad_name1 = "a" * 64 bad_name2 = "3cents" bad_name3 = ".threecents" - test_font.glyphOrder[2] = bad_name1 - test_font.glyphOrder[3] = bad_name2 - test_font.glyphOrder[4] = bad_name3 - test_font.glyphOrder[5] = good_name - message = assert_results_contain(check(test_font), + ttFont.glyphOrder[2] = bad_name1 + ttFont.glyphOrder[3] = bad_name2 + ttFont.glyphOrder[4] = bad_name3 + ttFont.glyphOrder[5] = good_name + message = assert_results_contain(check(ttFont), FAIL, 'found-invalid-names') assert good_name not in message assert bad_name1 in message @@ -110,58 +107,62 @@ def test_check_valid_glyphnames(): # no glyph names, so the check must be SKIP'd in that case. # # Upgrade to post format 3.0 and roundtrip data to update TTF object. - test_font = TTFont(test_font_path) - test_font["post"].formatType = 3.0 - test_file = io.BytesIO() - test_font.save(test_file) - test_font = TTFont(test_file) - assert_SKIP(check(test_font)) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + ttFont["post"].formatType = 3.0 + _file = io.BytesIO() + _file.name = ttFont.reader.file.name + ttFont.save(_file) + ttFont = TTFont(_file) + assert_SKIP(check(ttFont)) def test_check_unique_glyphnames(): """ Font contains unique glyph names? """ - import io - from fontbakery.profiles.universal import com_google_fonts_check_unique_glyphnames as check + check = CheckTester(universal_profile, + "com.google.fonts/check/unique_glyphnames") - test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") - test_font = TTFont(test_font_path) - assert_PASS(check(test_font)) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + assert_PASS(check(ttFont)) # Fonttools renames duplicate glyphs with #1, #2, ... on load. # Code snippet from https://github.com/fonttools/fonttools/issues/149. - glyph_names = test_font.getGlyphOrder() + glyph_names = ttFont.getGlyphOrder() glyph_names[2] = glyph_names[3] + # Load again, we changed the font directly. - test_font = TTFont(test_font_path) - test_font.setGlyphOrder(glyph_names) - test_font['post'] # Just access the data to make fonttools generate it. - test_file = io.BytesIO() - test_font.save(test_file) - test_font = TTFont(test_file) - message = assert_results_contain(check(test_font), + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + ttFont.setGlyphOrder(glyph_names) + ttFont['post'] # Just access the data to make fonttools generate it. + _file = io.BytesIO() + _file.name = ttFont.reader.file.name + ttFont.save(_file) + ttFont = TTFont(_file) + message = assert_results_contain(check(ttFont), FAIL, None) # FIXME: This needs a message keyword assert "space" in message # Upgrade to post format 3.0 and roundtrip data to update TTF object. - test_font = TTFont(test_font_path) - test_font.setGlyphOrder(glyph_names) - test_font["post"].formatType = 3.0 - test_file = io.BytesIO() - test_font.save(test_file) - test_font = TTFont(test_file) - assert_SKIP(check(test_font)) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + ttFont.setGlyphOrder(glyph_names) + ttFont["post"].formatType = 3.0 + _file = io.BytesIO() + _file.name = ttFont.reader.file.name + ttFont.save(_file) + ttFont = TTFont(_file) + assert_SKIP(check(ttFont)) def DISABLED_test_check_glyphnames_max_length(): """ Check that glyph names do not exceed max length. """ - from fontbakery.profiles.universal import com_google_fonts_check_glyphnames_max_length as check + check = CheckTester(universal_profile, + "com.google.fonts/check/glyphnames_max_length") import defcon import ufo2ft # TTF test_font = defcon.Font(TEST_FILE("test.ufo")) - test_ttf = ufo2ft.compileTTF(test_font) - assert_PASS(check(test_ttf)) + ttFont = ufo2ft.compileTTF(test_font) + assert_PASS(check(ttFont)) test_glyph = defcon.Glyph() test_glyph.unicode = 0x1234 @@ -169,48 +170,52 @@ def DISABLED_test_check_glyphnames_max_length(): "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") test_font.insertGlyph(test_glyph) - test_ttf = ufo2ft.compileTTF(test_font, useProductionNames=False) - assert_results_contain(check(test_ttf), + ttFont = ufo2ft.compileTTF(test_font, useProductionNames=False) + assert_results_contain(check(ttFont), FAIL, None) # FIXME: This needs a message keyword - test_ttf = ufo2ft.compileTTF(test_font, useProductionNames=True) - assert_PASS(check(test_ttf)) + + ttFont = ufo2ft.compileTTF(test_font, useProductionNames=True) + assert_PASS(check(ttFont)) + # Upgrade to post format 3.0 and roundtrip data to update TTF object. - test_ttf["post"].formatType = 3.0 - test_file = io.BytesIO() - test_ttf.save(test_file) - test_ttf = TTFont(test_file) - message = assert_PASS(check(test_ttf)) + ttFont["post"].formatType = 3.0 + _file = io.BytesIO() + _file.name = ttFont.reader.file.name + ttFont.save(_file) + ttFont = TTFont(_file) + message = assert_PASS(check(ttFont)) assert "format 3.0" in message - del test_font, test_ttf, test_file # Prevent copypasta errors. + del test_font, ttFont, _file # Prevent copypasta errors. # CFF test_font = defcon.Font(TEST_FILE("test.ufo")) - test_otf = ufo2ft.compileOTF(test_font) - assert_PASS(check(test_otf)) + ttFont = ufo2ft.compileOTF(test_font) + assert_PASS(check(ttFont)) test_font.insertGlyph(test_glyph) - test_otf = ufo2ft.compileOTF(test_font, useProductionNames=False) - assert_results_contain(check(test_otf), + ttFont = ufo2ft.compileOTF(test_font, useProductionNames=False) + assert_results_contain(check(ttFont), FAIL, None) # FIXME: This needs a message keyword - test_otf = ufo2ft.compileOTF(test_font, useProductionNames=True) - assert_PASS(check(test_otf)) + ttFont = ufo2ft.compileOTF(test_font, useProductionNames=True) + assert_PASS(check(ttFont)) def test_check_ttx_roundtrip(): """ Checking with fontTools.ttx """ - from fontbakery.profiles.universal import com_google_fonts_check_ttx_roundtrip as check + check = CheckTester(universal_profile, + "com.google.fonts/check/ttx-roundtrip") - good_font_path = TEST_FILE("mada/Mada-Regular.ttf") - assert_PASS(check(good_font_path)) + font = TEST_FILE("mada/Mada-Regular.ttf") + assert_PASS(check(font)) # TODO: Can anyone show us a font file that fails ttx roundtripping?! # - # bad_font_path = TEST_FILE("...") - # assert_results_contain(check(bad_font_path), + # font = TEST_FILE("...") + # assert_results_contain(check(font), # FAIL, None) # FIXME: This needs a message keyword @@ -239,12 +244,11 @@ def test_is_up_to_date(): def test_check_name_trailing_spaces(): """ Name table entries must not have trailing spaces. """ - from fontbakery.profiles.universal import com_google_fonts_check_name_trailing_spaces as check - # Our reference Cabin Regular is known to be good: - fontfile = TEST_FILE("cabin/Cabin-Regular.ttf") - ttFont = TTFont(fontfile) + check = CheckTester(universal_profile, + "com.google.fonts/check/name/trailing_spaces") - # So it must PASS the check: + # Our reference Cabin Regular is known to be good: + ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf")) assert_PASS(check(ttFont), 'with a good font...') @@ -262,7 +266,8 @@ def test_check_name_trailing_spaces(): def test_check_family_single_directory(): """ Fonts are all in the same directory. """ - from fontbakery.profiles.universal import com_google_fonts_check_family_single_directory as check + check = CheckTester(universal_profile, + "com.google.fonts/check/family/single_directory") same_dir = [ TEST_FILE("cabin/Cabin-Thin.ttf"), TEST_FILE("cabin/Cabin-ExtraLight.ttf") @@ -282,21 +287,22 @@ def test_check_family_single_directory(): def test_check_ftxvalidator_is_available(): """ Is the command `ftxvalidator` (Apple Font Tool Suite) available? """ - from fontbakery.profiles.universal import com_google_fonts_check_ftxvalidator_is_available as check + check = CheckTester(universal_profile, + "com.google.fonts/check/ftxvalidator_is_available") - found = '/usr/local/bin/ftxvalidator' - message = assert_PASS(check(found)) + font = "foo.ttf" + message = assert_PASS(check(font, {'ftxvalidator_cmd': '/usr/local/bin/ftxvalidator'})) assert "is available" in message - found = None - message = assert_results_contain(check(found), + message = assert_results_contain(check(font, {'ftxvalidator_cmd': None}), WARN, None) # FIXME: This needs a message keyword assert "Could not find" in message def NOT_IMPLEMENTED_test_check_ftxvalidator(): """ Checking with ftxvalidator. """ - # from fontbakery.profiles.universal import com_google_fonts_check_ftxvalidator as check + # check = CheckTester(universal_profile, + # "com.google.fonts/check/ftxvalidator") # TODO: Implement-me! # # code-paths: @@ -308,7 +314,8 @@ def NOT_IMPLEMENTED_test_check_ftxvalidator(): def test_check_ots(): """ Checking with ots-sanitize. """ - from fontbakery.profiles.universal import com_google_fonts_check_ots as check + check = CheckTester(universal_profile, + "com.google.fonts/check/ots") sanitary_font = TEST_FILE("cabin/Cabin-Regular.ttf") assert_PASS(check(sanitary_font)) @@ -322,59 +329,56 @@ def test_check_ots(): def test_check_mandatory_glyphs(): """ Font contains the first few mandatory glyphs (.null or NULL, CR and space)? """ - from fontbakery.profiles.universal import com_google_fonts_check_mandatory_glyphs as check + check = CheckTester(universal_profile, + "com.google.fonts/check/mandatory_glyphs") - test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) - assert_PASS(check(test_font)) + ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) + assert_PASS(check(ttFont)) import fontTools.subset subsetter = fontTools.subset.Subsetter() subsetter.populate(glyphs="n") # Arbitrarily remove everything except n. - subsetter.subset(test_font) - assert_results_contain(check(test_font), + subsetter.subset(ttFont) + assert_results_contain(check(ttFont), WARN, None) # FIXME: This needs a message keyword def test_check_whitespace_glyphs(): """ Font contains glyphs for whitespace characters ? """ - from fontbakery.profiles.universal import com_google_fonts_check_whitespace_glyphs as check - from fontbakery.profiles.shared_conditions import missing_whitespace_chars + check = CheckTester(universal_profile, + "com.google.fonts/check/whitespace_glyphs") # Our reference Mada Regular font is good here: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) - missing = missing_whitespace_chars(ttFont) - - # So it must PASS the check: - assert_PASS(check(ttFont, missing), + assert_PASS(check(ttFont), 'with a good font...') - # Then we remove the nbsp char (0x00A0) so that we get a FAIL: + # We remove the nbsp char (0x00A0) for table in ttFont['cmap'].tables: if 0x00A0 in table.cmap: del table.cmap[0x00A0] - missing = missing_whitespace_chars(ttFont) - assert_results_contain(check(ttFont, missing), + # And make sure the problem is detected: + assert_results_contain(check(ttFont), FAIL, 'missing-whitespace-glyph-0x00A0', 'with a font lacking a nbsp (0x00A0)...') # restore original Mada Regular font: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) - # And finally remove the space character (0x0020) to get another FAIL: + # And finally do the same with the space character (0x0020): for table in ttFont['cmap'].tables: if 0x0020 in table.cmap: del table.cmap[0x0020] - - missing = missing_whitespace_chars(ttFont) - assert_results_contain(check(ttFont, missing), + assert_results_contain(check(ttFont), FAIL, 'missing-whitespace-glyph-0x0020', 'with a font lacking a space (0x0020)...') def test_check_whitespace_glyphnames(): """ Font has **proper** whitespace glyph names ? """ - from fontbakery.profiles.universal import com_google_fonts_check_whitespace_glyphnames as check + check = CheckTester(universal_profile, + "com.google.fonts/check/whitespace_glyphnames") def deleteGlyphEncodings(font, cp): """ This routine is used on to introduce errors @@ -401,24 +405,25 @@ def editCmap(font, cp, name): # Our reference Mada Regular font is good here: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) - - # So it must PASS the check: assert_PASS(check(ttFont), 'with a good font...') + value = ttFont["post"].formatType ttFont["post"].formatType = 3.0 assert_SKIP(check(ttFont), 'with post.formatType == 3.0 ...') - # and restore good value: + # restore good value: ttFont["post"].formatType = value + deleteGlyphEncodings(ttFont, 0x0020) assert_results_contain(check(ttFont), FAIL, 'missing-0020', 'with missing glyph name for char 0x0020 ...') + # restore the original font object in preparation for the next test-case: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -427,6 +432,7 @@ def editCmap(font, cp, name): FAIL, 'missing-00a0', 'with missing glyph name for char 0x00A0 ...') + # restore the original font object in preparation for the next test-case: ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -437,21 +443,25 @@ def editCmap(font, cp, name): FAIL, 'non-compliant-00a0', 'with not AGL-compliant glyph name "nbsp" for char 0x00A0...') + editCmap(ttFont, 0x00A0, "nbspace") assert_results_contain(check(ttFont), WARN, 'not-recommended-00a0', 'for naming 0x00A0 "nbspace"...') + editCmap(ttFont, 0x0020, "foo") assert_results_contain(check(ttFont), FAIL, 'non-compliant-0020', 'with not AGL-compliant glyph name "foo" for char 0x0020...') + editCmap(ttFont, 0x0020, "uni0020") assert_results_contain(check(ttFont), WARN, 'not-recommended-0020', 'for naming 0x0020 "uni0020"...') + editCmap(ttFont, 0x0020, "space") editCmap(ttFont, 0x00A0, "uni00A0") assert_PASS(check(ttFont)) @@ -459,7 +469,8 @@ def editCmap(font, cp, name): def test_check_whitespace_ink(): """ Whitespace glyphs have ink? """ - from fontbakery.profiles.universal import com_google_fonts_check_whitespace_ink as check + check = CheckTester(universal_profile, + "com.google.fonts/check/whitespace_ink") test_font = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) assert_PASS(check(test_font)) @@ -485,7 +496,8 @@ def test_check_whitespace_ink(): def test_check_required_tables(): """ Font contains all required tables ? """ - from fontbakery.profiles.universal import com_google_fonts_check_required_tables as check + check = CheckTester(universal_profile, + "com.google.fonts/check/required_tables") required_tables = ["cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "post"] @@ -550,7 +562,8 @@ def test_check_required_tables(): def test_check_unwanted_tables(): """ Are there unwanted tables ? """ - from fontbakery.profiles.universal import com_google_fonts_check_unwanted_tables as check + check = CheckTester(universal_profile, + "com.google.fonts/check/unwanted_tables") unwanted_tables = [ "FFTM", # FontForge @@ -621,36 +634,39 @@ def mada_ttFonts(): def test_check_family_win_ascent_and_descent(mada_ttFonts): """ Checking OS/2 usWinAscent & usWinDescent. """ - from fontbakery.profiles.universal import com_google_fonts_check_family_win_ascent_and_descent as check + check = CheckTester(universal_profile, + "com.google.fonts/check/family/win_ascent_and_descent") from fontbakery.profiles.shared_conditions import vmetrics # Our reference Mada Regular is know to be bad here. - vm = vmetrics(mada_ttFonts) ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) # But we fix it first to test the PASS code-path: + vm = vmetrics(mada_ttFonts) ttFont['OS/2'].usWinAscent = vm['ymax'] ttFont['OS/2'].usWinDescent = abs(vm['ymin']) - assert_PASS(check(ttFont, vm), + assert_PASS(check(ttFont), 'with a good font...') # Then we break it: - ttFont['OS/2'].usWinAscent = vm['ymax'] - 1 + ttFont['OS/2'].usWinAscent = 0 # FIXME: this should be bad as well: vm['ymax'] - 1 ttFont['OS/2'].usWinDescent = abs(vm['ymin']) - assert_results_contain(check(ttFont, vm), + assert_results_contain(check(ttFont), FAIL, 'ascent', 'with a bad OS/2.usWinAscent...') + # and also this other way of breaking it: ttFont['OS/2'].usWinAscent = vm['ymax'] - ttFont['OS/2'].usWinDescent = abs(vm['ymin']) - 1 - assert_results_contain(check(ttFont, vm), + ttFont['OS/2'].usWinDescent = 0 # FIXME: this should be bad as well: abs(vm['ymin']) - 1 + assert_results_contain(check(ttFont), FAIL, 'descent', 'with a bad OS/2.usWinDescent...') def test_check_os2_metrics_match_hhea(mada_ttFonts): """ Checking OS/2 Metrics match hhea Metrics. """ - from fontbakery.profiles.universal import com_google_fonts_check_os2_metrics_match_hhea as check + check = CheckTester(universal_profile, + "com.google.fonts/check/os2_metrics_match_hhea") # Our reference Mada Regular is know to be good here. ttFont = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) @@ -677,7 +693,8 @@ def test_check_os2_metrics_match_hhea(mada_ttFonts): def test_check_family_vertical_metrics(montserrat_ttFonts): - from fontbakery.profiles.universal import com_google_fonts_check_family_vertical_metrics as check + check = CheckTester(universal_profile, + "com.google.fonts/check/family/vertical_metrics") assert_PASS(check(montserrat_ttFonts), 'with multiple good fonts...') @@ -704,10 +721,21 @@ def test_check_superfamily_vertical_metrics(montserrat_ttFonts, cabin_ttFonts, c montserrat_ttFonts]), WARN, None, # FIXME: This needs a message keyword 'with families that diverge on vertical metric values...') + # FIXME: + # check = CheckTester(universal_profile, + # "com.google.fonts/check/superfamily/vertical_metrics") + # + # assert_PASS(check(cabin_ttFonts + cabin_condensed_ttFonts), + # 'with multiple good families...') + # + # assert_results_contain(check(cabin_ttFonts + montserrat_ttFonts), + # WARN, None, # FIXME: This needs a message keyword + # 'with families that diverge on vertical metric values...') def test_check_STAT_strings(): - from fontbakery.profiles.universal import com_google_fonts_check_STAT_strings as check + check = CheckTester(universal_profile, + "com.google.fonts/check/STAT_strings") good = TTFont(TEST_FILE("ibmplexsans-vf/IBMPlexSansVar-Roman.ttf")) assert_PASS(check(good)) @@ -719,21 +747,34 @@ def test_check_STAT_strings(): def test_check_rupee(): """ Ensure indic fonts have the Indian Rupee Sign glyph. """ - from fontbakery.profiles.universal import com_google_fonts_check_rupee as check + check = CheckTester(universal_profile, + "com.google.fonts/check/rupee") + + # FIXME: This should be possible: + # The `assert_SKIP` helper should be able to detect when a + # check skips due to unmet @conditions. + # For now it only detects explicit SKIP messages instead. + # + # non_indic = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) + # assert_SKIP(check(non_indic), + # "with a non-indic font.") + # + # But for now we have to do this: + # from fontbakery.profiles.shared_conditions import is_indic_font + print("Ensure the check will SKIP when dealing with a non-indic font...") + non_indic = TTFont(TEST_FILE("mada/Mada-Regular.ttf")) + assert is_indic_font(non_indic) == False + # This one is good: - ttFont = TTFont("data/test/indic-font-with-rupee-sign/NotoSerifDevanagari-Regular.ttf") + ttFont = TTFont(TEST_FILE("indic-font-with-rupee-sign/NotoSerifDevanagari-Regular.ttf")) assert_PASS(check(ttFont), "with a sample font that has the Indian Rupee Sign.") # But this one lacks the glyph: - ttFont = TTFont("data/test/indic-font-without-rupee-sign/NotoSansOlChiki-Regular.ttf") + ttFont = TTFont(TEST_FILE("indic-font-without-rupee-sign/NotoSansOlChiki-Regular.ttf")) assert_results_contain(check(ttFont), FAIL, "missing-rupee", "with a sample font missing it.") - print("Ensure the check will SKIP when dealing with a non-indic font...") - non_indic = TTFont("data/test/mada/Mada-Regular.ttf") - assert is_indic_font(non_indic) == False -