Progress with testing

This commit is contained in:
Sergio Martin 2024-01-12 19:14:57 +01:00
parent b346a9da5b
commit 147d0d4f52
17 changed files with 28499 additions and 150 deletions

21
extern/json/LICENSE.MIT vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013-2021 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1701
extern/json/README.md vendored Normal file

File diff suppressed because it is too large Load Diff

26601
extern/json/nlohmann/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

23
extern/sha1/README.md vendored
View File

@ -1,23 +0,0 @@
Taken from https://github.com/SourMesen/Mesen2/blob/master/Utilities/sha1.cpp
/*
sha1.hpp - source code of
============
SHA-1 in C++
============
100% Public Domain.
Original C Code
-- Steve Reid <steve@edmweb.com>
Small changes to fit into bglibs
-- Bruce Guenter <bruce@untroubled.org>
Translation to simpler C++ Code
-- Volker Diels-Grabsch <v@njh.eu>
Safety fixes
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
Header-only library
-- Zlatko Michailov <zlatko@michailov.org>
*/

41
extern/sha1/sha1.h vendored
View File

@ -1,41 +0,0 @@
/*
sha1.h - header of
============
SHA-1 in C++
============
100% Public Domain.
Original C Code
-- Steve Reid <steve@edmweb.com>
Small changes to fit into bglibs
-- Bruce Guenter <bruce@untroubled.org>
Translation to simpler C++ Code
-- Volker Grabsch <vog@notjusthosting.com>
Safety fixes
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
*/
#pragma once
#include <cstdint>
#include <iostream>
#include <string>
class SHA1
{
public:
SHA1();
void update(const std::string &s);
void update(std::istream &is);
std::string final();
static std::string GetHash(const std::string &filename);
static std::string GetHash(std::istream &stream);
static std::string GetHash(uint8_t* data, size_t size);
private:
uint32_t digest[5];
std::string buffer;
uint64_t transforms;
};

View File

