эмуляция — написание эмулятора Gameboy на C ++, как тестировать коды операций (Google Test Framework)?

Я пытаюсь написать эмулятор GameBoy, но я не уверен, как мне проверить мой класс CPU_LR39502. Чтобы избежать огромных операторов if-else-if / switch-case, мне пришла в голову идея поместить функт кода операции в карту, который принимает код операции в качестве ключа:

class Functor
{
std::function<void()> m_function;
public:
Functor(std::function<void()>&& function)
{
m_function = std::move(function);
}

void operator()()
{
m_function();
}
};

class BaseOpcodeFunctor : public Functor
{
unsigned char m_opcode;
std::string m_disasmString;
public:
BaseOpcodeFunctor(std::function<void()>&& function,
unsigned char opcode,
std::string&& disasmString)
: Functor(std::move(function)),
m_opcode(opcode),
m_disasmString(std::move(disasmString)) {}

std::string disasm()
{
return m_disasmString;
}

unsigned char getAssignedOpcode()
{
return m_opcode;
}

};

И пример этого:

class CPU_LR35902
{
...
std::map<unsigned char, BaseOpcodeFunctor> m_baseOpcodeMap;

public:
CPU_LR35902()
{
...
initializeBaseOpcodeMap();
}
...

private:
void addFunctorToBaseOpcodeMap(BaseOpcodeFunctor&& functor);
void initializeBaseOpcodeMap()
{
...
addFunctorToBaseOpcodeMap(BaseOpcodeFunctor([this]() {
bitwiseRotationLeft(REGISTER_A);
}, 0x07, "RLCA"));
}
void bitwiseRotationLeft(LR35902_8BIT_REGISTERS reg)
{
resetFlag(FLAG_Z);
resetFlag(FLAG_N);
resetFlag(FLAG_H);
setFlag(FLAG_C, registers_8bit.at(reg) >> 7);
registers_8bit.at(reg) <<= 1;
registers_8bit.at(reg) |= getFlag(FLAG_C);
}
...
};

И это как-то заставляет меня задуматься о двух проблемах. Я на самом деле хотел написать реализацию кода операции сразу при добавлении его в m_baseOpcodeMap, но чтобы сделать его тестируемым, я написал реализацию как функцию-член (здесь bitwiseRotationLeft в качестве примера) и вызываю ее в лямбда-выражении — и я не уверен, что это правильный подход.

В настоящее время, чтобы протестировать некоторые реализации, у меня есть что-то вроде этого (с помощью Google Test Framework):

#include "cpu_lr35902.h"#include <gtest/gtest.h>

class CPUTest : public ::testing::Test
{
protected:
CPU_LR35902 cpu_testable;
};

TEST_F(CPUTest, test_bitwiseRotationLeft)
{
cpu_testable.flags = 0;
cpu_testable.clearRegisters();

//0xA5 = 1010 0101, after: 0100 1011 = 0x4B
cpu_testable.registers_8bit.at(CPU_LR35902::REGISTER_A) = 0xA5;
cpu_testable.bitwiseRotationLeft(CPU_LR35902::REGISTER_A);
ASSERT_EQ(1, cpu_testable.getFlag(CPU_LR35902::FLAG_C));
ASSERT_EQ(0x4B, cpu_testable.registers_8bit.at(CPU_LR35902::REGISTER_A));

}

но чтобы получить доступ к приватным членам CPU_LR35902, я должен добавить

FRIEND_TEST(CPUTest, test_name);

в классе CPU_LR35902 — после этого я могу получить доступ к закрытым членам тестируемого класса в TEST_F, но не могу получить к ним доступ в классе CPUTest (для SetUp / TearDown). Учитывая тот факт, что у меня есть немного больше тестов, и у меня их будет много, я думаю, что добавление FRIEND_TEST для каждого теста делает все как-то плохо. Некоторое время я общаюсь с C ++, но у меня совершенно нет опыта использования Google Test Framework, и моя интуиция подсказывает мне, что должен быть лучший способ сделать это. Любые подсказки будут с благодарностью оценены 🙂

3

Решение

Как мне протестировать закрытые классы, не написав FRIEND_TEST ()?

Напишите тесты как члены класса прибора:

class Foo {
friend class FooTest;
...
};

class FooTest : public ::testing::Test {
protected:
...
void Test1() {...} // This accesses private members of class Foo.
void Test2() {...} // So does this one.
};

TEST_F(FooTest, Test1) {
Test1();
}

TEST_F(FooTest, Test2) {
Test2();
}

Это позволяет вам использовать только один класс для каждого тестового устройства без необходимости включать gtest в ваш заголовок (или ваш проект).

Вы также можете просто создать обычный класс, являющийся другом основного класса, который используется только тестами для доступа к закрытым членам.

class Foo {
friend class FooTesting;
...
};

class FooTesting {
public:

static int read_private_variable1( Foo& );
};

TEST_F(FooTest, Test1) {
Foo bar;
EXPECT_EQ( FooTesting::read_private_variable1( bar ), 5 );
}
3

Другие решения

Вероятно, не тот ответ, который вы ищете, но вы можете условно сделать параметры процессора общедоступными для тестирования.

class CPU_LR35902
{
...
public:
...
#ifndef TESTING_CPU
private:
#endif
...
};
1

По вопросам рекламы [email protected]