def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
        manifest = RWPMManifest(
            metadata=PresentationMetadata("test"),
            links=LinkList([
                Link(
                    href="http://example.com",
                    rels=[RWPMLinkRelationsRegistry.SELF.key],
                )
            ]),
            reading_order=CompactCollection(
                role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                links=LinkList([
                    Link(href="test", _type=RWPMMediaTypesRegistry.JPEG.key),
                ]),
            ),
            resources=CompactCollection(
                role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                links=LinkList([
                    Link(href="test", _type=RWPMMediaTypesRegistry.JPEG.key),
                ]),
            ),
        )
        media_types_registry = RWPMMediaTypesRegistry()
        link_relations_registry = RWPMLinkRelationsRegistry()
        collection_roles_registry = RWPMCollectionRolesRegistry()
        semantic_analyzer = RWPMSemanticAnalyzer(media_types_registry,
                                                 link_relations_registry,
                                                 collection_roles_registry)

        # Act
        semantic_analyzer.visit(manifest)
Exemplo n.º 2
0
    def test_cast(self):
        # Arrange
        navigation_links = LinkList([Link(href="http://example.com")])
        navigation = OPDS2Navigation(links=navigation_links)

        # Act
        casted_navigation = cast(navigation, CompactCollection)

        # Assert
        self.assertIsInstance(casted_navigation, CompactCollection)
        self.assertEqual(navigation_links, casted_navigation.links)
    def test_syntax_analyzer_returns_ast(self):
        # Arrange
        syntax_analyzer = RWPMSyntaxAnalyzer()
        input_steam = six.StringIO(RWPM_MANIFEST)
        manifest_json = ManifestParser.get_manifest_json(input_steam)

        # Act
        manifest = syntax_analyzer.analyze(manifest_json)

        # Assert
        self.assertIsInstance(manifest, RWPMManifest)

        self.assertIsInstance(manifest.context, list)
        self.assertEqual(1, len(manifest.context))
        [context] = manifest.context
        self.assertEqual(context,
                         "https://readium.org/webpub-manifest/context.jsonld")

        self.assertIsInstance(manifest.metadata, Metadata)
        self.assertEqual("http://schema.org/Book", manifest.metadata.type)
        self.assertEqual("Moby-Dick", manifest.metadata.title)
        self.assertEqual(
            [Contributor(name="Herman Melville", roles=[], links=LinkList())],
            manifest.metadata.authors,
        )
        self.assertEqual("urn:isbn:978031600000X",
                         manifest.metadata.identifier)
        self.assertEqual(["en"], manifest.metadata.languages)
        self.assertEqual(
            datetime.datetime(2015, 9, 29, 17, 0, 0, tzinfo=tzutc()),
            manifest.metadata.modified,
        )

        self.assertIsInstance(manifest.links, list)
        self.assertEqual(1, len(manifest.links))
        [link] = manifest.links

        self.assertIsInstance(link.rels, list)
        self.assertEqual(1, len(link.rels))
        self.assertEqual(RWPMLinkRelationsRegistry.SELF.key, link.rels[0])
        self.assertEqual("https://example.com/manifest.json", link.href)
        self.assertEqual(RWPMMediaTypesRegistry.MANIFEST.key, link.type)

        self.assertIsInstance(manifest.reading_order, CompactCollection)
        self.assertIsInstance(manifest.reading_order.links, list)
        self.assertEqual(1, len(manifest.reading_order.links))
        [reading_order_link] = manifest.reading_order.links
        self.assertEqual("https://example.com/c001.html",
                         reading_order_link.href)
        self.assertEqual(RWPMMediaTypesRegistry.HTML.key,
                         reading_order_link.type)
        self.assertEqual("Chapter 1", reading_order_link.title)
