openblocks.yacodeblocks.BlockSaveFileTest.java Source code

Java tutorial

Introduction

Here is the source code for openblocks.yacodeblocks.BlockSaveFileTest.java

Source

// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the MIT License https://raw.github.com/mit-cml/app-inventor/master/mitlicense.txt

package openblocks.yacodeblocks;

import com.google.common.testing.junit4.JUnitAsserts;

import junit.framework.TestCase;
import org.apache.commons.io.FileUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Small tests for BlockSaveFile
 *
 * @author sharon@google.com (Sharon Perl)
 * @author lizlooney@google.com (Liz Looney)
 */
public class BlockSaveFileTest extends TestCase {
    @Override
    public void setUp() throws Exception {
        super.setUp();
        // This is necessary because the BlockSaveFile constructor transitively
        // calls BlockGenus.getGenusWithName(), which only gets initialized
        // when the language definition file gets read in, which occurs during
        // workspace setup.  For more detail, see the review log for CL 18903627.
        TestUtils.setupWorkspace(null, null);
    }

    private BlockSaveFile createBlockSaveFile(String filename) throws IOException {
        String fullFilename = TestUtils.TESTING_SOURCE_PATH + filename;
        return new BlockSaveFile(TestUtils.getLangDefRoot(),
                FileUtils.readFileToString(new File(fullFilename), "UTF-8"));
    }

    public void testNoVersionInfo() throws Exception {
        BlockSaveFile saveFile = createBlockSaveFile("blocksNoVersion.blk");
        assertTrue(saveFile.wasUpgraded());
    }

    public void testUpgradeFromBlocksVersion1() throws Exception {
        BlockSaveFile saveFile = createBlockSaveFile("blocksVersion1.blk");
        assertTrue(saveFile.wasUpgraded());
    }

    public void testUpgradeTrigBlocksFromBlockVersion4() throws Exception {
        // Read in version 4 block file.
        BlockSaveFile saveFile = createBlockSaveFile("blocksVersion4.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        // Check that the appropriate changes were made to trig blocks.
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        for (int i = 0; i < blocks.getLength(); i++) {
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");
            boolean matched = false;

            // Check that the argument label was changed (from "") to "degrees".
            if (genusName.equals("number-sin") || genusName.equals("number-cos")
                    || genusName.equals("number-tan")) {
                matched = true;
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "degrees");
            }

            // Check that a CompilerErrorMsg was added.
            if (matched || genusName.equals("number-asin") || genusName.equals("number-acos")
                    || genusName.equals("number-atan") || genusName.equals("number-atan2")) {
                assertEquals(1, block.getElementsByTagName("CompilerErrorMsg").getLength());
            } else {
                // Check that an error was NOT added to other nodes.
                assertEquals(0, block.getElementsByTagName("CompilerErrorMsg").getLength());
            }
        }
    }

