/* ptrnt - print labels on linux Copyright (C) 2025 Moritz Martinius This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "PtouchPrint.hpp" #include "cli/interface/ICliParser.hpp" #include "core/interface/IPrinterService.hpp" #include "printers/interface/IPrinterDriver.hpp" using ::testing::_; using ::testing::NiceMock; using ::testing::Return; using ::testing::ReturnRef; namespace ptprnt { // Mock CLI Parser class MockCliParser : public cli::ICliParser { public: MOCK_METHOD(int, parse, (int argc, char** argv), (override)); MOCK_METHOD(const cli::CliOptions&, getOptions, (), (const, override)); cli::CliOptions options; }; // Mock Printer Service class MockPrinterService : public core::IPrinterService { public: MOCK_METHOD(bool, initialize, (), (override)); MOCK_METHOD(std::vector>, detectPrinters, (), (override)); MOCK_METHOD(std::shared_ptr, selectPrinter, (const std::string& selection), (override)); MOCK_METHOD(std::shared_ptr, getCurrentPrinter, (), (const, override)); MOCK_METHOD(bool, printLabel, (std::unique_ptr label), (override)); }; // Mock Printer Driver class MockPrinterDriver : public IPrinterDriver { public: MOCK_METHOD(std::string_view, getDriverName, (), (override)); MOCK_METHOD(std::string_view, getName, (), (override)); MOCK_METHOD(libusbwrap::usbId, getUsbId, (), (override)); MOCK_METHOD(std::string_view, getVersion, (), (override)); MOCK_METHOD(PrinterInfo, getPrinterInfo, (), (override)); MOCK_METHOD(PrinterStatus, getPrinterStatus, (), (override)); MOCK_METHOD(bool, attachUsbDevice, (std::shared_ptr usbHndl), (override)); MOCK_METHOD(bool, detachUsbDevice, (), (override)); MOCK_METHOD(bool, printBitmap, (const graphics::Bitmap& bitmap), (override)); MOCK_METHOD(bool, printMonochromeData, (const graphics::MonochromeData& data), (override)); MOCK_METHOD(bool, printLabel, (std::unique_ptr label), (override)); MOCK_METHOD(bool, print, (), (override)); }; class PtouchPrintTest : public ::testing::Test { protected: void SetUp() override { mockCliParser = std::make_unique>(); mockPrinterService = std::make_unique>(); // Store raw pointers for setting expectations cliParserPtr = mockCliParser.get(); printerServicePtr = mockPrinterService.get(); // Default behavior: parse succeeds and returns empty options ON_CALL(*cliParserPtr, parse(_, _)).WillByDefault(Return(0)); ON_CALL(*cliParserPtr, getOptions()).WillByDefault(ReturnRef(cliParserPtr->options)); ON_CALL(*printerServicePtr, initialize()).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, printLabel(_)).WillByDefault(Return(true)); } void TearDown() override {} std::unique_ptr mockCliParser; std::unique_ptr mockPrinterService; MockCliParser* cliParserPtr; MockPrinterService* printerServicePtr; }; // Test: Constructor with default implementations TEST_F(PtouchPrintTest, ConstructorDefault) { EXPECT_NO_THROW(PtouchPrint app("v1.0.0")); } // Test: Constructor with custom implementations TEST_F(PtouchPrintTest, ConstructorCustom) { EXPECT_NO_THROW({ PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); }); } // Test: init with successful parse TEST_F(PtouchPrintTest, InitSuccess) { PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; int result = app.init(1, argv); EXPECT_EQ(result, 0); } // Test: init with parse returning help (should return 1) TEST_F(PtouchPrintTest, InitHelp) { ON_CALL(*cliParserPtr, parse(_, _)).WillByDefault(Return(1)); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt", (char*)"-h"}; int result = app.init(2, argv); EXPECT_EQ(result, 1); // Clean exit } // Test: init with parse error (should return -1) TEST_F(PtouchPrintTest, InitParseError) { ON_CALL(*cliParserPtr, parse(_, _)).WillByDefault(Return(-1)); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt", (char*)"--invalid"}; int result = app.init(2, argv); EXPECT_EQ(result, -1); // Error } // Test: init with printer service initialization failure TEST_F(PtouchPrintTest, InitPrinterServiceFailure) { ON_CALL(*printerServicePtr, initialize()).WillByDefault(Return(false)); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; int result = app.init(1, argv); EXPECT_EQ(result, -1); // Error } // Test: run with list drivers option TEST_F(PtouchPrintTest, RunListDrivers) { cliParserPtr->options.listDrivers = true; PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with no commands (should warn but succeed) TEST_F(PtouchPrintTest, RunNoCommands) { auto mockPrinter = std::make_shared>(); PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.commands.clear(); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with printer selection failure TEST_F(PtouchPrintTest, RunPrinterSelectionFailure) { ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(nullptr)); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Test"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, -1); } // Test: run with simple text command TEST_F(PtouchPrintTest, RunSimpleText) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Hello"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with font and text commands TEST_F(PtouchPrintTest, RunWithFormatting) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.commands.push_back({cli::CommandType::Font, "monospace"}); cliParserPtr->options.commands.push_back({cli::CommandType::FontSize, "48"}); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Formatted"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with alignment commands TEST_F(PtouchPrintTest, RunWithAlignment) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.commands.push_back({cli::CommandType::HAlign, "center"}); cliParserPtr->options.commands.push_back({cli::CommandType::VAlign, "middle"}); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Centered"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with invalid alignment (should handle gracefully) TEST_F(PtouchPrintTest, RunWithInvalidAlignment) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.commands.push_back({cli::CommandType::HAlign, "invalid"}); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Test"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); // Should handle gracefully } // Test: run with new label command TEST_F(PtouchPrintTest, RunWithNewLabel) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "First"}); cliParserPtr->options.commands.push_back({cli::CommandType::NewLabel, ""}); cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Second"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with verbose option TEST_F(PtouchPrintTest, RunWithVerbose) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.verbose = true; cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Test"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } // Test: run with trace option TEST_F(PtouchPrintTest, RunWithTrace) { auto mockPrinter = std::make_shared>(); PrinterInfo info{.driverName = "Test", .name = "Test", .version = "v1.0", .usbId = {0, 0}, .pixelLines = 128}; PrinterStatus status{.tapeWidthMm = 12}; ON_CALL(*mockPrinter, getPrinterInfo()).WillByDefault(Return(info)); ON_CALL(*mockPrinter, getPrinterStatus()).WillByDefault(Return(status)); ON_CALL(*mockPrinter, printLabel(_)).WillByDefault(Return(true)); ON_CALL(*printerServicePtr, selectPrinter(_)).WillByDefault(Return(mockPrinter)); cliParserPtr->options.trace = true; cliParserPtr->options.commands.push_back({cli::CommandType::Text, "Test"}); PtouchPrint app("v1.0.0", std::move(mockCliParser), std::move(mockPrinterService)); char* argv[] = {(char*)"ptprnt"}; app.init(1, argv); int result = app.run(); EXPECT_EQ(result, 0); } } // namespace ptprnt