Exemplo n.º 4
0
class RWPMSyntaxAnalyzerTest(TestCase):
    @parameterized.expand([
        (
            "when_manifest_does_not_contain_metadata",
            RWPM_MANIFEST_WITHOUT_METADATA,
            RWPMManifest,
            RWPMManifest.metadata.key,
        ),
        (
            "when_manifest_metadata_does_not_contain_title",
            RWPM_MANIFEST_WITH_MISSING_METADATA_TITLE_PROPERTY,
            PresentationMetadata,
            PresentationMetadata.title.key,
        ),
        (
            "when_metadata_contributor_does_not_contain_name",
            RWPM_MANIFEST_WITH_MISSING_CONTRIBUTOR_NAME_PROPERTY,
            Contributor,
            Contributor.name.key,
        ),
        (
            "when_metadata_subject_does_not_contain_name",
            RWPM_MANIFEST_WITH_MISSING_SUBJECT_NAME_PROPERTY,
            Subject,
            Subject.name.key,
        ),
        (
            "when_manifest_does_not_contain_links",
            RWPM_MANIFEST_WITHOUT_LINKS,
            RWPMManifest,
            RWPMManifest.links.key,
        ),
        (
            "when_manifest_link_does_not_contain_href",
            RWPM_MANIFEST_WITH_MISSING_LINK_HREF_PROPERTY,
            Link,
            Link.href.key,
        ),
        (
            "when_manifest_link_does_not_contain_reading_order_sub_collection",
            RWPM_MANIFEST_WITH_MISSING_READING_ORDER,
            RWPMManifest,
            RWPMManifest.reading_order.key,
        ),
    ])
    def test_syntax_analyzer_raises_missing_property_error_correctly(
        self,
        _,
        rwpm_manifest_content,
        expected_class_with_missing_property,
        expected_missing_property,
    ):
        # Arrange
        syntax_analyzer = RWPMSyntaxAnalyzer()
        input_steam = six.StringIO(rwpm_manifest_content)
        manifest_json = DocumentParser.get_manifest_json(input_steam)

        # Act
        with self.assertRaises(MissingPropertyError) as assert_raises_context:
            syntax_analyzer.analyze(manifest_json)

        # Assert
        self.assertEqual(
            expected_class_with_missing_property,
            assert_raises_context.exception.cls,
        )
        self.assertEqual(
            expected_missing_property,
            assert_raises_context.exception.object_property.key,
        )

    @parameterized.expand([
        (
            "when_manifest_context_has_duplicated_items",
            RWPM_MANIFEST_WINT_INCORRECT_CONTEXT,
            "Item 'context1' is not unique",
        ),
        (
            "when_metadata_modified_property_has_incorrect_format",
            RWPM_MANIFEST_WITH_INCORRECT_METADATA_MODIFIED_PROPERTY,
            "'2015-09-29T17:00:00Z---' is not a 'date-time'",
        ),
        (
            "when_metadata_modified_language_has_incorrect_format",
            RWPM_MANIFEST_WITH_INCORRECT_METADATA_LANGUAGE_PROPERTY,
            "String value 'eng;dat' does not match regular expression ^((?P<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?P<language>([A-Za-z]{2,3}(-(?P<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?P<script>[A-Za-z]{4}))?(-(?P<region>[A-Za-z]{2}|[0-9]{3}))?(-(?P<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?P<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?P<privateUse>x(-[A-Za-z0-9]{1,8})+))?)|(?P<privateUse2>x(-[A-Za-z0-9]{1,8})+))$",
        ),
        (
            "when_contributor_identifier_has_incorrect_format",
            RWPM_MANIFEST_WITH_INCORRECT_CONTRIBUTOR_IDENTIFIER_PROPERTY,
            "'x' is not a 'uri'",
        ),
        (
            "when_link_height_less_or_equal_than_the_exclusive_minimum",
            RWPM_MANIFEST_WITH_INCORRECT_LINK_HEIGHT_PROPERTY,
            "Value -10 is less or equal than the exclusive minimum (0)",
        ),
    ])
    def test_syntax_analyzer_raises_value_parsing_error_when_property_has_incorrect_value(
            self, _, rwpm_manifest_content, expected_error_message):
        # Arrange
        syntax_analyzer = RWPMSyntaxAnalyzer()
        input_steam = six.StringIO(rwpm_manifest_content)
        manifest_json = DocumentParser.get_manifest_json(input_steam)

        # Act
        with self.assertRaises(ValueParsingError) as assert_raises_context:
            syntax_analyzer.analyze(manifest_json)

        # Assert
        self.assertEqual(
            expected_error_message,
            six.text_type(assert_raises_context.exception).strip("u"),
        )

    @parameterized.expand([
        (
            "when_manifest_has_author_encoded_as_string",
            RWPM_MANIFEST_WITH_AUTHOR_ENCODED_AS_STRING,
            [Contributor(name="Herman Melville", roles=[], links=LinkList())],
        ),
        (
            "when_manifest_has_authors_encoded_as_array_of_strings",
            RWPM_MANIFEST_WITH_AUTHORS_ENCODED_AS_ARRAY_OF_STRINGS,
            [
                Contributor(name="Herman Melville 1",
                            roles=[],
                            links=LinkList()),
                Contributor(name="Herman Melville 2",
                            roles=[],
                            links=LinkList()),
            ],
        ),
        (
            "when_manifest_has_author_encoded_as_object",
            RWPM_MANIFEST_WITH_AUTHOR_ENCODED_AS_OBJECT,
            [Contributor(name="Herman Melville", roles=[], links=LinkList())],
        ),
        (
            "when_manifest_has_authors_encoded_as_array_of_objects",
            RWPM_MANIFEST_WITH_AUTHORS_ENCODED_AS_ARRAY_OF_OBJECTS,
            [
                Contributor(name="Herman Melville 1",
                            roles=[],
                            links=LinkList()),
                Contributor(name="Herman Melville 2",
                            roles=[],
                            links=LinkList()),
            ],
        ),
        (
            "when_manifest_has_authors_encoded_as_mixed_array_of_strings_and_objects",
            RWPM_MANIFEST_WITH_AUTHORS_ENCODED_AS_MIXED_ARRAY_OF_STRINGS_AND_OBJECTS,
            [
                Contributor(name="Herman Melville 1",
                            roles=[],
                            links=LinkList()),
                Contributor(name="Herman Melville 2",
                            roles=[],
                            links=LinkList()),
            ],
        ),
    ])
    def test_syntax_analyzer_correctly_parses_contributor_metadata_as_contributor_objects(
            self, _, rwpm_manifest_content, expected_authors):
        # Arrange
        syntax_analyzer = RWPMSyntaxAnalyzer()
        input_steam = six.StringIO(rwpm_manifest_content)
        manifest_json = DocumentParser.get_manifest_json(input_steam)

        # Act
        manifest = syntax_analyzer.analyze(manifest_json)

        # Assert
        self.assertEqual(expected_authors, manifest.metadata.authors)

    def test_syntax_analyzer_returns_ast(self):
        # Arrange
        syntax_analyzer = RWPMSyntaxAnalyzer()
        input_steam = six.StringIO(RWPM_MANIFEST)
        manifest_json = DocumentParser.get_manifest_json(input_steam)

        # Act
        manifest = syntax_analyzer.analyze(manifest_json)

        # Assert
        self.assertIsInstance(manifest, RWPMManifest)

        self.assertIsInstance(manifest.context, list)
        self.assertEqual(1, len(manifest.context))
        [context] = manifest.context
        self.assertEqual(context,
                         "https://readium.org/webpub-manifest/context.jsonld")

        self.assertIsInstance(manifest.metadata, Metadata)
        self.assertEqual("http://schema.org/Book", manifest.metadata.type)
        self.assertEqual("Moby-Dick", manifest.metadata.title)
        self.assertEqual(
            [Contributor(name="Herman Melville", roles=[], links=LinkList())],
            manifest.metadata.authors,
        )
        self.assertEqual("urn:isbn:978031600000X",
                         manifest.metadata.identifier)
        self.assertEqual(["en"], manifest.metadata.languages)
        self.assertEqual(datetime.datetime(2015, 9, 29, 17, 0, 0),
                         manifest.metadata.modified)

        self.assertIsInstance(manifest.links, list)
        self.assertEqual(1, len(manifest.links))
        [link] = manifest.links

        self.assertIsInstance(link.rels, list)
        self.assertEqual(1, len(link.rels))
        self.assertEqual(RWPMLinkRelationsRegistry.SELF.key, link.rels[0])
        self.assertEqual("https://example.com/manifest.json", link.href)
        self.assertEqual(RWPMMediaTypesRegistry.MANIFEST.key, link.type)

        self.assertIsInstance(manifest.reading_order, CompactCollection)
        self.assertIsInstance(manifest.reading_order.links, list)
        self.assertEqual(1, len(manifest.reading_order.links))
        [reading_order_link] = manifest.reading_order.links
        self.assertEqual("https://example.com/c001.html",
                         reading_order_link.href)
        self.assertEqual(RWPMMediaTypesRegistry.HTML.key,
                         reading_order_link.type)
        self.assertEqual("Chapter 1", reading_order_link.title)
                    "Expected error for {0} node's property '{1}' with error message \"{2}\" wasn't raised".format(
                        expected_error.node.__class__,
                        expected_error.node_property.key
                        if expected_error.node_property
                        else "",
                        expected_error.error_message,
                    )
                )


MANIFEST = Manifestlike(
    metadata=PresentationMetadata(title="Manifest # 1"),
    links=LinkList(
        [
            Link(
                href="http://example.com",
                rels=["test"],
            )
        ]
    ),
)

