From 7fc84c421760db4bf2e4253cd6ea020f3b9a0bb3 Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Sun, 28 Aug 2022 19:25:08 +0200 Subject: [PATCH] Initial unit tests based on GoogleTest and GoogleMock (#802) * Initial dummy test * Makefile update * Make protected method visible * Test update * Test update * Updated mode page device * Only build tests with explicit test target * make test builds and executes tests * Replaced constant * Added TODO * Merged develop * Added unit test * Comment update * Unit test update * Added tests * Added tests * Test cleanup * Updated error handling * Reverted MODE SENSE change * Reverted last change * Use GoogleMock * Comment update * Signature update * Cleanup * Cleanup * Further cleanup * Removed obsolete comment * Updated error handling * Cleanup * Added TODO * Added test * Added test * Renaming * Added test * Cleanup * Header update * Added two tests * Renaming * Fixed test argument order * Namespaces are needed in order to avoid name clashes * Added test * Added tests * Added tests * Added tests * Added tests * Updated host services test * Merge with develop * Moved code * Renaming * Added tests * Added tests * Initial device tests * Test cleanup * Added test * Removed cast * RASCSI_TEST target has to depend on SRC_PROTOBUF --- src/raspberrypi/Makefile | 36 ++- src/raspberrypi/devices/device_factory.h | 1 - src/raspberrypi/devices/host_services.h | 5 +- src/raspberrypi/test/device_factory_test.cpp | 286 +++++++++++++++++++ src/raspberrypi/test/device_test.cpp | 72 +++++ src/raspberrypi/test/mode_pages_test.cpp | 184 ++++++++++++ 6 files changed, 571 insertions(+), 13 deletions(-) create mode 100644 src/raspberrypi/test/device_factory_test.cpp create mode 100644 src/raspberrypi/test/device_test.cpp create mode 100644 src/raspberrypi/test/mode_pages_test.cpp diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index a623b12a..99473a8e 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -64,6 +64,7 @@ RASCTL = rasctl RASDUMP = rasdump SASIDUMP = sasidump SCSIMON = scsimon +RASCSI_TEST = rascsi_test SYSTEMD_CONF = /etc/systemd/system/rascsi.service RSYSLOG_CONF = /etc/rsyslog.d/rascsi.conf @@ -90,21 +91,22 @@ SRC_PROTOC = \ SRC_PROTOBUF = \ rascsi_interface.pb.cpp -SRC_RASCSI = \ - rascsi.cpp \ - scsi.cpp \ +SRC_RASCSI_CORE = scsi.cpp \ gpiobus.cpp \ filepath.cpp \ - fileio.cpp\ + fileio.cpp \ rascsi_version.cpp \ rascsi_image.cpp \ rascsi_response.cpp \ rasutil.cpp \ protobuf_util.cpp \ localizer.cpp -SRC_RASCSI += $(shell find ./controllers -name '*.cpp') -SRC_RASCSI += $(shell find ./devices -name '*.cpp') -SRC_RASCSI += $(SRC_PROTOBUF) +SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp') +SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp') +SRC_RASCSI_CORE += $(SRC_PROTOBUF) + +SRC_RASCSI = rascsi.cpp +SRC_RASCSI += $(SRC_RASCSI_CORE) SRC_SCSIMON = \ scsimon.cpp \ @@ -128,7 +130,7 @@ SRC_RASDUMP = \ scsi.cpp \ gpiobus.cpp \ filepath.cpp \ - fileio.cpp\ + fileio.cpp \ rascsi_version.cpp SRC_SASIDUMP = \ @@ -136,11 +138,16 @@ SRC_SASIDUMP = \ scsi.cpp \ gpiobus.cpp \ filepath.cpp \ - fileio.cpp\ + fileio.cpp \ rascsi_version.cpp + +SRC_RASCSI_TEST = $(shell find ./test -name '*.cpp') +SRC_RASCSI_TEST += $(SRC_RASCSI_CORE) + + vpath %.h ./ ./controllers ./devices ./monitor -vpath %.cpp ./ ./controllers ./devices ./monitor +vpath %.cpp ./ ./controllers ./devices ./monitor ./test vpath %.o ./$(OBJDIR) vpath ./$(BINDIR) @@ -150,13 +157,14 @@ OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o))) OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o))) OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) +OBJ_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o))) GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h # The following will include all of the auto-generated dependency files (*.d) # if they exist. This will trigger a rebuild of a source file if a header changes -ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON)) +ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST)) -include $(ALL_DEPS) $(OBJDIR) $(BINDIR): @@ -180,6 +188,9 @@ $(SRC_PROTOBUF): $(SRC_PROTOC) all: $(BIN_ALL) docs ALL: all +test: $(BINDIR)/$(RASCSI_TEST) + $(BINDIR)/$(RASCSI_TEST) + docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt $(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI) | $(BINDIR) @@ -197,6 +208,9 @@ $(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR) $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread +$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_TEST) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_TEST) -lpcap -lprotobuf -lgmock -lgtest -lgtest_main + # Phony rules for building individual utilities .PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON) diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h index 5628f3b8..46715119 100644 --- a/src/raspberrypi/devices/device_factory.h +++ b/src/raspberrypi/devices/device_factory.h @@ -26,7 +26,6 @@ class Device; class DeviceFactory { -private: DeviceFactory(); ~DeviceFactory() {} diff --git a/src/raspberrypi/devices/host_services.h b/src/raspberrypi/devices/host_services.h index 73c21325..826b4b0e 100644 --- a/src/raspberrypi/devices/host_services.h +++ b/src/raspberrypi/devices/host_services.h @@ -34,12 +34,15 @@ public: bool SupportsFile() const override { return false; } +protected: + + void AddModePages(map>&, int, bool) const override; + private: typedef ModePageDevice super; Dispatcher dispatcher; - void AddModePages(map>&, int, bool) const override; void AddRealtimeClockPage(map>&, bool) const; }; diff --git a/src/raspberrypi/test/device_factory_test.cpp b/src/raspberrypi/test/device_factory_test.cpp new file mode 100644 index 00000000..821b29d6 --- /dev/null +++ b/src/raspberrypi/test/device_factory_test.cpp @@ -0,0 +1,286 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +// Unit tests based on GoogleTest and GoogleMock +// +//--------------------------------------------------------------------------- + +#include + +#include "rascsi_version.h" +#include "../devices/device.h" +#include "../devices/device_factory.h" + +using namespace rascsi_interface; + +namespace DeviceFactoryTest +{ + +DeviceFactory& device_factory = DeviceFactory::instance(); + +TEST(DeviceFactoryTest, GetTypeForFile) +{ + EXPECT_EQ(device_factory.GetTypeForFile("test.hdf"), SAHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.hds"), SCHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.HDS"), SCHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.hda"), SCHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.hdn"), SCHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.hdi"), SCHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.nhd"), SCHD); + EXPECT_EQ(device_factory.GetTypeForFile("test.hdr"), SCRM); + EXPECT_EQ(device_factory.GetTypeForFile("test.mos"), SCMO); + EXPECT_EQ(device_factory.GetTypeForFile("test.iso"), SCCD); + EXPECT_EQ(device_factory.GetTypeForFile("test.suffix.iso"), SCCD); + EXPECT_EQ(device_factory.GetTypeForFile("bridge"), SCBR); + EXPECT_EQ(device_factory.GetTypeForFile("daynaport"), SCDP); + EXPECT_EQ(device_factory.GetTypeForFile("printer"), SCLP); + EXPECT_EQ(device_factory.GetTypeForFile("services"), SCHS); + EXPECT_EQ(device_factory.GetTypeForFile("unknown"), UNDEFINED); + EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED); +} + +TEST(DeviceFactoryTest, GetSectorSizes) +{ + unordered_set sector_sizes; + + sector_sizes = device_factory.GetSectorSizes("SCHD"); + EXPECT_EQ(4, sector_sizes.size()); + sector_sizes = device_factory.GetSectorSizes(SCHD); + EXPECT_EQ(4, sector_sizes.size()); + + EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(1024) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(4096) != sector_sizes.end()); + + sector_sizes = device_factory.GetSectorSizes("SCRM"); + EXPECT_EQ(4, sector_sizes.size()); + sector_sizes = device_factory.GetSectorSizes(SCRM); + EXPECT_EQ(4, sector_sizes.size()); + + EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(1024) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(4096) != sector_sizes.end()); + + sector_sizes = device_factory.GetSectorSizes("SCMO"); + EXPECT_EQ(4, sector_sizes.size()); + sector_sizes = device_factory.GetSectorSizes(SCMO); + EXPECT_EQ(4, sector_sizes.size()); + + EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(1024) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(4096) != sector_sizes.end()); + + sector_sizes = device_factory.GetSectorSizes("SCCD"); + EXPECT_EQ(2, sector_sizes.size()); + sector_sizes = device_factory.GetSectorSizes(SCCD); + EXPECT_EQ(2, sector_sizes.size()); + + EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end()); + EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end()); +} + +TEST(DeviceFactoryTest, UnknownDeviceType) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "test"); + EXPECT_EQ(nullptr, device); +} + +TEST(DeviceFactoryTest, SCHD_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "test.hda"); + EXPECT_NE(nullptr, device); + EXPECT_TRUE(device->SupportsFile()); + EXPECT_FALSE(device->SupportsParams()); + EXPECT_TRUE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_FALSE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_FALSE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_TRUE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("QUANTUM", device->GetVendor()) << "Invalid default vendor for Apple drive"; + EXPECT_EQ("FIREBALL", device->GetProduct()) << "Invalid default vendor for Apple drive"; + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCRM_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "test.hdr"); + EXPECT_NE(nullptr, device); + EXPECT_TRUE(device->SupportsFile()); + EXPECT_FALSE(device->SupportsParams()); + EXPECT_TRUE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_TRUE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_TRUE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_TRUE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("RaSCSI", device->GetVendor()); + EXPECT_EQ("SCSI HD (REM.)", device->GetProduct()); + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCMO_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "test.mos"); + EXPECT_NE(nullptr, device); + EXPECT_TRUE(device->SupportsFile()); + EXPECT_FALSE(device->SupportsParams()); + EXPECT_TRUE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_TRUE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_TRUE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_TRUE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("RaSCSI", device->GetVendor()); + EXPECT_EQ("SCSI MO", device->GetProduct()); + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCCD_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "test.iso"); + EXPECT_NE(nullptr, device); + EXPECT_TRUE(device->SupportsFile()); + EXPECT_FALSE(device->SupportsParams()); + EXPECT_FALSE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_TRUE(device->IsReadOnly()); + EXPECT_TRUE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_TRUE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_TRUE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("RaSCSI", device->GetVendor()); + EXPECT_EQ("SCSI CD-ROM", device->GetProduct()); + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCBR_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "bridge"); + EXPECT_NE(nullptr, device); + EXPECT_FALSE(device->SupportsFile()); + EXPECT_TRUE(device->SupportsParams()); + EXPECT_FALSE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_FALSE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_FALSE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_FALSE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("RaSCSI", device->GetVendor()); + EXPECT_EQ("SCSI HOST BRIDGE", device->GetProduct()); + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCDP_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "daynaport"); + EXPECT_NE(nullptr, device); + EXPECT_FALSE(device->SupportsFile()); + EXPECT_TRUE(device->SupportsParams()); + EXPECT_FALSE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_FALSE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_FALSE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_FALSE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("Dayna", device->GetVendor()); + EXPECT_EQ("SCSI/Link", device->GetProduct()); + EXPECT_EQ("1.4a", device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCHS_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "services"); + EXPECT_NE(nullptr, device); + EXPECT_FALSE(device->SupportsFile()); + EXPECT_FALSE(device->SupportsParams()); + EXPECT_FALSE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_FALSE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_FALSE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_FALSE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("RaSCSI", device->GetVendor()); + EXPECT_EQ("Host Services", device->GetProduct()); + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +TEST(DeviceFactoryTest, SCLP_Device_Defaults) +{ + Device *device = device_factory.CreateDevice(UNDEFINED, "printer"); + EXPECT_NE(nullptr, device); + EXPECT_FALSE(device->SupportsFile()); + EXPECT_TRUE(device->SupportsParams()); + EXPECT_FALSE(device->IsProtectable()); + EXPECT_FALSE(device->IsProtected()); + EXPECT_FALSE(device->IsReadOnly()); + EXPECT_FALSE(device->IsRemovable()); + EXPECT_FALSE(device->IsRemoved()); + EXPECT_FALSE(device->IsLockable()); + EXPECT_FALSE(device->IsLocked()); + EXPECT_FALSE(device->IsStoppable()); + EXPECT_FALSE(device->IsStopped()); + + EXPECT_EQ("RaSCSI", device->GetVendor()); + EXPECT_EQ("SCSI PRINTER", device->GetProduct()); + EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), + device->GetRevision()); + + EXPECT_EQ(32, device->GetSupportedLuns()); +} + +} diff --git a/src/raspberrypi/test/device_test.cpp b/src/raspberrypi/test/device_test.cpp new file mode 100644 index 00000000..a40ecd70 --- /dev/null +++ b/src/raspberrypi/test/device_test.cpp @@ -0,0 +1,72 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +// Unit tests based on GoogleTest and GoogleMock +// +//--------------------------------------------------------------------------- + +#include +#include + +#include "gpiobus.h" +#include "../devices/scsihd.h" +#include "../devices/device_factory.h" + +using namespace rascsi_interface; + +namespace DeviceTest +{ + +class MockController : public SCSIDEV +{ + MOCK_METHOD(BUS::phase_t, Process, (int), (override)); + MOCK_METHOD(int, GetEffectiveLun, (), ()); + MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override)); + MOCK_METHOD(int, GetInitiatorId, (), ()); + MOCK_METHOD(void, SetUnit, (int), ()); + MOCK_METHOD(void, Connect, (int, BUS *), ()); + MOCK_METHOD(void, Status, (), ()); + MOCK_METHOD(void, DataIn, (), ()); + MOCK_METHOD(void, DataOut, (), ()); + MOCK_METHOD(void, BusFree, (), ()); + MOCK_METHOD(void, Selection, (), ()); + MOCK_METHOD(void, Command, (), ()); + MOCK_METHOD(void, MsgIn, (), ()); + MOCK_METHOD(void, MsgOut, (), ()); + MOCK_METHOD(void, Send, (), (override)); + MOCK_METHOD(bool, XferMsg, (int), ()); + MOCK_METHOD(bool, XferIn, (BYTE *), ()); + MOCK_METHOD(bool, XferOut, (bool), (override)); + MOCK_METHOD(void, ReceiveBytes, (), ()); + MOCK_METHOD(void, Execute, (), (override)); + MOCK_METHOD(void, FlushUnit, (), ()); + MOCK_METHOD(void, Receive, (), (override)); + MOCK_METHOD(bool, HasUnit, (), (const override)); + + FRIEND_TEST(DeviceTest, TestUnitReady); + + MockController() { } + ~MockController() { } +}; + +DeviceFactory& device_factory = DeviceFactory::instance(); + +TEST(DeviceTest, TestUnitReady) +{ + MockController controller; + Device *device = device_factory.CreateDevice(SCHD, "test"); + + controller.ctrl.cmd[0] = eCmdTestUnitReady; + + device->SetReady(false); + EXPECT_CALL(controller, Error); + EXPECT_TRUE(device->Dispatch(&controller)); + + // TODO Add tests for a device that is ready after the SASI code removal +} + +} diff --git a/src/raspberrypi/test/mode_pages_test.cpp b/src/raspberrypi/test/mode_pages_test.cpp new file mode 100644 index 00000000..c685c5e8 --- /dev/null +++ b/src/raspberrypi/test/mode_pages_test.cpp @@ -0,0 +1,184 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI Reloaded +// for Raspberry Pi +// +// Copyright (C) 2022 Uwe Seimet +// +// Unit tests based on GoogleTest and GoogleMock +// +//--------------------------------------------------------------------------- + +#include +#include + +#include "../devices/scsihd.h" +#include "../devices/scsihd_nec.h" +#include "../devices/scsicd.h" +#include "../devices/scsimo.h" +#include "../devices/host_services.h" + +using namespace std; + +namespace ModePagesTest +{ + +unordered_set sector_sizes; + +class MockModePageDevice : public ModePageDevice +{ +public: + + MockModePageDevice() : ModePageDevice("test") { } + ~MockModePageDevice() { } + + MOCK_METHOD(vector, Inquiry, (), (const, override)); + MOCK_METHOD(int, ModeSense6, (const DWORD *, BYTE *), (override)); + MOCK_METHOD(int, ModeSense10, (const DWORD *, BYTE *, int), (override)); + + void AddModePages(map>& pages, int page, bool) const override { + // Return dummy data for other pages than page 0 + if (page) { + vector buf(255); + pages[page] = buf; + } + } + + // Make protected methods visible for testing + // TODO Why does FRIEND_TEST not work for this method? + + int AddModePages(const DWORD *cdb, BYTE *buf, int max_length) { + return ModePageDevice::AddModePages(cdb, buf, max_length); + } +}; + +class MockSCSIHD : public SCSIHD +{ + FRIEND_TEST(ModePagesTest, SCSIHD_AddModePages); + + MockSCSIHD(const unordered_set& sector_sizes) : SCSIHD(sector_sizes, false) { }; + ~MockSCSIHD() { }; +}; + +class MockSCSIHD_NEC : public SCSIHD_NEC +{ + FRIEND_TEST(ModePagesTest, SCSIHD_NEC_AddModePages); + + MockSCSIHD_NEC(const unordered_set& sector_sizes) : SCSIHD_NEC(sector_sizes) { }; + ~MockSCSIHD_NEC() { }; +}; + +class MockSCSICD : public SCSICD +{ + FRIEND_TEST(ModePagesTest, SCSICD_AddModePages); + + MockSCSICD(const unordered_set& sector_sizes) : SCSICD(sector_sizes) { }; + ~MockSCSICD() { }; +}; + +class MockSCSIMO : public SCSIMO +{ + FRIEND_TEST(ModePagesTest, SCSIMO_AddModePages); + + MockSCSIMO(const unordered_set& sector_sizes, const unordered_map& geometries) + : SCSIMO(sector_sizes, geometries) { }; + ~MockSCSIMO() { }; +}; + +class MockHostServices : public HostServices +{ + FRIEND_TEST(ModePagesTest, HostServices_AddModePages); + + MockHostServices() { }; + ~MockHostServices() { }; +}; + +TEST(ModePagesTest, ModePageDevice_AddModePages) +{ + DWORD cdb[6]; + BYTE buf[512]; + + MockModePageDevice device; + cdb[2] = 0x3f; + + EXPECT_EQ(1, device.AddModePages(cdb, buf, 1)) << "Allocation length was not limited"; + + cdb[2] = 0x00; + EXPECT_EQ(0, device.AddModePages(cdb, buf, 12)) << "Data for non-existing code page 0 were returned"; +} + +TEST(ModePagesTest, SCSIHD_AddModePages) +{ + map> mode_pages; + + MockSCSIHD device(sector_sizes); + device.AddModePages(mode_pages, 0x3f, false); + + EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of code pages"; + EXPECT_EQ(12, mode_pages[1].size()); + EXPECT_EQ(24, mode_pages[3].size()); + EXPECT_EQ(24, mode_pages[4].size()); + EXPECT_EQ(12, mode_pages[8].size()); + EXPECT_EQ(30, mode_pages[48].size()); +} + +TEST(ModePagesTest, SCSIHD_NEC_AddModePages) +{ + map> mode_pages; + + MockSCSIHD_NEC device(sector_sizes); + device.AddModePages(mode_pages, 0x3f, false); + + EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of code pages"; + EXPECT_EQ(8, mode_pages[1].size()); + EXPECT_EQ(24, mode_pages[3].size()); + EXPECT_EQ(20, mode_pages[4].size()); + EXPECT_EQ(12, mode_pages[8].size()); + EXPECT_EQ(30, mode_pages[48].size()); +} + +TEST(ModePagesTest, SCSICD_AddModePages) +{ + map> mode_pages; + + MockSCSICD device(sector_sizes); + device.AddModePages(mode_pages, 0x3f, false); + + EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of code pages"; + EXPECT_EQ(12, mode_pages[1].size()); + EXPECT_EQ(24, mode_pages[3].size()); + EXPECT_EQ(24, mode_pages[4].size()); + EXPECT_EQ(12, mode_pages[8].size()); + EXPECT_EQ(8, mode_pages[13].size()); + EXPECT_EQ(16, mode_pages[14].size()); +} + +TEST(ModePagesTest, SCSIMO_AddModePages) +{ + map> mode_pages; + unordered_map geometries; + + MockSCSIMO device(sector_sizes, geometries); + device.AddModePages(mode_pages, 0x3f, false); + + EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of code pages"; + EXPECT_EQ(12, mode_pages[1].size()); + EXPECT_EQ(24, mode_pages[3].size()); + EXPECT_EQ(24, mode_pages[4].size()); + EXPECT_EQ(4, mode_pages[6].size()); + EXPECT_EQ(12, mode_pages[8].size()); + EXPECT_EQ(12, mode_pages[32].size()); +} + +TEST(ModePagesTest, HostServices_AddModePages) +{ + map> mode_pages; + + MockHostServices device; + device.AddModePages(mode_pages, 0x3f, false); + + EXPECT_EQ(1, mode_pages.size()) << "Unexpected number of code pages"; + EXPECT_EQ(10, mode_pages[32].size()); +} + +}