com.kaikoda.cah.TestCardGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.kaikoda.cah.TestCardGenerator.java

Source

/* Cards Against Humanity Card Generator
 * Copyright (C) 2012  Sheila Thomson
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 com.kaikoda.cah;

import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.io.IOException;
import java.util.Locale;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.kaikoda.cah.CardGenerator.CardGeneratorProduct;
import com.kaikoda.cah.ProgressReporter.ProgressReporterMode;

/**
 * @author Sheila Thomson
 */
public class TestCardGenerator {

    /**
     * A reusable instance of a document builder configured for use by Deck.
     */
    private static DocumentBuilder documentBuilder;

    /**
     * A pointer to where the asset files that accompany the HTML file are
     * expected to be saved.
     */
    private static final File OUTPUT_FILE_ASSETS = new File(
            System.getProperty("user.dir") + File.separator + "assets");

    /**
     * A pointer to where the CSS file that accompanies the HTML file is
     * expected to be saved.
     */
    private static final File OUTPUT_FILE_CSS = new File(
            System.getProperty("user.dir") + File.separator + "assets" + File.separator + "style.css");

    /**
     * A pointer to where the HTML file is expected to be saved.
     */
    private static final File OUTPUT_FILE_HTML = new File(
            System.getProperty("user.dir") + File.separator + "cards_against_humanity.html");

    /**
     * A pointer to where the XML file is expected to be saved.
     */
    private static final File OUTPUT_FILE_XML = new File(
            System.getProperty("user.dir") + File.separator + "cards_against_humanity.xml");

    /**
     * For declaring what kind of exception is expected, when one is expected.
     */
    @Rule
    public ExpectedException exception = ExpectedException.none();

    /**
     * A reusable instance of CardGenerator.
     */
    private CardGenerator generator;

    /**
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws ParserConfigurationException
     */
    @BeforeClass
    public static void setupOnce() throws CardGeneratorConfigurationException, ParserConfigurationException {
        documentBuilder = Deck.newDocumentBuilder();
    }

    /**
     * Configure the test environment prior to each test.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     */
    @Before
    public void setup() throws CardGeneratorConfigurationException {

        this.generator = new CardGenerator();
        this.generator.setVerbosity(ProgressReporterMode.CALLBACK);

        XMLUnit.setIgnoreWhitespace(true);
        XMLUnit.setIgnoreAttributeOrder(true);
        XMLUnit.setIgnoreComments(true);
        XMLUnit.setXSLTVersion("2.0");

        XMLUnit.setControlEntityResolver(null);
        XMLUnit.setTestEntityResolver(null);

    }

    /**
     * Delete files generated by tests and release objects for garbage
     * collection.
     * 
     * @throws IOException if there's an error deleting the assets directory.
     */
    @After
    public void tearDown() throws IOException {

        // Delete the output files.
        TestCardGenerator.OUTPUT_FILE_HTML.delete();
        TestCardGenerator.OUTPUT_FILE_XML.delete();
        FileUtils.deleteDirectory(TestCardGenerator.OUTPUT_FILE_ASSETS);

        // Release the re-usable instance of CardGenerator.
        this.generator = null;

    }

    /**
     * Check that it's possible to construct a usable instance of CardGenerator.
     */
    @Test
    public void testCardGenerator_constructor() {
        assertNotNull(generator);
    }

