org.protelis.test.TestLanguage.java Source code

Java tutorial

Introduction

Here is the source code for org.protelis.test.TestLanguage.java

Source

/*
 * Copyright (C) 2010-2015, Danilo Pianini and contributors
 * listed in the project's pom.xml file.
 * 
 * This file is part of Alchemist, and is distributed under the terms of
 * the GNU General Public License, with a linking exception, as described
 * in the file LICENSE in the Alchemist distribution's top directory.
 */
package org.protelis.test; // NOPMD by jakebeal on 8/25/15 12:41 PM

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.danilopianini.io.FileUtilities;
import org.danilopianini.lang.LangUtils;
import org.junit.Test;
import org.protelis.lang.ProtelisLoader;
import org.protelis.lang.datatype.Tuple;
import org.protelis.vm.ProtelisProgram;
import org.protelis.vm.ProtelisVM;
import org.protelis.vm.impl.DummyContext;

/**
 * Main collection of tests for the Protelis language and VM.
 */
public class TestLanguage {

    private static final String SL_NAME = "singleLineComment";
    private static final String ML_NAME = "multilineComment";
    private static final String EXPECTED = "EXPECTED_RESULT:";
    private static final Pattern EXTRACT_RESULT = Pattern.compile(".*?" + EXPECTED + "\\s*(?<" + ML_NAME
            + ">.*?)\\s*\\*\\/|\\/\\/\\s*" + EXPECTED + "\\s*(?<" + SL_NAME + ">.*?)\\s*\\n", Pattern.DOTALL);
    private static final Pattern CYCLE = Pattern.compile("\\$CYCLE");
    private static final int MIN_CYCLE_NUM = 1;
    private static final int MAX_CYCLE_NUM = 100;

    /**
     * Test the alignedMap construct.
     */
    @Test
    public void testAlignedMap() {
        testFileWithMultipleRuns("/alignedMap.pt");
    }

    /**
     * Test closures.
     */
    @Test
    public void testClosure01() {
        testFile("/closure01.pt");
    }

    /**
     * Test the cyclic timer.
     */
    @Test
    public void testCyclicTimer() {
        testFileWithMultipleRuns("/cyclicTimer.pt");
    }

    /**
     * Test Boolean logic operators.
     */
    @Test
    public void testBinary01() {
        testFile("/binary01.pt");
    }

    /**
     * Test putting and getting of environment variables.
     */
    @Test
    public void testEnvironment01() {
        testFile("/environment01.pt");
    }

    /**
     * Smoke test of basic dynamic program evaluation.
     */
    @Test
    public void testEval01() {
        testFile("/eval01.pt");
    }

    /**
     * Test of dynamic evaluation of a complex program, including function
     * definitions.
     */
    @Test
    public void testEval02() {
        testFile("/eval02.pt");
    }

    /**
     * Exercise method calls on fields.
     */
    @Test
    public void testFieldMethod01() {
        testFile("/fieldMethod01.pt");
    }

    /**
     * Exercise method calls on fields.
     */
    @Test
    public void testFieldMethod02() {
        testFile("/fieldMethod02.pt");
    }

    /**
     * Exercise method calls on fields.
     */
    @Test
    public void testFieldMethod03() {
        testFile("/fieldMethod03.pt");
    }

    /**
     * Test simple function call with no arguments.
     */
    @Test
    public void testFunction01() {
        testFile("/function01.pt");
    }

    /**
     * Test simple function call with one argument.
     */
    @Test
    public void testFunction02() {
        testFile("/function02.pt");
    }

    /**
     * Test simple function call with two arguments.
     */
    @Test
    public void testFunction03() {
        testFile("/function03.pt");
    }

    /**
     * Test to make sure that more than one instance of a function can be active
     * at a time.
     */
    @Test
    public void testFunction04() {
        testFile("/function04.pt");
    }

    /**
     * Test hood with a lambda function.
     */
    @Test
    public void testGenericHood01() {
        testFile("/genericHood01.pt");
    }

