Writing Gameboy emulator in C++, how to test opcodes (Google Test Framework)?

1k views Asked by At

I'm trying to write GameBoy emulator, but I'm not sure how should I test my CPU_LR39502 class. To avoid huge if-else-if / switch-case statements, I came up with idea to put opcode functor into map, which takes opcode as key:

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;
    }

};

And example of it:

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);
    }
    ...
};

And this somehow makes me think about two problems. I actually wanted to write implementation of opcode immediately when adding it to m_baseOpcodeMap, but to make it testable, I wrote implementation as a member function (here bitwiseRotationLeft as example) and I call it in lambda - and I'm not sure if this is correct approach.

Currently, to test some implementations, I've got something like this (using 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));

}

but to get access to private members of CPU_LR35902, I have to add

FRIEND_TEST(CPUTest, test_name);

in CPU_LR35902 class - after that I can reach private members of tested class in TEST_F, but I can't access them in CPUTest class (for SetUp / TearDown). Considering the fact, that I've got a little bit more of tests and I'm going to have a plenty of them, I think that adding FRIEND_TEST for every test makes everything somehow bad-looking. I'm in touch with C++ for some time, but I've got completely zero experience in using Google Test Framework and my intuition tells me that there must be a better way to do it. Any clues will be gladly appreciated :)

2

There are 2 answers

0
mukunda On BEST ANSWER

How do I test private class members without writing FRIEND_TEST()s?

Write the tests as members of the fixture class:

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();
}

This makes it so you only have to friend one class per test fixture, without the need of including gtest in your header (or your project).

You can also just make a normal class that is a friend of the main class that is purely used by tests to access private members.

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 );
}
1
yogi On

Probable not the answer your looking for but you could conditional make the cpu params public for testing

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