    /**
     * Check that the Card Generator correctly removes duplicates entries in the
     * card data.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_dedupe()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve test card data containing multiple decks and duplicate cards
        File xml = this.getFile("/data/test/cards/duplicates.xml");

        // Build the cards
        String result = this.getXmlString(generator.generate(xml, null, null));

        // Retrieve test card data that contains the same cards but no
        // duplicates.
        String expected = this.getXmlString("/data/control/cards/no_duplicates.html");

        // Check that the result is the same cards, but in just two decks and
        // with no duplicates.
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the Card Generator correctly removes duplicate entries in the
     * card data.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_elementBlanks()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve test card data containing multiple decks and duplicate cards
        File xml = this.getFile("/data/test/cards/blanks_element.xml");

        // Build the cards
        Document result = this.getDocument(generator.generate(xml, null, null));

        // Retrieve test card data that contains the same cards but no
        // duplicates.
        Document expected = this.getDocument("/data/control/cards/blanks_element.html");

        // Check that the result is the same cards, but in just two decks and
        // with no duplicates.
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the Card Generator correctly translates from American to
     * British English.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_translate_english()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve the test card data for American CAH.
        File xml = this.getFile("/data/test/cards/usa.xml");

        // Build the cards
        Document result = this.getDocument(generator.generate(xml, Locale.forLanguageTag("en-gb"),
                this.getFile("/data/control/dictionaries/english.xml")));

        // Retrieve the test card data for British CAH.
        Document expected = this.getDocument("/data/control/cards/uk.html");

        // Check that the result is the same cards, translated into British
        // English (which is the default output language).
        assertXMLEqual(expected, result);

    }

    /**
     * Check that a null parameter value is ignored.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_translate_nullTargetLanguage()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve the test card data for British CAH.
        File xml = this.getFile("/data/test/cards/uk.xml");

        // Build the cards
        Document result = this
                .getDocument(generator.generate(xml, null, this.getFile("/data/control/dictionaries/english.xml")));

        // Retrieve the control card data for American CAH.
        Document expected = this.getDocument("/data/control/cards/uk.html");

        // Check that the result is the same cards, still in British English.
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the Card Generator falls back to the default output language
     * when none other is specified (and an English dictionary is available).
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_translate_params_defaultOutputLangage()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve the test card data for British CAH.
        File xml = this.getFile("/data/test/cards/uk.xml");

        // Build the cards
        Document result = this
                .getDocument(generator.generate(xml, null, this.getFile("/data/control/dictionaries/english.xml")));

        // Retrieve the control card HTML for British CAH.
        Document expected = this.getDocument("/data/control/cards/uk.html");

        // Check that the result is the same cards, still in British English
        // (source language).
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the Card Generator doesn't fail when params is null.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_translate_params_null()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve the test card data for British CAH.
        File xml = this.getFile("/data/test/cards/uk.xml");

        // Build the cards
        String result = this.getXmlString(generator.generate(xml, null, null));

        // Retrieve the control card data for British CAH.
        String expected = this.getXmlString(this.getFile("/data/control/cards/uk.html"));

        // Check that the result is untranslated (as no dictionary has been
        // specified).
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the Card Generator falls back to the default output language
     * when none other is specified (and an English dictionary is available).
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorGenerate_translate_params_outputLangageEqualsCurrentLanguage()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve the test card data for British CAH.
        File xml = this.getFile("/data/test/cards/uk.xml");

        // Build the cards
        Document result = this.getDocument(generator.generate(xml, Locale.forLanguageTag("en-gb"),
                this.getFile("/data/control/dictionaries/english.xml")));

        // Retrieve the control card HTML for British CAH.
        Document expected = this.getDocument("/data/control/cards/uk.html");

        // Check that the result is the same cards, still in British English
        // (source language).
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the Card Generator correctly saves a file
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws SAXException when something goes wrong while building the DOM
     *         Document.
     * @throws IOException when something goes wrong while reading or writing to
     *         the file.
     * @throws ParserConfigurationException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain() throws CardGeneratorConfigurationException, SAXException, IOException,
            ParserConfigurationException, ParseException {

        File input = this.getFile("/data/test/cards/html5.xml");

        String[] args = new String[] { "-f", input.getAbsolutePath(), "-v", "callback" };
        CardGenerator.main(args);

        // Check that a file has been created where the HTML file is expected.
        assertEquals(true, OUTPUT_FILE_HTML.exists());

        Document expected = this.getDocument("/data/control/cards/html5.html");
        Document result = this.getDocument(OUTPUT_FILE_HTML);
        assertXMLEqual(expected, result);

        // Check that a file has been created where the CSS file is expected.
        assertEquals(true, OUTPUT_FILE_CSS.exists());

    }

    /**
     * Check that the Card Generator correctly fails to execute if the card data
     * isn't found.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain_cardDataNotFound() throws CardGeneratorConfigurationException, SAXException,
            IOException, ParserConfigurationException, ParseException {

        String[] args = new String[] { "-f", "phantom.xml", "-v", "callback" };

        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("File not found: ");

        CardGenerator.main(args);

    }

    /**
     * Check that the Card Generator correctly fails to execute if a path to the
     * card data isn't supplied.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain_cardDataNotSupplied() throws CardGeneratorConfigurationException,
            SAXException, IOException, ParserConfigurationException, ParseException {

        exception.expect(MissingArgumentException.class);
        exception.expectMessage("Missing argument for option: f");

        String[] args = new String[] { "-f", "-v", "callback" };
        CardGenerator.main(args);

        File result = OUTPUT_FILE_HTML;
        assertEquals(false, result.exists());

    }

    /**
     * Check that the Card Generator correctly fails to execute if no dictionary
     * is specified when the dictionary option is supplied as an argument.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain_dictionaryNotFound() throws CardGeneratorConfigurationException, SAXException,
            IOException, ParserConfigurationException, ParseException {

        exception.expect(MissingArgumentException.class);
        exception.expectMessage("Missing argument for option: d");

        File input = this.getFile("/data/test/cards/html5.xml");

        String[] args = new String[] { "-f", input.getAbsolutePath(), "-d", "-v", "callback" };
        CardGenerator.main(args);

        File result = OUTPUT_FILE_HTML;
        assertEquals(false, result.exists());

    }

    /**
     * Check that the Card Generator correctly fails to execute if help is
     * requested.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain_help() throws CardGeneratorConfigurationException, SAXException, IOException,
            ParserConfigurationException, ParseException {

        File input = this.getFile("/data/test/cards/html5.xml");

        String[] args = new String[] { "-f", input.getAbsolutePath(), "-h", "-v", "callback" };
        CardGenerator.main(args);

        File result = OUTPUT_FILE_HTML;
        assertEquals(false, result.exists());

    }

    /**
     * Check that the Card Generator correctly fails to execute if no card data
     * is supplied.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain_noCardData() throws CardGeneratorConfigurationException, SAXException,
            IOException, ParserConfigurationException, ParseException {

        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("Nothing to process; no file or directory was specified.");

        String[] args = new String[] { "-v", "callback" };
        CardGenerator.main(args);

        File result = OUTPUT_FILE_HTML;
        assertEquals(false, result.exists());

    }

    /**
     * Check that the Card Generator correctly translates card data when
     * generating an HTML file.
     * 
     * @throws CardGeneratorConfigurationException if it's not possible to
     *         construct a usable instance of CardGenerator.
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws ParserConfigurationException
     * @throws ParseException if there are any problems encountered while
     *         parsing the command line tokens.
     */
    @Test
    public void testCardGeneratorMain_translate() throws CardGeneratorConfigurationException, SAXException,
            IOException, ParserConfigurationException, ParseException {

        File input = this.getFile("/data/test/cards/usa.xml");
        File dictionary = this.getFile("/data/control/dictionaries/english.xml");

        String[] args = new String[] { "-f", input.getAbsolutePath(), "-d", dictionary.getAbsolutePath(), "-l",
                "en-gb", "-v", "callback" };
        CardGenerator.main(args);

        // Check that a file has been created where the HTML file is expected.
        assertEquals(true, OUTPUT_FILE_HTML.exists());

        String expected = this.getXmlString("/data/control/cards/uk.html");
        String result = this.getXmlString(OUTPUT_FILE_HTML);
        assertXMLEqual(expected, result);

        // Check that a file has been created where the CSS file is expected.
        assertEquals(true, OUTPUT_FILE_CSS.exists());

    }

