Skip to content
Permalink
Browse files

Merge pull request #160 from Prof9/command-line-tests

Test cases with arbitrary command line arguments (e.g. -definelabel)
  • Loading branch information
Kingcom committed Feb 22, 2020
2 parents dfc2c01 + aa5b9a5 commit 6cae84f853f80f280c31e09aa94a523d8a5a4d28
@@ -129,6 +129,8 @@ add_library(armips STATIC
)

add_executable(armips-bin
Main/CommandLineInterface.cpp
Main/CommandLineInterface.h
Main/main.cpp
Main/Tests.cpp
Main/Tests.h
@@ -139,6 +141,8 @@ add_executable(armips-bin
set_target_properties(armips-bin PROPERTIES OUTPUT_NAME armips)

add_executable(armipstests
Main/CommandLineInterface.cpp
Main/CommandLineInterface.h
Main/main.cpp
Main/Tests.cpp
Main/Tests.h
@@ -112,7 +112,7 @@ bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symD
return true;
}

bool runArmips(ArmipsArguments& arguments)
bool runArmips(ArmipsArguments& settings)
{
// initialize and reset global data
Global.Section = 0;
@@ -137,28 +137,28 @@ bool runArmips(ArmipsArguments& arguments)

Arm.clear();

// process arguments
// process settings
Parser parser;
SymbolData symData;
TempData tempData;

Logger::setSilent(arguments.silent);
Logger::setErrorOnWarning(arguments.errorOnWarning);
Logger::setSilent(settings.silent);
Logger::setErrorOnWarning(settings.errorOnWarning);

if (!arguments.symFileName.empty())
symData.setNocashSymFileName(arguments.symFileName,arguments.symFileVersion);
if (!settings.symFileName.empty())
symData.setNocashSymFileName(settings.symFileName, settings.symFileVersion);

if (!arguments.tempFileName.empty())
tempData.setFileName(arguments.tempFileName);
if (!settings.tempFileName.empty())
tempData.setFileName(settings.tempFileName);

Token token;
for (size_t i = 0; i < arguments.equList.size(); i++)
for (size_t i = 0; i < settings.equList.size(); i++)
{
parser.addEquation(token,arguments.equList[i].name, arguments.equList[i].value);
parser.addEquation(token, settings.equList[i].name, settings.equList[i].value);
}

Global.symbolTable.addLabels(arguments.labels);
for (const LabelDefinition& label : arguments.labels)
Global.symbolTable.addLabels(settings.labels);
for (const LabelDefinition& label : settings.labels)
{
symData.addLabel(label.value, label.name);
}
@@ -168,20 +168,20 @@ bool runArmips(ArmipsArguments& arguments)

// run assembler
TextFile input;
switch (arguments.mode)
switch (settings.mode)
{
case ArmipsMode::FILE:
Global.memoryMode = false;
if (input.open(arguments.inputFileName,TextFile::Read) == false)
if (input.open(settings.inputFileName,TextFile::Read) == false)
{
Logger::printError(Logger::Error,L"Could not open file");
return false;
}
break;
case ArmipsMode::MEMORY:
Global.memoryMode = true;
Global.memoryFile = arguments.memoryFile;
input.openMemory(arguments.content);
Global.memoryFile = settings.memoryFile;
input.openMemory(settings.content);
break;
}

@@ -200,11 +200,11 @@ bool runArmips(ArmipsArguments& arguments)
}

// return errors
if (arguments.errorsResult != nullptr)
if (settings.errorsResult != nullptr)
{
StringList errors = Logger::getErrors();
for (size_t i = 0; i < errors.size(); i++)
arguments.errorsResult->push_back(errors[i]);
settings.errorsResult->push_back(errors[i]);
}

return result;
@@ -36,6 +36,7 @@ struct ArmipsArguments
std::wstring inputFileName;
std::wstring tempFileName;
std::wstring symFileName;
bool useAbsoluteFileNames;

// memory mode
std::shared_ptr<AssemblerFile> memoryFile;
@@ -44,10 +45,12 @@ struct ArmipsArguments
ArmipsArguments()
{
mode = ArmipsMode::FILE;
symFileVersion = 0;
errorOnWarning = false;
silent = false;
errorsResult = nullptr;
useAbsoluteFileNames = true;
}
};

bool runArmips(ArmipsArguments& arguments);
bool runArmips(ArmipsArguments& settings);
@@ -0,0 +1,176 @@
#include "stdafx.h"
#include "Core/Common.h"
#include "Core/Assembler.h"
#include "CommandLineInterface.h"

#if defined(_WIN64) || defined(__x86_64__) || defined(__amd64__)
#define ARMIPSNAME "ARMIPS64"
#else
#define ARMIPSNAME "ARMIPS"
#endif

static void printUsage(std::wstring executableName)
{
Logger::printLine(L"%s Assembler v%d.%d.%d (%s %s) by Kingcom",
ARMIPSNAME, ARMIPS_VERSION_MAJOR, ARMIPS_VERSION_MINOR, ARMIPS_VERSION_REVISION, __DATE__, __TIME__);
Logger::printLine(L"Usage: %s [optional parameters] <FILE>", executableName);
Logger::printLine(L"");
Logger::printLine(L"Optional parameters:");
Logger::printLine(L" -temp <TEMP> Output temporary assembly data to <TEMP> file");
Logger::printLine(L" -sym <SYM> Output symbol data in the sym format to <SYM> file");
Logger::printLine(L" -sym2 <SYM2> Output symbol data in the sym2 format to <SYM2> file");
Logger::printLine(L" -root <ROOT> Use <ROOT> as working directory during execution");
Logger::printLine(L" -equ <NAME> <VAL> Equivalent to \'<NAME> equ <VAL>\' in code");
Logger::printLine(L" -strequ <NAME> <VAL> Equivalent to \'<NAME> equ \"<VAL>\"\' in code");
Logger::printLine(L" -definelabel <NAME> <VAL> Equivalent to \'.definelabel <NAME>, <VAL>\' in code");
Logger::printLine(L" -erroronwarning Treat all warnings like errors");
Logger::printLine(L"");
Logger::printLine(L"File arguments:");
Logger::printLine(L" <FILE> Main assembly code file");
}

static bool parseArguments(const StringList& arguments, ArmipsArguments& settings)
{
size_t argpos = 0;
bool readflags = true;
while (argpos < arguments.size())
{
if (readflags && arguments[argpos][0] == L'-')
{
if (arguments[argpos] == L"--")
{
readflags = false;
argpos += 1;
}
else if (arguments[argpos] == L"-temp" && argpos + 1 < arguments.size())
{
settings.tempFileName = arguments[argpos + 1];
argpos += 2;
}
else if (arguments[argpos] == L"-sym" && argpos + 1 < arguments.size())
{
settings.symFileName = arguments[argpos + 1];
settings.symFileVersion = 1;
argpos += 2;
}
else if (arguments[argpos] == L"-sym2" && argpos + 1 < arguments.size())
{
settings.symFileName = arguments[argpos + 1];
settings.symFileVersion = 2;
argpos += 2;
}
else if (arguments[argpos] == L"-erroronwarning")
{
settings.errorOnWarning = true;
argpos += 1;
}
else if (arguments[argpos] == L"-equ" && argpos + 2 < arguments.size())
{
EquationDefinition def;
def.name = arguments[argpos + 1];
std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower);
def.value = arguments[argpos + 2];
settings.equList.push_back(def);
argpos += 3;
}
else if (arguments[argpos] == L"-strequ" && argpos + 2 < arguments.size())
{
EquationDefinition def;
def.name = arguments[argpos + 1];
std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower);
def.value = formatString(L"\"%s\"", arguments[argpos + 2]);
settings.equList.push_back(def);
argpos += 3;
}
else if (arguments[argpos] == L"-time")
{
Logger::printError(Logger::Warning, L"-time flag is deprecated");
argpos += 1;
}
else if (arguments[argpos] == L"-root" && argpos + 1 < arguments.size())
{
changeDirectory(arguments[argpos + 1]);
argpos += 2;
}
else if (arguments[argpos] == L"-definelabel" && argpos + 2 < arguments.size())
{
LabelDefinition def;

def.name = arguments[argpos + 1];
std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower);

int64_t value;
if (!stringToInt(arguments[argpos + 2], 0, arguments[argpos + 2].size(), value))
{
Logger::printError(Logger::Error, L"Invalid definelabel value '%s'\n", arguments[argpos + 2]);
printUsage(arguments[0]);
return false;
}
def.value = value;

settings.labels.push_back(def);
argpos += 3;
}
else {
Logger::printError(Logger::Error, L"Invalid command line argument '%s'\n", arguments[argpos]);
printUsage(arguments[0]);
return false;
}
}
else {
// only allow one input filename
if (settings.inputFileName == L"")
{
settings.inputFileName = arguments[argpos];
argpos++;
}
else {
Logger::printError(Logger::Error, L"Multiple input assembly files specified\n");
printUsage(arguments[0]);
return false;
}
}
}

