package org.reasm.m68k.assembly.internal;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.reasm.AssemblyMessage;
import org.reasm.Environment;
import org.reasm.FileFetcher;
import org.reasm.commons.messages.ValueOutOfRangeErrorMessage;
import org.reasm.m68k.M68KArchitecture;
import org.reasm.m68k.messages.IncbinLengthMustNotBeNegativeErrorMessage;
import org.reasm.m68k.messages.IncbinStartMustNotBeNegativeErrorMessage;
import org.reasm.m68k.messages.NotSupportedOnArchitectureErrorMessage;
import org.reasm.messages.ArchitectureNotRegisteredErrorMessage;
import org.reasm.messages.IOErrorMessage;
import org.reasm.source.SourceFile;
/**
* Test class for the <code>BINCLUDE</code>, <code>INCBIN</code> and <code>INCLUDE</code> directives.
*
* @author Francis Gagné
*/
@RunWith(Parameterized.class)
public class IncbinIncludeTest extends BaseProgramsTest {
@Nonnull
private static final IOErrorMessage FILE_0_NOT_FOUND = new IOErrorMessage(new FileNotFoundException("0"));
@Nonnull
private static final IOErrorMessage FILE_0_0_NOT_FOUND = new IOErrorMessage(new FileNotFoundException("0.0"));
@Nonnull
private static final IOErrorMessage FILE_A_NOT_FOUND = new IOErrorMessage(new FileNotFoundException("A"));
@Nonnull
static final byte[] FILE_B = new byte[] { 1, 2, 3, 4 };
@Nonnull
static final SourceFile FILE_C = new SourceFile(" DC.B $77", "C");
@Nonnull
private static final byte[] FILE_C_OUTPUT = new byte[] { 0x77 };
@Nonnull
static final SourceFile FILE_D = new SourceFile(" LPSTOP #$1234", "D");
@Nonnull
private static final byte[] FILE_D_OUTPUT = new byte[] { (byte) 0xF8, 0x00, 0x01, (byte) 0xC0, 0x12, 0x34 };
@Nonnull
private static final FileFetcher FILE_FETCHER = new FileFetcher() {
@Override
public byte[] fetchBinaryFile(String filePath) throws IOException {
if ("B".equals(filePath)) {
return FILE_B.clone();
}
return this.getNull();
}
@Override
public SourceFile fetchSourceFile(String filePath) throws IOException {
if ("C".equals(filePath)) {
return FILE_C;
}
if ("D".equals(filePath)) {
return FILE_D;
}
return this.getNull();
}
// method to bypass FindBugs's null analysis
private <T> T getNull() {
return null;
}
};
@Nonnull
private static final ArrayList<Object[]> TEST_DATA = new ArrayList<>();
static {
// BINCLUDE
addDataItem(" BINCLUDE 'B'", 2, FILE_B);
addDataItem(" BINCLUDE 'B',1", 2, new byte[] { 2, 3, 4 });
addDataItem(" BINCLUDE 'B',0,4", 2, FILE_B);
// --> see INCBIN for more tests
// INCBIN
addDataItem(" INCBIN", 2, NO_DATA, WRONG_NUMBER_OF_OPERANDS);
addDataItem(" INCBIN UNDEFINED", 2, NO_DATA, UNDEFINED_SYMBOL);
addDataItem(" INCBIN 0", 2, NO_DATA, FILE_0_NOT_FOUND);
addDataItem(" INCBIN +0", 2, NO_DATA, FILE_0_NOT_FOUND);
addDataItem(" INCBIN 0.0", 2, NO_DATA, FILE_0_0_NOT_FOUND);
addDataItem(" INCBIN 'A'", 2, NO_DATA, FILE_A_NOT_FOUND);
addDataItem(" INCBIN 'B'", 2, FILE_B);
addDataItem(" INCBIN 'B',0", 2, FILE_B);
addDataItem(" INCBIN 'B',1", 2, new byte[] { 2, 3, 4 });
addDataItem(" INCBIN 'B',4", 2, NO_DATA);
addDataItem(" INCBIN 'B',5", 2, NO_DATA, new ValueOutOfRangeErrorMessage(5));
addDataItem(" INCBIN 'B',0,0", 2, NO_DATA);
addDataItem(" INCBIN 'B',0,4", 2, FILE_B);
addDataItem(" INCBIN 'B',0,-1", 2, FILE_B, new IncbinLengthMustNotBeNegativeErrorMessage());
addDataItem(" INCBIN 'B',0,5", 2, FILE_B, new ValueOutOfRangeErrorMessage(5));
addDataItem(" INCBIN 'B',-1,4", 2, FILE_B, new IncbinStartMustNotBeNegativeErrorMessage());
addDataItem(" INCBIN 'B',1,3", 2, new byte[] { 2, 3, 4 });
addDataItem(" INCBIN 'B',1,4", 2, new byte[] { 2, 3, 4 }, new ValueOutOfRangeErrorMessage(4));
addDataItem(" INCBIN 'B',0,4,1", 2, FILE_B, WRONG_NUMBER_OF_OPERANDS);
// TODO: test with a built-in function
//addDataItem(" INCBIN STRLEN", 2, NO_DATA, new FunctionCannotBeConvertedToStringErrorMessage());
// INCLUDE
addDataItem(" INCLUDE", 2, NO_DATA, WRONG_NUMBER_OF_OPERANDS);
addDataItem(" INCLUDE UNDEFINED", 2, NO_DATA, UNDEFINED_SYMBOL);
addDataItem(" INCLUDE 0", 2, NO_DATA, FILE_0_NOT_FOUND);
addDataItem(" INCLUDE +0", 2, NO_DATA, FILE_0_NOT_FOUND);
addDataItem(" INCLUDE 0.0", 2, NO_DATA, FILE_0_0_NOT_FOUND);
addDataItem(" INCLUDE 'A'", 2, NO_DATA, FILE_A_NOT_FOUND);
addDataItem(" INCLUDE 'C'", 4, FILE_C_OUTPUT);
addDataItem(" INCLUDE 'C',UNREGISTERED", 4, FILE_C_OUTPUT, new ArchitectureNotRegisteredErrorMessage("UNREGISTERED"));
addDataItem(" INCLUDE 'D'", 4, FILE_D_OUTPUT, new NotSupportedOnArchitectureErrorMessage());
addDataItem(" INCLUDE 'D',CPU32", 4, FILE_D_OUTPUT);
addDataItem(" INCLUDE 'D',CPU32,1", 4, FILE_D_OUTPUT, WRONG_NUMBER_OF_OPERANDS);
// TODO: test with a built-in function
//addDataItem(" INCLUDE STRLEN", 2, NO_DATA, new FunctionCannotBeConvertedToStringErrorMessage());
}
/**
* Gets the test data for this parameterized test.
*
* @return the test data
*/
@Nonnull
@Parameters
public static List<Object[]> data() {
return TEST_DATA;
}
private static void addDataItem(@Nonnull String code, int steps, @Nonnull byte[] output) {
addDataItem(code, steps, output, (AssemblyMessage) null);
}
private static void addDataItem(@Nonnull String code, int steps, @Nonnull byte[] output,
@CheckForNull AssemblyMessage expectedMessage) {
TEST_DATA.add(new Object[] { code, steps, output, expectedMessage });
}
/**
* Initializes a new IncbinIncludeTest.
*
* @param code
* assembly code to assemble
* @param steps
* the number of steps the program is expected to take to assemble completely
* @param output
* the program's output
* @param expectedMessage
* an {@link AssemblyMessage} that is expected to be generated while assembling the code
*/
public IncbinIncludeTest(@Nonnull String code, int steps, @Nonnull byte[] output, @CheckForNull AssemblyMessage expectedMessage) {
super(code, steps, output, M68KArchitecture.MC68000, expectedMessage, null, null);
}
@Override
protected Environment getEnvironment() {
return super.getEnvironment().addArchitecture(M68KArchitecture.CPU32);
}
@Nonnull
@Override
protected FileFetcher getFileFetcher() {
return FILE_FETCHER;
}
}