e25420d53f64efd00baf062a1645165fe2f1b6f3
[Sone.git] / src / test / java / net / pterodactylus / sone / text / SoneTextParserTest.java
1 /*
2  * Sone - SoneTextParserTest.java - Copyright © 2011–2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.text;
19
20 import static com.google.common.collect.ImmutableList.builder;
21 import static java.util.Arrays.asList;
22 import static java.util.UUID.randomUUID;
23 import static org.hamcrest.MatcherAssert.assertThat;
24 import static org.hamcrest.Matchers.is;
25
26 import java.io.IOException;
27 import java.io.StringReader;
28 import java.util.Collection;
29
30 import net.pterodactylus.sone.data.Mocks;
31 import net.pterodactylus.sone.data.Post;
32 import net.pterodactylus.sone.data.Sone;
33 import net.pterodactylus.sone.data.impl.DefaultSone;
34
35 import com.google.common.collect.ImmutableList;
36 import org.hamcrest.Description;
37 import org.hamcrest.Matcher;
38 import org.hamcrest.TypeSafeMatcher;
39 import org.hamcrest.collection.IsIterableContainingInOrder;
40 import org.junit.Test;
41
42 /**
43  * JUnit test case for {@link SoneTextParser}.
44  *
45  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
46  */
47 public class SoneTextParserTest {
48
49         private final Mocks mocks = new Mocks();
50         private final SoneTextParser soneTextParser = new SoneTextParser(mocks.database);
51
52         private Matcher<Iterable<Part>> matches(final Matcher<? extends Part>... partsToMatch) {
53                 return new TypeSafeMatcher<Iterable<Part>>() {
54
55                         private Matcher<Iterable<? extends Part>> iterableMatcher;
56
57                         @Override
58                         protected boolean matchesSafely(Iterable<Part> parts) {
59                                 iterableMatcher = new IsIterableContainingInOrder(asList(partsToMatch));
60                                 return iterableMatcher.matches(collapseParts(expandParts(parts)));
61                         }
62
63                         private Iterable<Part> expandParts(Iterable<? extends Part> parts) {
64                                 PartContainer partContainer = new PartContainer();
65                                 for (Part part : parts) {
66                                         partContainer.add(part);
67                                 }
68                                 return partContainer;
69                         }
70
71                         private Collection<Part> collapseParts(Iterable<? extends Part> parts) {
72                                 ImmutableList.Builder<Part> collapsedPartsBuilder = builder();
73                                 PlainTextPart lastPlainTextPart = null;
74                                 for (Part part : parts) {
75                                         if (part instanceof PlainTextPart) {
76                                                 if (lastPlainTextPart != null) {
77                                                         lastPlainTextPart = new PlainTextPart(lastPlainTextPart.getText() + ((PlainTextPart) part).getText());
78                                                 } else {
79                                                         lastPlainTextPart = (PlainTextPart) part;
80                                                 }
81                                         } else {
82                                                 if (lastPlainTextPart != null) {
83                                                         collapsedPartsBuilder.add(lastPlainTextPart);
84                                                         lastPlainTextPart = null;
85                                                 }
86                                                 collapsedPartsBuilder.add(part);
87                                         }
88                                 }
89                                 if (lastPlainTextPart != null) {
90                                         collapsedPartsBuilder.add(lastPlainTextPart);
91                                 }
92                                 return collapsedPartsBuilder.build();
93                         }
94
95                         @Override
96                         protected void describeMismatchSafely(Iterable<Part> parts, Description mismatchDescription) {
97                                 iterableMatcher.describeMismatch(collapseParts(parts), mismatchDescription);
98                         }
99
100                         @Override
101                         public void describeTo(Description description) {
102                                 iterableMatcher.describeTo(description);
103                         }
104                 };
105         }
106
107         private Iterable<Part> parse(String text) throws IOException {
108                 return soneTextParser.parse(null, new StringReader(text));
109         }
110
111         private Iterable<Part> parse(SoneTextParserContext context, String text) throws IOException {
112                 return soneTextParser.parse(context, new StringReader(text));
113         }
114
115         @Test
116         public void parsePlainText() throws IOException {
117                 assertThat(parse("Test."), matches(is(new PlainTextPart("Test."))));
118         }
119
120         @Test
121         public void parsePlainTextWithEmptyLinesAtTheBeginningAndEnd() throws IOException {
122                 assertThat(parse("\nTest.\n\n"), matches(is(new PlainTextPart("Test."))));
123         }
124
125         @Test
126         public void parsePlainTextAndCollapseMultipleEmptyLines() throws IOException {
127                 assertThat(parse("\nTest.\n\n\nTest."), matches(is(new PlainTextPart("Test.\n\nTest."))));
128         }
129
130         @Test
131         public void parseSimpleKskLinks() throws IOException {
132                 assertThat(parse("KSK@gpl.txt"), matches(is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false))));
133         }
134
135         @Test
136         public void parseEmbeddedLinks() throws IOException {
137                 assertThat(parse("Link is KSK@gpl.txt\u200b."), matches(
138                                 is(new PlainTextPart("Link is ")),
139                                 is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false)),
140                                 is(new PlainTextPart("\u200b."))
141                 ));
142         }
143
144         @Test
145         public void parseEmbeddedLinksAndLineBreaks() throws IOException {
146                 assertThat(parse("Link is KSK@gpl.txt\nKSK@test.dat\n"), matches(
147                                 is(new PlainTextPart("Link is ")),
148                                 is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false)),
149                                 is(new PlainTextPart("\n")),
150                                 is(new FreenetLinkPart("KSK@test.dat", "test.dat", false))
151                 ));
152         }
153
154         @Test
155         public void parseEmptyLinesAndSoneLinks() throws IOException {
156                 Sone sone = mocks.mockSone("DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU").create();
157                 assertThat(parse("Some text.\n\nLink to sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU and stuff."), matches(
158                                 is(new PlainTextPart("Some text.\n\nLink to ")),
159                                 is(new SonePart(sone)),
160                                 is(new PlainTextPart(" and stuff."))
161                 ));
162         }
163
164         @Test
165         public void parseEmptyHttpLinks() throws IOException {
166                 assertThat(parse("Some text. Empty link: http:// – nice!"), matches(
167                                 is(new PlainTextPart("Some text. Empty link: http:// – nice!"))
168                 ));
169         }
170
171         @Test
172         public void parseTrustedSoneLinks() throws IOException {
173                 Sone trustedSone = mocks.mockSone("DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU").create();
174                 assertThat(parse(new SoneTextParserContext(trustedSone), "Get SSK@DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU/file.txt\u200b!"), matches(
175                                 is(new PlainTextPart("Get ")),
176                                 is(new FreenetLinkPart("SSK@DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU/file.txt", "file.txt", true)),
177                                 is(new PlainTextPart("\u200b!"))
178                 ));
179         }
180
181         @Test
182         public void parseHttpLink() throws IOException {
183                 assertThat(parse("http://w3.org/foo.html"), matches(
184                                 is(new LinkPart("http://w3.org/foo.html", "w3.org/foo.html", "w3.org/foo.html"))
185                 ));
186         }
187
188         @Test
189         public void twoNonEmptyLinesAreParsedCorrectly() throws IOException {
190                 assertThat(parse("First line.\nSecond line."), matches(
191                                 is(new PlainTextPart("First line.\nSecond line."))
192                 ));
193         }
194
195         @Test
196         public void malformedChkLinkIsParsedAsText() throws IOException {
197                 assertThat(parse("CHK@key/gpl.txt"), matches(
198                                 is(new PlainTextPart("CHK@key/gpl.txt"))
199                 ));
200         }
201
202         @Test
203         public void malformedUskLinkIsParsedAsText() throws IOException {
204                 assertThat(parse("USK@key/site/"), matches(
205                                 is(new PlainTextPart("USK@key/site/"))
206                 ));
207         }
208
209         @Test
210         public void httpsLinksAreParsedCorrectly() throws IOException {
211                 assertThat(parse("https://site/file.txt"), matches(
212                                 is(new LinkPart("https://site/file.txt", "site/file.txt"))
213                 ));
214         }
215
216         @Test
217         public void postLinksAreParsedCorrectly() throws IOException {
218                 Sone sone = mocks.mockSone("Sone").create();
219                 Post post = mocks.mockPost(sone, randomUUID().toString()).create();
220                 assertThat(parse("post://" + post.getId()), matches(
221                                 is(new PostPart(post))
222                 ));
223         }
224
225         @Test
226         public void linkToNonExistingPostIsParsedAsPlainText() throws IOException {
227                 String postId = randomUUID().toString();
228                 assertThat(parse("post://" + postId), matches(
229                                 is(new PlainTextPart("post://" + postId))
230                 ));
231         }
232
233         @Test
234         public void tooShortPostLinkIsParsedAsPlainText() throws IOException {
235                 assertThat(parse("post://post"), matches(
236                                 is(new PlainTextPart("post://post"))
237                 ));
238         }
239
240         @Test
241         public void freenetPrefixBeforeKeysIsCutOff() throws IOException {
242                 assertThat(parse("freenet:KSK@gpl.txt"), matches(
243                                 is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false))
244                 ));
245         }
246
247         @Test
248         public void linkToNonExistingSoneCreatesLinkToEmptyShell() throws IOException {
249                 assertThat(parse("sone://1234567890123456789012345678901234567890123"), matches(
250                                 is(new SonePart(new DefaultSone(mocks.database, "1234567890123456789012345678901234567890123", false, null)))
251                 ));
252         }
253
254         @Test
255         public void linkToTooShortSoneIdIsParsedAsPlainText() throws IOException {
256                 assertThat(parse("sone://Sone"), matches(
257                                 is(new PlainTextPart("sone://Sone"))
258                 ));
259         }
260
261         @Test
262         public void cutOffQueryFromTextOfFreenetLink() throws IOException {
263                 assertThat(parse("KSK@gpl.txt?max-size=17"), matches(
264                                 is(new FreenetLinkPart("KSK@gpl.txt?max-size=17", "gpl.txt", false))
265                 ));
266         }
267
268         @Test
269         public void linkWithoutMetaInformationShowsShortenedRoutingKey() throws IOException {
270                 assertThat(parse("CHK@DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU"), matches(
271                                 is(new FreenetLinkPart("CHK@DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU", "CHK@DAxKQ", false))
272                 ));
273         }
274
275         @Test
276         public void httpLinkGetsPartOfPathRemoved() throws IOException {
277                 assertThat(parse("http://server.com/path/foo/test.html"), matches(
278                                 is(new LinkPart("http://server.com/path/foo/test.html", "server.com/…/test.html"))
279                 ));
280         }
281
282         @Test
283         public void httpLinkThatEndsInASlashGetsSlashRemoved() throws IOException {
284                 assertThat(parse("http://server.com/path/foo/"), matches(
285                                 is(new LinkPart("http://server.com/path/foo/", "server.com/…"))
286                 ));
287         }
288
289         @Test
290         public void httpLinkGetsWwwRemoved() throws IOException {
291                 assertThat(parse("http://www.server.com/foo.html"), matches(
292                                 is(new LinkPart("http://www.server.com/foo.html", "server.com/foo.html"))
293                 ));
294         }
295
296         @Test
297         public void httpLinkGetsQueryRemoved() throws IOException {
298                 assertThat(parse("http://server.com/foo.html?id=4"), matches(
299                                 is(new LinkPart("http://server.com/foo.html?id=4", "server.com/foo.html"))
300                 ));
301         }
302
303         @Test
304         public void multipleLinksInOneLine() throws IOException {
305                 assertThat(parse("KSK@gpl.txt and http://server.com/"), matches(
306                                 is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false)),
307                                 is(new PlainTextPart(" and ")),
308                                 is(new FreenetLinkPart("http://server.com/", "server.com", false))
309                 ));
310         }
311
312 }