RWPM_MANIFEST = RWPMManifest(
    metadata=PresentationMetadata("test"),
    links=LinkList(
        [
            Link(
                href="example.com",
                rels=[RWPMLinkRelationsRegistry.SELF.key],
            )
        ]
    ),
Exemplo n.º 6
0
class SemanticAnalyzerTest(TestCase):
    @parameterized.expand([
        (
            "when_feed_does_not_contain_neither_publications_nor_navigation_nor_groups",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
            ),
            MISSING_REQUIRED_FEED_SUB_COLLECTIONS,
        ),
        (
            "when_navigation_link_does_not_contain_title",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                navigation=OPDS2Navigation(
                    links=LinkList([Link(href="http://example.com")])),
            ),
            MISSING_NAVIGATION_LINK_TITLE_ERROR,
        ),
        (
            "when_publication_does_not_contain_acquisition_link",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                publications=CollectionList([
                    OPDS2Publication(
                        metadata=PresentationMetadata(title="Publication 1"),
                        links=LinkList([Link(href="http://example.com")]),
                    )
                ]),
            ),
            MISSING_ACQUISITION_LINK,
        ),
        (
            "when_navigation_contains_both_links_and_publications",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                groups=CollectionList([
                    OPDS2Group(
                        navigation=OPDS2Navigation(links=LinkList([
                            Link(
                                href="http://example.com",
                                rels=["current"],
                            )
                        ])),
                        publications=CollectionList([
                            OPDS2Publication(
                                metadata=PresentationMetadata(
                                    title="Publication 1"),
                                links=LinkList(
                                    [Link(href="http://example.com")]),
                            )
                        ]),
                    )
                ]),
            ),
            WRONG_GROUP_STRUCTURE,
        ),
    ])
    def test_semantic_analyzer_raises_error(self, _, manifest, expected_error):
        # Arrange
        media_types_registry = OPDS2MediaTypesRegistry()
        link_relations_registry = OPDS2LinkRelationsRegistry()
        collection_roles_registry = OPDS2CollectionRolesRegistry()
        semantic_analyzer = OPDS2SemanticAnalyzer(media_types_registry,
                                                  link_relations_registry,
                                                  collection_roles_registry)

        # Act
        with assert_raises(expected_error.__class__) as assert_raises_context:
            semantic_analyzer.visit(manifest)

        # Assert
        eq_(str(assert_raises_context.exception), str(expected_error))

    def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
        # Arrange
        feed = OPDS2Feed(
            metadata=OPDS2FeedMetadata(title="test"),
            links=LinkList([
                Link(
                    href="http://example.com",
                    rels=[RWPMLinkRelationsRegistry.SELF.key],
                )
            ]),
            publications=CollectionList([
                OPDS2Publication(
                    metadata=PresentationMetadata(title="Publication 1"),
                    links=LinkList([
                        Link(
                            href="http://example.com",
                            rels=[OPDS2LinkRelationsRegistry.ACQUISITION.key],
                        )
                    ]),
                )
            ]),
            navigation=OPDS2Navigation(links=LinkList([
                Link(
                    href="/new",
                    title="New Publications",
                    _type=OPDS2MediaTypesRegistry.OPDS_FEED,
                    rels=["current"],
                )
            ])),
            groups=CollectionList([
                OPDS2Group(
                    metadata=OPDS2FeedMetadata(title="Group 1"),
                    publications=CollectionList([
                        OPDS2Publication(
                            metadata=PresentationMetadata(
                                title="Publication 1.1"),
                            links=LinkList([
                                Link(
                                    href="http://example.com",
                                    rels=[
                                        OPDS2LinkRelationsRegistry.ACQUISITION.
                                        key
                                    ],
                                )
                            ]),
                        )
                    ]),
                )
            ]),
        )
        media_types_registry = OPDS2MediaTypesRegistry()
        link_relations_registry = OPDS2LinkRelationsRegistry()
        collection_roles_registry = OPDS2CollectionRolesRegistry()
        semantic_analyzer = OPDS2SemanticAnalyzer(media_types_registry,
                                                  link_relations_registry,
                                                  collection_roles_registry)

        semantic_analyzer.visit = MagicMock(
            side_effect=semantic_analyzer.visit)

        # Act
        semantic_analyzer.visit(feed)

        # Assert
        semantic_analyzer.visit.assert_has_calls(
            calls=[
                call(feed),
                call(feed.metadata),
                call(feed.links),
                call(feed.links[0]),
                call(feed.sub_collections),
                call(feed.publications),
                call(feed.publications[0]),
                call(feed.publications[0].metadata),
                call(feed.publications[0].links),
                call(feed.publications[0].links[0]),
                call(feed.publications[0].sub_collections),
                call(feed.navigation),
                call(feed.navigation),
                call(feed.navigation.links),
                call(feed.navigation.links[0]),
                call(feed.groups),
                call(feed.groups[0]),
                call(feed.groups[0].metadata),
                call(feed.groups[0].publications),
                call(feed.groups[0].publications[0]),
                call(feed.groups[0].publications[0].metadata),
                call(feed.groups[0].publications[0].links),
                call(feed.groups[0].publications[0].links[0]),
            ],
            any_order=False,
        )
Exemplo n.º 7
0
    def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
        # Arrange
        feed = OPDS2Feed(
            metadata=OPDS2FeedMetadata(title="test"),
            links=LinkList([
                Link(
                    href="http://example.com",
                    rels=[RWPMLinkRelationsRegistry.SELF.key],
                )
            ]),
            publications=CollectionList([
                OPDS2Publication(
                    metadata=PresentationMetadata(title="Publication 1"),
                    links=LinkList([
                        Link(
                            href="http://example.com",
                            rels=[OPDS2LinkRelationsRegistry.ACQUISITION.key],
                        )
                    ]),
                )
            ]),
            navigation=OPDS2Navigation(links=LinkList([
                Link(
                    href="/new",
                    title="New Publications",
                    _type=OPDS2MediaTypesRegistry.OPDS_FEED,
                    rels=["current"],
                )
            ])),
            groups=CollectionList([
                OPDS2Group(
                    metadata=OPDS2FeedMetadata(title="Group 1"),
                    publications=CollectionList([
                        OPDS2Publication(
                            metadata=PresentationMetadata(
                                title="Publication 1.1"),
                            links=LinkList([
                                Link(
                                    href="http://example.com",
                                    rels=[
                                        OPDS2LinkRelationsRegistry.ACQUISITION.
                                        key
                                    ],
                                )
                            ]),
                        )
                    ]),
                )
            ]),
        )
        media_types_registry = OPDS2MediaTypesRegistry()
        link_relations_registry = OPDS2LinkRelationsRegistry()
        collection_roles_registry = OPDS2CollectionRolesRegistry()
        semantic_analyzer = OPDS2SemanticAnalyzer(media_types_registry,
                                                  link_relations_registry,
                                                  collection_roles_registry)

        semantic_analyzer.visit = MagicMock(
            side_effect=semantic_analyzer.visit)

        # Act
        semantic_analyzer.visit(feed)

        # Assert
        semantic_analyzer.visit.assert_has_calls(
            calls=[
                call(feed),
                call(feed.metadata),
                call(feed.links),
                call(feed.links[0]),
                call(feed.sub_collections),
                call(feed.publications),
                call(feed.publications[0]),
                call(feed.publications[0].metadata),
                call(feed.publications[0].links),
                call(feed.publications[0].links[0]),
                call(feed.publications[0].sub_collections),
                call(feed.navigation),
                call(feed.navigation),
                call(feed.navigation.links),
                call(feed.navigation.links[0]),
                call(feed.groups),
                call(feed.groups[0]),
                call(feed.groups[0].metadata),
                call(feed.groups[0].publications),
                call(feed.groups[0].publications[0]),
                call(feed.groups[0].publications[0].metadata),
                call(feed.groups[0].publications[0].links),
                call(feed.groups[0].publications[0].links[0]),
            ],
            any_order=False,
        )