    /**
     * Test hoodPlusSelf with a function reference.
     */
    @Test
    public void testGenericHood02() {
        testFile("/genericHood02.pt");
    }

    /**
     * Test hoodPlusSelf with a Java method reference.
     */
    @Test
    public void testGenericHood03() {
        testFile("/genericHood03.pt");
    }

    /**
     * Test hood with a generated field.
     */
    @Test
    public void testGenericHood04() {
        testFile("/genericHood04.pt");
    }

    /**
     * Test hood with a generated field.
     */
    @Test
    public void testGenericHood05() {
        testFile("/genericHood05.pt");
    }

    /**
     * Test hood with a generated field.
     */
    @Test
    public void testGenericHood06() {
        testFile("/genericHood06.pt");
    }

    /**
     * Test simple use of apply.
     */
    @Test
    public void testHof01() {
        testFileWithMultipleRuns("/hof01.pt");
    }

    /**
     * Test apply on a more complex function.
     */
    @Test
    public void testHof02() {
        testFileWithMultipleRuns("/hof02.pt");
    }

    /**
     * Test to make sure that each apply call bounds to a different instance of
     * the function.
     */
    @Test
    public void testHof03() {
        testFileWithMultipleRuns("/hof03.pt");
    }

    /**
     * Test using apply to define a higher-order map function.
     */
    @Test
    public void testHof04() {
        testFile("/hof04.pt");
    }

    /**
     * Test multiple applications of a higher-order map function.
     */
    @Test
    public void testHof05() {
        testFile("/hof05.pt");
    }

    /**
     * Test that plain hood functions don't include local value.
     */
    @Test
    public void testHood01() {
        testFile("/hood01.pt");
    }

    /**
     * Test that PlusSelf hood functions do include local value.
     */
    @Test
    public void testHood02() {
        testFile("/hood02.pt");
    }

    /**
     * Test a more complex hood function.
     */
    @Test
    public void testHood03() {
        testFile("/hood03.pt");
    }

    /**
     * Test operation of "if" restrictive branching.
     */
    @Test
    public void testIf01() {
        testFileWithMultipleRuns("/if01.pt");
    }

    /**
     * Make sure if throws exceptions instead of returning fields.
     */
    @Test
    public void testIf02() {
        try {
            testFile("/if02.pt", 1, null);
            fail("If should never return fields");
        } catch (IllegalStateException e) {
            assertNotNull(e);
        }
    }

    /**
     * Test a simple anonymous function inline definition and application.
     */
    @Test
    public void testLambda01() {
        testFile("/lambda01.pt");
    }

    /**
     * Test a complex passing and application of lambdas, including an if.
     */
    @Test
    public void testLambda02() {
        testFileWithMultipleRuns("/lambda02.pt");
    }

    /**
     * Test a complex passing and application of lambdas, including a mux.
     */
    @Test
    public void testLambda03() {
        testFileWithMultipleRuns("/lambda03.pt");
    }

    /**
     * Test constants: -Infinity.
     */
    @Test
    public void testMath01() {
        testFile("/math01.pt");
    }

    /**
     * Test constants: -3.
     */
    @Test
    public void testMath02() {
        testFile("/math02.pt");
    }

    /**
     * Test arithmetic: addition.
     */
    @Test
    public void testMath03() {
        testFile("/math03.pt");
    }

    /**
     * Test arithmetic: equality.
     */
    @Test
    public void testMath04() {
        testFile("/math04.pt");
    }

    /**
     * Test fully-qualified call of individually imported static Java method.
     */
    @Test
    public void testMethod01() {
        testFile("/method01.pt");
    }

    /**
     * Test unqualified call of individually imported static Java method.
     */
    @Test
    public void testMethod02() {
        testFileWithExplicitResult("/method02.pt", Collections.EMPTY_LIST);
    }

    /**
     * Test unqualified call of batch-imported static Java methods.
     */
    @Test
    public void testMethod03() {
        testFile("/method03.pt");
    }