// ensure input file was specified
if (settings.inputFileName == L"")
{
if (arguments.size() > 1)
Logger::printError(Logger::Error, L"Missing input assembly file\n");

printUsage(arguments[0]);
return false;
}

// turn input filename into an absolute path
if (settings.useAbsoluteFileNames && isAbsolutePath(settings.inputFileName) == false)
settings.inputFileName = formatString(L"%s/%s", getCurrentDirectory(), settings.inputFileName);

if (fileExists(settings.inputFileName) == false)
{
Logger::printError(Logger::Error, L"File '%s' not found\n", settings.inputFileName);
return false;
}
return true;
}

int runFromCommandLine(const StringList& arguments, ArmipsArguments settings)
{
if (parseArguments(arguments, settings) == false)
{
if (!settings.silent)
Logger::printLine(L"Cannot parse arguments; aborting.");

return 1;
}

if (runArmips(settings) == false)
{
if (!settings.silent)
Logger::printLine(L"Aborting.");

return 1;
}

return 0;
}
@@ -0,0 +1,4 @@
#pragma once
#include "Core/Assembler.h"

int runFromCommandLine(const StringList& arguments, ArmipsArguments settings = {});
@@ -1,4 +1,5 @@
#include "stdafx.h"
#include "CommandLineInterface.h"
#include "Tests.h"
#include "Util/Util.h"
#include "Core/Common.h"
@@ -143,19 +144,48 @@ bool TestRunner::executeTest(const std::wstring& dir, const std::wstring& testNa
std::wstring oldDir = getCurrentDirectory();
changeDirectory(dir);

ArmipsArguments args;
ArmipsArguments settings;
StringList errors;
int expectedRetVal = 0;
int retVal = 0;
bool checkRetVal = false;
bool result = true;
StringList args;

args.inputFileName = testName + L".asm";
args.tempFileName = testName + L".temp.txt";
args.errorsResult = &errors;
args.silent = true;
if (fileExists(L"commandLine.txt"))
{
TextFile f;
f.open(L"commandLine.txt",TextFile::Read);
std::wstring command = f.readLine();
f.close();

args = splitString(command,L' ',true);
checkRetVal = true;

// first word is error code, rest is arguments
expectedRetVal = std::stoi(args[0]);
args.erase(args.begin());
}
else
{
settings.inputFileName = testName + L".asm";
settings.tempFileName = testName + L".temp.txt";
}

settings.errorsResult = &errors;
settings.silent = true;
settings.useAbsoluteFileNames = false;

// may or may not be supposed to cause errors
runArmips(args);
retVal = runFromCommandLine(args, settings);

if (checkRetVal && retVal != expectedRetVal)
{
errorString += formatString(L"Exit code did not match: expected %S, got %S\n",expectedRetVal,retVal);
result = false;
}

// check errors
bool result = true;
if (fileExists(L"expected.txt"))
{
TextFile f;

0 comments on commit 6cae84f

Please sign in to comment.
You can’t perform that action at this time.