class SemanticAnalyzerTest(TestCase):
    @parameterized.expand([
        (
            "when_reading_order_link_does_not_have_type_property",
            RWPMManifest(
                metadata=PresentationMetadata("test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                reading_order=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([Link(href="test")]),
                ),
            ),
            MISSING_READING_ORDER_LINK_TYPE_PROPERTY_ERROR,
        ),
        (
            "when_resources_link_does_not_have_type_property",
            RWPMManifest(
                metadata=PresentationMetadata("test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                reading_order=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([
                        Link(href="test",
                             _type=RWPMMediaTypesRegistry.JPEG.key)
                    ]),
                ),
                resources=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([Link(href="test")]),
                ),
            ),
            MISSING_RESOURCES_LINK_TYPE_PROPERTY_ERROR,
        ),
    ])
    def test_semantic_analyzer_raises_error(self, _, manifest, expected_error):
        # Arrange
        media_types_registry = RWPMMediaTypesRegistry()
        link_relations_registry = RWPMLinkRelationsRegistry()
        collection_roles_registry = RWPMCollectionRolesRegistry()
        semantic_analyzer = RWPMSemanticAnalyzer(media_types_registry,
                                                 link_relations_registry,
                                                 collection_roles_registry)

        # Act
        with assert_raises(expected_error.__class__) as assert_raises_context:
            semantic_analyzer.visit(manifest)

        # Assert
        eq_(str(assert_raises_context.exception), str(expected_error))

    def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
        manifest = RWPMManifest(
            metadata=PresentationMetadata("test"),
            links=LinkList([
                Link(
                    href="http://example.com",
                    rels=[RWPMLinkRelationsRegistry.SELF.key],
                )
            ]),
            reading_order=CompactCollection(
                role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                links=LinkList([
                    Link(href="test", _type=RWPMMediaTypesRegistry.JPEG.key),
                ]),
            ),
            resources=CompactCollection(
                role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                links=LinkList([
                    Link(href="test", _type=RWPMMediaTypesRegistry.JPEG.key),
                ]),
            ),
        )
        media_types_registry = RWPMMediaTypesRegistry()
        link_relations_registry = RWPMLinkRelationsRegistry()
        collection_roles_registry = RWPMCollectionRolesRegistry()
        semantic_analyzer = RWPMSemanticAnalyzer(media_types_registry,
                                                 link_relations_registry,
                                                 collection_roles_registry)

        # Act
        semantic_analyzer.visit(manifest)
    def test(self):
        # Arrange
        parser_factory = OPDS2FeedParserFactory()
        parser = parser_factory.create()
        input_file_path = os.path.join(os.path.dirname(__file__),
                                       "../../files/opds2/feed.json")

        # Act
        result = parser.parse_file(input_file_path)

        # Assert
        self.assertIsInstance(result, ManifestParserResult)
        self.assertEqual(0, len(result.errors))

        feed = result.root
        self.assertIsInstance(feed, OPDS2Feed)

        self.assertIsInstance(feed.metadata, OPDS2FeedMetadata)
        self.assertEqual("Example listing publications", feed.metadata.title)

        self.assertIsInstance(feed.links, list)
        self.assertEqual(1, len(feed.links))
        [manifest_link] = feed.links
        self.assertEqual(OPDS2LinkRelationsRegistry.SELF.key,
                         manifest_link.rels[0])
        self.assertEqual("http://example.com/new", manifest_link.href)
        self.assertEqual(OPDS2MediaTypesRegistry.OPDS_FEED.key,
                         manifest_link.type)

        self.assertIsInstance(feed.publications, list)
        self.assertEqual(2, len(feed.publications))
        publication = feed.publications[0]

        self.assertIsInstance(publication.metadata, PresentationMetadata)
        self.assertEqual("http://schema.org/Book", publication.metadata.type)
        self.assertEqual("Moby-Dick", publication.metadata.title)
        self.assertEqual(
            [Contributor(name="Herman Melville", roles=[], links=LinkList())],
            publication.metadata.authors,
        )
        self.assertEqual("urn:isbn:978-3-16-148410-0",
                         publication.metadata.identifier)
        self.assertEqual(["en"], publication.metadata.languages)
        self.assertEqual(
            datetime.datetime(2015, 9, 29, 17, 0, tzinfo=tzutc()),
            publication.metadata.modified,
        )

        self.assertIsInstance(publication.links, list)
        self.assertEqual(len(publication.links), 2)

        publication_self_link = first_or_default(
            publication.links.get_by_rel(OPDS2LinkRelationsRegistry.SELF.key))
        self.assertEqual(OPDS2LinkRelationsRegistry.SELF.key,
                         publication_self_link.rels[0])
        self.assertEqual("http://example.org/publication.json",
                         publication_self_link.href)
        self.assertEqual(OPDS2MediaTypesRegistry.OPDS_PUBLICATION.key,
                         publication_self_link.type)

        publication_acquisition_link = first_or_default(
            publication.links.get_by_rel(
                OPDS2LinkRelationsRegistry.OPEN_ACCESS.key))
        self.assertEqual(
            OPDS2LinkRelationsRegistry.OPEN_ACCESS.key,
            publication_acquisition_link.rels[0],
        )
        self.assertEqual("http://example.org/file.epub",
                         publication_acquisition_link.href)
        self.assertEqual(
            OPDS2MediaTypesRegistry.EPUB_PUBLICATION_PACKAGE.key,
            publication_acquisition_link.type,
        )

        self.assertIsInstance(publication.images, CompactCollection)
        self.assertIsInstance(publication.images.links, list)
        self.assertEqual(3, len(publication.images.links))

        jpeg_cover_link = first_or_default(
            publication.images.links.get_by_href(
                "http://example.org/cover.jpg"))
        self.assertEqual([], jpeg_cover_link.rels)
        self.assertEqual("http://example.org/cover.jpg", jpeg_cover_link.href)
        self.assertEqual(OPDS2MediaTypesRegistry.JPEG.key,
                         jpeg_cover_link.type)
        self.assertEqual(1400, jpeg_cover_link.height)
        self.assertEqual(800, jpeg_cover_link.width)

        small_jpeg_cover_link = first_or_default(
            publication.images.links.get_by_href(
                "http://example.org/cover-small.jpg"))
        self.assertEqual("http://example.org/cover-small.jpg",
                         small_jpeg_cover_link.href)
        self.assertEqual(OPDS2MediaTypesRegistry.JPEG.key,
                         small_jpeg_cover_link.type)
        self.assertEqual(700, small_jpeg_cover_link.height)
        self.assertEqual(400, small_jpeg_cover_link.width)

        svg_cover_link = first_or_default(
            publication.images.links.get_by_href(
                "http://example.org/cover.svg"))
        self.assertEqual(svg_cover_link.href, "http://example.org/cover.svg")
        self.assertEqual(svg_cover_link.type,
                         OPDS2MediaTypesRegistry.SVG_XML.key)

        publication = feed.publications[1]
        self.assertIsInstance(publication.metadata, PresentationMetadata)
        self.assertEqual("http://schema.org/Book", publication.metadata.type)
        self.assertEqual("Adventures of Huckleberry Finn",
                         publication.metadata.title)
        self.assertEqual(
            [
                Contributor(name="Mark Twain", roles=[], links=LinkList()),
                Contributor(name="Samuel Langhorne Clemens",
                            roles=[],
                            links=LinkList()),
            ],
            publication.metadata.authors,
        )
        self.assertEqual("urn:isbn:978-3-16-148410-0",
                         publication.metadata.identifier)
        self.assertEqual(["eng", "fre"], publication.metadata.languages)
        self.assertEqual(
            datetime.datetime(2015, 9, 29, 0, 0, tzinfo=tzutc()),
            publication.metadata.published,
        )
        self.assertEqual(
            datetime.datetime(2015, 9, 29, 17, 0, 0, tzinfo=tzutc()),
            publication.metadata.modified,
        )

        self.assertIsInstance(publication.links, list)

        publication_acquisition_link = first_or_default(
            publication.links.get_by_rel(
                OPDS2LinkRelationsRegistry.BORROW.key))
        self.assertEqual(OPDS2LinkRelationsRegistry.BORROW.key,
                         publication_acquisition_link.rels[0])
        self.assertEqual(
            OPDS2MediaTypesRegistry.OPDS_PUBLICATION.key,
            publication_acquisition_link.type,
        )

        link_properties = publication_acquisition_link.properties
        self.assertIsInstance(link_properties, OPDS2LinkProperties)

        self.assertEqual(OPDS2AvailabilityType.AVAILABLE.value,
                         link_properties.availability.state)

        self.assertEqual(2, len(link_properties.indirect_acquisition))

        indirect_acquisition_object = link_properties.indirect_acquisition[0]
        self.assertEqual("application/vnd.adobe.adept+xml",
                         indirect_acquisition_object.type)
        self.assertEqual(1, len(indirect_acquisition_object.child))
        self.assertIsInstance(indirect_acquisition_object.child[0],
                              OPDS2AcquisitionObject)
        self.assertEqual("application/epub+zip",
                         indirect_acquisition_object.child[0].type)

        indirect_acquisition_object = link_properties.indirect_acquisition[1]
        self.assertEqual(
            "application/vnd.readium.lcp.license.v1.0+json",
            indirect_acquisition_object.type,
        )
        self.assertEqual(1, len(indirect_acquisition_object.child))
        self.assertIsInstance(indirect_acquisition_object.child[0],
                              OPDS2AcquisitionObject)
        self.assertEqual("application/epub+zip",
                         indirect_acquisition_object.child[0].type)
