|
| 1 | +"""Test code for iiif/info.py for Image API v3.0.""" |
| 2 | +import unittest |
| 3 | +from .testlib.assert_json_equal_mixin import AssertJSONEqual |
| 4 | +import json |
| 5 | +from iiif.info import IIIFInfo |
| 6 | + |
| 7 | + |
| 8 | +class TestAll(unittest.TestCase, AssertJSONEqual): |
| 9 | + """Tests.""" |
| 10 | + |
| 11 | + def test01_minmal(self): |
| 12 | + """Trivial JSON test.""" |
| 13 | + # ?? should this empty case raise and error instead? |
| 14 | + ir = IIIFInfo(identifier="http://example.com/i1", api_version='3.0') |
| 15 | + self.assertJSONEqual(ir.as_json(validate=False), |
| 16 | + '{\n "@context": "http://iiif.io/api/image/3/context.json", \n "@id": "http://example.com/i1", \n "profile": [\n "http://iiif.io/api/image/3/level1.json"\n ], \n "protocol": "http://iiif.io/api/image"\n}') |
| 17 | + ir.width = 100 |
| 18 | + ir.height = 200 |
| 19 | + self.assertJSONEqual(ir.as_json(), |
| 20 | + '{\n "@context": "http://iiif.io/api/image/3/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": [\n "http://iiif.io/api/image/3/level1.json"\n ], \n "protocol": "http://iiif.io/api/image", \n "width": 100\n}') |
| 21 | + |
| 22 | + def test04_conf(self): |
| 23 | + """Tile parameter configuration.""" |
| 24 | + conf = {'tiles': [{'width': 999, 'scaleFactors': [9, 8, 7]}]} |
| 25 | + i = IIIFInfo(api_version='3.0', conf=conf) |
| 26 | + self.assertEqual(i.tiles[0]['width'], 999) |
| 27 | + self.assertEqual(i.tiles[0]['scaleFactors'], [9, 8, 7]) |
| 28 | + # 1.1 style values |
| 29 | + self.assertEqual(i.tile_width, 999) |
| 30 | + self.assertEqual(i.scale_factors, [9, 8, 7]) |
| 31 | + |
| 32 | + def test05_level_and_profile(self): |
| 33 | + """Test level and profile setting.""" |
| 34 | + i = IIIFInfo(api_version='3.0') |
| 35 | + i.level = 0 |
| 36 | + self.assertEqual(i.level, 0) |
| 37 | + self.assertEqual(i.compliance, "http://iiif.io/api/image/3/level0.json") |
| 38 | + i.level = 2 |
| 39 | + self.assertEqual(i.level, 2) |
| 40 | + self.assertEqual(i.compliance, "http://iiif.io/api/image/3/level2.json") |
| 41 | + # Set via compliance |
| 42 | + i.compliance = "http://iiif.io/api/image/3/level1.json" |
| 43 | + self.assertEqual(i.level, 1) |
| 44 | + # Set via profile |
| 45 | + i.profile = ["http://iiif.io/api/image/3/level1.json"] |
| 46 | + self.assertEqual(i.level, 1) |
| 47 | + # Set new via compliance |
| 48 | + i = IIIFInfo(api_version='3.0') |
| 49 | + i.compliance = "http://iiif.io/api/image/3/level1.json" |
| 50 | + self.assertEqual(i.level, 1) |
| 51 | + |
| 52 | + def test06_validate(self): |
| 53 | + """Test validate method.""" |
| 54 | + i = IIIFInfo(api_version='3.0') |
| 55 | + self.assertRaises(Exception, i.validate, ()) |
| 56 | + i = IIIFInfo(identifier='a') |
| 57 | + self.assertRaises(Exception, i.validate, ()) |
| 58 | + i = IIIFInfo(identifier='a', width=1, height=2) |
| 59 | + self.assertTrue(i.validate()) |
| 60 | + |
| 61 | + def test10_read_examples_from_spec(self): |
| 62 | + """Test reading of examples from spec.""" |
| 63 | + # Section 5.2, full example |
| 64 | + i = IIIFInfo(api_version='3.0') |
| 65 | + fh = open('tests/testdata/info_json_3_0/info_from_spec_section_5_2.json') |
| 66 | + i.read(fh) |
| 67 | + self.assertEqual(i.context, |
| 68 | + "http://iiif.io/api/image/3/context.json") |
| 69 | + self.assertEqual(i.id, |
| 70 | + "http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C") |
| 71 | + self.assertEqual(i.protocol, "http://iiif.io/api/image") |
| 72 | + self.assertEqual(i.width, 6000) |
| 73 | + self.assertEqual(i.height, 4000) |
| 74 | + self.assertEqual(i.sizes, [{"width": 150, "height": 100}, |
| 75 | + {"width": 600, "height": 400}, |
| 76 | + {"width": 3000, "height": 2000}]) |
| 77 | + self.assertEqual(i.tiles, [{"width": 512, |
| 78 | + "scaleFactors": [1, 2, 4, 8, 16]}]) |
| 79 | + self.assertEqual(i.profile, |
| 80 | + ["http://iiif.io/api/image/3/level2.json"]) |
| 81 | + # extracted information |
| 82 | + self.assertEqual(i.compliance, |
| 83 | + "http://iiif.io/api/image/3/level2.json") |
| 84 | + # and 1.1 style tile properties |
| 85 | + self.assertEqual(i.tile_width, 512) |
| 86 | + self.assertEqual(i.tile_height, 512) |
| 87 | + self.assertEqual(i.scale_factors, [1, 2, 4, 8, 16]) |
| 88 | + |
| 89 | + # Section 5.3, full example |
| 90 | + i = IIIFInfo(api_version='3.0') |
| 91 | + fh = open('tests/testdata/info_json_3_0/info_from_spec_section_5_3.json') |
| 92 | + i.read(fh) |
| 93 | + self.assertEqual(i.context, |
| 94 | + "http://iiif.io/api/image/3/context.json") |
| 95 | + self.assertEqual(i.id, |
| 96 | + "http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C") |
| 97 | + self.assertEqual(i.protocol, "http://iiif.io/api/image") |
| 98 | + self.assertEqual(i.width, 4000) |
| 99 | + self.assertEqual(i.height, 3000) |
| 100 | + self.assertEqual( |
| 101 | + i.profile, |
| 102 | + ["http://iiif.io/api/image/3/level2.json", |
| 103 | + {"formats": ["gif", "pdf"], |
| 104 | + "maxWidth": 2000, |
| 105 | + "qualities": ["color", "gray"], |
| 106 | + "supports": ["canonicalLinkHeader", "rotationArbitrary", |
| 107 | + "profileLinkHeader", "http://example.com/feature/"]}]) |
| 108 | + # extracted information |
| 109 | + self.assertEqual(i.compliance, |
| 110 | + "http://iiif.io/api/image/3/level2.json") |
| 111 | + |
| 112 | + # Section 5.6, full example |
| 113 | + i = IIIFInfo(api_version='3.0') |
| 114 | + fh = open('tests/testdata/info_json_3_0/info_from_spec_section_5_6.json') |
| 115 | + i.read(fh) |
| 116 | + self.assertEqual(i.context, |
| 117 | + "http://iiif.io/api/image/3/context.json") |
| 118 | + self.assertEqual(i.id, |
| 119 | + "http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C") |
| 120 | + self.assertEqual(i.protocol, "http://iiif.io/api/image") |
| 121 | + self.assertEqual(i.width, 6000) |
| 122 | + self.assertEqual(i.height, 4000) |
| 123 | + self.assertEqual(i.sizes, [{"width": 150, "height": 100}, |
| 124 | + {"width": 600, "height": 400}, |
| 125 | + {"width": 3000, "height": 2000}]) |
| 126 | + |
| 127 | + def test11_read_example_with_extra(self): |
| 128 | + """Test read of exampe with extra info.""" |
| 129 | + i = IIIFInfo(api_version='3.0') |
| 130 | + fh = open('tests/testdata/info_json_3_0/info_with_extra.json') |
| 131 | + i.read(fh) |
| 132 | + self.assertEqual(i.context, |
| 133 | + "http://iiif.io/api/image/3/context.json") |
| 134 | + self.assertEqual(i.id, |
| 135 | + "http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C") |
| 136 | + self.assertEqual(i.protocol, "http://iiif.io/api/image") |
| 137 | + self.assertEqual(i.width, 6000) |
| 138 | + self.assertEqual(i.height, 4000) |
| 139 | + self.assertEqual( |
| 140 | + i.tiles, [{"width": 512, "scaleFactors": [1, 2, 4, 8, 16]}]) |
| 141 | + # and should have 1.1-like params too |
| 142 | + self.assertEqual(i.tile_width, 512) |
| 143 | + self.assertEqual(i.scale_factors, [1, 2, 4, 8, 16]) |
| 144 | + self.assertEqual(i.compliance, "http://iiif.io/api/image/3/level2.json") |
| 145 | + |
| 146 | + def test12_read_unknown_context(self): |
| 147 | + """Test bad/unknown context.""" |
| 148 | + i = IIIFInfo(api_version='3.0') |
| 149 | + fh = open('tests/testdata/info_json_3_0/info_bad_context.json') |
| 150 | + self.assertRaises(Exception, i.read, fh) |
| 151 | + |
| 152 | + def test20_write_example_in_spec(self): |
| 153 | + """Create example info.json in spec.""" |
| 154 | + i = IIIFInfo( |
| 155 | + api_version='3.0', |
| 156 | + id="http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C", |
| 157 | + # "protocol" : "http://iiif.io/api/image", |
| 158 | + width=6000, |
| 159 | + height=4000, |
| 160 | + sizes=[ |
| 161 | + {"width": 150, "height": 100}, |
| 162 | + {"width": 600, "height": 400}, |
| 163 | + {"width": 3000, "height": 2000}], |
| 164 | + tiles=[ |
| 165 | + {"width": 512, "scaleFactors": [1, 2, 4]}, |
| 166 | + {"width": 1024, "height": 2048, "scaleFactors": [8, 16]}], |
| 167 | + attribution=[ |
| 168 | + {"@value": "<span>Provided by Example Organization</span>", |
| 169 | + "@language": "en"}, |
| 170 | + {"@value": "<span>Darparwyd gan Enghraifft Sefydliad</span>", |
| 171 | + "@language": "cy"}], |
| 172 | + logo={"@id": "http://example.org/image-service/logo/full/200,/0/default.png", |
| 173 | + "service": |
| 174 | + {"@context": "http://iiif.io/api/image/3/context.json", |
| 175 | + "@id": "http://example.org/image-service/logo", |
| 176 | + "profile": "http://iiif.io/api/image/3/level2.json"}}, |
| 177 | + license=[ |
| 178 | + "http://example.org/rights/license1.html", |
| 179 | + "https://creativecommons.org/licenses/by/4.0/"], |
| 180 | + profile=["http://iiif.io/api/image/3/level2.json"], |
| 181 | + formats=["gif", "pdf"], |
| 182 | + qualities=["color", "gray"], |
| 183 | + supports=["canonicalLinkHeader", "rotationArbitrary", |
| 184 | + "profileLinkHeader", "http://example.com/feature/"], |
| 185 | + service=[ |
| 186 | + {"@context": "http://iiif.io/api/annex/service/physdim/1/context.json", |
| 187 | + "profile": "http://iiif.io/api/annex/service/physdim", |
| 188 | + "physicalScale": 0.0025, |
| 189 | + "physicalUnits": "in"}, |
| 190 | + {"@context": "http://geojson.org/contexts/geojson-base.jsonld", |
| 191 | + "@id": "http://www.example.org/geojson/paris.json"}] |
| 192 | + ) |
| 193 | + reparsed_json = json.loads(i.as_json()) |
| 194 | + example_json = json.load( |
| 195 | + open('tests/testdata/info_json_3_0/info_from_spec_section_5_6.json')) |
| 196 | + self.maxDiff = 4000 |
| 197 | + self.assertEqual(reparsed_json, example_json) |
| 198 | + |
| 199 | + def test21_write_profile(self): |
| 200 | + """Test writing of profile information.""" |
| 201 | + i = IIIFInfo( |
| 202 | + api_version='3.0', |
| 203 | + id="http://example.org/svc/a", width=1, height=2, |
| 204 | + profile=['pfl'], formats=["fmt1", "fmt2"]) |
| 205 | + j = json.loads(i.as_json()) |
| 206 | + self.assertEqual(len(j['profile']), 2) |
| 207 | + self.assertEqual(j['profile'][0], 'pfl') |
| 208 | + self.assertEqual(j['profile'][1], {'formats': ['fmt1', 'fmt2']}) |
| 209 | + i = IIIFInfo( |
| 210 | + api_version='3.0', |
| 211 | + id="http://example.org/svc/a", width=1, height=2, |
| 212 | + profile=['pfl'], qualities=None) |
| 213 | + j = json.loads(i.as_json()) |
| 214 | + self.assertEqual(len(j['profile']), 1) |
| 215 | + self.assertEqual(j['profile'][0], 'pfl') |
| 216 | + i = IIIFInfo( |
| 217 | + api_version='3.0', |
| 218 | + id="http://example.org/svc/a", width=1, height=2, |
| 219 | + profile=['pfl'], qualities=['q1', 'q2', 'q0']) |
| 220 | + j = json.loads(i.as_json()) |
| 221 | + self.assertEqual(len(j['profile']), 2) |
| 222 | + self.assertEqual(j['profile'][0], 'pfl') |
| 223 | + self.assertEqual(j['profile'][1], {'qualities': ['q1', 'q2', 'q0']}) |
| 224 | + i = IIIFInfo( |
| 225 | + api_version='3.0', |
| 226 | + id="http://example.org/svc/a", width=1, height=2, |
| 227 | + profile=['pfl'], supports=['a', 'b']) |
| 228 | + j = json.loads(i.as_json()) |
| 229 | + self.assertEqual(len(j['profile']), 2) |
| 230 | + self.assertEqual(j['profile'][0], 'pfl') |
| 231 | + self.assertEqual(j['profile'][1], {'supports': ['a', 'b']}) |
| 232 | + i = IIIFInfo( |
| 233 | + api_version='3.0', |
| 234 | + id="http://example.org/svc/a", width=1, height=2, |
| 235 | + profile=['pfl'], formats=["fmt1", "fmt2"], |
| 236 | + qualities=['q1', 'q2', 'q0'], supports=['a', 'b']) |
| 237 | + j = json.loads(i.as_json()) |
| 238 | + self.assertEqual(len(j['profile']), 2) |
| 239 | + self.assertEqual(j['profile'][0], 'pfl') |
| 240 | + self.assertEqual(j['profile'][1]['formats'], ['fmt1', 'fmt2']) |
| 241 | + self.assertEqual(j['profile'][1]['qualities'], ['q1', 'q2', 'q0']) |
| 242 | + self.assertEqual(j['profile'][1]['supports'], ['a', 'b']) |
0 commit comments