    public void testUpgradeTextBlocksFromBlockVersion7() throws Exception {
        // Read in version 7 block file.
        BlockSaveFile saveFile = createBlockSaveFile("blocksVersion7.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        List<String> checkedBlocks = new ArrayList<String>();

        // Check that the appropriate changes were made to text blocks.
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        for (int i = 0; i < blocks.getLength(); i++) {
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");

            if (genusName.equals("string-less-than") || genusName.equals("string-equal")
                    || genusName.equals("string-greater-than")) {
                // Check that the argument labels were changed (from both "") to "text1" and "text2".
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "text1", "text2");
                checkedBlocks.add(genusName);
            }

            if (genusName.equals("string-upcase") || genusName.equals("string-downcase")
                    || genusName.equals("string-trim")) {
                // Check that the argument label was changed (from "") to "text".
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "text");
                checkedBlocks.add(genusName);
            }

            if (genusName.equals("string-replace-all")) {
                // Check that the second argument label was changed (from "substring") to "segment".
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "text", "segment", "replacement");
                checkedBlocks.add(genusName);
            }
        }

        assertTrue(checkedBlocks.contains("string-less-than"));
        assertTrue(checkedBlocks.contains("string-equal"));
        assertTrue(checkedBlocks.contains("string-greater-than"));
        assertTrue(checkedBlocks.contains("string-upcase"));
        assertTrue(checkedBlocks.contains("string-downcase"));
        assertTrue(checkedBlocks.contains("string-trim"));
        assertTrue(checkedBlocks.contains("string-replace-all"));
    }

    public void testUpgradeRadiansBlocksFromBlockVersion8() throws Exception {
        // Read in version 8 block file.
        BlockSaveFile saveFile = createBlockSaveFile("blocksVersion8.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        List<String> checkedBlocks = new ArrayList<String>();

        // Check that the appropriate changes were made to degrees/radians blocks.
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        for (int i = 0; i < blocks.getLength(); i++) {
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");

            if (genusName.equals("number-degrees-to-radians")) {
                // Check that the argument label was changed (from "tangent") to "degrees".
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "degrees");
                checkedBlocks.add(genusName);
            }

            if (genusName.equals("number-radians-to-degrees")) {
                // Check that the argument label was changed (from "tangent") to "radians".
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "radians");
                checkedBlocks.add(genusName);
            }
        }

        assertTrue(checkedBlocks.contains("number-degrees-to-radians"));
        assertTrue(checkedBlocks.contains("number-radians-to-degrees"));
    }

    public void testUpgradeMinusAndTimesBlocksFromBlockVersion10() throws Exception {
        // Read in version 10 block file with minus and times blocks.
        BlockSaveFile saveFile = createBlockSaveFile("blocksVersion10.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        List<String> checkedBlocks = new ArrayList<String>();

        // Check that the appropriate changes were made to minus and times blocks.
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        for (int i = 0; i < blocks.getLength(); i++) {
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");

            if (genusName.equals("number-minus")) {
                assertEquals("\\u2212", getBlockLabel(block));
                checkedBlocks.add(genusName);

            } else if (genusName.equals("number-times")) {
                assertEquals("\\u00D7", getBlockLabel(block));
                checkedBlocks.add(genusName);
            }
        }

        assertTrue(checkedBlocks.contains("number-minus"));
        assertTrue(checkedBlocks.contains("number-times"));
    }

    public void testUpgradedBlocksHaveOnlyAsciiCharacters() throws Exception {
        // Read in version 10 block file with minus and times blocks, as well as text and comments with
        // I18N characters.
        BlockSaveFile saveFile = createBlockSaveFile("blocksI18N.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        // Check that the xml has only ascii characters.
        assertNodeContainsOnlyAsciiCharacters(saveFile.getRoot());
    }

    public void testUpgradeFormRenamedScreen() throws Exception {
        // Read in version 13 block file.
        BlockSaveFile saveFile = createBlockSaveFile("blocksVersion13.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        List<String> screenBlocks = new ArrayList<String>();

        // Check that no blocks have genus that equals "Form" or starts with "Form-".
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        for (int i = 0; i < blocks.getLength(); i++) {
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");
            assertFalse("There should not be any blocks whose genus is \"Form\"!", genusName.equals("Form"));
            assertFalse("There should not be any blocks whose genus starts with \"Form-\"!",
                    genusName.startsWith("Form-"));
            if (genusName.startsWith("Screen-")) {
                screenBlocks.add(genusName);
            }
        }
        // Check that we found blocks whose genus is "Screen-Initialize", "Screen-OtherScreenClosed",
        // "Screen-ScreenOrientationChanged", "Screen-ErrorOccurred".
        assertTrue("There should be a block whose genus is \"Screen-Initialize\"!",
                screenBlocks.contains("Screen-Initialize"));
        assertTrue("There should be a block whose genus is \"Screen-OtherScreenClosed\"!",
                screenBlocks.contains("Screen-OtherScreenClosed"));
        assertTrue("There should be a block whose genus is \"Screen-ScreenOrientationChanged\"!",
                screenBlocks.contains("Screen-ScreenOrientationChanged"));
        assertTrue("There should be a block whose genus is \"Screen-ErrorOccurred\"!",
                screenBlocks.contains("Screen-ErrorOccurred"));

        // Check that the YoungAndroidUuidEntry with component-id Screen1 still has component-genus
        // Form.
        boolean foundScreen1 = false;
        NodeList entries = documentRoot.getElementsByTagName("YoungAndroidUuidEntry");
        for (int i = 0; i < entries.getLength(); i++) {
            Element entry = (Element) entries.item(i);
            String componentId = entry.getAttribute("component-id");
            if (componentId.equals("Screen1")) {
                foundScreen1 = true;
                assertEquals("Form", entry.getAttribute("component-genus"));
            }
        }
        // Check that we found an entry whose component-id is Screen1.
        assertTrue(foundScreen1);
    }

    public void testUpgradeOrientationSensorFromComponentVersion1() throws Exception {
        // Read in block file with version 1 of the OrientationSensor component.
        // The block file has two calls to
        // OrientationSensor.OrientationChanged(yaw, pitch, roll)
        // and an unrelated procedure named OrientationChanged with a yaw parameter.
        BlockSaveFile saveFile = createBlockSaveFile("yaw-azimuth.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        // Check that the first three sockets for both of the calls to
        // OrientationSensor.OrientationChanged are (azimuth, pitch, roll).
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        int numCalls = 0;
        for (int i = 0; i < blocks.getLength(); i++) {
            assert blocks.item(i) instanceof Element;
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");
            if (genusName.equals("OrientationSensor-OrientationChanged")) {
                numCalls++;
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "azimuth", "pitch", "roll", "do");
            } else if (genusName.equals("caller-command")) {
                // The name of the procedure parameter should not have changed.
                JUnitAsserts.assertContentsInOrder(getSocketLabels(block), "yaw");
            }
        }
        // There should be exactly two calls.
        assertEquals(2, numCalls);
    }

    public void testUpgradeWebFromComponentVersion1() throws Exception {
        // Read in block file with version 1 of the Web component.
        // The block file has one call to Web1.PostText(text, encoding).
        BlockSaveFile saveFile = createBlockSaveFile("WebVersion1.blk");

        // Check that the need to upgrade was recognized.
        assertTrue(saveFile.wasUpgraded());

        // Check that the Web1.PostText block was changed to Web1.PostTextWithEncoding.
        Element documentRoot = saveFile.getRoot();
        NodeList blocks = documentRoot.getElementsByTagName("Block");
        int numCalls = 0;
        for (int i = 0; i < blocks.getLength(); i++) {
            assert blocks.item(i) instanceof Element;
            Element block = (Element) blocks.item(i);
            String genusName = block.getAttribute("genus-name");
            if (genusName.equals("Web-PostTextWithEncoding")) {
                numCalls++;
                // The label of the block should have been updated.
                assertEquals("Web1.PostTextWithEncoding", getBlockLabel(block));
            } else if (genusName.equals("Web-PostText")) {
                fail("Should not have found a block with genus name \"Web-PostText\".");
            }
        }
        // There should be exactly one call.
        assertEquals(1, numCalls);
    }

    /*
     * Checks whether the given block has been marked bad by
     * BlockSaveFile.markBlockBad().
     */
    static boolean blockMarkedBad(Node block) {
        NodeList childNodes = block.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            if (childNodes.item(i).getNodeName().equals(BlockSaveFile.BAD_BLOCK)) {
                return true;
            }
        }
        return false;
    }

    /*
     * Finds the sockets for this block and returns the values of their "label"
     * attributes as a List<String>.  If none is found, this returns an empty
     * List.
     */
    private List<String> getSocketLabels(Element block) {
        List<String> labels = new ArrayList<String>();
        NodeList connectors = block.getElementsByTagName("BlockConnector");
        for (int i = 0; i < connectors.getLength(); i++) {
            Element connector = (Element) connectors.item(i);
            if (connector.getAttribute("connector-kind").equals("socket")) {
                labels.add(connector.getAttribute("label"));
            }
        }
        return labels;
    }

    private String getBlockLabel(Element block) {
        return BlockSaveFile.getBlockLabelChild(block).getNodeValue();
    }

    private void assertNodeContainsOnlyAsciiCharacters(Node node) throws Exception {
        if (node instanceof Element) {
            assertElementContainsOnlyAsciiCharacters((Element) node);
        } else if (node instanceof Attr) {
            assertAttrContainsOnlyAsciiCharacters((Attr) node);
        } else if (node instanceof Text) {
            assertTextContainsOnlyAsciiCharacters((Text) node);
        } else {
            fail("Found a node that is not an Element, Attr, or Text");
        }
    }

    private void assertElementContainsOnlyAsciiCharacters(Element element) throws Exception {
        assertStringContainsOnlyAsciiCharacters(element.getNodeName(), "name", element);

        if (element.hasAttributes()) {
            NamedNodeMap attributes = element.getAttributes();
            for (int i = 0; i < attributes.getLength(); i++) {
                assertNodeContainsOnlyAsciiCharacters(attributes.item(i));
            }
        }

        if (element.hasChildNodes()) {
            NodeList childNodes = element.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++) {
                assertNodeContainsOnlyAsciiCharacters(childNodes.item(i));
            }
        }
    }

    private void assertAttrContainsOnlyAsciiCharacters(Attr attr) throws Exception {
        assertStringContainsOnlyAsciiCharacters(attr.getName(), "name", attr);
        assertStringContainsOnlyAsciiCharacters(attr.getValue(), "value", attr);
    }

    private void assertTextContainsOnlyAsciiCharacters(Text text) throws Exception {
        assertStringContainsOnlyAsciiCharacters(text.getNodeValue(), "text value", text);
    }

    private void assertStringContainsOnlyAsciiCharacters(String s, String description, Node node) {
        if (s != null) {
            for (int i = 0; i < s.length(); i++) {
                char ch = s.charAt(i);
                if (ch > '~') {
                    // Build a failure message that tells where the non-ascii character was found.
                    StringBuilder failureMessage = new StringBuilder();

                    // Find the id attribute of the containing block.
                    Node parent = node;
                    while (parent != null) {
                        if (parent.getNodeName().equals("Block") && parent instanceof Element) {
                            String blockId = ((Element) parent).getAttribute("id");
                            failureMessage.append("In the Block whose id attribute is " + blockId + ", ");
                            break;
                        }
                        if (parent instanceof Attr) {
                            parent = ((Attr) parent).getOwnerElement();
                        } else {
                            parent = parent.getParentNode();
                        }
                    }

                    if (failureMessage.length() == 0) {
                        failureMessage.append("The ");
                    } else {
                        failureMessage.append("the ");
                    }

                    failureMessage.append(description).append(" of the ").append(describeNode(node))
                            .append(" contains the non-ascii character 0x").append(Integer.toHexString(ch))
                            .append(" at index ").append(i).append(": ").append(s).append(".");

                    fail(failureMessage.toString());
                }
            }
        }
    }

    private String describeNode(Node node) {
        if (node instanceof Element) {
            return "<" + node.getNodeName() + "> element";
        } else if (node instanceof Attr) {
            return node.getNodeName() + " attribute of the " + describeNode(((Attr) node).getOwnerElement());
        } else if (node instanceof Text) {
            return describeNode(node.getParentNode());
        }
        throw new IllegalArgumentException(
                "node must an Element, an Attr, or a Text: " + node.getClass().getName());
    }
}