Exemplo n.º 10
0
class RWPMSemanticAnalyzerTest(AnalyzerTest):
    @parameterized.expand([
        (
            "when_reading_order_link_does_not_have_type_property",
            RWPMManifest(
                metadata=PresentationMetadata("test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                reading_order=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([Link(href="test")]),
                ),
            ),
            [
                READING_ORDER_SUBCOLLECTION_LINK_MISSING_TYPE_PROPERTY_ERROR(
                    node=Link(href="test"), node_property=Link.type)
            ],
        ),
        (
            "when_resources_link_does_not_have_type_property",
            RWPMManifest(
                metadata=PresentationMetadata("test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                reading_order=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([
                        Link(href="test",
                             _type=RWPMMediaTypesRegistry.JPEG.key)
                    ]),
                ),
                resources=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([Link(href="test")]),
                ),
            ),
            [
                RESOURCES_SUBCOLLECTION_MISSING_LINK_TYPE_PROPERTY_ERROR(
                    node=Link(href="test"), node_property=Link.type)
            ],
        ),
        (
            "when_reading_order_and_resource_links_do_not_have_type_property_and_self_link_has_incorrect_href",
            RWPMManifest(
                metadata=PresentationMetadata("test"),
                links=LinkList([
                    Link(
                        href="example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                reading_order=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([Link(href="test")]),
                ),
                resources=CompactCollection(
                    role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                    links=LinkList([Link(href="test")]),
                ),
            ),
            [
                MANIFEST_SELF_LINK_WRONG_HREF_FORMAT_ERROR(
                    node=Link(href="example.com"), node_property=Link.href),
                READING_ORDER_SUBCOLLECTION_LINK_MISSING_TYPE_PROPERTY_ERROR(
                    node=Link(href="test"), node_property=Link.type),
                RESOURCES_SUBCOLLECTION_MISSING_LINK_TYPE_PROPERTY_ERROR(
                    node=Link(href="test"), node_property=Link.type),
            ],
        ),
    ])
    def test_semantic_analyzer_raises_error(self, _, manifest,
                                            expected_errors):
        """Ensure that the base semantic analyzer correctly raises errors and saves them in the current context.

        :param manifest: AST object containing the RWPM-like manifest
        :type manifest: webpub_manifest_parser.core.ast.Node

        :param expected_errors: List of expected semantic errors
        :type expected_errors: List[webpub_manifest_parser.core.analyzer.BaseAnalyzerError]
        """
        # Arrange
        media_types_registry = RWPMMediaTypesRegistry()
        link_relations_registry = RWPMLinkRelationsRegistry()
        collection_roles_registry = RWPMCollectionRolesRegistry()
        semantic_analyzer = RWPMSemanticAnalyzer(media_types_registry,
                                                 link_relations_registry,
                                                 collection_roles_registry)

        # Act
        semantic_analyzer.visit(manifest)

        # Assert
        self.check_analyzer_errors(semantic_analyzer.context.errors,
                                   expected_errors, SemanticAnalyzerError)

    def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
        manifest = RWPMManifest(
            metadata=PresentationMetadata("test"),
            links=LinkList([
                Link(
                    href="http://example.com",
                    rels=[RWPMLinkRelationsRegistry.SELF.key],
                )
            ]),
            reading_order=CompactCollection(
                role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                links=LinkList([
                    Link(href="test", _type=RWPMMediaTypesRegistry.JPEG.key),
                ]),
            ),
            resources=CompactCollection(
                role=RWPMCollectionRolesRegistry.READING_ORDER.key,
                links=LinkList([
                    Link(href="test", _type=RWPMMediaTypesRegistry.JPEG.key),
                ]),
            ),
        )
        media_types_registry = RWPMMediaTypesRegistry()
        link_relations_registry = RWPMLinkRelationsRegistry()
        collection_roles_registry = RWPMCollectionRolesRegistry()
        semantic_analyzer = RWPMSemanticAnalyzer(media_types_registry,
                                                 link_relations_registry,
                                                 collection_roles_registry)

        # Act
        semantic_analyzer.visit(manifest)
Exemplo n.º 11
0
                result[property_object.key] = property_value
    elif isinstance(rwpm_item, RegistryItem):
        result = rwpm_item.key

    return result


PROQUEST_PUBLICATION_1 = OPDS2Publication(
    metadata=PresentationMetadata(
        identifier="urn:proquest.com/document-id/1",
        title="Publićation # 1",
        modified=datetime_utc(2020, 1, 31, 0, 0, 0),
    ),
    links=LinkList([
        Link(
            href="https://feed.org/document-id/1",
            rels=[OPDS2LinkRelationsRegistry.ACQUISITION],
        )
    ]),
)

