diff --git a/src/printers/FakePrinter.cpp b/src/printers/FakePrinter.cpp index 8efe009..2e5c3e1 100644 --- a/src/printers/FakePrinter.cpp +++ b/src/printers/FakePrinter.cpp @@ -113,6 +113,11 @@ bool FakePrinter::printMonochromeData(const graphics::MonochromeData& data) { } bool FakePrinter::printLabel(const std::unique_ptr label) { + if (!label) { + spdlog::error("FakePrinter: Cannot print null label"); + return false; + } + // Convert label directly to MonochromeData // getRaw() returns data in Cairo surface coordinates matching getWidth() × getHeight() auto pixels = label->getRaw(); diff --git a/tests/fake_printer_test/fake_printer_test.cpp b/tests/fake_printer_test/fake_printer_test.cpp new file mode 100644 index 0000000..258743b --- /dev/null +++ b/tests/fake_printer_test/fake_printer_test.cpp @@ -0,0 +1,247 @@ +/* + 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 "graphics/Bitmap.hpp" +#include "graphics/Label.hpp" +#include "graphics/Monochrome.hpp" +#include "printers/FakePrinter.hpp" + +namespace ptprnt::printer { + +class FakePrinterTest : public ::testing::Test { + protected: + void SetUp() override { + printer = std::make_unique(); + } + + void TearDown() override { printer.reset(); } + + std::unique_ptr printer; +}; + +// Test: Get driver name +TEST_F(FakePrinterTest, GetDriverName) { + auto name = printer->getDriverName(); + EXPECT_FALSE(name.empty()); + EXPECT_EQ(name, "FakePrinter"); +} + +// Test: Get printer name +TEST_F(FakePrinterTest, GetName) { + auto name = printer->getName(); + EXPECT_FALSE(name.empty()); +} + +// Test: Get USB ID +TEST_F(FakePrinterTest, GetUsbId) { + auto usbId = printer->getUsbId(); + EXPECT_EQ(usbId.first, 0x0000); // Virtual printer - no USB ID + EXPECT_EQ(usbId.second, 0x0000); // Virtual printer - no USB ID +} + +// Test: Get printer version +TEST_F(FakePrinterTest, GetVersion) { + auto version = printer->getVersion(); + EXPECT_FALSE(version.empty()); +} + +// Test: Get printer info +TEST_F(FakePrinterTest, GetPrinterInfo) { + auto info = printer->getPrinterInfo(); + + EXPECT_FALSE(info.driverName.empty()); + EXPECT_FALSE(info.name.empty()); + EXPECT_GT(info.pixelLines, 0); +} + +// Test: Get printer status without device +TEST_F(FakePrinterTest, GetPrinterStatusWithoutDevice) { + auto status = printer->getPrinterStatus(); + + // FakePrinter should return empty status when no device attached + EXPECT_EQ(status.tapeWidthMm, 0); +} + +// Test: Get printer status with device +TEST_F(FakePrinterTest, GetPrinterStatusWithDevice) { + printer->attachUsbDevice(nullptr); + auto status = printer->getPrinterStatus(); + + // FakePrinter should return default status when device attached + EXPECT_EQ(status.tapeWidthMm, 12); // Default tape width +} + +// Test: Attach USB device (should always succeed for fake printer) +TEST_F(FakePrinterTest, AttachUsbDevice) { + bool result = printer->attachUsbDevice(nullptr); + + // FakePrinter doesn't need a real device + EXPECT_TRUE(result); +} + +// Test: Detach USB device +TEST_F(FakePrinterTest, DetachUsbDevice) { + printer->attachUsbDevice(nullptr); + bool result = printer->detachUsbDevice(); + + EXPECT_TRUE(result); +} + +// Test: Print without attaching device first +TEST_F(FakePrinterTest, PrintWithoutDevice) { + bool result = printer->print(); + + // FakePrinter should fail if no device attached + EXPECT_FALSE(result); +} + +// Test: Print after attaching device +TEST_F(FakePrinterTest, PrintWithDevice) { + printer->attachUsbDevice(nullptr); + bool result = printer->print(); + + EXPECT_TRUE(result); +} + +// Test: Print bitmap +TEST_F(FakePrinterTest, PrintBitmap) { + graphics::Bitmap bitmap(100, 128); + + // Fill with some pattern + std::vector pixels(bitmap.getWidth() * bitmap.getHeight()); + for (size_t i = 0; i < pixels.size(); ++i) { + pixels[i] = (i % 2) ? 0xFF : 0x00; + } + bitmap.setPixels(pixels); + + printer->attachUsbDevice(nullptr); + bool result = printer->printBitmap(bitmap); + + EXPECT_TRUE(result); + + // Check that last print was saved + const auto& lastPrint = printer->getLastPrint(); + EXPECT_GT(lastPrint.getWidth(), 0); + EXPECT_GT(lastPrint.getHeight(), 0); +} + +// Test: Print monochrome data +TEST_F(FakePrinterTest, PrintMonochromeData) { + graphics::Bitmap bitmap(50, 128); + auto pixels = bitmap.getPixelsCpy(); + auto mono = graphics::Monochrome(pixels, bitmap.getWidth(), bitmap.getHeight()); + auto data = mono.get(); + + printer->attachUsbDevice(nullptr); + bool result = printer->printMonochromeData(data); + + EXPECT_TRUE(result); + + // Verify last print + const auto& lastPrint = printer->getLastPrint(); + EXPECT_GT(lastPrint.getWidth(), 0); +} + +// Test: Print label +TEST_F(FakePrinterTest, PrintLabel) { + auto label = std::make_unique(128); + label->create("Test Label"); + + printer->attachUsbDevice(nullptr); + bool result = printer->printLabel(std::move(label)); + + EXPECT_TRUE(result); +} + +// Test: Print null label +TEST_F(FakePrinterTest, PrintNullLabel) { + printer->attachUsbDevice(nullptr); + bool result = printer->printLabel(nullptr); + + EXPECT_FALSE(result); +} + +// Test: Print empty bitmap +TEST_F(FakePrinterTest, PrintEmptyBitmap) { + graphics::Bitmap bitmap(0, 0); + + printer->attachUsbDevice(nullptr); + bool result = printer->printBitmap(bitmap); + + // Should handle empty bitmap gracefully + EXPECT_TRUE(result); +} + +// Test: Get last print before printing +TEST_F(FakePrinterTest, GetLastPrintBeforePrinting) { + // Should throw when getting last print before any print operation + EXPECT_THROW({ + const auto& lastPrint = printer->getLastPrint(); + (void)lastPrint; // Suppress unused variable warning + }, std::runtime_error); +} + +// Test: Save last print to PNG (may fail without valid data) +TEST_F(FakePrinterTest, SaveLastPrintToPng) { + graphics::Bitmap bitmap(100, 128); + + printer->attachUsbDevice(nullptr); + printer->printBitmap(bitmap); + + // Try to save to /tmp + bool result = printer->saveLastPrintToPng("/tmp/test_fake_printer_output.png"); + + // Should succeed if we have valid print data + EXPECT_TRUE(result); +} + +// Test: Multiple prints +TEST_F(FakePrinterTest, MultiplePrints) { + graphics::Bitmap bitmap1(50, 128); + graphics::Bitmap bitmap2(100, 128); + + printer->attachUsbDevice(nullptr); + + bool result1 = printer->printBitmap(bitmap1); + EXPECT_TRUE(result1); + + bool result2 = printer->printBitmap(bitmap2); + EXPECT_TRUE(result2); + + // Last print should be from second bitmap + const auto& lastPrint = printer->getLastPrint(); + EXPECT_GT(lastPrint.getWidth(), 0); +} + +// Test: Detach and reattach +TEST_F(FakePrinterTest, DetachAndReattach) { + printer->attachUsbDevice(nullptr); + EXPECT_TRUE(printer->detachUsbDevice()); + + // Should be able to reattach + EXPECT_TRUE(printer->attachUsbDevice(nullptr)); + + // Should be able to print after reattach + graphics::Bitmap bitmap(50, 128); + EXPECT_TRUE(printer->printBitmap(bitmap)); +} + +} // namespace ptprnt::printer diff --git a/tests/label_builder_test/label_builder_test.cpp b/tests/label_builder_test/label_builder_test.cpp new file mode 100644 index 0000000..f4a3dc8 --- /dev/null +++ b/tests/label_builder_test/label_builder_test.cpp @@ -0,0 +1,230 @@ +/* + 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 "graphics/LabelBuilder.hpp" +#include "graphics/interface/ILabel.hpp" + +namespace ptprnt::graphics { + +class LabelBuilderTest : public ::testing::Test { + protected: + void SetUp() override { + builder = std::make_unique(128); // P700 printer height + } + + void TearDown() override { builder.reset(); } + + std::unique_ptr builder; +}; + +// Test: Constructor +TEST_F(LabelBuilderTest, Constructor) { + EXPECT_NO_THROW(LabelBuilder(128)); + EXPECT_NO_THROW(LabelBuilder(64)); +} + +// Test: Add single text +TEST_F(LabelBuilderTest, AddSingleText) { + auto& result = builder->addText("Hello"); + + // Should return reference to builder for chaining + EXPECT_EQ(&result, builder.get()); +} + +// Test: Add multiple texts +TEST_F(LabelBuilderTest, AddMultipleTexts) { + builder->addText("Line 1") + .addText("Line 2") + .addText("Line 3"); + + // Build should succeed + auto label = builder->build(); + EXPECT_NE(label, nullptr); +} + +// Test: Add empty text (should be ignored) +TEST_F(LabelBuilderTest, AddEmptyText) { + builder->addText(""); + + auto label = builder->build(); + EXPECT_NE(label, nullptr); +} + +// Test: Set font family +TEST_F(LabelBuilderTest, SetFontFamily) { + auto& result = builder->setFontFamily("monospace"); + + // Should return reference to builder for chaining + EXPECT_EQ(&result, builder.get()); +} + +// Test: Set font size +TEST_F(LabelBuilderTest, SetFontSize) { + auto& result = builder->setFontSize(24.0); + + // Should return reference to builder for chaining + EXPECT_EQ(&result, builder.get()); +} + +// Test: Set horizontal alignment +TEST_F(LabelBuilderTest, SetHAlign) { + auto& result = builder->setHAlign(HAlignPosition::CENTER); + + // Should return reference to builder for chaining + EXPECT_EQ(&result, builder.get()); +} + +// Test: Set vertical alignment +TEST_F(LabelBuilderTest, SetVAlign) { + auto& result = builder->setVAlign(VAlignPosition::TOP); + + // Should return reference to builder for chaining + EXPECT_EQ(&result, builder.get()); +} + +// Test: Build with default settings +TEST_F(LabelBuilderTest, BuildWithDefaults) { + builder->addText("Test"); + + auto label = builder->build(); + + EXPECT_NE(label, nullptr); + EXPECT_GT(label->getWidth(), 0); + EXPECT_GT(label->getHeight(), 0); +} + +// Test: Build with custom settings +TEST_F(LabelBuilderTest, BuildWithCustomSettings) { + builder->setFontFamily("serif") + .setFontSize(48.0) + .setHAlign(HAlignPosition::CENTER) + .setVAlign(VAlignPosition::MIDDLE) + .addText("Custom Text"); + + auto label = builder->build(); + + EXPECT_NE(label, nullptr); +} + +// Test: Method chaining +TEST_F(LabelBuilderTest, MethodChaining) { + auto label = builder->setFontFamily("monospace") + .setFontSize(20.0) + .setHAlign(HAlignPosition::RIGHT) + .setVAlign(VAlignPosition::BOTTOM) + .addText("Chained") + .addText("Methods") + .build(); + + EXPECT_NE(label, nullptr); +} + +// Test: Reset builder +TEST_F(LabelBuilderTest, Reset) { + builder->setFontFamily("monospace") + .setFontSize(48.0) + .setHAlign(HAlignPosition::RIGHT) + .setVAlign(VAlignPosition::BOTTOM) + .addText("Test"); + + auto& result = builder->reset(); + + // Should return reference to builder for chaining + EXPECT_EQ(&result, builder.get()); + + // After reset, should be able to build with defaults + builder->addText("After Reset"); + auto label = builder->build(); + EXPECT_NE(label, nullptr); +} + +// Test: Build empty label +TEST_F(LabelBuilderTest, BuildEmptyLabel) { + // Build without adding any text + auto label = builder->build(); + + EXPECT_NE(label, nullptr); +} + +// Test: Multiple builds from same builder +TEST_F(LabelBuilderTest, MultipleBuilds) { + builder->addText("First Build"); + auto label1 = builder->build(); + EXPECT_NE(label1, nullptr); + + // Build again with same content + auto label2 = builder->build(); + EXPECT_NE(label2, nullptr); + + // Both labels should be valid + EXPECT_GT(label1->getWidth(), 0); + EXPECT_GT(label2->getWidth(), 0); +} + +// Test: All horizontal alignments +TEST_F(LabelBuilderTest, AllHorizontalAlignments) { + builder->addText("Left").setHAlign(HAlignPosition::LEFT); + auto label1 = builder->build(); + EXPECT_NE(label1, nullptr); + + builder->reset().addText("Center").setHAlign(HAlignPosition::CENTER); + auto label2 = builder->build(); + EXPECT_NE(label2, nullptr); + + builder->reset().addText("Right").setHAlign(HAlignPosition::RIGHT); + auto label3 = builder->build(); + EXPECT_NE(label3, nullptr); + + builder->reset().addText("Justify").setHAlign(HAlignPosition::JUSTIFY); + auto label4 = builder->build(); + EXPECT_NE(label4, nullptr); +} + +// Test: All vertical alignments +TEST_F(LabelBuilderTest, AllVerticalAlignments) { + builder->addText("Top").setVAlign(VAlignPosition::TOP); + auto label1 = builder->build(); + EXPECT_NE(label1, nullptr); + + builder->reset().addText("Middle").setVAlign(VAlignPosition::MIDDLE); + auto label2 = builder->build(); + EXPECT_NE(label2, nullptr); + + builder->reset().addText("Bottom").setVAlign(VAlignPosition::BOTTOM); + auto label3 = builder->build(); + EXPECT_NE(label3, nullptr); +} + +// Test: Different font sizes +TEST_F(LabelBuilderTest, DifferentFontSizes) { + builder->setFontSize(12.0).addText("Small"); + auto label1 = builder->build(); + EXPECT_NE(label1, nullptr); + + builder->reset().setFontSize(64.0).addText("Large"); + auto label2 = builder->build(); + EXPECT_NE(label2, nullptr); + + // Larger font should produce wider label (assuming same text length) + // Note: This is a heuristic test and may not always be true depending on font rendering +} + +} // namespace ptprnt::graphics diff --git a/tests/meson.build b/tests/meson.build index 5a61a74..c664d95 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -5,13 +5,16 @@ test_sources = [ 'bitmap_test/bitmap_test.cpp', 'monochrome_test/monochrome_test.cpp', 'label_test/label_test.cpp', + 'label_builder_test/label_builder_test.cpp', 'printer_service_test/printer_service_test.cpp', 'p700_printer_test/p700_printer_test.cpp', + 'fake_printer_test/fake_printer_test.cpp', # Source files under test - graphics '../src/graphics/Bitmap.cpp', '../src/graphics/Monochrome.cpp', '../src/graphics/Label.cpp', + '../src/graphics/LabelBuilder.cpp', # Source files under test - core '../src/core/PrinterService.cpp',