1from __future__ import print_function, division, absolute_import 2 3from ufo2ft.filters import ( 4 getFilterClass, BaseFilter, loadFilters, UFO2FT_FILTERS_KEY, logger) 5 6from fontTools.misc.py23 import SimpleNamespace 7from fontTools.misc.loggingTools import CapturingLogHandler 8 9import pytest 10from ..testSupport import _TempModule 11 12 13class FooBarFilter(BaseFilter): 14 """A filter that does nothing.""" 15 16 _args = ("a", "b") 17 _kwargs = {"c": 0} 18 19 def filter(self, glyph): 20 return False 21 22 23@pytest.fixture(scope="module", autouse=True) 24def fooBar(): 25 """Make a temporary 'ufo2ft.filters.fooBar' module containing a 26 'FooBarFilter' class for testing the filter loading machinery. 27 """ 28 with _TempModule("ufo2ft.filters.fooBar") as temp_module: 29 temp_module.module.__dict__["FooBarFilter"] = FooBarFilter 30 yield 31 32 33def test_getFilterClass(): 34 assert getFilterClass("Foo Bar") == FooBarFilter 35 assert getFilterClass("FooBar") == FooBarFilter 36 assert getFilterClass("fooBar") == FooBarFilter 37 with pytest.raises(ImportError): 38 getFilterClass("Baz") 39 40 with _TempModule("myfilters"), \ 41 _TempModule("myfilters.fooBar") as temp_module: 42 43 with pytest.raises(AttributeError): 44 # this fails because `myfilters.fooBar` module does not 45 # have a `FooBarFilter` class 46 getFilterClass("Foo Bar", pkg="myfilters") 47 48 temp_module.module.__dict__['FooBarFilter'] = FooBarFilter 49 50 # this will attempt to import the `FooBarFilter` class from the 51 # `myfilters.fooBar` module 52 assert getFilterClass("Foo Bar", pkg="myfilters") == FooBarFilter 53 54 55class MockFont(SimpleNamespace): 56 pass 57 58 59class MockGlyph(SimpleNamespace): 60 pass 61 62 63def test_loadFilters_empty(): 64 ufo = MockFont(lib={}) 65 assert UFO2FT_FILTERS_KEY not in ufo.lib 66 assert loadFilters(ufo) == ([], []) 67 68 69@pytest.fixture 70def ufo(): 71 ufo = MockFont(lib={}) 72 ufo.lib[UFO2FT_FILTERS_KEY] = [{ 73 "name": "Foo Bar", 74 "args": ["foo", "bar"], 75 }] 76 return ufo 77 78 79def test_loadFilters_pre(ufo): 80 ufo.lib[UFO2FT_FILTERS_KEY][0]["pre"] = True 81 pre, post = loadFilters(ufo) 82 assert len(pre) == 1 83 assert not post 84 assert isinstance(pre[0], FooBarFilter) 85 86 87def test_loadFilters_custom_namespace(ufo): 88 ufo.lib[UFO2FT_FILTERS_KEY][0]["name"] = "Self Destruct" 89 ufo.lib[UFO2FT_FILTERS_KEY][0]["namespace"] = "my_dangerous_filters" 90 91 class SelfDestructFilter(FooBarFilter): 92 def filter(glyph): 93 # Don't try this at home!!! LOL :) 94 # shutil.rmtree(os.path.expanduser("~")) 95 return True 96 97 with _TempModule("my_dangerous_filters"), \ 98 _TempModule("my_dangerous_filters.selfDestruct") as temp: 99 temp.module.__dict__["SelfDestructFilter"] = SelfDestructFilter 100 101 _, [filter_obj] = loadFilters(ufo) 102 103 assert isinstance(filter_obj, SelfDestructFilter) 104 105 106def test_loadFilters_args_missing(ufo): 107 del ufo.lib[UFO2FT_FILTERS_KEY][0]["args"] 108 109 with pytest.raises(TypeError) as exc_info: 110 loadFilters(ufo) 111 112 assert exc_info.match("missing") 113 114 115def test_loadFilters_args_unsupported(ufo): 116 ufo.lib[UFO2FT_FILTERS_KEY][0]["args"].append("baz") 117 118 with pytest.raises(TypeError) as exc_info: 119 loadFilters(ufo) 120 121 assert exc_info.match('unsupported') 122 123 124def test_loadFilters_include_all(ufo): 125 _, [filter_obj] = loadFilters(ufo) 126 127 assert filter_obj.include(MockGlyph(name="hello")) 128 assert filter_obj.include(MockGlyph(name="world")) 129 130 131def test_loadFilters_include_list(ufo): 132 ufo.lib[UFO2FT_FILTERS_KEY][0]["include"] = ["a", "b"] 133 134 _, [filter_obj] = loadFilters(ufo) 135 136 assert filter_obj.include(MockGlyph(name="a")) 137 assert filter_obj.include(MockGlyph(name="b")) 138 assert not filter_obj.include(MockGlyph(name="c")) 139 140 141def test_loadFilters_exclude_list(ufo): 142 ufo.lib[UFO2FT_FILTERS_KEY][0]["exclude"] = ["a", "b"] 143 144 _, [filter_obj] = loadFilters(ufo) 145 146 assert not filter_obj.include(MockGlyph(name="a")) 147 assert not filter_obj.include(MockGlyph(name="b")) 148 assert filter_obj.include(MockGlyph(name="c")) 149 150 151def test_loadFilters_both_include_exclude(ufo): 152 ufo.lib[UFO2FT_FILTERS_KEY][0]["include"] = ["a", "b"] 153 ufo.lib[UFO2FT_FILTERS_KEY][0]["exclude"] = ["c", "d"] 154 155 with pytest.raises(ValueError) as exc_info: 156 loadFilters(ufo) 157 158 assert exc_info.match("arguments are mutually exclusive") 159 160 161def test_loadFilters_failed(ufo): 162 ufo.lib[UFO2FT_FILTERS_KEY].append(dict(name="Non Existent")) 163 164 with CapturingLogHandler(logger, level="ERROR") as captor: 165 loadFilters(ufo) 166 167 captor.assertRegex("Failed to load filter") 168 169 170def test_loadFilters_kwargs_unsupported(ufo): 171 ufo.lib[UFO2FT_FILTERS_KEY][0]["kwargs"] = {} 172 ufo.lib[UFO2FT_FILTERS_KEY][0]["kwargs"]["c"] = 1 173 ufo.lib[UFO2FT_FILTERS_KEY][0]["kwargs"]["d"] = 2 # unknown 174 175 with pytest.raises(TypeError) as exc_info: 176 loadFilters(ufo) 177 178 assert exc_info.match("got an unsupported keyword") 179 180 181def test_BaseFilter_repr(): 182 class NoArgFilter(BaseFilter): 183 pass 184 185 assert repr(NoArgFilter()) == "NoArgFilter()" 186 187 assert repr(FooBarFilter("a", "b", c=1)) == ( 188 "FooBarFilter('a', 'b', c=1)") 189 190 assert repr(FooBarFilter("c", "d", include=["x", "y"])) == \ 191 "FooBarFilter('c', 'd', c=0, include=['x', 'y'])" 192 193 assert repr(FooBarFilter("e", "f", c=2.0, exclude=("z",))) == \ 194 "FooBarFilter('e', 'f', c=2.0, exclude=('z',))" 195 196 f = lambda g: False 197 assert repr(FooBarFilter("g", "h", include=f)) == \ 198 "FooBarFilter('g', 'h', c=0, include={})".format(repr(f)) 199 200 201if __name__ == "__main__": 202 sys.exit(pytest.main(sys.argv)) 203