PROQUEST_PUBLICATION_2 = OPDS2Publication(
    metadata=PresentationMetadata(
        identifier="urn:proquest.com/document-id/2",
        title="Publication # 2",
        modified=datetime_utc(2020, 1, 30, 0, 0, 0),
    ),
    links=LinkList([
        Link(
            href="https://feed.org/document-id/2",
            rels=[OPDS2LinkRelationsRegistry.ACQUISITION],
        )
Exemplo n.º 12
0
class OPDS2SemanticAnalyzerTest(AnalyzerTest):
    @parameterized.expand([
        (
            "when_feed_does_not_contain_neither_publications_nor_navigation_nor_groups",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
            ),
            [
                MISSING_REQUIRED_FEED_SUB_COLLECTIONS(node=OPDS2Feed(),
                                                      node_property=None)
            ],
        ),
        (
            "when_navigation_link_does_not_contain_title",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                navigation=OPDS2Navigation(
                    links=LinkList([Link(href="http://example.com")])),
            ),
            [
                MISSING_NAVIGATION_LINK_TITLE_ERROR(node=Link(),
                                                    node_property=Link.title)
            ],
        ),
        (
            "when_publication_does_not_contain_acquisition_link",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                publications=CollectionList([
                    OPDS2Publication(
                        metadata=PresentationMetadata(title="Publication 1"),
                        links=LinkList([Link(href="http://example.com")]),
                    )
                ]),
            ),
            [
                MISSING_ACQUISITION_LINK(node=OPDS2Publication(),
                                         node_property=None)
            ],
        ),
        (
            "when_navigation_contains_both_links_and_publications",
            OPDS2Feed(
                metadata=OPDS2FeedMetadata(title="test"),
                links=LinkList([
                    Link(
                        href="http://example.com",
                        rels=[RWPMLinkRelationsRegistry.SELF.key],
                    )
                ]),
                groups=CollectionList([
                    OPDS2Group(
                        navigation=OPDS2Navigation(links=LinkList([
                            Link(
                                href="http://example.com",
                                rels=["current"],
                            )
                        ])),
                        publications=CollectionList([
                            OPDS2Publication(
                                metadata=PresentationMetadata(
                                    title="Publication 1"),
                                links=LinkList(
                                    [Link(href="http://example.com")]),
                            )
                        ]),
                    )
                ]),
            ),
            [
                WRONG_GROUP_STRUCTURE(node=OPDS2Group(), node_property=None),
                MISSING_ACQUISITION_LINK(node=OPDS2Publication(),
                                         node_property=None),
                MISSING_NAVIGATION_LINK_TITLE_ERROR(node=Link(),
                                                    node_property=Link.title),
            ],
        ),
    ])
    def test_semantic_analyzer_raises_error(self, _, manifest,
                                            expected_errors):
        """Ensure that the OPDS 2.x semantic analyzer correctly raises errors and saves them in the current context.

        :param manifest: AST object containing the RWPM-like manifest
        :type manifest: webpub_manifest_parser.core.ast.Node

        :param expected_errors: List of expected semantic errors
        :type expected_errors: List[webpub_manifest_parser.core.analyzer.BaseAnalyzerError]
        """
        # Arrange
        media_types_registry = OPDS2MediaTypesRegistry()
        link_relations_registry = OPDS2LinkRelationsRegistry()
        collection_roles_registry = OPDS2CollectionRolesRegistry()
        semantic_analyzer = OPDS2SemanticAnalyzer(media_types_registry,
                                                  link_relations_registry,
                                                  collection_roles_registry)

        # Act
        semantic_analyzer.visit(manifest)

        # Assert
        self.check_analyzer_errors(semantic_analyzer.context.errors,
                                   expected_errors, SemanticAnalyzerError)

    def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
        # Arrange
        feed = OPDS2Feed(
            metadata=OPDS2FeedMetadata(title="test"),
            links=LinkList([
                Link(
                    href="http://example.com",
                    rels=[RWPMLinkRelationsRegistry.SELF.key],
                )
            ]),
            publications=CollectionList([
                OPDS2Publication(
                    metadata=PresentationMetadata(title="Publication 1"),
                    links=LinkList([
                        Link(
                            href="http://example.com",
                            rels=[OPDS2LinkRelationsRegistry.ACQUISITION.key],
                        )
                    ]),
                )
            ]),
            navigation=OPDS2Navigation(links=LinkList([
                Link(
                    href="/new",
                    title="New Publications",
                    _type=OPDS2MediaTypesRegistry.OPDS_FEED,
                    rels=["current"],
                )
            ])),
            groups=CollectionList([
                OPDS2Group(
                    metadata=OPDS2FeedMetadata(title="Group 1"),
                    publications=CollectionList([
                        OPDS2Publication(
                            metadata=PresentationMetadata(
                                title="Publication 1.1"),
                            links=LinkList([
                                Link(
                                    href="http://example.com",
                                    rels=[
                                        OPDS2LinkRelationsRegistry.ACQUISITION.
                                        key
                                    ],
                                )
                            ]),
                        )
                    ]),
                )
            ]),
        )
        media_types_registry = OPDS2MediaTypesRegistry()
        link_relations_registry = OPDS2LinkRelationsRegistry()
        collection_roles_registry = OPDS2CollectionRolesRegistry()
        semantic_analyzer = OPDS2SemanticAnalyzer(media_types_registry,
                                                  link_relations_registry,
                                                  collection_roles_registry)

        semantic_analyzer.visit = MagicMock(
            side_effect=semantic_analyzer.visit)

        # Act
        semantic_analyzer.visit(feed)

        # Assert
        semantic_analyzer.visit.assert_has_calls(
            calls=[
                call(feed),
                call(feed.metadata),
                call(feed.links),
                call(feed.links[0]),
                call(feed.sub_collections),
                call(feed.publications),
                call(feed.publications[0]),
                call(feed.publications[0].metadata),
                call(feed.publications[0].links),
                call(feed.publications[0].links[0]),
                call(feed.publications[0].sub_collections),
                call(feed.navigation),
                call(feed.navigation),
                call(feed.navigation.links),
                call(feed.navigation.links[0]),
                call(feed.groups),
                call(feed.groups[0]),
                call(feed.groups[0].metadata),
                call(feed.groups[0].publications),
                call(feed.groups[0].publications[0]),
                call(feed.groups[0].publications[0].metadata),
                call(feed.groups[0].publications[0].links),
                call(feed.groups[0].publications[0].links[0]),
            ],
            any_order=False,
        )
    def test(self):
        # Arrange
        parser_factory = RWPMDocumentParserFactory()
        parser = parser_factory.create()
        input_file_path = os.path.join(
            os.path.dirname(__file__), "../../files/rwpm/spec_example.json"
        )

        # Act
        manifest = parser.parse_file(input_file_path)

        # Assert
        self.assertIsInstance(manifest.context, list)
        self.assertEqual(1, len(manifest.context))
        [context] = manifest.context
        self.assertEqual(context, "https://readium.org/webpub-manifest/context.jsonld")

        self.assertIsInstance(manifest.metadata, Metadata)
        self.assertEqual("http://schema.org/Book", manifest.metadata.type)
        self.assertEqual("Moby-Dick", manifest.metadata.title)
        self.assertEqual(
            [Contributor(name="Herman Melville", roles=[], links=LinkList())],
            manifest.metadata.authors,
        )
        self.assertEqual("urn:isbn:978031600000X", manifest.metadata.identifier)
        self.assertEqual(["en"], manifest.metadata.languages)
        self.assertEqual(
            datetime.datetime(2015, 9, 29, 17, 0, 0), manifest.metadata.modified
        )

        self.assertIsInstance(manifest.links, list)
        self.assertEqual(3, len(manifest.links))

        self_link = first_or_default(
            manifest.links.get_by_rel(RWPMLinkRelationsRegistry.SELF.key)
        )
        self.assertIsNotNone(self_link)
        self.assertIn(RWPMLinkRelationsRegistry.SELF.key, self_link.rels)
        self.assertEqual("https://example.com/manifest.json", self_link.href)
        self.assertEqual(RWPMMediaTypesRegistry.MANIFEST.key, self_link.type)

        alternate_link = first_or_default(
            manifest.links.get_by_rel(RWPMLinkRelationsRegistry.ALTERNATE.key)
        )
        self.assertIsNotNone(alternate_link)
        self.assertIn(RWPMLinkRelationsRegistry.ALTERNATE.key, alternate_link.rels)
        self.assertEqual("https://example.com/publication.epub", alternate_link.href)
        self.assertEqual(
            RWPMMediaTypesRegistry.EPUB_PUBLICATION_PACKAGE.key, alternate_link.type
        )

        search_link = first_or_default(
            manifest.links.get_by_rel(RWPMLinkRelationsRegistry.SEARCH.key)
        )
        self.assertIsNotNone(search_link)
        self.assertIn(RWPMLinkRelationsRegistry.SEARCH.key, search_link.rels)
        self.assertEqual("https://example.com/search{?query}", search_link.href)
        self.assertEqual(RWPMMediaTypesRegistry.HTML.key, search_link.type)

        self.assertIsInstance(manifest.reading_order, CompactCollection)
        self.assertIsInstance(manifest.reading_order.links, list)
        self.assertEqual(2, len(manifest.reading_order.links))

        reading_order_link = manifest.reading_order.links[0]
        self.assertEqual("https://example.com/c001.html", reading_order_link.href)
        self.assertEqual(RWPMMediaTypesRegistry.HTML.key, reading_order_link.type)
        self.assertEqual("Chapter 1", reading_order_link.title)

        reading_order_link = manifest.reading_order.links[1]
        self.assertEqual("https://example.com/c002.html", reading_order_link.href)
        self.assertEqual(RWPMMediaTypesRegistry.HTML.key, reading_order_link.type)
        self.assertEqual("Chapter 2", reading_order_link.title)

        resources_sub_collection = manifest.resources
        self.assertEqual(5, len(resources_sub_collection.links))
        self.assertEqual(
            [RWPMLinkRelationsRegistry.COVER.key],
            resources_sub_collection.links[0].rels,
        )
        self.assertEqual(
            "https://example.com/cover.jpg", resources_sub_collection.links[0].href
        )
        self.assertEqual(
            RWPMMediaTypesRegistry.JPEG.key, resources_sub_collection.links[0].type
        )
        self.assertEqual(600, resources_sub_collection.links[0].height)
        self.assertEqual(400, resources_sub_collection.links[0].width)

        self.assertEqual(
            "https://example.com/style.css", resources_sub_collection.links[1].href
        )
        self.assertEqual(
            RWPMMediaTypesRegistry.CSS.key, resources_sub_collection.links[1].type
        )

        self.assertEqual(
            "https://example.com/whale.jpg", resources_sub_collection.links[2].href
        )
        self.assertEqual(
            RWPMMediaTypesRegistry.JPEG.key, resources_sub_collection.links[2].type
        )

        self.assertEqual(
            "https://example.com/boat.svg", resources_sub_collection.links[3].href
        )
        self.assertEqual(
            RWPMMediaTypesRegistry.SVG_XML.key, resources_sub_collection.links[3].type
        )

        self.assertEqual(
            "https://example.com/notes.html", resources_sub_collection.links[4].href
        )
        self.assertEqual(
            RWPMMediaTypesRegistry.HTML.key, resources_sub_collection.links[4].type
        )
