/* * Copyright (C) 2011 Laurent Caillette * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.novelang.novella; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.List; import com.google.common.collect.Lists; import org.antlr.runtime.RecognitionException; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import static org.fest.assertions.Assertions.assertThat; import static org.novelang.parser.NodeKind.*; import static org.novelang.parser.antlr.TreeFixture.tree; import org.novelang.ResourcesForTests; import org.novelang.common.Location; import org.novelang.common.Problem; import org.novelang.common.SyntacticTree; import org.novelang.common.filefixture.ResourceInstaller; import org.novelang.logger.Logger; import org.novelang.logger.LoggerFactory; import org.novelang.parser.NodeKind; import org.novelang.parser.SourceUnescape; import org.novelang.parser.antlr.TreeFixture; import org.novelang.testing.junit.MethodSupport; /** * @author Laurent Caillette */ public class NovellaTest { @Test public void loadPartOk() throws IOException { final Novella novella = new Novella( resourceInstaller.copy( ResourcesForTests.Parts.NOVELLA_JUST_SECTIONS ) ) ; final SyntacticTree partTree = novella.getDocumentTree(); Assert.assertNotNull( partTree ) ; final SyntacticTree expected = tree( NOVELLA, tree( _LEVEL, tree( _IMPLICIT_TAG, "Section1novella" ), tree( LEVEL_TITLE, tree( WORD_, "Section1novella" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "p00" ), tree( WORD_, "w001" ) ) ), tree( _LEVEL, tree( _IMPLICIT_TAG, "section1W11" ), tree( LEVEL_TITLE, tree( WORD_, "section1" ), tree( WORD_, "w11" ) ), tree( PARAGRAPH_REGULAR, tree( WORD_, "p10" ), tree( WORD_, "w101" ), tree( WORD_, "w102" ) ) ) ) ; TreeFixture.assertEqualsNoSeparators( expected, partTree ) ; Assert.assertFalse( novella.getProblems().iterator().hasNext() ) ; } @Test public void partWithMissingImagesHasProblem() throws IOException { final File partFile = resourceInstaller.copy( ResourcesForTests.Parts.NOVELLA_MISSING_IMAGES ) ; final Novella novella = new Novella( partFile ) ; novella.relocateResourcePaths( partFile.getParentFile() ) ; Assert.assertTrue( novella.hasProblem() ) ; final List< Problem > problems = Lists.newArrayList( novella.getProblems() ) ; LOGGER.debug( "Got problems: ", problems ) ; Assert.assertEquals( 2, problems.size() ) ; } @Test public void badCharacterCorrectlyShownInProblem() throws IOException { final Novella novella = new Novella( "b\u00A4d" ) ; Assert.assertTrue( novella.hasProblem() ) ; final List< Problem > problems = Lists.newArrayList( novella.getProblems() ) ; LOGGER.debug( "Got problems: ", problems ) ; Assert.assertEquals( "No viable alternative at input '\u00A4' CURRENCY_SIGN [0x00A4]", novella.getProblems().iterator().next().getMessage() ) ; } @Test public void loadPartWithMetadata() throws IOException { final Novella novella = new Novella( resourceInstaller.copy( ResourcesForTests.Parts.NOVELLA_JUST_SECTIONS ) ).makeStandalone() ; final SyntacticTree partTree = novella.getDocumentTree(); Assert.assertNotNull( partTree ) ; final SyntacticTree expected = tree( NOVELLA, tree( _META, tree( _WORD_COUNT, "8" ) ), tree( _LEVEL, tree( _IMPLICIT_IDENTIFIER, "Section1novella" ), tree( _IMPLICIT_TAG, "Section1novella" ), tree( LEVEL_TITLE, tree( WORD_, "Section1novella" ) ), tree( PARAGRAPH_REGULAR, tree( WORD_, "p00" ), tree( WORD_, "w001" ) ) ), tree( _LEVEL, tree( _IMPLICIT_IDENTIFIER, "section1W11" ), tree( _IMPLICIT_TAG, "section1W11" ), tree( LEVEL_TITLE, tree( WORD_, "section1" ), tree( WORD_, "w11" ) ), tree( PARAGRAPH_REGULAR, tree( WORD_, "p10" ), tree( WORD_, "w101" ), tree( WORD_, "w102" ) ) ) ) ; TreeFixture.assertEqualsNoSeparators( expected, partTree ) ; Assert.assertFalse( novella.getProblems().iterator().hasNext() ) ; } /** * Checks that a single Novella file gets rehierarchized. * @throws IOException */ @Test public void loadSimpleStructure() throws IOException { final Novella novella = new Novella( resourceInstaller.copy( ResourcesForTests.Parts.NOVELLA_SIMPLE_STRUCTURE ) ) ; final SyntacticTree partTree = novella.getDocumentTree(); Assert.assertNotNull( partTree ) ; final SyntacticTree expected = tree( NOVELLA, tree( _LEVEL, tree( _IMPLICIT_TAG, "Chapter-0" ), tree( LEVEL_TITLE, tree( WORD_, "Chapter-0" ) ), tree( _LEVEL, tree( _IMPLICIT_TAG, "Section-0-0" ), tree( LEVEL_TITLE, tree( WORD_, "Section-0-0" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "Paragraph-0-0-0" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "Paragraph-0-0-1" ) ) ), tree( _LEVEL, tree( _IMPLICIT_TAG, "Section-0-1" ), tree( LEVEL_TITLE, tree( WORD_, "Section-0-1" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "Paragraph-0-1-0" ) ) ) ), tree( _LEVEL, tree( _IMPLICIT_TAG, "Chapter-1" ), tree( LEVEL_TITLE, tree( WORD_, "Chapter-1" ) ), tree( _LEVEL, tree( _IMPLICIT_TAG, "Section-1-0" ), tree( LEVEL_TITLE, tree( WORD_, "Section-1-0" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "Paragraph-1-0-0" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "Paragraph-1-0-1" ) ) ), tree( _LEVEL, tree( _IMPLICIT_TAG, "Section-1-1" ), tree( LEVEL_TITLE, tree( WORD_, "Section-1-1" ) ), tree( NodeKind.PARAGRAPH_REGULAR, tree( WORD_, "Paragraph-1-1-0" ) ) ) ) ) ; TreeFixture.assertEqualsNoSeparators( expected, partTree ) ; Assert.assertFalse( novella.getProblems().iterator().hasNext() ) ; } @Test public void partWithParsingErrorDoesNotAttemptToCountWords() { final Novella novella = NovellaFixture.createStandaloneNovella( "````" ) ; Assert.assertTrue( novella.hasProblem() ) ; } @Test public void problemWithBadEscapeCodeHasLocation() { final Novella novella = new Novella( "\n" + "..." + SourceUnescape.ESCAPE_START + "unknown-escape-code" + SourceUnescape.ESCAPE_END ) ; Assert.assertTrue( novella.hasProblem() ) ; final Iterator<Problem> problems = novella.getProblems().iterator(); final Problem problem = problems.next() ; Assert.assertFalse( problems.hasNext() ) ; Assert.assertEquals( 2, problem.getLocation().getLine() ) ; Assert.assertEquals( 4, problem.getLocation().getColumn() ) ; } @Test( timeout = TEST_TIMEOUT_MILLISECONDS ) public void problemWithSeparatorsAndEmbeddedLists() { final Novella novella = new Novella( "- y `z`" ) ; final SyntacticTree expected = tree( NOVELLA, tree( PARAGRAPH_REGULAR, tree( _EMBEDDED_LIST_WITH_HYPHEN, tree( _EMBEDDED_LIST_ITEM, tree( WORD_, "y" ), tree( BLOCK_OF_LITERAL_INSIDE_GRAVE_ACCENTS, "z" ) ) ) ) ) ; final SyntacticTree partTree = novella.getDocumentTree() ; TreeFixture.assertEqualsWithSeparators( expected, partTree ) ; } @Test( timeout = TEST_TIMEOUT_MILLISECONDS ) public void problemWithSeparatorsAndTitle() { final Novella novella = new Novella( "== y `z`" ) ; final SyntacticTree expected = tree( NOVELLA, tree( _LEVEL, tree( _IMPLICIT_TAG, "yZ" ), tree( LEVEL_TITLE, tree( WORD_, "y" ), tree( BLOCK_OF_LITERAL_INSIDE_GRAVE_ACCENTS, "z" ) ) ) ) ; final SyntacticTree partTree = novella.getDocumentTree() ; TreeFixture.assertEqualsWithSeparators( expected, partTree ) ; } @Test( timeout = TEST_TIMEOUT_MILLISECONDS ) public void dontCalculateImpossibleIdentifier() { final Novella novella = new Novella( "== ..." ) ; final SyntacticTree expected = tree( NOVELLA, tree( _META, tree( _WORD_COUNT, "0" ) ), tree( _LEVEL, tree( LEVEL_TITLE, tree( PUNCTUATION_SIGN, tree( SIGN_ELLIPSIS, "..." ) ) ) ) ) ; final SyntacticTree partTree = novella.makeStandalone().getDocumentTree() ; TreeFixture.assertEqualsWithSeparators( expected, partTree ) ; } @Test public void dontLoseLocationDuringLevelMangling() throws RecognitionException { final Novella novella = new Novella( "\n" + "\n" + "== Lz\u00E9ro" + "\n" + // '\u00E9' == 'é' Makes tag appear different, debugging easier. "\n" + "p0" ) ; final SyntacticTree expected = tree( NOVELLA, new Location( "<String>", 1, 0 ), tree( _LEVEL, new Location( "<String>", 3, 0 ), tree( _IMPLICIT_TAG, "Lzero" ), tree( LEVEL_TITLE, new Location( "<String>" ), tree( WORD_, new Location( "<String>" ), "Lz\u00E9ro" ) ), tree( PARAGRAPH_REGULAR, new Location( "<String>", 5, 0 ), tree( WORD_, new Location( "<String>" ), "p0" ) ) ) ) ; final SyntacticTree partTree = novella.getDocumentTree() ; TreeFixture.assertEquals( expected, partTree, true ) ; } @Test public void loadPartUtf8WithBom() throws IOException { final Novella novella = new Novella( resourceInstaller.copy( ResourcesForTests.Parts.NOVELLA_UTF8_BOM ) ) ; Assert.assertFalse( novella.getProblems().iterator().hasNext() ) ; } @Test public void rewriteEarlyExitCountedAsAProblem() throws IOException { final Novella novella = new Novella( "\"\"" ) ; assertThat( novella.getProblems() ).hasSize( 1 ) ; final Problem problem = novella.getProblems().iterator().next() ; assertThat( problem.getLocation().isPositionDefined() ).isTrue() ; } // ======= // Fixture // ======= private static final Logger LOGGER = LoggerFactory.getLogger( NovellaTest.class ) ; private static final int TEST_TIMEOUT_MILLISECONDS = 10 * 60 * 1000 ; static { ResourcesForTests.initialize() ; } @Rule public final MethodSupport methodSupport = new MethodSupport() ; private final ResourceInstaller resourceInstaller = new ResourceInstaller( methodSupport ) ; private static final SyntacticTree $FULLSTOP$ = tree( PUNCTUATION_SIGN, tree( SIGN_FULLSTOP, "." ) ); private static final SyntacticTree $COMMA$ = tree( PUNCTUATION_SIGN, tree( SIGN_COMMA, "," ) ); }