2 * Sone - SoneTextParserTest.java - Copyright © 2011–2013 David Roden
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.
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.
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/>.
18 package net.pterodactylus.sone.text;
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;
26 import java.io.IOException;
27 import java.io.StringReader;
28 import java.util.Collection;
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;
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;
43 * JUnit test case for {@link SoneTextParser}.
45 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
47 public class SoneTextParserTest {
49 private final Mocks mocks = new Mocks();
50 private final SoneTextParser soneTextParser = new SoneTextParser(mocks.database);
52 private Matcher<Iterable<Part>> matches(final Matcher<? extends Part>... partsToMatch) {
53 return new TypeSafeMatcher<Iterable<Part>>() {
55 private Matcher<Iterable<? extends Part>> iterableMatcher;
58 protected boolean matchesSafely(Iterable<Part> parts) {
59 iterableMatcher = new IsIterableContainingInOrder(asList(partsToMatch));
60 return iterableMatcher.matches(collapseParts(expandParts(parts)));
63 private Iterable<Part> expandParts(Iterable<? extends Part> parts) {
64 PartContainer partContainer = new PartContainer();
65 for (Part part : parts) {
66 partContainer.add(part);
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());
79 lastPlainTextPart = (PlainTextPart) part;
82 if (lastPlainTextPart != null) {
83 collapsedPartsBuilder.add(lastPlainTextPart);
84 lastPlainTextPart = null;
86 collapsedPartsBuilder.add(part);
89 if (lastPlainTextPart != null) {
90 collapsedPartsBuilder.add(lastPlainTextPart);
92 return collapsedPartsBuilder.build();
96 protected void describeMismatchSafely(Iterable<Part> parts, Description mismatchDescription) {
97 iterableMatcher.describeMismatch(collapseParts(parts), mismatchDescription);
101 public void describeTo(Description description) {
102 iterableMatcher.describeTo(description);
107 private Iterable<Part> parse(String text) throws IOException {
108 return soneTextParser.parse(null, new StringReader(text));
111 private Iterable<Part> parse(SoneTextParserContext context, String text) throws IOException {
112 return soneTextParser.parse(context, new StringReader(text));
116 public void parsePlainText() throws IOException {
117 assertThat(parse("Test."), matches(is(new PlainTextPart("Test."))));
121 public void parsePlainTextWithEmptyLinesAtTheBeginningAndEnd() throws IOException {
122 assertThat(parse("\nTest.\n\n"), matches(is(new PlainTextPart("Test."))));
126 public void parsePlainTextAndCollapseMultipleEmptyLines() throws IOException {
127 assertThat(parse("\nTest.\n\n\nTest."), matches(is(new PlainTextPart("Test.\n\nTest."))));
131 public void parseSimpleKskLinks() throws IOException {
132 assertThat(parse("KSK@gpl.txt"), matches(is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false))));
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."))
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))
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."))
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!"))
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!"))
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"))
189 public void twoNonEmptyLinesAreParsedCorrectly() throws IOException {
190 assertThat(parse("First line.\nSecond line."), matches(
191 is(new PlainTextPart("First line.\nSecond line."))
196 public void malformedChkLinkIsParsedAsText() throws IOException {
197 assertThat(parse("CHK@key/gpl.txt"), matches(
198 is(new PlainTextPart("CHK@key/gpl.txt"))
203 public void malformedUskLinkIsParsedAsText() throws IOException {
204 assertThat(parse("USK@key/site/"), matches(
205 is(new PlainTextPart("USK@key/site/"))
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"))
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))
226 public void linkToNonExistingPostIsParsedAsPlainText() throws IOException {
227 String postId = randomUUID().toString();
228 assertThat(parse("post://" + postId), matches(
229 is(new PlainTextPart("post://" + postId))
234 public void tooShortPostLinkIsParsedAsPlainText() throws IOException {
235 assertThat(parse("post://post"), matches(
236 is(new PlainTextPart("post://post"))
241 public void freenetPrefixBeforeKeysIsCutOff() throws IOException {
242 assertThat(parse("freenet:KSK@gpl.txt"), matches(
243 is(new FreenetLinkPart("KSK@gpl.txt", "gpl.txt", false))
248 public void linkToNonExistingSoneCreatesLinkToEmptyShell() throws IOException {
249 assertThat(parse("sone://1234567890123456789012345678901234567890123"), matches(
250 is(new SonePart(new DefaultSone(mocks.database, "1234567890123456789012345678901234567890123", false, null)))
255 public void linkToTooShortSoneIdIsParsedAsPlainText() throws IOException {
256 assertThat(parse("sone://Sone"), matches(
257 is(new PlainTextPart("sone://Sone"))
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))
269 public void linkWithoutMetaInformationShowsShortenedRoutingKey() throws IOException {
270 assertThat(parse("CHK@DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU"), matches(
271 is(new FreenetLinkPart("CHK@DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU", "CHK@DAxKQ", false))
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"))
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/…"))
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"))
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"))