class ODLSemanticAnalyzerTest(AnalyzerTest):
    @parameterized.expand(
        [
            (
                "when_feed_does_not_contain_publications",
                ODLFeed(metadata=OPDS2FeedMetadata(title="test"), links=LinkList()),
                [
                    ODL_FEED_MISSING_PUBLICATIONS_SUBCOLLECTION_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.publications,
                    ),
                    MANIFEST_MISSING_SELF_LINK_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=None,
                    ),
                ],
            ),
            (
                "when_feed_contains_groups",
                ODLFeed(
                    metadata=OPDS2FeedMetadata(title="test"),
                    links=LinkList(),
                    groups=CollectionList([OPDS2Group()]),
                ),
                [
                    ODL_FEED_MISSING_PUBLICATIONS_SUBCOLLECTION_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.publications,
                    ),
                    MANIFEST_MISSING_SELF_LINK_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=None,
                    ),
                    ODL_FEED_CONTAINS_REDUNDANT_GROUPS_SUBCOLLECTIONS_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.groups,
                    ),
                ],
            ),
            (
                "when_feed_contains_facets",
                ODLFeed(
                    metadata=OPDS2FeedMetadata(title="test"),
                    links=LinkList(),
                    facets=CollectionList([OPDS2Facet()]),
                ),
                [
                    ODL_FEED_MISSING_PUBLICATIONS_SUBCOLLECTION_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.publications,
                    ),
                    MANIFEST_MISSING_SELF_LINK_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=None,
                    ),
                    ODL_FEED_CONTAINS_REDUNDANT_FACETS_SUBCOLLECTIONS_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.facets,
                    ),
                ],
            ),
            (
                "when_feed_contains_navigation",
                ODLFeed(
                    metadata=OPDS2FeedMetadata(title="test"),
                    links=LinkList(),
                    navigation=OPDS2Navigation(),
                ),
                [
                    ODL_FEED_MISSING_PUBLICATIONS_SUBCOLLECTION_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.publications,
                    ),
                    MANIFEST_MISSING_SELF_LINK_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=None,
                    ),
                    ODL_FEED_CONTAINS_REDUNDANT_NAVIGATION_SUBCOLLECTION_ERROR(
                        node=ODLFeed(metadata=OPDS2FeedMetadata(title="test")),
                        node_property=ODLFeed.navigation,
                    ),
                ],
            ),
            (
                "when_publication_does_not_contain_neither_licenses_nor_oa_link",
                ODLFeed(
                    metadata=OPDS2FeedMetadata(title="test"),
                    links=LinkList(
                        [
                            Link(
                                href="http://example.com",
                                rels=[LinkRelationsRegistry.SELF.key],
                            )
                        ]
                    ),
                    publications=CollectionList(
                        [
                            ODLPublication(
                                metadata=PresentationMetadata(title="Publication 1"),
                                licenses=CollectionList(),
                            )
                        ]
                    ),
                ),
                [
                    ODL_PUBLICATION_MUST_CONTAIN_EITHER_LICENSES_OR_OA_ACQUISITION_LINK_ERROR(
                        node=ODLPublication(
                            metadata=PresentationMetadata(title="Publication 1")
                        ),
                        node_property=None,
                    )
                ],
            ),
            (
                "when_publication_does_not_contain_neither_licenses_nor_oa_link",
                ODLFeed(
                    metadata=OPDS2FeedMetadata(title="test"),
                    links=LinkList(
                        [
                            Link(
                                href="http://example.com",
                                rels=[LinkRelationsRegistry.SELF.key],
                            )
                        ]
                    ),
                    publications=CollectionList(
                        [
                            ODLPublication(
                                metadata=PresentationMetadata(title="Publication 1"),
                                links=LinkList([Link(href="http://example.com")]),
                            )
                        ]
                    ),
                ),
                [
                    ODL_PUBLICATION_MUST_CONTAIN_EITHER_LICENSES_OR_OA_ACQUISITION_LINK_ERROR(
                        node=ODLPublication(
                            metadata=PresentationMetadata(title="Publication 1")
                        ),
                        node_property=None,
                    )
                ],
            ),
            (
                "when_license_does_not_contain_self_link_and_borrow_link",
                ODLFeed(
                    metadata=OPDS2FeedMetadata(title="test"),
                    links=LinkList(
                        [
                            Link(
                                href="http://example.com",
                                rels=[LinkRelationsRegistry.SELF.key],
                            )
                        ]
                    ),
                    publications=CollectionList(
                        [
                            ODLPublication(
                                metadata=PresentationMetadata(title="Publication 1"),
                                links=LinkList([Link(href="http://example.com")]),
                                licenses=CollectionList(
                                    [
                                        ODLLicense(
                                            metadata=ODLLicenseMetadata(
                                                identifier="license-1",
                                                formats=["text/html"],
                                                created=datetime.datetime(
                                                    2021, 1, 1, 0, 0, 0
                                                ),
                                            )
                                        )
                                    ]
                                ),
                            )
                        ]
                    ),
                ),
                [
                    ODL_LICENSE_MUST_CONTAIN_SELF_LINK_TO_LICENSE_INFO_DOCUMENT_ERROR(
                        node=ODLLicense(
                            metadata=ODLLicenseMetadata(identifier="license-1")
                        ),
                        node_property=None,
                    ),
                    ODL_LICENSE_MUST_CONTAIN_CHECKOUT_LINK_TO_LICENSE_STATUS_DOCUMENT_ERROR(
                        node=ODLLicense(
                            metadata=ODLLicenseMetadata(identifier="license-1")
                        ),
                        node_property=None,
                    ),
                ],
            ),
        ]
    )
    def test_semantic_analyzer_raises_error(self, _, manifest, expected_errors):
        """Ensure that the ODL 2.x semantic analyzer correctly raises errors and saves them in the current context.

        :param manifest: AST object containing the RWPM-like manifest
        :type manifest: webpub_manifest_parser.odl.ast.ODLFeed

        :param expected_errors: List of expected semantic errors
        :type expected_errors: List[webpub_manifest_parser.core.analyzer.BaseAnalyzerError]
        """
        # Arrange
        media_types_registry = OPDS2MediaTypesRegistry()
        link_relations_registry = OPDS2LinkRelationsRegistry()
        collection_roles_registry = OPDS2CollectionRolesRegistry()
        semantic_analyzer = ODLSemanticAnalyzer(
            media_types_registry, link_relations_registry, collection_roles_registry
        )

        # Act
        semantic_analyzer.visit(manifest)

        # Assert
        self.check_analyzer_errors(
            semantic_analyzer.context.errors, expected_errors, SemanticAnalyzerError
        )
