/* 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 "graphics/Label.hpp" #include #include #include #include #include "graphics/interface/ILabel.hpp" #include "mocks/MockCairoWrapper.hpp" using ::testing::_; using ::testing::DoAll; using ::testing::NiceMock; using ::testing::Return; using ::testing::SetArgPointee; namespace ptprnt::graphics { // Test fixture for Label tests with comprehensive mock setup class LabelTest : public ::testing::Test { protected: void SetUp() override { mockWrapper = std::make_shared>(); // Mock pointers for temporary surface (used in size calculation) mockTempSurface = reinterpret_cast(0x2000); mockTempCr = reinterpret_cast(0x2001); mockTempCtx = reinterpret_cast(0x2002); mockTempLayout = reinterpret_cast(0x2003); // Mock pointers for final surface (used in rendering) mockFinalSurface = reinterpret_cast(0x3000); mockFinalCr = reinterpret_cast(0x3001); mockFinalCtx = reinterpret_cast(0x3002); mockFinalLayout = reinterpret_cast(0x3003); // Mock font description mockFontDesc = reinterpret_cast(0x2004); // Default layout size: 100x30 pixels (in PANGO_SCALE units) defaultLayoutWidth = 100; defaultLayoutHeight = 30; SetupDefaultBehaviors(); } void SetupDefaultBehaviors() { // Font map ON_CALL(*mockWrapper, pango_cairo_font_map_new()) .WillByDefault(Return(reinterpret_cast(0x1000))); // Temporary surface creation (for size calculation) ON_CALL(*mockWrapper, cairo_image_surface_create(CAIRO_FORMAT_A8, 1, 1)).WillByDefault(Return(mockTempSurface)); ON_CALL(*mockWrapper, cairo_create(mockTempSurface)).WillByDefault(Return(mockTempCr)); ON_CALL(*mockWrapper, pango_cairo_create_context(mockTempCr)).WillByDefault(Return(mockTempCtx)); ON_CALL(*mockWrapper, pango_layout_new(mockTempCtx)).WillByDefault(Return(mockTempLayout)); // Final surface creation (for rendering) - use _ for width since it varies ON_CALL(*mockWrapper, cairo_image_surface_create(CAIRO_FORMAT_A8, _, _)) .WillByDefault(Return(mockFinalSurface)); ON_CALL(*mockWrapper, cairo_create(mockFinalSurface)).WillByDefault(Return(mockFinalCr)); ON_CALL(*mockWrapper, pango_cairo_create_context(mockFinalCr)).WillByDefault(Return(mockFinalCtx)); ON_CALL(*mockWrapper, pango_layout_new(mockFinalCtx)).WillByDefault(Return(mockFinalLayout)); // Font description ON_CALL(*mockWrapper, pango_font_description_new()).WillByDefault(Return(mockFontDesc)); // Layout size - return default dimensions ON_CALL(*mockWrapper, pango_layout_get_size(_, _, _)) .WillByDefault(DoAll(SetArgPointee<1>(defaultLayoutWidth * PANGO_SCALE), SetArgPointee<2>(defaultLayoutHeight * PANGO_SCALE))); // Surface status - always success ON_CALL(*mockWrapper, cairo_surface_status(_)).WillByDefault(Return(CAIRO_STATUS_SUCCESS)); // Surface properties for getRaw() ON_CALL(*mockWrapper, cairo_image_surface_get_format(_)).WillByDefault(Return(CAIRO_FORMAT_A8)); ON_CALL(*mockWrapper, cairo_image_surface_get_width(_)).WillByDefault(Return(defaultLayoutWidth)); ON_CALL(*mockWrapper, cairo_image_surface_get_height(_)).WillByDefault(Return(128)); ON_CALL(*mockWrapper, cairo_image_surface_get_stride(_)).WillByDefault(Return(defaultLayoutWidth)); // Mock data pointer mockSurfaceData.resize(defaultLayoutWidth * 128, 0xFF); ON_CALL(*mockWrapper, cairo_image_surface_get_data(_)).WillByDefault(Return(mockSurfaceData.data())); } // Helper method to set custom layout dimensions void SetLayoutSize(int width, int height) { defaultLayoutWidth = width; defaultLayoutHeight = height; // Update the mock to return new dimensions ON_CALL(*mockWrapper, pango_layout_get_size(_, _, _)) .WillByDefault(DoAll(SetArgPointee<1>(width * PANGO_SCALE), SetArgPointee<2>(height * PANGO_SCALE))); ON_CALL(*mockWrapper, cairo_image_surface_get_width(_)).WillByDefault(Return(width)); ON_CALL(*mockWrapper, cairo_image_surface_get_stride(_)).WillByDefault(Return(width)); // Resize mock data mockSurfaceData.resize(width * 128, 0xFF); ON_CALL(*mockWrapper, cairo_image_surface_get_data(_)).WillByDefault(Return(mockSurfaceData.data())); } std::shared_ptr> mockWrapper; // Mock pointers cairo_surface_t* mockTempSurface; cairo_t* mockTempCr; PangoContext* mockTempCtx; PangoLayout* mockTempLayout; cairo_surface_t* mockFinalSurface; cairo_t* mockFinalCr; PangoContext* mockFinalCtx; PangoLayout* mockFinalLayout; PangoFontDescription* mockFontDesc; // Default layout dimensions int defaultLayoutWidth; int defaultLayoutHeight; // Mock surface data std::vector mockSurfaceData; }; // Smoke test with real Cairo/Pango TEST(basic_test, Label_smokeTest_succeeds) { auto label = Label(128); EXPECT_EQ(label.getHeight(), 128); EXPECT_EQ(label.getWidth(), 0); // No label created yet } // Constructor test with mock TEST_F(LabelTest, Constructor_InitializesFontMap) { EXPECT_CALL(*mockWrapper, pango_cairo_font_map_new()).Times(1); auto label = Label(128, mockWrapper); EXPECT_EQ(label.getHeight(), 128); EXPECT_EQ(label.getWidth(), 0); } // Test getters before label creation TEST_F(LabelTest, Getters_BeforeCreate_ReturnDefaults) { auto label = Label(256, mockWrapper); EXPECT_EQ(label.getHeight(), 256); EXPECT_EQ(label.getWidth(), 0); } // Test setters TEST_F(LabelTest, Setters_ModifyProperties) { auto label = Label(128, mockWrapper); label.setFontSize(24.0); label.setFontFamily("Arial"); label.setText("Test"); label.setHAlign(HAlignPosition::CENTER); label.setVAlign(VAlignPosition::BOTTOM); // Properties are set (no way to verify without create, but no crash is good) SUCCEED(); } // Test create() - basic functionality with simplified setup TEST_F(LabelTest, Create_WithText_Succeeds) { auto label = Label(128, mockWrapper); label.setFontSize(12.0); label.setFontFamily("Sans"); bool result = label.create("Hello"); EXPECT_TRUE(result); EXPECT_EQ(label.getWidth(), defaultLayoutWidth); EXPECT_EQ(label.getHeight(), 128); } // Test horizontal alignment - RIGHT TEST_F(LabelTest, Create_WithRightAlignment_SetsCorrectPangoAlignment) { auto label = Label(128, mockWrapper); label.setHAlign(HAlignPosition::RIGHT); // Verify RIGHT alignment is set (temp + final layout) EXPECT_CALL(*mockWrapper, pango_layout_set_alignment(_, PANGO_ALIGN_RIGHT)).Times(2); label.create("Right"); } // Test horizontal alignment - JUSTIFY TEST_F(LabelTest, Create_WithJustifyAlignment_SetsJustifyAndAlignment) { auto label = Label(128, mockWrapper); label.setHAlign(HAlignPosition::JUSTIFY); // Verify JUSTIFY requires LEFT alignment + justify flag EXPECT_CALL(*mockWrapper, pango_layout_set_alignment(_, PANGO_ALIGN_LEFT)).Times(2); EXPECT_CALL(*mockWrapper, pango_layout_set_justify(_, true)).Times(2); #if PANGO_VERSION_MAJOR >= 1 && PANGO_VERSION_MINOR >= 50 EXPECT_CALL(*mockWrapper, pango_layout_set_justify_last_line(_, true)).Times(2); #endif label.create("Justify"); } // Test vertical alignment - TOP (no cairo_move_to) TEST_F(LabelTest, Create_WithTopAlignment_NoMoveToCall) { auto label = Label(128, mockWrapper); label.setVAlign(VAlignPosition::TOP); // TOP alignment should NOT call cairo_move_to EXPECT_CALL(*mockWrapper, cairo_move_to(_, _, _)).Times(0); label.create("Top"); } // Test vertical alignment - BOTTOM TEST_F(LabelTest, Create_WithBottomAlignment_CallsMoveToWithCorrectOffset) { auto label = Label(128, mockWrapper); label.setVAlign(VAlignPosition::BOTTOM); SetLayoutSize(50, 20); // Use helper to set custom size // BOTTOM alignment: offset = printerHeight - layoutHeight = 128 - 20 = 108 EXPECT_CALL(*mockWrapper, cairo_move_to(mockFinalCr, 0.0, 108.0)).Times(1); label.create("Bottom"); } // Test vertical alignment - MIDDLE TEST_F(LabelTest, Create_WithMiddleAlignment_CallsMoveToWithCenteredOffset) { auto label = Label(128, mockWrapper); label.setVAlign(VAlignPosition::MIDDLE); SetLayoutSize(50, 20); // Use helper to set custom size // MIDDLE alignment: offset = (printerHeight - layoutHeight) / 2 = (128 - 20) / 2 = 54 EXPECT_CALL(*mockWrapper, cairo_move_to(mockFinalCr, 0.0, 54.0)).Times(1); label.create("Middle"); } } // namespace ptprnt::graphics