package org.reasm.m68k.assembly.internal; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import javax.annotation.Nonnull; import org.reasm.Assembly; import org.reasm.AssemblyCompletionStatus; import org.reasm.Configuration; import org.reasm.Environment; import org.reasm.Function; import org.reasm.FunctionValue; import org.reasm.SymbolContext; import org.reasm.Value; import org.reasm.m68k.M68KArchitecture; import org.reasm.source.SourceFile; import org.reasm.testhelpers.ValueVisitorAdapter; import ca.fragag.testhelpers.HasType; import ca.fragag.testhelpers.ObjectHashCodeEqualsContract; /** * Test class for {@link UserFunction}. * * @author Francis Gagné */ public class UserFunctionTest extends ObjectHashCodeEqualsContract { @Nonnull private static final Object MAIN_OBJECT; @Nonnull private static final Object OTHER_EQUAL_OBJECT; @Nonnull private static final Object ANOTHER_EQUAL_OBJECT; @Nonnull private static final Object DIFFERENT_OBJECT_0; @Nonnull private static final Object DIFFERENT_OBJECT_1; @Nonnull private static final Object DIFFERENT_OBJECT_2; static { // On passes 1-3, the ELSE block is assembled. This gives us 3 identical function definitions. // On pass 4, the ELSEIF X block is assembled. The function definition in this block swaps the parameter names. // On pass 5, the IF W block is assembled. The function definition in this block changes the expression. final Configuration configuration = new Configuration(Environment.DEFAULT, new SourceFile(" IF W\n" + "F FUNCTION A,B,A-B\n" + " ELSEIF X\n" + "F FUNCTION B,A,A+B\n" + " ELSE\n" + "F FUNCTION A,B,A+B\n" + " ENDIF\n" + "W EQU X\n" + "X EQU Y\n" + "Y EQU Z\n" + "Z EQU 1", null), M68KArchitecture.MC68000); final Assembly assembly = new Assembly(configuration); final int numberOfPasses = 5; final Object[] functions = new Object[numberOfPasses]; for (int i = 0; i < numberOfPasses; i++) { int stepLimit = 11; AssemblyCompletionStatus status; do { status = assembly.step(); --stepLimit; if (stepLimit < 0) { fail("The assembly is performing too many steps."); } } while (status == AssemblyCompletionStatus.PENDING); if (i == numberOfPasses - 1) { // Normally, the result should be COMPLETE. // However, if UserFunction.equals() or UserFunction.hashCode() has a bug, // the assembly may be performing infinite passes // because the UserFunction created in a subsequent pass // is not equal to the UserFunction created in a prior pass. // In that case, we'll let the class initialize // and errors will be reported when running the test cases. assertThat(status, is(either(equalTo(AssemblyCompletionStatus.COMPLETE)) .or(equalTo(AssemblyCompletionStatus.STARTED_NEW_PASS)))); } else { assertThat(status, is(AssemblyCompletionStatus.STARTED_NEW_PASS)); } functions[i] = getFunction(assembly, "F"); } MAIN_OBJECT = functions[0]; OTHER_EQUAL_OBJECT = functions[1]; ANOTHER_EQUAL_OBJECT = functions[2]; // This UserFunction has a different parameterNames. DIFFERENT_OBJECT_0 = functions[3]; // This UserFunction has a different functionExpression. DIFFERENT_OBJECT_1 = functions[4]; } static { final Configuration configuration = new Configuration(Environment.DEFAULT, new SourceFile("F FUNCTION A,B,A+B", null), M68KArchitecture.MC68000); final Assembly assembly = new Assembly(configuration); assertThat(assembly.step(), is(AssemblyCompletionStatus.PENDING)); assertThat(assembly.step(), is(AssemblyCompletionStatus.COMPLETE)); // This UserFunction has a different context. DIFFERENT_OBJECT_2 = getFunction(assembly, "F"); } @Nonnull private static Object getFunction(@Nonnull Assembly assembly, @Nonnull String symbolName) { final Object symbolValue = assembly.resolveSymbolReference(SymbolContext.VALUE, symbolName, false, null, null).getValue(); assertThat(symbolValue, HasType.hasType(FunctionValue.class)); return Value.accept((FunctionValue) symbolValue, new ValueVisitorAdapter<Function>() { @Override public Function visitFunction(Function value) { return value; } }); } /** * Initializes a new UserFunctionTest. */ public UserFunctionTest() { super(MAIN_OBJECT, OTHER_EQUAL_OBJECT, ANOTHER_EQUAL_OBJECT, new Object[] { DIFFERENT_OBJECT_0, DIFFERENT_OBJECT_1, DIFFERENT_OBJECT_2, new Object() }); } }