♻️ Convert MovieExtractorTest to Kotlin
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 8 Mar 2024 11:16:29 +0000 (12:16 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 8 Mar 2024 11:18:04 +0000 (12:18 +0100)
src/test/java/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.java [deleted file]
src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.kt [new file with mode: 0644]

diff --git a/src/test/java/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.java b/src/test/java/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.java
deleted file mode 100644 (file)
index 11e4a30..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-package net.pterodactylus.rhynodge.filters.webpages.savoy;
-
-import kotlin.Triple;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeDiagnosingMatcher;
-import org.jsoup.nodes.Document;
-import org.junit.Test;
-
-import javax.annotation.Nonnull;
-import java.io.IOException;
-import java.io.InputStream;
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-
-import static java.time.LocalDateTime.of;
-import static java.util.Optional.empty;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-import static org.jsoup.Jsoup.parse;
-
-/**
- * Unit test for {@link MovieExtractor}.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class MovieExtractorTest {
-
-       private final Document document;
-       private final MovieExtractor movieExtractor = new MovieExtractor();
-       private Collection<Movie> movies;
-
-       public MovieExtractorTest() throws IOException {
-               document = loadDocument("savoy.html", "https://savoy.premiumkino.de/programmwoche");
-               movies = movieExtractor.getMovies(document);
-       }
-
-       private Document loadDocument(String resourceName, String baseUri) throws IOException {
-               InputStream inputStream = getClass().getResourceAsStream(resourceName);
-               return parse(inputStream, "UTF-8", baseUri);
-       }
-
-       @Test
-       public void moviesAreLocated() throws IOException {
-               assertThat(movies, containsInAnyOrder(
-                               movie("All of Us Strangers", "https://cdn.premiumkino.de/movie/2809/db0e5c3ed02ba7fe4afa3b12aabb611f.jpg",
-                                               allOf(startsWith("Sich neu verlieben, durch eine Zeitschleife"), endsWith("Quelle: disney-content.de")),
-                                               performance(of(2024, 2, 9, 16, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240209/1615/HkKdhlHMvtfSMy1fqYYtYuVdgGIKtnT7i7ddY5jzRfY~"),
-                                               performance(of(2024, 2, 10, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240210/2215/znWqm8FQUNSbrODY_A0jw8Au2nW6uSqbFE7Co8UgQv0~"),
-                                               performance(of(2024, 2, 11, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240211/2015/I642oTHBKpy7sz2RULHIQK6cykSCPi57_c0TApiKbUk~"),
-                                               performance(of(2024, 2, 12, 17, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240212/1715/2lEiqVP1hueXZhWwy9FSUfRgFKf8iTCKRMdJOTfBf8A~"),
-                                               performance(of(2024, 2, 13, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240213/2030/M0mIWdKm5gZkan5na6zOVFBzsHWAHhqod8RCmG2Fr0Y~"),
-                                               performance(of(2024, 2, 14, 14, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240214/1430/VZhgxh4PZutWWlLPEAe9dfOUJkB3sLnXzWoduICJYTk~")
-                               ),
-                               movie("Dune", "https://cdn.premiumkino.de/movie/3261/bcc70cb1cc4559731b6fd547d0f5cee1.jpg",
-                                               allOf(startsWith("&quot;Dune&quot; erzählt die packende Geschichte"), endsWith("Quelle: mediapass.warnerbros.com")),
-                                               performance(of(2024, 2, 9, 19, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240209/1900/o9jDuan4yyxaW7-Jg3hCJpiAM4CLZei8J2IX-O5-hA0~"),
-                                               performance(of(2024, 2, 10, 15, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240210/1530/jXbZnYD8R5djVnj3Ojjcsc9qdSJ0JBMVhn7PeP88HyY~"),
-                                               performance(of(2024, 2, 11, 13, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240211/1330/aJmlM8wOaGl_jtuWDvWG9TF7RR1zRpNrSZeArCBhn90~"),
-                                               performance(of(2024, 2, 12, 20, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240212/2000/2KLOCZy5zU060zKj-4zafxm3oalEyT4tNi2Fxnw5D7E~"),
-                                               performance(of(2024, 2, 13, 17, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240213/1700/QCphOtH-WrZ2tRbENwaxXPskrN2gH3I8dHR0Y5L3l0Y~")
-                               ),
-                               movie("SAVOY Sneak-Preview", "https://cdn.premiumkino.de/movie/5617/33dc4c74b4cca5cf4585b4cadfc38ec4.png",
-                                               allOf(startsWith("Wir zeigen unsere OV-Sneak freitagabends"), endsWith("Logenpreis 1,- Euro Ermäßigung. ")),
-                                               performance(of(2024, 2, 9, 22, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/sneak-preview/20240209/2230/GtYBcOR_Jy7a8xDxwwPHI0wfY_v_Ep2P6rV0w4wJ7SM~"),
-                                               performance(of(2024, 2, 16, 22, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/sneak-preview/20240216/2200/ZRC7iir9Hu8nIpH1PsiA_UDvckcj7yGqgMEYXHAs9Qw~")
-                               ),
-                               movie("Der Junge und der Reiher", "https://cdn.premiumkino.de/movie/4013/b129f329bc07315ccd9591fd2cde19b4.jpg",
-                                               performance(of(2024, 2, 10, 12, 30), "2D OmeU", "https://savoy.premiumkino.de/vorstellung/der-junge-und-der-reiher/20240210/1230/F0atZERI4Gssj3LGC-2fQlLF3rM9Uk8IbpOzHaXyx7w~")
-                               ),
-                               movie("Poor Things", "https://cdn.premiumkino.de/movie/1066/aba09af737677ff6a15676ae588098b1.jpg",
-                                               performance(of(2024, 2, 10, 19, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240210/1900/SqolavKZAlBMZH_JAN-OqdZqowBv-aRqhPGHvPTphao~"),
-                                               performance(of(2024, 2, 11, 17, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240211/1700/UrS33k-jY7_ZAsm4B7wydy0SBfdVjdd73On68HtIy9E~"),
-                                               performance(of(2024, 2, 13, 13, 45), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240213/1345/gma7KCzuaJQCSoBm8NMJb3ATjtMISm4M9IiwiVNpXpw~"),
-                                               performance(of(2024, 2, 14, 17, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240214/1715/cBjQj4qRrJaSX3m_acvfxNL1Tclg0Rrt_X63Md0rkEw~")
-                               ),
-                               movie("Vergiss mein nicht- Eternal Sunshine of the Spotless Mind", "https://cdn.premiumkino.de/movie/2641/963c11bfaccb7c66da0b6b25ea8ed299.jpg",
-                                               performance(of(2024, 2, 14, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/vergiss-mein-nicht-eternal-sunshine-of-the-spotless-mind/20240214/2030/QqoCtd4ls09AD04rGSNh8qPkf02rcRzp2wFfF2BoobE~")
-                               ),
-                               movie("Bob Marley: One Love", "https://cdn.premiumkino.de/movie/1243/772641b8454ce4ea28c5eb56ec74365f.jpg",
-                                               performance(of(2024, 2, 15, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/bob-marley-one-love/20240215/2015/q4dxyPDNVvZDEeIuh1r9Y4OJ7UK36CKSr2d7p3h9LMs~")
-                               ),
-                               movie("The Hateful 8", "https://cdn.premiumkino.de/movie/6651/164fd22df165c914f118f85ff0bd34aa.jpg",
-                                               performance(of(2024, 2, 18, 19, 45), "70mm OV", "https://savoy.premiumkino.de/vorstellung/the-hateful-8/20240218/1945/dusKDbTYgF79OipGmQO0Q4P5K02rclkjIV9At8qKfOA~")
-                               ),
-                               movie("Prinzessin Mononoke", "https://cdn.premiumkino.de/movie/6085/062bbdbbda9f86b7e066486412532369.jpg",
-                                               performance(of(2024, 2, 19, 20, 15), "2D OmeU", "https://savoy.premiumkino.de/vorstellung/prinzessin-mononoke/20240219/2015/LmbLtOrTZxBgPlBMzoRZ4_C-PfO64GeXNIKr5KGs-2M~"),
-                                               performance(of(2024, 2, 22, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/prinzessin-mononoke/20240222/2015/mvbGK2fA5AgPbADOzm7sqFYYzm2qdqqdwqdLu4rMPrg~")
-                               ),
-                               movie("Das fünfte Element (Best of Cinema)", "https://cdn.premiumkino.de/movie/2861/69770d12a16f13df00b70cb830b9d75b.jpg",
-                                               performance(of(2024, 2, 20, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/das-fuenfte-element-best-of-cinema/20240220/2030/QcJZ5Yh8P2j_HKLSP8cjcXsqkbdqYdYGTexCF3RF2R4~")
-                               ),
-                               movie("Heaven Can Wait - wir leben jetzt", "https://cdn.premiumkino.de/movie/3191/b9698912f3fdec6bd72fe66be9cab6f0.jpg",
-                                               performance(of(2024, 2, 21, 11, 0), "2D", "https://savoy.premiumkino.de/vorstellung/heaven-can-wait-wir-leben-jetzt/20240221/1100/GW7pwEYabwN3-e8uTGmSPTfdbyXoKe4la-B2L1SI8XI~")
-                               ),
-                               movie("Demon Slayer: Kimetsu no yaiba - Zum Training der Säulen", "https://cdn.premiumkino.de/movie/3478/96e87d7ec36c9c2b2d4ff2cc933c9401.jpg",
-                                               performance(of(2024, 2, 27, 20, 30), "2D OmeU", "https://savoy.premiumkino.de/vorstellung/demon-slayer-kimetsu-no-yaiba-zum-training-der-saeulen/20240227/2030/UsAsz9NQPZGfdyLelrAHZ50MXeoElhNIPcgg3unBR0o~")
-                               ),
-                               movie("Double Feature DUNE 1&2", "https://cdn.premiumkino.de/movie/7300/14d1b21dee51a82a7b096ca282bf01c8.png",
-                                               performance(of(2024, 2, 28, 17, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/double-feature-dune-1und2/20240228/1730/Ni6Yo0fA4k7gEZiJB8Cnq33Axjnw85JBG2lBIVRwYok~"),
-                                               performance(of(2024, 3, 3, 10, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/double-feature-dune-1und2/20240303/1000/AXcH4nnYJB9Ar0aR5zFl3Bfa238POna3UVY66_nHL6c~")
-                               ),
-                               movie("Dune: Part Two", "https://cdn.premiumkino.de/movie/4669/f0a036d58d75302739ea5680ab8405bc.jpg",
-                                               performance(of(2024, 2, 29, 12, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240229/1230/Osq78ODtBKchFFx1HgAmEPAAIWLC6JgxWqC638UaRws~"),
-                                               performance(of(2024, 2, 29, 16, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240229/1615/hJ3kXaFB-LjfZu2pYBVx0R_2UoqOJANwWX_AS4KW6FI~"),
-                                               performance(of(2024, 2, 29, 20, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240229/2000/EwA7Z5KlKNnIr0e4o_tXGpYp6xN90juBl92wcTVIZ-Q~"),
-                                               performance(of(2024, 3, 1, 13, 45), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240301/1345/-kxmskcNJYhWCr9z5MfK0p2w2qlu3CDu8rVtDLmxSQA~"),
-                                               performance(of(2024, 3, 1, 17, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240301/1730/yXQQSsMug0nRQGN1BNpBXEA3Udf_asggPHwk4a4zdBE~"),
-                                               performance(of(2024, 3, 1, 21, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240301/2115/FN7huOUR4eLA-2EKxi9lTykJUFQNYc4LTHRHtyDlAgo~"),
-                                               performance(of(2024, 3, 2, 10, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/1000/84ntwEOFY6aqexIEd9tXESW6qrl9aXk-x2l-LsHWvfk~"),
-                                               performance(of(2024, 3, 2, 13, 45), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/1345/9lxXFXQU6FHmyv1WpKX5ETpd24daZT16hNvXUJEUwik~"),
-                                               performance(of(2024, 3, 2, 17, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/1730/ynmy2dsbgYygz2hPUS_vx-9YL13m2KZ0-9tw37XuBMQ~"),
-                                               performance(of(2024, 3, 2, 21, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/2115/rH8i8BAFaaDjfNJBbZjj_7CV2dzOWcEOzyL618KYQVo~"),
-                                               performance(of(2024, 3, 3, 16, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240303/1630/fU4VOzrp34B48KdsB1vpo2pi18sWSo2Tkknk03sO4HY~"),
-                                               performance(of(2024, 3, 3, 20, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240303/2015/b1yBhvNosqm0nbWju1aCfb7LRBhRnMo_Y_h0VLZwmr0~"),
-                                               performance(of(2024, 3, 4, 12, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240304/1230/DcGmb0KshRY0qv55Tuo4ydiJswd1H9wonrYGF1zGqQ0~"),
-                                               performance(of(2024, 3, 4, 16, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240304/1615/BEd4wzePEH_Sxxuum4cXpsoOubN8fA6oecrWpwGJpcc~"),
-                                               performance(of(2024, 3, 4, 20, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240304/2000/7Qldfc-0lDNALUw_lzuzM3KfarQWQAUi91qz2cMrNDk~"),
-                                               performance(of(2024, 3, 5, 12, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240305/1230/QiP6Vvb9htMnFyTSUZRte1b_Ykw_n_yzaGpErBLFjOE~"),
-                                               performance(of(2024, 3, 5, 16, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240305/1615/eY9ufKz1UM777apocN37PLvlsWwSJXc0xu7LMHecCbk~"),
-                                               performance(of(2024, 3, 5, 20, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240305/2000/2gqVyU2_3NXv_fZiiAxV8Utom82nPW52kzVxzUDrMkI~"),
-                                               performance(of(2024, 3, 6, 16, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240306/1600/Y8x2dIEq1QbEpZf7Td-6-3xqg-d4iDQU9p1fIjxJB0M~"),
-                                               performance(of(2024, 3, 6, 19, 45), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240306/1945/mSiOQk7DuM02IdmaVIvsv5nechCpRkWZwsK37hoXTyg~")
-                               ),
-                               movie("791 KM", "https://cdn.premiumkino.de/movie/2753/9585e7bd814dd2bef5d45427df2f2e92.jpg",
-                                               performance(of(2024, 3, 6, 11, 0), "2D", "https://savoy.premiumkino.de/vorstellung/791-km/20240306/1100/Mcvtz3tawhaIuyCtLtBkHwt6EI8b7oGXwh5OV4YWwNQ~")
-                               ),
-                               movie("Following", "https://cdn.premiumkino.de/movie/3972/b2e687cfb6dffeb4f64acde41e7d286a.jpg",
-                                               performance(of(2024, 3, 9, 12, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/following/20240309/1200/-Aetrz3gC8l4rgVfGc98XXU7B87mTA4RCbWCgZuXHzc~")
-                               ),
-                               movie("Donnie Darko (Best of Cinema)", "https://cdn.premiumkino.de/movie/5245/05adfa102a8fb61f47727224369af706.jpg",
-                                               performance(of(2024, 3, 16, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/donnie-darko-best-of-cinema/20240316/2215/ZIBOwCyKuZ7A6OyYZWQ1AqF9O487dY_LAMmURYrGqUs~")
-                               ),
-                               movie("NT: Vanya", "https://cdn.premiumkino.de/movie/7449/cb5f27438bbffa247ca4081690f0ded9.jpg",
-                                               performance(of(2024, 3, 18, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/nt-vanya/20240318/2030/X9L2eAn_XElOtZRg7u4eiQ6kUCqcuJhrwQ5T76OAt2s~"),
-                                               performance(of(2024, 3, 23, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/nt-vanya/20240323/2215/xXgXAH_I7xX3GsQ4eA4rZsOOrJqAtK0hCCXexFMj0Fk~"),
-                                               performance(of(2024, 4, 8, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/nt-vanya/20240408/2030/PDQ-R_ltFWo8QAn-BbloxETvgegcTyaQyCZrG_MwQfo~")
-                               ),
-                               movie("No Country For Old Men", "https://cdn.premiumkino.de/movie/1290/dcab4000b5b95ebcc4108906d9b4e0a1.jpg",
-                                               performance(of(2024, 3, 26, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/no-country-for-old-men/20240326/2015/jtZD6DfmChngY1mDGWlaaV8kZdUPL_QT2Fh-t0clBpU~")
-                               ),
-                               movie("Lisa Achatzi: Vom Traum zum Trauma - und zurück", "https://cdn.premiumkino.de/movie/7729/373dec2c25c06034c5d95e2d205a603c.jpg",
-                                               performance(of(2024, 4, 14, 12, 0), "2D", "https://savoy.premiumkino.de/vorstellung/lisa-achatzi-vom-traum-zum-trauma-und-zurueck/20240414/1200/MOLgymd988D7m8ZOvhM75aDc5suv3VO9aHobbLsaOgQ~")
-                               ),
-                               movie("Movie Quiz: Test Your Movie Knowledge", "https://cdn.premiumkino.de/movie/1702/ebfecdd4c3cacb6435ba2bffb84a6902.jpg",
-                                               performance(of(2024, 4, 15, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/movie-quiz-test-your-movie-knowledge/20240415/2015/DMAaL86yGoobZhPTDdU2ksO9RUTFoPCiQKGOY7TO6tg~")
-                               )
-               ));
-       }
-
-       @SafeVarargs
-       private Matcher<Movie> movie(String name, String imageUrl, Triple<LocalDateTime, String, String>... presentationTimesTypesAndLinks) {
-               return movie(name, imageUrl, anything(), presentationTimesTypesAndLinks);
-       }
-
-       @SafeVarargs
-       private Matcher<Movie> movie(String name, String imageUrl, Matcher<? super String> descriptionMatcher, Triple<LocalDateTime, String, String>... presentationTimesTypesAndLinks) {
-               return new TypeSafeDiagnosingMatcher<Movie>() {
-                       @Override
-                       protected boolean matchesSafely(Movie movie, Description mismatchDescription) {
-                               if (!movie.name.equals(name)) {
-                                       mismatchDescription.appendText("movie is named ").appendValue(movie.name);
-                                       return false;
-                               }
-                               if (!movie.imageUrl.equals(imageUrl)) {
-                                       mismatchDescription.appendText("image URL is ").appendValue(movie.imageUrl);
-                                       return false;
-                               }
-                               if (!descriptionMatcher.matches(movie.description)) {
-                                       descriptionMatcher.describeMismatch(movie.description, mismatchDescription);
-                                       return false;
-                               }
-                               List<Performance> performances = new ArrayList<>(movie.performances);
-                               if (performances.size() != presentationTimesTypesAndLinks.length) {
-                                       mismatchDescription.appendText("has ").appendValue(performances.size()).appendText(" presentations");
-                                       return false;
-                               }
-                               for (Triple<LocalDateTime, String, String> presentationTimeTypeAndLink : presentationTimesTypesAndLinks) {
-                                       Optional<Performance> foundLink = empty();
-                                       for (Performance performance : performances) {
-                                               if (performance.getTime().equals(presentationTimeTypeAndLink.getFirst()) && performance.getType().equals(presentationTimeTypeAndLink.getSecond()) && performance.getLink().equals(presentationTimeTypeAndLink.getThird())) {
-                                                       foundLink = Optional.of(performance);
-                                                       break;
-                                               }
-                                       }
-                                       if (!foundLink.isPresent()) {
-                                               mismatchDescription.appendValue("has no presentation at ").appendValue(presentationTimeTypeAndLink.getFirst());
-                                               return false;
-                                       }
-                                       performances.remove(foundLink.get());
-                               }
-                               if (!performances.isEmpty()) {
-                                       mismatchDescription.appendText("has no presentations at ").appendValueList("", ", ", "", performances);
-                                       return false;
-                               }
-                               return true;
-                       }
-
-                       @Override
-                       public void describeTo(Description description) {
-                               description.appendText("movie with name ").appendValue(name);
-                               description.appendText(", description ").appendDescriptionOf(descriptionMatcher);
-                               description.appendText(" and ").appendValue(presentationTimesTypesAndLinks.length).appendText(" presentations");
-                       }
-               };
-       }
-
-       @Nonnull
-       private static Triple<LocalDateTime, String, String> performance(@Nonnull LocalDateTime dateTime, @Nonnull String type, @Nonnull String link) {
-               return new Triple<>(dateTime, type, link);
-       }
-
-}
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.kt
new file mode 100644 (file)
index 0000000..5fdfcb2
--- /dev/null
@@ -0,0 +1,209 @@
+package net.pterodactylus.rhynodge.filters.webpages.savoy
+
+import org.hamcrest.Description
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers
+import org.hamcrest.Matchers.allOf
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.hamcrest.Matchers.endsWith
+import org.hamcrest.Matchers.startsWith
+import org.hamcrest.TypeSafeDiagnosingMatcher
+import org.jsoup.Jsoup
+import org.junit.Test
+import java.io.IOException
+import java.time.LocalDateTime
+import java.util.Optional
+
+/**
+ * Unit test for [MovieExtractor].
+ */
+class MovieExtractorTest {
+
+       @Test
+       fun moviesAreLocated() {
+               assertThat(
+                       movies, containsInAnyOrder(
+                               movie(
+                                       "All of Us Strangers", "https://cdn.premiumkino.de/movie/2809/db0e5c3ed02ba7fe4afa3b12aabb611f.jpg",
+                                       allOf(startsWith("Sich neu verlieben, durch eine Zeitschleife"), endsWith("Quelle: disney-content.de")),
+                                       performance(LocalDateTime.of(2024, 2, 9, 16, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240209/1615/HkKdhlHMvtfSMy1fqYYtYuVdgGIKtnT7i7ddY5jzRfY~"),
+                                       performance(LocalDateTime.of(2024, 2, 10, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240210/2215/znWqm8FQUNSbrODY_A0jw8Au2nW6uSqbFE7Co8UgQv0~"),
+                                       performance(LocalDateTime.of(2024, 2, 11, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240211/2015/I642oTHBKpy7sz2RULHIQK6cykSCPi57_c0TApiKbUk~"),
+                                       performance(LocalDateTime.of(2024, 2, 12, 17, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240212/1715/2lEiqVP1hueXZhWwy9FSUfRgFKf8iTCKRMdJOTfBf8A~"),
+                                       performance(LocalDateTime.of(2024, 2, 13, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240213/2030/M0mIWdKm5gZkan5na6zOVFBzsHWAHhqod8RCmG2Fr0Y~"),
+                                       performance(LocalDateTime.of(2024, 2, 14, 14, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240214/1430/VZhgxh4PZutWWlLPEAe9dfOUJkB3sLnXzWoduICJYTk~")
+                               ),
+                               movie(
+                                       "Dune", "https://cdn.premiumkino.de/movie/3261/bcc70cb1cc4559731b6fd547d0f5cee1.jpg",
+                                       allOf(startsWith("&quot;Dune&quot; erzählt die packende Geschichte"), endsWith("Quelle: mediapass.warnerbros.com")),
+                                       performance(LocalDateTime.of(2024, 2, 9, 19, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240209/1900/o9jDuan4yyxaW7-Jg3hCJpiAM4CLZei8J2IX-O5-hA0~"),
+                                       performance(LocalDateTime.of(2024, 2, 10, 15, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240210/1530/jXbZnYD8R5djVnj3Ojjcsc9qdSJ0JBMVhn7PeP88HyY~"),
+                                       performance(LocalDateTime.of(2024, 2, 11, 13, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240211/1330/aJmlM8wOaGl_jtuWDvWG9TF7RR1zRpNrSZeArCBhn90~"),
+                                       performance(LocalDateTime.of(2024, 2, 12, 20, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240212/2000/2KLOCZy5zU060zKj-4zafxm3oalEyT4tNi2Fxnw5D7E~"),
+                                       performance(LocalDateTime.of(2024, 2, 13, 17, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240213/1700/QCphOtH-WrZ2tRbENwaxXPskrN2gH3I8dHR0Y5L3l0Y~")
+                               ),
+                               movie(
+                                       "SAVOY Sneak-Preview", "https://cdn.premiumkino.de/movie/5617/33dc4c74b4cca5cf4585b4cadfc38ec4.png",
+                                       allOf(startsWith("Wir zeigen unsere OV-Sneak freitagabends"), endsWith("Logenpreis 1,- Euro Ermäßigung. ")),
+                                       performance(LocalDateTime.of(2024, 2, 9, 22, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/sneak-preview/20240209/2230/GtYBcOR_Jy7a8xDxwwPHI0wfY_v_Ep2P6rV0w4wJ7SM~"),
+                                       performance(LocalDateTime.of(2024, 2, 16, 22, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/sneak-preview/20240216/2200/ZRC7iir9Hu8nIpH1PsiA_UDvckcj7yGqgMEYXHAs9Qw~")
+                               ),
+                               movie(
+                                       "Der Junge und der Reiher", "https://cdn.premiumkino.de/movie/4013/b129f329bc07315ccd9591fd2cde19b4.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 10, 12, 30), "2D OmeU", "https://savoy.premiumkino.de/vorstellung/der-junge-und-der-reiher/20240210/1230/F0atZERI4Gssj3LGC-2fQlLF3rM9Uk8IbpOzHaXyx7w~")
+                               ),
+                               movie(
+                                       "Poor Things", "https://cdn.premiumkino.de/movie/1066/aba09af737677ff6a15676ae588098b1.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 10, 19, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240210/1900/SqolavKZAlBMZH_JAN-OqdZqowBv-aRqhPGHvPTphao~"),
+                                       performance(LocalDateTime.of(2024, 2, 11, 17, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240211/1700/UrS33k-jY7_ZAsm4B7wydy0SBfdVjdd73On68HtIy9E~"),
+                                       performance(LocalDateTime.of(2024, 2, 13, 13, 45), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240213/1345/gma7KCzuaJQCSoBm8NMJb3ATjtMISm4M9IiwiVNpXpw~"),
+                                       performance(LocalDateTime.of(2024, 2, 14, 17, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/poor-things/20240214/1715/cBjQj4qRrJaSX3m_acvfxNL1Tclg0Rrt_X63Md0rkEw~")
+                               ),
+                               movie(
+                                       "Vergiss mein nicht- Eternal Sunshine of the Spotless Mind", "https://cdn.premiumkino.de/movie/2641/963c11bfaccb7c66da0b6b25ea8ed299.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 14, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/vergiss-mein-nicht-eternal-sunshine-of-the-spotless-mind/20240214/2030/QqoCtd4ls09AD04rGSNh8qPkf02rcRzp2wFfF2BoobE~")
+                               ),
+                               movie(
+                                       "Bob Marley: One Love", "https://cdn.premiumkino.de/movie/1243/772641b8454ce4ea28c5eb56ec74365f.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 15, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/bob-marley-one-love/20240215/2015/q4dxyPDNVvZDEeIuh1r9Y4OJ7UK36CKSr2d7p3h9LMs~")
+                               ),
+                               movie(
+                                       "The Hateful 8", "https://cdn.premiumkino.de/movie/6651/164fd22df165c914f118f85ff0bd34aa.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 18, 19, 45), "70mm OV", "https://savoy.premiumkino.de/vorstellung/the-hateful-8/20240218/1945/dusKDbTYgF79OipGmQO0Q4P5K02rclkjIV9At8qKfOA~")
+                               ),
+                               movie(
+                                       "Prinzessin Mononoke", "https://cdn.premiumkino.de/movie/6085/062bbdbbda9f86b7e066486412532369.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 19, 20, 15), "2D OmeU", "https://savoy.premiumkino.de/vorstellung/prinzessin-mononoke/20240219/2015/LmbLtOrTZxBgPlBMzoRZ4_C-PfO64GeXNIKr5KGs-2M~"),
+                                       performance(LocalDateTime.of(2024, 2, 22, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/prinzessin-mononoke/20240222/2015/mvbGK2fA5AgPbADOzm7sqFYYzm2qdqqdwqdLu4rMPrg~")
+                               ),
+                               movie(
+                                       "Das fünfte Element (Best of Cinema)", "https://cdn.premiumkino.de/movie/2861/69770d12a16f13df00b70cb830b9d75b.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 20, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/das-fuenfte-element-best-of-cinema/20240220/2030/QcJZ5Yh8P2j_HKLSP8cjcXsqkbdqYdYGTexCF3RF2R4~")
+                               ),
+                               movie(
+                                       "Heaven Can Wait - wir leben jetzt", "https://cdn.premiumkino.de/movie/3191/b9698912f3fdec6bd72fe66be9cab6f0.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 21, 11, 0), "2D", "https://savoy.premiumkino.de/vorstellung/heaven-can-wait-wir-leben-jetzt/20240221/1100/GW7pwEYabwN3-e8uTGmSPTfdbyXoKe4la-B2L1SI8XI~")
+                               ),
+                               movie(
+                                       "Demon Slayer: Kimetsu no yaiba - Zum Training der Säulen", "https://cdn.premiumkino.de/movie/3478/96e87d7ec36c9c2b2d4ff2cc933c9401.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 27, 20, 30), "2D OmeU", "https://savoy.premiumkino.de/vorstellung/demon-slayer-kimetsu-no-yaiba-zum-training-der-saeulen/20240227/2030/UsAsz9NQPZGfdyLelrAHZ50MXeoElhNIPcgg3unBR0o~")
+                               ),
+                               movie(
+                                       "Double Feature DUNE 1&2", "https://cdn.premiumkino.de/movie/7300/14d1b21dee51a82a7b096ca282bf01c8.png",
+                                       performance(LocalDateTime.of(2024, 2, 28, 17, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/double-feature-dune-1und2/20240228/1730/Ni6Yo0fA4k7gEZiJB8Cnq33Axjnw85JBG2lBIVRwYok~"),
+                                       performance(LocalDateTime.of(2024, 3, 3, 10, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/double-feature-dune-1und2/20240303/1000/AXcH4nnYJB9Ar0aR5zFl3Bfa238POna3UVY66_nHL6c~")
+                               ),
+                               movie(
+                                       "Dune: Part Two", "https://cdn.premiumkino.de/movie/4669/f0a036d58d75302739ea5680ab8405bc.jpg",
+                                       performance(LocalDateTime.of(2024, 2, 29, 12, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240229/1230/Osq78ODtBKchFFx1HgAmEPAAIWLC6JgxWqC638UaRws~"),
+                                       performance(LocalDateTime.of(2024, 2, 29, 16, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240229/1615/hJ3kXaFB-LjfZu2pYBVx0R_2UoqOJANwWX_AS4KW6FI~"),
+                                       performance(LocalDateTime.of(2024, 2, 29, 20, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240229/2000/EwA7Z5KlKNnIr0e4o_tXGpYp6xN90juBl92wcTVIZ-Q~"),
+                                       performance(LocalDateTime.of(2024, 3, 1, 13, 45), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240301/1345/-kxmskcNJYhWCr9z5MfK0p2w2qlu3CDu8rVtDLmxSQA~"),
+                                       performance(LocalDateTime.of(2024, 3, 1, 17, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240301/1730/yXQQSsMug0nRQGN1BNpBXEA3Udf_asggPHwk4a4zdBE~"),
+                                       performance(LocalDateTime.of(2024, 3, 1, 21, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240301/2115/FN7huOUR4eLA-2EKxi9lTykJUFQNYc4LTHRHtyDlAgo~"),
+                                       performance(LocalDateTime.of(2024, 3, 2, 10, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/1000/84ntwEOFY6aqexIEd9tXESW6qrl9aXk-x2l-LsHWvfk~"),
+                                       performance(LocalDateTime.of(2024, 3, 2, 13, 45), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/1345/9lxXFXQU6FHmyv1WpKX5ETpd24daZT16hNvXUJEUwik~"),
+                                       performance(LocalDateTime.of(2024, 3, 2, 17, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/1730/ynmy2dsbgYygz2hPUS_vx-9YL13m2KZ0-9tw37XuBMQ~"),
+                                       performance(LocalDateTime.of(2024, 3, 2, 21, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240302/2115/rH8i8BAFaaDjfNJBbZjj_7CV2dzOWcEOzyL618KYQVo~"),
+                                       performance(LocalDateTime.of(2024, 3, 3, 16, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240303/1630/fU4VOzrp34B48KdsB1vpo2pi18sWSo2Tkknk03sO4HY~"),
+                                       performance(LocalDateTime.of(2024, 3, 3, 20, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240303/2015/b1yBhvNosqm0nbWju1aCfb7LRBhRnMo_Y_h0VLZwmr0~"),
+                                       performance(LocalDateTime.of(2024, 3, 4, 12, 30), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240304/1230/DcGmb0KshRY0qv55Tuo4ydiJswd1H9wonrYGF1zGqQ0~"),
+                                       performance(LocalDateTime.of(2024, 3, 4, 16, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240304/1615/BEd4wzePEH_Sxxuum4cXpsoOubN8fA6oecrWpwGJpcc~"),
+                                       performance(LocalDateTime.of(2024, 3, 4, 20, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240304/2000/7Qldfc-0lDNALUw_lzuzM3KfarQWQAUi91qz2cMrNDk~"),
+                                       performance(LocalDateTime.of(2024, 3, 5, 12, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240305/1230/QiP6Vvb9htMnFyTSUZRte1b_Ykw_n_yzaGpErBLFjOE~"),
+                                       performance(LocalDateTime.of(2024, 3, 5, 16, 15), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240305/1615/eY9ufKz1UM777apocN37PLvlsWwSJXc0xu7LMHecCbk~"),
+                                       performance(LocalDateTime.of(2024, 3, 5, 20, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240305/2000/2gqVyU2_3NXv_fZiiAxV8Utom82nPW52kzVxzUDrMkI~"),
+                                       performance(LocalDateTime.of(2024, 3, 6, 16, 0), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240306/1600/Y8x2dIEq1QbEpZf7Td-6-3xqg-d4iDQU9p1fIjxJB0M~"),
+                                       performance(LocalDateTime.of(2024, 3, 6, 19, 45), "70mm OV", "https://savoy.premiumkino.de/vorstellung/dune-part-two/20240306/1945/mSiOQk7DuM02IdmaVIvsv5nechCpRkWZwsK37hoXTyg~")
+                               ),
+                               movie(
+                                       "791 KM", "https://cdn.premiumkino.de/movie/2753/9585e7bd814dd2bef5d45427df2f2e92.jpg",
+                                       performance(LocalDateTime.of(2024, 3, 6, 11, 0), "2D", "https://savoy.premiumkino.de/vorstellung/791-km/20240306/1100/Mcvtz3tawhaIuyCtLtBkHwt6EI8b7oGXwh5OV4YWwNQ~")
+                               ),
+                               movie(
+                                       "Following", "https://cdn.premiumkino.de/movie/3972/b2e687cfb6dffeb4f64acde41e7d286a.jpg",
+                                       performance(LocalDateTime.of(2024, 3, 9, 12, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/following/20240309/1200/-Aetrz3gC8l4rgVfGc98XXU7B87mTA4RCbWCgZuXHzc~")
+                               ),
+                               movie(
+                                       "Donnie Darko (Best of Cinema)", "https://cdn.premiumkino.de/movie/5245/05adfa102a8fb61f47727224369af706.jpg",
+                                       performance(LocalDateTime.of(2024, 3, 16, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/donnie-darko-best-of-cinema/20240316/2215/ZIBOwCyKuZ7A6OyYZWQ1AqF9O487dY_LAMmURYrGqUs~")
+                               ),
+                               movie(
+                                       "NT: Vanya", "https://cdn.premiumkino.de/movie/7449/cb5f27438bbffa247ca4081690f0ded9.jpg",
+                                       performance(LocalDateTime.of(2024, 3, 18, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/nt-vanya/20240318/2030/X9L2eAn_XElOtZRg7u4eiQ6kUCqcuJhrwQ5T76OAt2s~"),
+                                       performance(LocalDateTime.of(2024, 3, 23, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/nt-vanya/20240323/2215/xXgXAH_I7xX3GsQ4eA4rZsOOrJqAtK0hCCXexFMj0Fk~"),
+                                       performance(LocalDateTime.of(2024, 4, 8, 20, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/nt-vanya/20240408/2030/PDQ-R_ltFWo8QAn-BbloxETvgegcTyaQyCZrG_MwQfo~")
+                               ),
+                               movie(
+                                       "No Country For Old Men", "https://cdn.premiumkino.de/movie/1290/dcab4000b5b95ebcc4108906d9b4e0a1.jpg",
+                                       performance(LocalDateTime.of(2024, 3, 26, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/no-country-for-old-men/20240326/2015/jtZD6DfmChngY1mDGWlaaV8kZdUPL_QT2Fh-t0clBpU~")
+                               ),
+                               movie(
+                                       "Lisa Achatzi: Vom Traum zum Trauma - und zurück", "https://cdn.premiumkino.de/movie/7729/373dec2c25c06034c5d95e2d205a603c.jpg",
+                                       performance(LocalDateTime.of(2024, 4, 14, 12, 0), "2D", "https://savoy.premiumkino.de/vorstellung/lisa-achatzi-vom-traum-zum-trauma-und-zurueck/20240414/1200/MOLgymd988D7m8ZOvhM75aDc5suv3VO9aHobbLsaOgQ~")
+                               ),
+                               movie(
+                                       "Movie Quiz: Test Your Movie Knowledge", "https://cdn.premiumkino.de/movie/1702/ebfecdd4c3cacb6435ba2bffb84a6902.jpg",
+                                       performance(LocalDateTime.of(2024, 4, 15, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/movie-quiz-test-your-movie-knowledge/20240415/2015/DMAaL86yGoobZhPTDdU2ksO9RUTFoPCiQKGOY7TO6tg~")
+                               )
+                       )
+               )
+       }
+
+       private val movies: Collection<Movie> = MovieExtractor().getMovies(loadDocument("savoy.html", "https://savoy.premiumkino.de/programmwoche"));
+
+}
+
+private fun movie(name: String, imageUrl: String, vararg presentationTimesTypesAndLinks: Triple<LocalDateTime, String, String>): Matcher<Movie> {
+       return movie(name, imageUrl, Matchers.anything(), *presentationTimesTypesAndLinks)
+}
+
+private fun movie(name: String, imageUrl: String, descriptionMatcher: Matcher<in String>, vararg presentationTimesTypesAndLinks: Triple<LocalDateTime, String, String>): Matcher<Movie> {
+       return object : TypeSafeDiagnosingMatcher<Movie>() {
+               override fun matchesSafely(movie: Movie, mismatchDescription: Description): Boolean {
+                       if (movie.name != name) {
+                               mismatchDescription.appendText("movie is named ").appendValue(movie.name)
+                               return false
+                       }
+                       if (movie.imageUrl != imageUrl) {
+                               mismatchDescription.appendText("image URL is ").appendValue(movie.imageUrl)
+                               return false
+                       }
+                       if (!descriptionMatcher.matches(movie.description)) {
+                               descriptionMatcher.describeMismatch(movie.description, mismatchDescription)
+                               return false
+                       }
+                       val performances = movie.performances.toMutableList()
+                       if (performances.size != presentationTimesTypesAndLinks.size) {
+                               mismatchDescription.appendText("has ").appendValue(performances.size).appendText(" presentations")
+                               return false
+                       }
+                       for ((time, type, link) in presentationTimesTypesAndLinks) {
+                               val foundLink = performances.find {( it.time == time) && (it.type == type) && (it.link == link) }
+                               if (foundLink == null) {
+                                       mismatchDescription.appendValue("has no presentation at ").appendValue(time)
+                                       return false
+                               }
+                               performances -= foundLink
+                       }
+                       if (performances.isNotEmpty()) {
+                               mismatchDescription.appendText("has no presentations at ").appendValueList("", ", ", "", performances)
+                               return false
+                       }
+                       return true
+               }
+
+               override fun describeTo(description: Description) {
+                       description.appendText("movie with name ").appendValue(name)
+                       description.appendText(", description ").appendDescriptionOf(descriptionMatcher)
+                       description.appendText(" and ").appendValue(presentationTimesTypesAndLinks.size).appendText(" presentations")
+               }
+       }
+}
+
+private fun loadDocument(resourceName: String, baseUri: String) =
+       Jsoup.parse(MovieExtractorTest::class.java.getResourceAsStream(resourceName)!!, "UTF-8", baseUri)
+
+private fun performance(dateTime: LocalDateTime, type: String, link: String) = Triple(dateTime, type, link)