2014-12-06 20 views
2

我想寫GameBoy模擬器,但我不知道如何測試我的CPU_LR39502類。爲了避免巨大的if-else-IF /開關case語句,我想出了主意,把操作碼仿成地圖,這需要操作碼的關鍵:在C++中編寫Gameboy模擬器,如何測試操作碼(Google Test Framework)?

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中時立即編寫opcode的實現,但爲了使其可測試,我將實現作爲成員函數(此處爲bitwiseRotationLeft,作爲示例)編寫,並且在lambda中將其稱爲 - 我不確定這是否是正確的做法。

目前,測試一些實現,我有這樣的事情(使用谷歌測試框架):

#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的經驗,而我的直覺告訴我必須有更好的方式來實現它。任何線索將欣然讚賞:)

回答

1

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

寫測試作爲成員夾具類:

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); 
} 
+0

我錯過了,我喜歡這個解決方案,謝謝:) – pablo432 2014-12-07 11:43:35

1

可能不是問題的答案你尋找,但你可以有條件使CPU​​ PARAMS公衆進行測試

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

感謝您的建議,但我敢肯定,這不是正確的方法。 – pablo432 2014-12-07 01:37:13

相關問題