    /**
     * Check that the HTML version is generated correctly.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorToHtml()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve test card data containing enough cards for multiple pages of
        // each deck
        File xml = this.getFile("/data/test/cards/html5.xml");

        // Build the cards
        String result = this.getXmlString(generator.generate(xml, null, null, CardGeneratorProduct.HTML));

        // Retrieve test card data that contains the same cards but no
        // duplicates.
        String expected = this.getXmlString(this.getFile("/data/control/cards/html5.html"));

        // Check that the result is the same cards, but in just two decks and
        // with no duplicates.
        assertXMLEqual(expected, result);

    }

    /**
     * Check that the XML version is generated correctly.
     * 
     * @throws SAXException if an error occurs while building one of the test or
     *         control documents.
     * @throws IOException if an error occurs while reading one of the test or
     *         control documents.
     * @throws TransformerException is an unrecoverable error occurs during the
     *         transformation.
     * @throws ParserConfigurationException
     */
    @Test
    public void testCardGeneratorToXml()
            throws SAXException, IOException, TransformerException, ParserConfigurationException {

        // Retrieve test card data containing enough cards for multiple pages of
        // each deck
        File xml = this.getFile("/data/test/cards/html5.xml");

        // Build the cards
        String result = this.getXmlString(generator.generate(xml, null, null, CardGeneratorProduct.XML));

        // Retrieve test card data that contains the same cards but no
        // duplicates.
        String expected = this.getXmlString(this.getFile("/data/control/cards/html5.xml"));

        // Check that the result is the same cards, but in just two decks and
        // with no duplicates.
        assertXMLEqual(expected, result);

    }

    /**
     * Builds a DOM Document from the file specified.
     * 
     * @param input a file containing XML.
     * @return the XML as a DOM Document.
     * @throws SAXException when something goes wrong while building the DOM
     *         Document.
     * @throws IOException when something goes wrong while reading or writing to
     *         the file.
     * @throws ParserConfigurationException
     */
    private Document getDocument(File input) throws SAXException, IOException, ParserConfigurationException {

        TestCardGenerator.documentBuilder.reset();

        return TestCardGenerator.documentBuilder.parse(input);
    }

    /**
     * Builds a DOM Document from a file at the path specified.
     * 
     * @param path a path to a file containing XML.
     * @return the XML as a DOM Document.
     * @throws SAXException when something goes wrong while building the DOM
     *         Document.
     * @throws IOException when something goes wrong while reading or writing to
     *         the file.
     * @throws ParserConfigurationException
     */
    private Document getDocument(String path) throws SAXException, IOException, ParserConfigurationException {
        return this.getDocument(this.getFile(path));
    }

    /**
     * Finds a file that matches the path specified.
     * 
     * @param path a potential path to the file sought.
     * @return the file sought.
     */
    private File getFile(String path) {
        return new File(this.getClass().getResource(path).getFile());
    }

    /**
     * Builds an XML String from the file specified.
     * 
     * @param input a file containing XML.
     * @return the XML as an XML String.
     * @throws IOException when something goes wrong while reading the input
     *         file.
     */
    private String getXmlString(File input) throws IOException {

        return FileUtils.readFileToString(input, "UTF-8");

    }

    /**
     * Builds an XML String from the file at the location specified.
     * 
     * @param input a file containing XML.
     * @return the XML as an XML String.
     * @throws IOException when something goes wrong while reading the input
     *         file.
     */
    private String getXmlString(String input) throws IOException {
        return this.getXmlString(this.getFile(input));
    }

}