class SemanticAnalyzerTest(AnalyzerTest):
    @parameterized.expand([
        (
            "when_manifest_link_rel_property_is_missing",
            Manifestlike(
                metadata=PresentationMetadata(title="Manifest # 1"),
                links=LinkList([Link(href="http://example.com")]),
            ),
            [
                MANIFEST_LINK_MISSING_REL_PROPERTY_ERROR(
                    node=Link(href="http://example.com"),
                    node_property=Link.rels),
                MANIFEST_MISSING_SELF_LINK_ERROR(
                    node=Manifestlike(metadata=PresentationMetadata(
                        identifier="Manifest # 1")),
                    node_property=None,
                ),
            ],
        ),
        (
            "when_manifest_self_link_is_missing",
            Manifestlike(
                metadata=PresentationMetadata(title="Manifest # 1"),
                links=LinkList(
                    [Link(
                        href="http://example.com",
                        rels=["test"],
                    )]),
            ),
            [
                MANIFEST_MISSING_SELF_LINK_ERROR(
                    node=Manifestlike(metadata=PresentationMetadata(
                        title="Manifest # 1")),
                    node_property=None,
                )
            ],
        ),
        (
            "when_manifest_self_link_has_wrong_href",
            Manifestlike(
                metadata=PresentationMetadata(title="Manifest # 1"),
                links=LinkList([
                    Link(
                        href="example.com",
                        rels=[LinkRelationsRegistry.SELF.key],
                    )
                ]),
            ),
            [
                MANIFEST_SELF_LINK_WRONG_HREF_FORMAT_ERROR(
                    node=Link(href="example.com"), node_property=Link.href)
            ],
        ),
        (
            "when_manifest_link_rel_property_is_missing_and_self_link_has_incorrect_href",
            Manifestlike(
                metadata=PresentationMetadata(title="Manifest # 1"),
                links=LinkList([
                    Link(href="http://example.com"),
                    Link(
                        href="example.com",
                        rels=[LinkRelationsRegistry.SELF.key],
                    ),
                ]),
            ),
            [
                MANIFEST_LINK_MISSING_REL_PROPERTY_ERROR(
                    node=Link(href="http://example.com"),
                    node_property=Link.rels),
                MANIFEST_SELF_LINK_WRONG_HREF_FORMAT_ERROR(
                    node=Link(href="example.com"), node_property=Link.href),
            ],
        ),
    ])
    def test_semantic_analyzer_raises_error(self, _, manifest,
                                            expected_errors):
        """Ensure that the base semantic analyzer correctly raises errors and saves them in the current context.

        :param manifest: AST object containing the RWPM-like manifest
        :type manifest: webpub_manifest_parser.core.ast.Manifestlike

        :param expected_errors: List of expected semantic errors
        :type expected_errors: List[webpub_manifest_parser.core.analyzer.BaseAnalyzerError]
        """
        # Arrange
        media_types_registry = Registry()
        link_relations_registry = Registry()
        collection_roles_registry = Registry()
        semantic_analyzer = SemanticAnalyzer(media_types_registry,
                                             link_relations_registry,
                                             collection_roles_registry)

        # Act
        semantic_analyzer.visit(manifest)

        # Assert
        self.check_analyzer_errors(semantic_analyzer.context.errors,
                                   expected_errors, SemanticAnalyzerError)