    /**
     * Test "dot" call of non-static Java methods.
     */
    @Test
    public void testMethod04() {
        testFile("/method04.pt");
    }

    /**
     * Confirm that qualified and unqualified methods are equal.
     */
    @Test
    public void testMethod05() {
        testFile("/method05.pt");
    }

    /**
     * Test showing that when unqualified imported Protelis method names
     * conflict, first imported shadows later imports.
     */
    @Test
    public void testModules01() {
        testFile("/modules01.pt");
    }

    /**
     * Confirm that local definitions shadow imported Protelis definitions.
     */
    @Test
    public void testModules02() {
        testFile("/modules02.pt");
    }

    /**
     * Test that shadowed imported Protelis functions can still be called via
     * their fully qualified names.
     */
    @Test
    public void testModules03() {
        testFile("/modules03.pt");
    }

    /**
     * Test that module imports can handle circular references between modules.
     */
    @Test
    public void testModules04() {
        testFile("/modules04.pt");
    }

    /**
     * Test that imported modules can import other modules in different
     * packages.
     */
    @Test
    public void testModules05() {
        testFile("/modules05.pt");
    }

    /**
     * Test operation of "mux" inclusive branching.
     */
    @Test
    public void testMux01() {
        testFileWithMultipleRuns("/mux01.pt");
    }

    /**
     * Test two statement sequence.
     */
    @Test
    public void testMultiStatement01() {
        testFile("/multistatement01.pt");
    }

    /**
     * Test multiple variable re-assignments.
     */
    @Test
    public void testMultiStatement02() {
        testFile("/multistatement02.pt");
    }

    /**
     * Test independence of sequential invocations of a function.
     */
    @Test
    public void testMultiStatement03() {
        testFile("/multistatement03.pt");
    }

    /**
     * Test assignment within nested lexical scope.
     */
    @Test
    public void testMultiStatement04() {
        testFile("/multistatement04.pt");
    }

    /**
     * Test rep via a canonical use: creating a counter.
     */
    @Test
    public void testRep01() {
        testFileWithMultipleRuns("/rep01.pt", IntStream.range(0, 4).map(i -> (int) Math.round(Math.pow(10, i))));
    }

    /**
     * Test nested rep statements.
     */
    @Test
    public void testRep02() {
        double prev = 1;
        for (int i = 1; i < 100; i++) {
            testFile("/rep02.pt", i, prev);
            prev = prev * (prev + 1);
        }
    }

    /**
     * Test infix addition.
     */
    @Test
    public void testSum() {
        testFile("/sum.pt");
    }

    /**
     * Test loading of a file from name without explicit classpath statement.
     */
    @Test
    public void testLoadFile() {
        testFile("/sum.pt");
    }

    /**
     * Test loading of a file with explicit classpath statement.
     */
    @Test
    public void testLoadFromClasspath() {
        testFileWithExplicitResult("classpath:/sum.pt", 8d);
    }

    /**
     * Test parsing of anonymous expression.
     */
    @Test
    public void testLoadModule() {
        testFileWithExplicitResult("5+3", 8d);
    }

    /**
     * Test import of a module as part of an anonymous expression.
     */
    @Test
    public void testAnonymousLoadModule() {
        testFileWithExplicitResult("import protelis:test:circular02\nfun3()", 1d);
    }

    /**
     * Test loading from a module name with default package.
     */
    @Test
    public void testLoadFromModuleName01() {
        testFileWithExplicitResult("modules04", 1d);
    }

    /**
     * Test loading from a module name from a nested package.
     */
    @Test
    public void testLoadFromModuleName02() {
        testFileWithExplicitResult("protelis:test:circular02", 1d);
    }

    /**
     * Test construction of tuple using '[]' syntax.
     */
    @Test
    public void testTuple01() {
        final Tuple expectedResult = Tuple.create(new Object[] { 5.0, 4.0, 3.0, 2.0, 1.0, 0.0 });
        testFileWithExplicitResult("/tuple01.pt", expectedResult);
    }

