/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.backend.tests; import static abs.backend.tests.ReflectionUtils.getField; import static abs.backend.tests.ReflectionUtils.setField; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.hamcrest.CoreMatchers.both; import static org.hamcrest.CoreMatchers.everyItem; import static org.hamcrest.CoreMatchers.hasItem; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Test; import abs.frontend.analyser.SemanticCondition; import abs.frontend.analyser.SemanticConditionList; import abs.frontend.ast.ClassDecl; import abs.frontend.ast.InterfaceDecl; import abs.frontend.ast.Model; import abs.frontend.ast.ModuleDecl; import abs.frontend.parser.Main; import abs.frontend.parser.ParserError; /** * Unit tests for {@link ASTBasedABSTestRunnerGenerator} * @author woner * */ public class ASTBasedABSTestRunnerGeneratorTest { private final static String ABS_UNIT = "module AbsUnit; export *;" + "[TypeAnnotation] data DataPoint = DataPoint; " + "[TypeAnnotation] data Ignored = Ignored;" + "[TypeAnnotation] data Test = Test; " + "[TypeAnnotation] data Suite = Suite; " + "[TypeAnnotation] data Fixture = Fixture; "; private final static String TEST_CODE = "module Test; export *; import * from AbsUnit;" + "[Fixture] interface T { [Test] Unit t(); }" + "[Suite] class TI implements T { Unit t() { } }"; private final static Iterable<Entry<InterfaceDecl, Set<ClassDecl>>> EMPTY_MAP = Collections.<InterfaceDecl, Set<ClassDecl>>emptyMap().entrySet(); private static class SizeMatcher extends BaseMatcher<Iterable<Entry<InterfaceDecl, Set<ClassDecl>>>> { private final int size; public SizeMatcher(int size) { this.size = size; } public boolean matches(Object arg0) { if (arg0 instanceof Iterable) { Iterable<?> it = (Iterable<?>) arg0; Iterator<?> tr = it.iterator(); int count = 0; while (count < size) { if (! tr.hasNext()) { return false; } tr.next(); count++; } return ! tr.hasNext(); } return false; } @SuppressWarnings("unused") public void describeTo(Description arg0) { // TODO Auto-generated method stub } } /** * @see ASTBasedABSTestRunnerGeneratorTest.ModuleMatcher below for note about generics! */ private static class TestClassMatcher<I,C> extends BaseMatcher<Entry<I, Set<C>>> { public boolean matches(Object arg0) { if (!(arg0 instanceof Entry)) { return false; } final Entry<?, ?> entry = (Entry<?, ?>) arg0; if (!(entry.getKey() instanceof InterfaceDecl)) { return false; } if (!(entry.getValue() instanceof Set)) { return false; } final Set<?> set = (Set<?>) entry.getValue(); if (set.size() != 1) { return false; } final Object ele = set.iterator().next(); if (!(ele instanceof ClassDecl)) { return false; } final InterfaceDecl intf = (InterfaceDecl) entry.getKey(); final ClassDecl clazz = (ClassDecl) ele; return intf.getName().equals("T") && clazz.getName().equals("TI"); } @SuppressWarnings("unused") public void describeTo(Description arg0) { } } /** * NB: type patched to be generic instead of the more specific ModuleDecl because * javac is too picky about hamcrests' generics! */ private static class ModuleMatcher<T> extends BaseMatcher<T> { public boolean matches(Object arg0) { if (arg0 instanceof ModuleDecl) { ModuleDecl module = (ModuleDecl) arg0; if (module.getName().equals(ASTBasedABSTestRunnerGenerator.RUNNER_MAIN)) { return module.hasBlock(); } } return false; } @SuppressWarnings("unused") public void describeTo(Description arg0) { // TODO Auto-generated method stub } } @SuppressWarnings("unchecked") private static void assertMatches( Model model, Matcher<Object> testType, Matcher<Object> dataPointType, Matcher<Object> fixtureType, Matcher<Object> suiteType, Matcher<Iterable<Entry<InterfaceDecl, Set<ClassDecl>>>> tests, Boolean isEmpty, ABSTestRunnerGenerator aut) { assertSame(model,getField(aut, AbstractABSTestRunnerGenerator.class, "model")); assertThat(getField(aut, AbstractABSTestRunnerGenerator.class, "testType"),testType); assertThat(getField(aut, AbstractABSTestRunnerGenerator.class, "dataPointType"),dataPointType); assertThat(getField(aut, AbstractABSTestRunnerGenerator.class, "fixtureType"),fixtureType); assertThat(getField(aut, AbstractABSTestRunnerGenerator.class, "suiteType"),suiteType); Map<InterfaceDecl, Set<ClassDecl>> actual = (Map<InterfaceDecl, Set<ClassDecl>>) getField(aut, AbstractABSTestRunnerGenerator.class, "tests"); assertThat(actual.entrySet(),tests); assertEquals(isEmpty,getField(aut, AbstractABSTestRunnerGenerator.class, "isEmpty")); } @SuppressWarnings("unused") @Test(expected=IllegalArgumentException.class) public final void testABSTestRunnerGeneratorNull() { new ASTBasedABSTestRunnerGenerator(null); } @Test public final void testABSTestRunnerGenerator() { Model model = new Model(); ABSTestRunnerGenerator generator = new ASTBasedABSTestRunnerGenerator(model); assertMatches(model, nullValue(), nullValue(), nullValue(), nullValue(), equalTo(EMPTY_MAP), Boolean.TRUE, generator); try { model = Main.parseString(ABS_UNIT, true); generator = new ASTBasedABSTestRunnerGenerator(model); assertMatches(model, notNullValue(), notNullValue(), notNullValue(), notNullValue(), equalTo(EMPTY_MAP), Boolean.TRUE, generator); model = Main.parseString(ABS_UNIT + TEST_CODE, true); generator = new ASTBasedABSTestRunnerGenerator(model); assertMatches(model, notNullValue(), notNullValue(), notNullValue(), notNullValue(), both(everyItem(new TestClassMatcher())). and(new SizeMatcher(1)), Boolean.FALSE, generator); } catch (Exception e) { throw new IllegalStateException("Cannot parse test code",e); } } @Test public final void testHasUnitTest() { Model model = new Model(); ABSTestRunnerGenerator generator = new ASTBasedABSTestRunnerGenerator(model); generator = setField(generator, AbstractABSTestRunnerGenerator.class, "isEmpty", Boolean.TRUE); assertFalse(generator.hasUnitTest()); generator = setField(generator, AbstractABSTestRunnerGenerator.class, "isEmpty", Boolean.FALSE); assertTrue(generator.hasUnitTest()); } @Test public final void testGenerateTestRunner() { final Model model; try { model = Main.parseString(ABS_UNIT + TEST_CODE, true); } catch (Exception e) { throw new IllegalStateException("Cannot parse test code",e); } ABSTestRunnerGenerator generator = new ASTBasedABSTestRunnerGenerator(model); ByteArrayOutputStream stream = new ByteArrayOutputStream(); PrintStream print = new PrintStream(stream); generator.generateTestRunner(print); String runner = stream.toString(); try { Model result = Main.parseString(ABS_UNIT + TEST_CODE + runner, true); StringBuilder parseErrors = new StringBuilder(); if (result.hasParserErrors()) { parseErrors.append("Syntactic errors: "); List<ParserError> es = result.getParserErrors(); parseErrors.append(es.size()); parseErrors.append("\n"); for (ParserError e : es) { parseErrors.append(e.getHelpMessage()); parseErrors.append("\n"); } } assertFalse("Generated code must not have parse error: "+parseErrors,result.hasParserErrors()); StringBuilder errors = new StringBuilder(); if (result.hasErrors()) { SemanticConditionList el = result.getErrors(); errors.append("Semantic errors: "); errors.append(el.getErrorCount()); errors.append("\n"); for (SemanticCondition error : el) { errors.append(error.getHelpMessage()); errors.append("\n"); } } assertFalse("Generated code must not have semantic error: "+errors,result.hasErrors()); result.typeCheck(); assertFalse("Generated code must not have type error",result.hasTypeErrors()); assertThat("Has one module that has the name 'AbsUnit.TestRunner' and a main block", result.getModuleDecls(),hasItem(new ModuleMatcher())); } catch (Exception e) { fail("Cannot throw an exception "); } } }