Skip to content

Commit

Permalink
tilem: Add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
timower committed Oct 16, 2023
1 parent 239b9ec commit 44c94ec
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 27 deletions.
2 changes: 2 additions & 0 deletions apps/tilem/Calculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ CalcState::~CalcState() {
tilem_calc_save_state(mCalc, nullptr, save);
fclose(save);
}

tilem_calc_free(mCalc);
}

TilemCalc*
Expand Down
2 changes: 1 addition & 1 deletion apps/tilem/Dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ErrorDialog : public rmlib::StatelessWidget<ErrorDialog> {
using namespace rmlib;
return Center((Border(
Cleared(Column(
Text("Loading ROMjk'" + romPath + "' failed"),
Text("Loading ROM '" + romPath + "' failed"),
Row(Padding(Button("Download", [&ctx] { Navigator::of(ctx).pop(); }),
Insets::all(10)),
Padding(Button("Exit", [&appCtx] { appCtx.stop(); }),
Expand Down
2 changes: 2 additions & 0 deletions apps/tilem/KeyPad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ KeypadRenderObject::handleInput(const Event& ev) {
return;
}

std::cout << "Releasing: " << it->second->scancode << "\n";
tilem_keypad_release_key(widget->calc, it->second->scancode);
keyPointers.erase(it);
return;
Expand All @@ -227,6 +228,7 @@ KeypadRenderObject::handleInput(const Event& ev) {
if (ev.isDown()) {
for (const auto& [rect, keyPtr] : keyLocations) {
if (rect.contains(ev.location)) {
std::cout << "Pressing: " << keyPtr->scancode << "\n";
tilem_keypad_press_key(widget->calc, keyPtr->scancode);
keyPointers.emplace(ev.id, keyPtr);
break;
Expand Down
25 changes: 19 additions & 6 deletions apps/tilem/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ ScreenRenderObject::ScreenRenderObject(const Screen& widget)
}
}

ScreenRenderObject::~ScreenRenderObject() {
if (timerID != -1) {
tilem_z80_remove_timer(widget->calc, timerID);
}

tilem_lcd_buffer_free(lcd);
tilem_lcd_buffer_free(oldLcd);
}

void
ScreenRenderObject::stateFrameCallback(TilemCalc* calc, void* selfPtr) {
auto* self = reinterpret_cast<ScreenRenderObject*>(selfPtr);
Expand All @@ -25,17 +34,21 @@ ScreenRenderObject::stateFrameCallback(TilemCalc* calc, void* selfPtr) {

void
ScreenRenderObject::addTimer(TilemCalc* calc) {
tilem_z80_add_timer(calc,
std::chrono::microseconds(frame_time).count(),
std::chrono::microseconds(frame_time).count(),
/* real time */ 1,
&stateFrameCallback,
this);
timerID = tilem_z80_add_timer(calc,
std::chrono::microseconds(frame_time).count(),
std::chrono::microseconds(frame_time).count(),
/* real time */ 1,
&stateFrameCallback,
this);
}

void
ScreenRenderObject::update(const Screen& newWidget) {
if (newWidget.calc != widget->calc && newWidget.calc != nullptr) {
if (timerID != -1) {
tilem_z80_remove_timer(widget->calc, timerID);
}

addTimer(newWidget.calc);
}
widget = &newWidget;
Expand Down
2 changes: 2 additions & 0 deletions apps/tilem/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Screen : public rmlib::Widget<ScreenRenderObject> {
class ScreenRenderObject : public rmlib::LeafRenderObject<Screen> {
public:
ScreenRenderObject(const Screen& widget);
~ScreenRenderObject();

static void stateFrameCallback(TilemCalc* calc, void* selfPtr);

Expand All @@ -35,4 +36,5 @@ class ScreenRenderObject : public rmlib::LeafRenderObject<Screen> {
private:
TilemLCDBuffer* lcd = nullptr;
TilemLCDBuffer* oldLcd = nullptr;
int timerID = -1;
};
1 change: 0 additions & 1 deletion libs/rMlib/include/UI/Stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class StackRenderObject : public MultiChildRenderObject<Stack<Child>> {
result.height = std::max(result.height, childSize.height);
}

std::cout << "Stack layout result: " << result << "\n";
return result;
}

Expand Down
9 changes: 7 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ add_subdirectory(unit)

if (COVERAGE)
set(COVERAGE_OUT "${CMAKE_BINARY_DIR}/coverage")

set(IGNORE_REGEX "'build/|stb_.*|test/unit/|tilem/emu'")

add_custom_target(make-coverage
COMMAND rm -rf "${COVERAGE_OUT}"
COMMAND mkdir "${COVERAGE_OUT}"
COMMAND ctest
COMMAND llvm-profdata merge -o merged.profdata
"${CMAKE_BINARY_DIR}/test/unit/default.profraw"

COMMAND llvm-cov show
--format html -output-dir "${COVERAGE_OUT}"
-ignore-filename-regex "'build/|stb_.*|test/unit/'"
-ignore-filename-regex "${IGNORE_REGEX}"
--instr-profile merged.profdata
-Xdemangler c++filt -Xdemangler -n
"${CMAKE_BINARY_DIR}/test/unit/unit-tests"

COMMAND llvm-cov export
--format lcov
-ignore-filename-regex "'build/|stb_.*|test/unit/'"
-ignore-filename-regex "${IGNORE_REGEX}"
--instr-profile merged.profdata
-Xdemangler c++filt -Xdemangler -n
"${CMAKE_BINARY_DIR}/test/unit/unit-tests" > "${CMAKE_BINARY_DIR}/report.lcov"
Expand Down
9 changes: 7 additions & 2 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ FetchContent_MakeAvailable(catch2)

add_executable(${PROJECT_NAME}
TestUnistdpp.cpp
TestRMLib.cpp)
TestRMLib.cpp
TestTilem.cpp)

target_compile_definitions(${PROJECT_NAME} PRIVATE
ASSETS_PATH="${CMAKE_CURRENT_SOURCE_DIR}/assets")

target_link_libraries(${PROJECT_NAME}
PRIVATE Catch2::Catch2WithMain unistdpp rMlib)
PRIVATE
Catch2::Catch2WithMain
unistdpp
rMlib
tilem::lib)

add_test(NAME "unit" COMMAND ${PROJECT_NAME})
74 changes: 74 additions & 0 deletions test/unit/TestTilem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include <catch2/catch_test_macros.hpp>

#include "rMLibTestHelper.h"

#include "Calculator.h"

#include <UI/Navigator.h>

#include <cstdlib>

using namespace rmlib;

class TemporaryDirectory {
public:
TemporaryDirectory() : dir("/tmp/tilem") {
REQUIRE_NOTHROW(std::filesystem::create_directory(dir));
}

~TemporaryDirectory() {
if (!dir.empty() && std::filesystem::is_directory(dir)) {
REQUIRE_NOTHROW(std::filesystem::remove_all(dir));
}
}

std::string dir;
};

TEST_CASE("Tilem", "[tilem][ui]") {
TemporaryDirectory tmp;
const auto romPath = tmp.dir + "/unit_test.rom";

auto ctx = TestContext::make();

ctx.pumpWidget(Navigator(Calculator(romPath)));

auto calc = ctx.findByType<Calculator>();

REQUIRE_THAT(calc, ctx.matchesGolden("tilem-init.png"));

ctx.pump();

REQUIRE_THAT(calc, ctx.matchesGolden("tilem-error.png"));

SECTION("Exit") {
ctx.tap(ctx.findByText("Exit"));
ctx.pump();
REQUIRE(ctx.shouldStop());
}

SECTION("Download & Run") {

ctx.tap(ctx.findByText("Download"));

// Wait for the download to finish.
ctx.pump();
while (!ctx.findByText("Downloading ROM '" + romPath + "' ...").empty()) {
ctx.pump();
}
ctx.pump(std::chrono::milliseconds(500));

REQUIRE_THAT(calc, ctx.matchesGolden("tilem-start.png"));

ctx.press(ctx.findByType<Keypad>(), { 0.01f, 0.99f });
ctx.pump(std::chrono::milliseconds(500));
ctx.release(ctx.findByType<Keypad>(), { 0.01f, 0.99f });

ctx.pump(std::chrono::milliseconds(500));
REQUIRE_THAT(calc, ctx.matchesGolden("tilem-on.png"));

ctx.tap(ctx.findByText("X"));
ctx.pump();
REQUIRE(ctx.shouldStop());
}
}
3 changes: 3 additions & 0 deletions test/unit/assets/tilem-error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/unit/assets/tilem-init.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/unit/assets/tilem-on.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions test/unit/assets/tilem-start.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 19 additions & 15 deletions test/unit/rMLibTestHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ using Finder = std::function<bool(const rmlib::RenderObject*)>;
template<typename RO = rmlib::RenderObject>
using FindResult = std::vector<RO*>;

struct Offset {
float x = 0.5f;
float y = 0.5f;
};

struct TestContext : rmlib::AppContext {
struct GoldenImageMatcher : Catch::Matchers::MatcherGenericBase {
static inline const bool update_golden_files = [] {
Expand Down Expand Up @@ -152,34 +157,33 @@ struct TestContext : rmlib::AppContext {
}

template<typename RO>
void press(const FindResult<RO>& ros) {
void sendInput(bool press, const FindResult<RO>& ros, Offset offset) {
REQUIRE(ros.size() == 1);
auto pos = ros.front()->getRect().center();
rmlib::Rect rect = ros.front()->getRect();
auto pos = rect.topLeft + rmlib::Point{ int(offset.x * rect.width()),
int(offset.y * rect.height()) };

SDL_Event ev;
ev.type = SDL_MOUSEBUTTONDOWN;
ev.type = press ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
ev.motion.x = pos.x / EMULATE_SCALE;
ev.motion.y = pos.y / EMULATE_SCALE;
ev.button.button = SDL_BUTTON_LEFT;
sendEv(ev);
}

template<typename RO>
void release(const FindResult<RO>& ros) {
REQUIRE(ros.size() == 1);
auto pos = ros.front()->getRect().center();
void press(const FindResult<RO>& ros, Offset offset = {}) {
sendInput(true, ros, offset);
}

SDL_Event ev;
ev.type = SDL_MOUSEBUTTONUP;
ev.motion.x = pos.x / EMULATE_SCALE;
ev.motion.y = pos.y / EMULATE_SCALE;
ev.button.button = SDL_BUTTON_LEFT;
sendEv(ev);
template<typename RO>
void release(const FindResult<RO>& ros, Offset offset = {}) {
sendInput(false, ros, offset);
}

template<typename RO>
void tap(const FindResult<RO>& ros) {
press(ros);
release(ros);
void tap(const FindResult<RO>& ros, Offset offset = {}) {
press(ros, offset);
release(ros, offset);
}
};

0 comments on commit 44c94ec

Please sign in to comment.