Multilabel support & Label refactoring (#17)
All checks were successful
Build ptprnt / build (push) Successful in 3m47s
All checks were successful
Build ptprnt / build (push) Successful in 3m47s
Reviewed-on: moritz/ptouch-prnt#17
This commit was merged in pull request #17.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
ptrnt - print labels on linux
|
||||
Copyright (C) 2023 Moritz Martinius
|
||||
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
|
||||
@@ -19,8 +19,244 @@
|
||||
|
||||
#include "graphics/Label.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "../../tests/mocks/MockCairoWrapper.hpp"
|
||||
#include "graphics/interface/ILabel.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<NiceMock<MockCairoWrapper>>();
|
||||
|
||||
// Mock pointers for temporary surface (used in size calculation)
|
||||
mockTempSurface = reinterpret_cast<cairo_surface_t*>(0x2000);
|
||||
mockTempCr = reinterpret_cast<cairo_t*>(0x2001);
|
||||
mockTempCtx = reinterpret_cast<PangoContext*>(0x2002);
|
||||
mockTempLayout = reinterpret_cast<PangoLayout*>(0x2003);
|
||||
|
||||
// Mock pointers for final surface (used in rendering)
|
||||
mockFinalSurface = reinterpret_cast<cairo_surface_t*>(0x3000);
|
||||
mockFinalCr = reinterpret_cast<cairo_t*>(0x3001);
|
||||
mockFinalCtx = reinterpret_cast<PangoContext*>(0x3002);
|
||||
mockFinalLayout = reinterpret_cast<PangoLayout*>(0x3003);
|
||||
|
||||
// Mock font description
|
||||
mockFontDesc = reinterpret_cast<PangoFontDescription*>(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<PangoFontMap*>(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<NiceMock<MockCairoWrapper>> 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<unsigned char> mockSurfaceData;
|
||||
};
|
||||
|
||||
// Smoke test with real Cairo/Pango
|
||||
TEST(basic_test, Label_smokeTest_succeeds) {
|
||||
auto im = ptprnt::graphics::Label(4711);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user