@ -1,27 +1,34 @@
/*
sha1.cpp - source code of
sha1.h - header of
============
SHA-1 in C++
============
============
SHA-1 in C++
============
100% Public Domain.
100% Public Domain.
Original C Code
-- Steve Reid <steve@edmweb.com>
Small changes to fit into bglibs
-- Bruce Guenter <bruce@untroubled.org>
Translation to simpler C++ Code
-- Volker Grabsch <vog@notjusthosting.com>
Safety fixes
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
Taken and adapted to be a single header from https://github.com/SourMesen/Mesen2/blob/master/Utilities/sha1.cpp
by Sergio Martin (eien86)
Original C Code
-- Steve Reid <steve@edmweb.com>
Small changes to fit into bglibs
-- Bruce Guenter <bruce@untroubled.org>
Translation to simpler C++ Code
-- Volker Grabsch <vog@notjusthosting.com>
Safety fixes
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
*/
#include "sha1.h"
#pragma once
#include <cstdint>
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <vector>
static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */
static const size_t BLOCK_BYTES = BLOCK_INTS * 4;
@ -215,21 +222,23 @@ static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS
}
}
SHA1::SHA1()
class SHA1
{
public:
SHA1()
{
reset(digest, buffer, transforms);
}
void SHA1::update(const std::string &s)
void update(const std::string &s)
{
std::istringstream is(s);
update(is);
}
void SHA1::update(std::istream &is)
void update(std::istream &is)
{
char sbuf[BLOCK_BYTES];
uint32_t block[BLOCK_INTS];
@ -252,7 +261,7 @@ void SHA1::update(std::istream &is)
* Add padding and return the message digest.
*/
std::string SHA1::final()
std::string final()
{
/* Total number of hashed bits */
uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8;
@ -293,7 +302,7 @@ std::string SHA1::final()
}
std::string SHA1::GetHash(uint8_t* data, size_t size)
static std::string GetHash(uint8_t* data, size_t size)
{
std::stringstream ss;
ss.write((char*)data, size);
@ -303,17 +312,24 @@ std::string SHA1::GetHash(uint8_t* data, size_t size)
return checksum.final();
}
std::string SHA1::GetHash(std::istream &stream)
static std::string GetHash(std::istream &stream)
{
SHA1 checksum;
checksum.update(stream);
return checksum.final();
}
std::string SHA1::GetHash(const std::string &filename)
static std::string GetHash(const std::string &filename)
{
std::ifstream stream(filename.c_str(), std::ios::binary);
SHA1 checksum;
checksum.update(stream);
return checksum.final();
}
private:
uint32_t digest[5];
std::string buffer;
uint64_t transforms;
};

View File

@ -35,8 +35,7 @@ quickerNESCoreSrc = [
'source/core/Multi_Buffer.cpp',
'source/core/Nes_Emu.cpp',
'source/core/nes_ntsc.cpp',
'source/core/Nes_Vrc7.cpp',
'extern/sha1/sha1.cpp'
'source/core/Nes_Vrc7.cpp'
]
# quickerNES Core Configuration
@ -47,7 +46,7 @@ quickerNESCoreSrc = [
sources : quickerNESCoreSrc,
)
# Building playback/validation tool
# Building playback tool
quickerNESPlayerSrc = [
'extern/hqn/hqn.cpp',
@ -57,19 +56,23 @@ quickerNESPlayerSrc = [
'extern/hqn/options.cpp'
]
quickerNESPlayerDependency = declare_dependency(
compile_args : [ '-DNCURSES' ],
executable('player',
'source/player.cpp',
cpp_args : [ '-DNCURSES' ],
dependencies : [ quickerNESCoreDependency, dependency('sdl2'), dependency('SDL2_image') ],
include_directories : include_directories(['source']),
link_args : [ '-lncurses' ],
sources : quickerNESPlayerSrc
)
executable('player',
'source/player.cpp',
dependencies: quickerNESPlayerDependency,
)
# Building tester tool
tester = executable('tester',
'source/tester.cpp',
dependencies: [ quickerNESCoreDependency ],
include_directories: include_directories(['../extern/json'])
)
# Building tests
if get_option('buildTests') == true
subdir('tests')

View File

@ -4,6 +4,7 @@
#include <core/Nes_State.h>
#include <string>
#include <utils.hpp>
#include "sha1/sha1.hpp"
#define _LOW_MEM_SIZE 0x800
#define _HIGH_MEM_SIZE 0x2000
@ -26,7 +27,13 @@ class EmuInstance
// Loading ROM
std::string romData;
loadStringFromFile(romData, romFilePath.c_str());
bool status = loadStringFromFile(romData, romFilePath.c_str());
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", romFilePath.c_str());
// Calculating ROM hash value
_romSHA1String = SHA1::GetHash((uint8_t*)romData.data(), romData.size());
// Loading the rom into the emulator
Mem_File_Reader romReader(romData.data(), (int)romData.size());
Auto_File_Reader romFile(romReader);
auto result = _nes->load_ines(romFile);
@ -42,12 +49,14 @@ class EmuInstance
uint8_t* getLowMem() { return _nes->low_mem(); };
uint8_t* getNametableMem() { return _nes->nametable_mem(); };
uint8_t* getHighMem() { return _nes->high_mem();};
const std::string getRomSHA1() const { return _romSHA1String; };
void loadStateFile(const std::string& stateFilePath)
{
// Loading state data
std::string stateData;
if (loadStringFromFile(stateData, stateFilePath.c_str()) == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
bool status = loadStringFromFile(stateData, stateFilePath.c_str());
if (status == false) EXIT_WITH_ERROR("Could not find/read state file: %s\n", stateFilePath.c_str());
Mem_File_Reader stateReader(stateData.data(), (int)stateData.size());
Auto_File_Reader stateFile(stateReader);
@ -169,4 +178,7 @@ class EmuInstance
// State size for the given rom
size_t _stateSize;
// SHA1 rom hash
std::string _romSHA1String;
};

94
source/tester.cpp Normal file
View File

@ -0,0 +1,94 @@
#include <sstream>
#include <chrono>
#include "argparse/argparse.hpp"
#include "sha1/sha1.hpp"
#include "utils.hpp"
#include "nlohmann/json.hpp"
#include "emuInstance.hpp"
int main(int argc, char *argv[])
{
// Parsing command line arguments
argparse::ArgumentParser program("player", "1.0");
program.add_argument("scriptFile")
.help("Path to the test script file to run.")
.required();
// Try to parse arguments
try { program.parse_args(argc, argv); }
catch (const std::runtime_error &err) { EXIT_WITH_ERROR("%s\n%s", err.what(), program.help().str().c_str()); }
// Getting test script file path
std::string scriptFilePath = program.get<std::string>("scriptFile");
// Loading script file
std::string scriptJsonRaw;
if (loadStringFromFile(scriptJsonRaw, scriptFilePath.c_str()) == false) EXIT_WITH_ERROR("Could not find/read script file: %s\n", scriptFilePath.c_str());
// Parsing script
const auto scriptJson = nlohmann::json::parse(scriptJsonRaw);
// Getting rom file path
if (scriptJson.contains("Rom File") == false) EXIT_WITH_ERROR("Script file missing 'Rom File' entry\n");
if (scriptJson["Rom File"].is_string() == false) EXIT_WITH_ERROR("Script file 'Rom File' entry is not a string\n");
std::string romFilePath = scriptJson["Rom File"].get<std::string>();
// Getting initial state file path
if (scriptJson.contains("Initial State File") == false) EXIT_WITH_ERROR("Script file missing 'Initial State File' entry\n");
if (scriptJson["Initial State File"].is_string() == false) EXIT_WITH_ERROR("Script file 'Initial State File' entry is not a string\n");
std::string initialStateFilePath = scriptJson["Initial State File"].get<std::string>();
// Getting sequence file path
if (scriptJson.contains("Sequence File") == false) EXIT_WITH_ERROR("Script file missing 'Sequence File' entry\n");
if (scriptJson["Sequence File"].is_string() == false) EXIT_WITH_ERROR("Script file 'Sequence File' entry is not a string\n");
std::string sequenceFilePath = scriptJson["Sequence File"].get<std::string>();
// Getting expected ROM SHA1 hash
if (scriptJson.contains("Expected ROM SHA1") == false) EXIT_WITH_ERROR("Script file missing 'Expected ROM SHA1' entry\n");
if (scriptJson["Expected ROM SHA1"].is_string() == false) EXIT_WITH_ERROR("Script file 'Expected ROM SHA1' entry is not a string\n");
std::string expectedROMSHA1 = scriptJson["Expected ROM SHA1"].get<std::string>();
// Creating emulator instance
auto e = EmuInstance(romFilePath, initialStateFilePath);
// Getting actual ROM SHA1
auto romSHA1 = e.getRomSHA1();
// Checking with the expected SHA1 hash
if (romSHA1 != expectedROMSHA1) EXIT_WITH_ERROR("Wrong ROM SHA1. Found: '%s', Expected: '%s'\n", romSHA1.c_str(), expectedROMSHA1.c_str());
// Loading sequence file
std::string sequenceRaw;
if (loadStringFromFile(sequenceRaw, sequenceFilePath.c_str()) == false) EXIT_WITH_ERROR("[ERROR] Could not find or read from input sequence file: %s\n", sequenceFilePath.c_str());
// Building sequence information
const auto sequence = split(sequenceRaw, ' ');
// Getting sequence lenght
const auto sequenceLength = sequence.size();
// Printing test information
printf("[] Running Script: '%s'\n", scriptFilePath.c_str());
printf("[] ROM File: '%s'\n", romFilePath.c_str());
printf("[] ROM SHA1: '%s'\n", romSHA1.c_str());
printf("[] Sequence File: '%s'\n", sequenceFilePath.c_str());
printf("[] Sequence Length: %lu\n", sequenceLength);
fflush(stdout);
// Actually running the sequence
auto t0 = std::chrono::high_resolution_clock::now();
for (const auto& input : sequence) e.advanceState(input);
auto tf = std::chrono::high_resolution_clock::now();
// Calculating running time
auto dt = std::chrono::duration_cast<std::chrono::nanoseconds>(tf - t0).count();
double elapsedTimeSeconds = (double)dt * 1.0e-9;
// Printing time information
printf("[] Elapsed time: %3.3fs\n", (double)dt * 1.0e-9);
printf("[] Performance: %.3f steps / s\n", (double)sequenceLength / elapsedTimeSeconds);
return 0;
}

View File

@ -1,36 +0,0 @@
#include "argparse/argparse.hpp"
#include "sha1/sha1.h"
#include "utils.hpp"
#include "emuInstance.hpp"
#include <sstream>
int main(int argc, char *argv[])
{
// Parsing command line arguments
argparse::ArgumentParser program("player", "1.0");
program.add_argument("romFile")
.help("Path to the rom file to run.")
.required();
// Try to parse arguments
try { program.parse_args(argc, argv); }
catch (const std::runtime_error &err) { EXIT_WITH_ERROR("%s\n%s", err.what(), program.help().str().c_str()); }
// Getting ROM file path
std::string romFilePath = program.get<std::string>("romFile");
// Getting ROM data
std::string romData;
auto status = loadStringFromFile(romData, romFilePath.c_str());
if (status == false) EXIT_WITH_ERROR("Could not read rom file: '%s'\n", romFilePath.c_str());
// Getting SHA1 String
std::string sha1String = SHA1::GetHash((uint8_t*)romData.data(), romData.size());
// Printing ROM SHA1
printf("%s\n", sha1String.c_str());
return 0;
}

View File

@ -0,0 +1,7 @@
{
"Rom File": "Castlevania (U) (PRG0) [!].nes",
"Expected ROM SHA1": "A31B8BD5B370A9103343C866F3C2B2998E889341",
"Initial State File": "",
"Final State File": "anyPercent.final.state",
"Sequence File": "anyPercent.sol"
}

View File

@ -0,0 +1,6 @@
nomalloc = environment({'MALLOC_PERTURB_': '0'})
# Castlevania 1 Tests
game = 'castlevania1'
goal = 'anyPercent'
test(goal, tester, workdir : meson.current_source_dir(), args : [ goal + '.test'], suite: [ game, goal ])

1
tests/games/meson.build Normal file
View File

@ -0,0 +1 @@
subdir('castlevania1')

View File

@ -1,10 +1,3 @@
nomalloc = environment({'MALLOC_PERTURB_': '0'})
# Adding game ending checks
endingChecker = executable('endingChecker',
files(['endingChecker.cpp']),
dependencies: [ quickerNESCoreDependency ]
)
testSuite = [ 'gameEndings' ]
test('castlevania1AnyPercent', endingChecker, workdir : meson.current_source_dir(), args : ['roms/Castlevania (U) (PRG0) [!].nes'], suite: testSuite)
subdir('games')

View File

@ -1,6 +0,0 @@
Solution sequence taken from https://tasvideos.org/UserFiles/Info/637879644789320048 by eien86
Rom Information
Name: Castlevania (U) (PRG0) [!].nes
SHA1: A31B8BD5B370A9103343C866F3C2B2998E889341
MD5:00D93C9F6B8AEFB8B6C02B20147DF4EC