    /**
     * Test construction of tuple with some elements computed rather than inline
     * constants.
     */
    @Test
    public void testTuple02() {
        testFile("/tuple02.pt");
    }

    /**
     * Test calling of tuple methods, in particular Tuple.size().
     */
    @Test
    public void testTuple03() {
        testFileWithExplicitResult("/tuple03.pt", Integer.valueOf(3));
    }

    /**
     * Test the Tuple.indexof method.
     */
    @Test
    public void testTuple04() {
        testFile("/tuple04.pt");
    }

    /**
     * Test the Tuple.fill method.
     */
    @Test
    public void testTuple05() {
        final Tuple expectedResult = Tuple.create(new Object[] { 2.0, 2.0, 2.0 });
        testFileWithExplicitResult("/tuple05.pt", expectedResult);
    }

    /**
     * Test the Tuple.map method.
     */
    @Test
    public void testTupleMap01() {
        testFile("/TupleMap01.pt");
    }

    /**
     * Test the Tuple.reduce method.
     */
    @Test
    public void testTupleReduce01() {
        testFile("/TupleReduce01.pt");
    }

    /**
     * Test the Tuple.filter method.
     */
    @Test
    public void testTupleFilter01() {
        testFile("/TupleFilter01.pt");
    }

    /**
     * Test the unary '!' operator.
     */
    @Test
    public void testUnary01() {
        testFile("/unary01.pt");
    }

    /**
     * Test the unary '-' operator.
     */
    @Test
    public void testUnary02() {
        testFileWithExplicitResult("/unary02.pt", -Math.PI);
    }

    /*
     * From this point the rest of the file is not tests, but utility methods
     */

    private static void testFileWithExplicitResult(final String file, final Object expectedResult) {
        testFile(file, 1, expectedResult);
    }

    private static void testFile(final String file) {
        testFile(file, 1);
    }

    private static void testFileWithMultipleRuns(final String file) {
        testFileWithMultipleRuns(file, MIN_CYCLE_NUM, MAX_CYCLE_NUM);
    }

    private static void testFileWithMultipleRuns(final String file, final int min, final int max) {
        testFileWithMultipleRuns(file, IntStream.rangeClosed(min, max));
    }

    private static void testFileWithMultipleRuns(final String file, final IntStream stream) {
        stream.parallel().forEach(i -> {
            testFile(file, i);
        });
    }

    private static void testFile(final String file, final int runs) {
        final Object execResult = runProgram(file, runs);
        final InputStream is = TestLanguage.class.getResourceAsStream(file);
        try {
            final String test = IOUtils.toString(is, Charsets.UTF_8);
            final Matcher extractor = EXTRACT_RESULT.matcher(test);
            if (extractor.find()) {
                String result = extractor.group(ML_NAME);
                if (result == null) {
                    result = extractor.group(SL_NAME);
                }
                final String toCheck = CYCLE.matcher(result).replaceAll(Integer.toString(runs));
                final ProtelisVM vm = new ProtelisVM(ProtelisLoader.parse(toCheck), new DummyContext());
                vm.runCycle();
                assertEquals(vm.getCurrentValue(),
                        execResult instanceof Number ? ((Number) execResult).doubleValue() : execResult);
            } else {
                fail("Your test does not include the expected result");
            }
        } catch (IOException e) {
            fail(LangUtils.stackTraceToString(e));
        }
    }

    private static void testFile(final String file, final int runs, final Object expectedResult) {
        assertEquals(expectedResult, runProgram(file, runs));
    }

    private static Object runProgram(final String s, final int runs) {
        final ProtelisProgram program = ProtelisLoader.parse(s);
        try {
            FileUtilities.serializeObject(program);
        } catch (Exception e) {
            fail();
        }
        final ProtelisVM vm = new ProtelisVM(program, new DummyContext());
        for (int i = 0; i < runs; i++) {
            vm.runCycle();
        }
        return vm.getCurrentValue();
    }
}