fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed

Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
This commit is contained in:
2026-05-05 20:20:37 +01:00
parent a5f97b6632
commit f31522130f
81834 changed files with 11051982 additions and 108 deletions
@@ -0,0 +1,19 @@
# QTBUG-103094
[render]
android
[renderWithMultipleSp]
android
[radialGrad]
android
[conicalGrad]
android
[renderPolyline]
android
[renderMultiline]
android
[polylineDataTypes]
android
[multilineDataTypes]
android
[multilineStronglyTyped]
android
@@ -0,0 +1,56 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Generated from qquickshape.pro.
#####################################################################
## tst_qquickshape Test:
#####################################################################
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qquickshape LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
data/*)
list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qquickshape
SOURCES
tst_qquickshape.cpp
LIBRARIES
Qt::CorePrivate
Qt::Gui
Qt::GuiPrivate
Qt::Qml
Qt::QmlPrivate
Qt::QuickPrivate
Qt::QuickShapesPrivate
Qt::QuickTestUtilsPrivate
TESTDATA ${test_data}
)
#### Keys ignored in scope 1:.:.:qquickshape.pro:<TRUE>:
# DISTFILES = "data/*"
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qquickshape CONDITION TARGET Qt::Widgets
LIBRARIES
Qt::Widgets
)
qt_internal_extend_target(tst_qquickshape CONDITION ANDROID OR IOS
DEFINES
QT_QMLTEST_DATADIR=":/data"
)
qt_internal_extend_target(tst_qquickshape CONDITION NOT ANDROID AND NOT IOS
DEFINES
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
)
@@ -0,0 +1,21 @@
import QtQuick
import QtQuick.Shapes
Shape {
width: 640
height: 480
Component.onCompleted: {
bottomPathLine.x = 20
}
ShapePath {
PathLine {}
PathLine {
id: bottomPathLine
}
PathArc {}
PathLine {}
PathArc {}
}
}
@@ -0,0 +1,58 @@
import QtQuick
import QtQuick.Shapes
import tst_qquickpathitem
Rectangle {
width: 440
height: 220
color: "white"
Shape {
objectName: "shape1"
ShapePath {
id: path1
objectName: "path1"
fillGradient: RadialGradient {
centerX: path1.startX + 100
centerY: path1.startY + 100
centerRadius: 100
focalX: centerX
focalY: centerY
GradientStop { position: 0.0; color: "blue" }
GradientStop { position: 0.5; color: "cyan" }
GradientStop { position: 1.0; color: "blue" }
}
fillTransform: PlanarTransform.fromScale(2, 1);
startX: 10
startY: 10
PathLine { relativeX: 200; relativeY: 0 }
PathLine { relativeX: 0; relativeY: 200 }
PathLine { relativeX: -200; relativeY: 0 }
PathLine { relativeX: 0; relativeY: -200 }
}
ShapePath {
id: path2
objectName: "path2"
fillGradient: RadialGradient {
centerX: path2.startX + 100
centerY: path2.startY + 100
centerRadius: 100
focalX: centerX
focalY: centerY
GradientStop { position: 0.0; color: "blue" }
GradientStop { position: 0.5; color: "cyan" }
GradientStop { position: 1.0; color: "blue" }
}
startX: 220 + 10
startY: 10
PathLine { relativeX: 200; relativeY: 0 }
PathLine { relativeX: 0; relativeY: 200 }
PathLine { relativeX: -200; relativeY: 0 }
PathLine { relativeX: 0; relativeY: -200 }
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,27 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
Shape {
width: 200
height: 150
vendorExtensionsEnabled: false
objectName: "shape"
id: shape
property alias paths: multiline.paths
property point vertexBeingChecked;
function checkVertexAt(i, j) {
vertexBeingChecked = multiline.paths[i][j]
}
ShapePath {
strokeWidth: 4
strokeColor: "green"
PathMultiline {
id: multiline
}
}
}
@@ -0,0 +1,34 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
import Qt.test 1.0
Shape {
width: 200
height: 150
vendorExtensionsEnabled: false
objectName: "shape"
id: shape
property alias paths: multiline.paths
property point vertexBeingChecked;
function checkVertexAt(i, j) {
vertexBeingChecked = multiline.paths[i][j]
}
PolygonProvider {
id: provider
objectName: "provider"
}
ShapePath {
strokeWidth: 4
strokeColor: "green"
PathMultiline {
id: multiline
paths: provider.paths
}
}
}
@@ -0,0 +1,5 @@
import QtQuick 2.9
import tst_qquickpathitem 1.0
Shape {
}
@@ -0,0 +1,7 @@
import QtQuick 2.9
import tst_qquickpathitem 1.0
Shape {
ShapePath { }
ShapePath { }
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1,33 @@
import QtQuick 2.9
import tst_qquickpathitem 1.0
Item {
width: 200
height: 150
Shape {
vendorExtensionsEnabled: false
objectName: "pathItem"
anchors.fill: parent
ShapePath {
strokeWidth: 4
strokeColor: "red"
fillGradient: LinearGradient {
x1: 20; y1: 20
x2: 180; y2: 130
GradientStop { position: 0; color: "blue" }
GradientStop { position: 0.2; color: "green" }
GradientStop { position: 0.4; color: "red" }
GradientStop { position: 0.6; color: "yellow" }
GradientStop { position: 1; color: "cyan" }
}
strokeStyle: ShapePath.DashLine
dashPattern: [ 1, 4 ]
startX: 20; startY: 20
PathLine { x: 180; y: 130 }
PathLine { x: 20; y: 130 }
PathLine { x: 20; y: 20 }
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@@ -0,0 +1,56 @@
import QtQuick 2.9
import tst_qquickpathitem 1.0
Item {
width: 200
height: 150
Shape {
vendorExtensionsEnabled: false
objectName: "pathItem"
anchors.fill: parent
ShapePath {
strokeColor: "red"
fillColor: "green"
startX: 40; startY: 30
PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 }
PathLine { x: 150; y: 80 }
PathQuad { x: 160; y: 30; controlX: 200; controlY: 80 }
}
ShapePath {
strokeWidth: 10
fillColor: "transparent"
strokeColor: "blue"
startX: 40; startY: 30
PathCubic { x: 50; y: 80; control1X: 0; control1Y: 80; control2X: 100; control2Y: 100 }
}
ShapePath {
fillGradient: LinearGradient {
y2: 150
GradientStop { position: 0; color: "yellow" }
GradientStop { position: 1; color: "green" }
}
startX: 10; startY: 100
PathArc {
relativeX: 50; y: 100
radiusX: 25; radiusY: 25
}
PathArc {
relativeX: 50; y: 100
radiusX: 25; radiusY: 35
}
PathArc {
relativeX: 50; y: 100
radiusX: 25; radiusY: 60
}
PathArc {
relativeX: 50; y: 100
radiusX: 50; radiusY: 120
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

@@ -0,0 +1,37 @@
import QtQuick 2.9
import tst_qquickpathitem 1.0
Item {
width: 200
height: 150
Shape {
vendorExtensionsEnabled: false
objectName: "pathItem"
anchors.fill: parent
ShapePath {
strokeWidth: 4
strokeColor: "red"
fillGradient: RadialGradient {
centerX: 100; centerY: 100; centerRadius: 100
focalX: 100; focalY: 100; focalRadius: 10
GradientStop { position: 0; color: "#ffffff" }
GradientStop { position: 0.11; color: "#f9ffa0" }
GradientStop { position: 0.13; color: "#f9ff99" }
GradientStop { position: 0.14; color: "#f3ff86" }
GradientStop { position: 0.49; color: "#93b353" }
GradientStop { position: 0.87; color: "#264619" }
GradientStop { position: 0.96; color: "#0c1306" }
GradientStop { position: 1; color: "#000000" }
}
fillColor: "blue" // ignored with the gradient set
strokeStyle: ShapePath.DashLine
dashPattern: [ 1, 4 ]
startX: 20; startY: 20
PathLine { x: 180; y: 130 }
PathLine { x: 20; y: 130 }
PathLine { x: 20; y: 20 }
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,35 @@
import QtQuick 2.9
import tst_qquickpathitem 1.0
Item {
width: 200
height: 150
Shape {
vendorExtensionsEnabled: false
objectName: "pathItem"
anchors.fill: parent
ShapePath {
strokeWidth: 4
strokeColor: "red"
fillGradient: ConicalGradient {
centerX: 100; centerY: 100; angle: 45
GradientStop { position: 0; color: "#00000000" }
GradientStop { position: 0.10; color: "#ffe0cc73" }
GradientStop { position: 0.17; color: "#ffc6a006" }
GradientStop { position: 0.46; color: "#ff600659" }
GradientStop { position: 0.72; color: "#ff0680ac" }
GradientStop { position: 0.92; color: "#ffb9d9e6" }
GradientStop { position: 1.00; color: "#00000000" }
}
fillColor: "blue" // ignored with the gradient set
strokeStyle: ShapePath.DashLine
dashPattern: [ 1, 4 ]
startX: 20; startY: 20
PathLine { x: 180; y: 130 }
PathLine { x: 20; y: 130 }
PathLine { x: 20; y: 20 }
}
}
}
@@ -0,0 +1,42 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import tst_qquickpathitem 1.0
Item {
id: item
width: 200
height: 150
Shape {
vendorExtensionsEnabled: false
objectName: "shape"
id: shape
anchors.fill: parent
ShapePath {
strokeWidth: 4
strokeColor: "red"
scale: Qt.size(shape.width - 1, shape.height - 1)
fillGradient: LinearGradient {
x1: 20; y1: 20
x2: 180; y2: 130
GradientStop { position: 0; color: "blue" }
GradientStop { position: 0.2; color: "green" }
GradientStop { position: 0.4; color: "red" }
GradientStop { position: 0.6; color: "yellow" }
GradientStop { position: 1; color: "cyan" }
}
strokeStyle: ShapePath.DashLine
dashPattern: [ 1, 4 ]
startX: 20; startY: 20 // unnecessary, PathPolyline moves to the first vertex.
PathPolyline {
path: [ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)),
Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ]
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

@@ -0,0 +1,63 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
Item {
id: item
width: 200
height: 150
Shape {
vendorExtensionsEnabled: false
objectName: "shape"
id: shape
anchors.fill: parent
ShapePath {
strokeWidth: 4
strokeColor: "red"
scale: Qt.size(shape.width - 1, shape.height - 1)
fillGradient: LinearGradient {
x1: 20; y1: 20
x2: 180; y2: 130
GradientStop { position: 0; color: "blue" }
GradientStop { position: 0.2; color: "green" }
GradientStop { position: 0.4; color: "red" }
GradientStop { position: 0.6; color: "yellow" }
GradientStop { position: 1; color: "cyan" }
}
strokeStyle: ShapePath.DashLine
dashPattern: [ 1, 4 ]
PathMultiline {
paths: [[Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)),
Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ],
[Qt.point( 0.45 , 0.67 ),
Qt.point( 0.414906666468 , 0.573581858547 ),
Qt.point( 0.32604722665 , 0.522278837048 ),
Qt.point( 0.225 , 0.540096189432 ),
Qt.point( 0.159046106882 , 0.618696978501 ),
Qt.point( 0.159046106882 , 0.721303021499 ),
Qt.point( 0.225 , 0.799903810568 ),
Qt.point( 0.32604722665 , 0.817721162952 ),
Qt.point( 0.414906666468 , 0.766418141453 ),
Qt.point( 0.45 , 0.67 ),
],
[Qt.point( 0.69 , 0.75 ),
Qt.point( 0.668943999881 , 0.692149115128 ),
Qt.point( 0.61562833599 , 0.661367302229 ),
Qt.point( 0.555 , 0.672057713659 ),
Qt.point( 0.515427664129 , 0.719218187101 ),
Qt.point( 0.515427664129 , 0.780781812899 ),
Qt.point( 0.555 , 0.827942286341 ),
Qt.point( 0.61562833599 , 0.838632697771 ),
Qt.point( 0.668943999881 , 0.807850884872 ),
Qt.point( 0.69 , 0.75 ),
]]
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

@@ -0,0 +1,27 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.14
import QtQuick.Shapes 1.14
Shape {
width: 200
height: 150
vendorExtensionsEnabled: false
objectName: "shape"
id: shape
property alias path: polyline.path
property point vertexBeingChecked;
function checkVertexAt(i) {
vertexBeingChecked = polyline.path[i]
}
ShapePath {
strokeWidth: 4
strokeColor: "green"
PathPolyline {
id: polyline
}
}
}
@@ -0,0 +1,728 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QTest>
#include <QtTest/QSignalSpy>
#include <QtQuick/qquickview.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
#include <QtQuickShapes/private/qquickshape_p.h>
#include <QStandardPaths>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/viewtestutils_p.h>
#include <QtQuickTestUtils/private/visualtestutils_p.h>
using namespace QQuickViewTestUtils;
using namespace QQuickVisualTestUtils;
class PolygonProvider : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QPolygonF> paths READ paths WRITE setPaths NOTIFY pathsChanged)
public:
QList<QPolygonF> paths() const { return m_paths; }
void setPaths(QList<QPolygonF> paths)
{
if (m_paths == paths)
return;
m_paths = paths;
emit pathsChanged();
}
signals:
void pathsChanged();
private:
QList<QPolygonF> m_paths;
};
class tst_QQuickShape : public QQmlDataTest
{
Q_OBJECT
public:
tst_QQuickShape();
private slots:
void initValues();
void vpInitValues();
void basicShape();
void changeSignals();
void render();
void renderWithMultipleSp();
void radialGrad();
void conicalGrad();
void renderPolyline();
void renderMultiline();
void polylineDataTypes_data();
void polylineDataTypes();
void multilineDataTypes_data();
void multilineDataTypes();
void multilineStronglyTyped();
void fillTransform();
void changeElementsImperatively();
private:
QList<QPolygonF> m_lowPolyLogo;
};
tst_QQuickShape::tst_QQuickShape()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
// Force the software backend to get reliable rendering results regardless of the hw and drivers.
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
const char *uri = "tst_qquickpathitem";
qmlRegisterType<QQuickShape>(uri, 1, 0, "Shape");
qmlRegisterType<QQuickShapePath, 14>(uri, 1, 0, "ShapePath");
qmlRegisterUncreatableType<QQuickShapeGradient>(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class"));
qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient");
qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient");
qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient");
qmlRegisterType<QQuickPathPolyline>(uri, 1, 0, "PathPolyline");
qmlRegisterType<PolygonProvider>("Qt.test", 1, 0, "PolygonProvider");
m_lowPolyLogo << (QPolygonF() <<
QPointF(20, 0) <<
QPointF(140, 0) <<
QPointF(140, 80) <<
QPointF(120, 100) <<
QPointF(0, 100) <<
QPointF(0, 20) <<
QPointF(20, 0) )
<< (QPolygonF() << QPointF(20, 80) <<
QPointF(60, 80) <<
QPointF(80, 60) <<
QPointF(80, 20) <<
QPointF(40, 20) <<
QPointF(20, 40) <<
QPointF(20, 80) )
<< (QPolygonF() << QPointF(80, 80) <<
QPointF(70, 70) )
<< (QPolygonF() << QPointF(120, 80) <<
QPointF(100, 60) <<
QPointF(100, 20) )
<< (QPolygonF() << QPointF(100, 40) <<
QPointF(120, 40) );
}
void tst_QQuickShape::initValues()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("pathitem1.qml"));
QQuickShape *obj = qobject_cast<QQuickShape *>(c.create());
QVERIFY(obj != nullptr);
QVERIFY(obj->rendererType() == QQuickShape::UnknownRenderer);
QVERIFY(!obj->asynchronous());
QVERIFY(!obj->vendorExtensionsEnabled());
QVERIFY(obj->status() == QQuickShape::Null);
auto vps = obj->data();
QVERIFY(vps.count(&vps) == 0);
delete obj;
}
void tst_QQuickShape::vpInitValues()
{
QQmlEngine engine;
QQmlComponent c(&engine, testFileUrl("pathitem2.qml"));
QQuickShape *obj = qobject_cast<QQuickShape *>(c.create());
QVERIFY(obj != nullptr);
QVERIFY(obj->rendererType() == QQuickShape::UnknownRenderer);
QVERIFY(!obj->asynchronous());
QVERIFY(!obj->vendorExtensionsEnabled());
QVERIFY(obj->status() == QQuickShape::Null);
auto vps = obj->data();
QVERIFY(vps.count(&vps) == 2);
QQuickShapePath *vp = qobject_cast<QQuickShapePath *>(vps.at(&vps, 0));
QVERIFY(vp != nullptr);
QQmlListReference pathList(vp, "pathElements");
QCOMPARE(pathList.count(), 0);
QCOMPARE(vp->strokeColor(), QColor(Qt::white));
QCOMPARE(vp->strokeWidth(), 1.0f);
QCOMPARE(vp->cosmeticStroke(), false);
QCOMPARE(vp->fillColor(), QColor(Qt::white));
QCOMPARE(vp->fillRule(), QQuickShapePath::OddEvenFill);
QCOMPARE(vp->joinStyle(), QQuickShapePath::BevelJoin);
QCOMPARE(vp->miterLimit(), 2);
QCOMPARE(vp->capStyle(), QQuickShapePath::SquareCap);
QCOMPARE(vp->strokeStyle(), QQuickShapePath::SolidLine);
QCOMPARE(vp->dashOffset(), 0.0f);
QCOMPARE(vp->dashPattern(), QList<qreal>() << 4 << 2);
QVERIFY(!vp->fillGradient());
delete obj;
}
void tst_QQuickShape::basicShape()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem3.qml"));
qApp->processEvents();
QQuickShape *obj = findItem<QQuickShape>(window->rootObject(), "pathItem");
QVERIFY(obj != nullptr);
QQmlListReference list(obj, "data");
QCOMPARE(list.count(), 1);
QQuickShapePath *vp = qobject_cast<QQuickShapePath *>(list.at(0));
QVERIFY(vp != nullptr);
QCOMPARE(vp->strokeWidth(), 4.0f);
QCOMPARE(vp->cosmeticStroke(), false);
QVERIFY(vp->fillGradient() != nullptr);
QCOMPARE(vp->strokeStyle(), QQuickShapePath::DashLine);
vp->setStrokeWidth(5.0f);
QCOMPARE(vp->strokeWidth(), 5.0f);
vp->setStrokeWidth(-5.0f);
QCOMPARE(vp->strokeWidth(), -5.0f); // but it won't render
QCOMPARE(vp->cosmeticStroke(), false);
vp->setCosmeticStroke(true);
QCOMPARE(vp->cosmeticStroke(), true);
QCOMPARE(vp->strokeWidth(), -5.0f);
vp->setStrokeWidth(5.0f);
QCOMPARE(vp->strokeWidth(), 5.0f);
QCOMPARE(vp->strokeWidth(), 5.0f);
QCOMPARE(vp->cosmeticStroke(), true);
QQuickShapeLinearGradient *lgrad = qobject_cast<QQuickShapeLinearGradient *>(vp->fillGradient());
QVERIFY(lgrad != nullptr);
QCOMPARE(lgrad->spread(), QQuickShapeGradient::PadSpread);
QCOMPARE(lgrad->x1(), 20.0f);
QQmlListReference stopList(lgrad, "stops");
QCOMPARE(stopList.count(), 5);
QVERIFY(stopList.at(2) != nullptr);
QQuickPath *path = vp;
QCOMPARE(path->startX(), 20.0f);
QQmlListReference pathList(path, "pathElements");
QCOMPARE(pathList.count(), 3);
}
void tst_QQuickShape::changeSignals()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem3.qml"));
qApp->processEvents();
QQuickShape *obj = findItem<QQuickShape>(window->rootObject(), "pathItem");
QVERIFY(obj != nullptr);
QSignalSpy asyncPropSpy(obj, SIGNAL(asynchronousChanged()));
obj->setAsynchronous(true);
obj->setAsynchronous(false);
QCOMPARE(asyncPropSpy.size(), 2);
QQmlListReference list(obj, "data");
QQuickShapePath *vp = qobject_cast<QQuickShapePath *>(list.at(0));
QVERIFY(vp != nullptr);
// Verify that VisualPath property changes emit shapePathChanged().
QSignalSpy vpChangeSpy(vp, SIGNAL(shapePathChanged()));
QSignalSpy strokeColorPropSpy(vp, SIGNAL(strokeColorChanged()));
vp->setStrokeColor(Qt::blue);
vp->setStrokeWidth(1.0f);
QQuickShapeGradient *g = vp->fillGradient();
vp->setFillGradient(nullptr);
vp->setFillColor(Qt::yellow);
vp->setFillRule(QQuickShapePath::WindingFill);
vp->setJoinStyle(QQuickShapePath::MiterJoin);
vp->setMiterLimit(5);
vp->setCapStyle(QQuickShapePath::RoundCap);
vp->setDashOffset(10);
vp->setDashPattern(QList<qreal>() << 1 << 2 << 3 << 4);
QCOMPARE(strokeColorPropSpy.size(), 1);
QCOMPARE(vpChangeSpy.size(), 10);
// Verify that property changes from Path and its elements bubble up and result in shapePathChanged().
QQuickPath *path = vp;
path->setStartX(30);
QCOMPARE(vpChangeSpy.size(), 11);
QQmlListReference pathList(path, "pathElements");
qobject_cast<QQuickPathLine *>(pathList.at(1))->setY(200);
QCOMPARE(vpChangeSpy.size(), 12);
// Verify that property changes from the gradient bubble up and result in shapePathChanged().
vp->setFillGradient(g);
QCOMPARE(vpChangeSpy.size(), 13);
QQuickShapeLinearGradient *lgrad = qobject_cast<QQuickShapeLinearGradient *>(g);
lgrad->setX2(200);
QCOMPARE(vpChangeSpy.size(), 14);
QQmlListReference stopList(lgrad, "stops");
QCOMPARE(stopList.count(), 5);
qobject_cast<QQuickGradientStop *>(stopList.at(1))->setPosition(0.3);
QCOMPARE(vpChangeSpy.size(), 15);
qobject_cast<QQuickGradientStop *>(stopList.at(1))->setColor(Qt::black);
QCOMPARE(vpChangeSpy.size(), 16);
vp->setFillTransform(QMatrix4x4(QTransform::fromScale(3, 0.14)));
QCOMPARE(vpChangeSpy.size(), 17);
}
void tst_QQuickShape::render()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem3.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("pathitem3.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
QVERIFY2(QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage),
qPrintable(errorMessage));
}
void tst_QQuickShape::renderWithMultipleSp()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem4.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("pathitem4.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
QVERIFY2(QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage),
qPrintable(errorMessage));
}
void tst_QQuickShape::radialGrad()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem5.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("pathitem5.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
QVERIFY2(QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage),
qPrintable(errorMessage));
}
void tst_QQuickShape::conicalGrad()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem6.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("pathitem6.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
QVERIFY2(QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage),
qPrintable(errorMessage));
}
void tst_QQuickShape::renderPolyline()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem7.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("pathitem3.png")); // It's a recreation of pathitem3 using PathPolyline
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
const bool res = QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage);
if (!res) { // For visual inspection purposes.
QTest::qWait(5000);
const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
actualImg.save(tempLocation + QLatin1String("/pathitem7.png"));
}
QVERIFY2(res, qPrintable(errorMessage));
}
void tst_QQuickShape::renderMultiline()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("pathitem8.qml"));
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("pathitem8.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
const bool res = QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage);
if (!res) { // For visual inspection purposes.
QTest::qWait(5000);
const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
actualImg.save(tempLocation + QLatin1String("/pathitem8.png"));
}
QVERIFY2(res, qPrintable(errorMessage));
}
void tst_QQuickShape::polylineDataTypes_data()
{
QTest::addColumn<QVariant>("path");
QTest::newRow("polygon") << QVariant::fromValue(m_lowPolyLogo.first());
{
QList<QPointF> points;
points << m_lowPolyLogo.first();
QTest::newRow("vector of points") << QVariant::fromValue(points);
}
{
QList<QPointF> points;
for (const auto &point : m_lowPolyLogo.first())
points << point;
QTest::newRow("list of points") << QVariant::fromValue(points);
}
{
QVariantList points;
for (const auto &point : m_lowPolyLogo.first())
points << point;
QTest::newRow("QVariantList of points") << QVariant::fromValue(points);
}
{
QList<QPoint> points;
for (const auto &point : m_lowPolyLogo.first())
points << point.toPoint();
QTest::newRow("vector of QPoint (integer points)") << QVariant::fromValue(points);
}
// Oddly, QPolygon is not supported, even though it's really QList<QPoint>.
// We don't want to have a special case for it in QQuickPathPolyline::setPath(),
// but it could potentially be supported by fixing one of the QVariant conversions.
}
void tst_QQuickShape::polylineDataTypes()
{
SKIP_IF_NO_WINDOW_GRAB;
QFETCH(QVariant, path);
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("polyline.qml"));
QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject());
QVERIFY(shape);
shape->setProperty("path", path);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("polyline.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
const bool res = QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage);
if (!res) { // For visual inspection purposes.
QTest::qWait(5000);
const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
actualImg.save(tempLocation + QLatin1String("/polyline.png"));
}
QVERIFY2(res, qPrintable(errorMessage));
QCOMPARE(shape->property("path").value<QList<QPointF>>(), m_lowPolyLogo.first());
// Verify that QML sees it as an array of points
int i = 0;
for (QPointF p : m_lowPolyLogo.first()) {
QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i++)));
QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p);
}
}
void tst_QQuickShape::multilineDataTypes_data()
{
QTest::addColumn<QVariant>("paths");
QTest::newRow("vector of polygons") << QVariant::fromValue(m_lowPolyLogo);
{
QList<QList<QPointF>> paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPointF> points;
points << poly;
paths << points;
}
QTest::newRow("vector of point vectors") << QVariant::fromValue(paths);
}
{
QList<QList<QPointF>> paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPointF> points;
points << poly;
paths << points;
}
QTest::newRow("list of point vectors") << QVariant::fromValue(paths);
}
{
QList<QList<QPointF>> paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPointF> points;
for (const auto &point : poly)
points << point;
paths << points;
}
QTest::newRow("list of point lists") << QVariant::fromValue(paths);
}
{
QVariantList paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPointF> points;
points << poly;
paths << QVariant::fromValue(points);
}
QTest::newRow("QVariantList of point vectors") << QVariant::fromValue(paths);
}
{
QVariantList paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPointF> points;
for (const auto &point : poly)
points << point;
paths << QVariant::fromValue(points);
}
QTest::newRow("QVariantList of point lists") << QVariant::fromValue(paths);
}
{
QVariantList paths;
for (const auto &poly : m_lowPolyLogo) {
QVariantList points;
for (const auto &point : poly)
points << point;
paths << QVariant::fromValue(points);
}
QTest::newRow("QVariantList of QVariantLists") << QVariant::fromValue(paths);
}
/* These could be supported if QVariant knew how to convert lists and vectors of QPolygon to QPolygonF.
But they are omitted for now because we don't want to have special cases for them
in QQuickPathMultiline::setPaths(). Floating point is preferred for geometry in Qt Quick.
{
QList<QPolygon> paths;
for (const auto &poly : m_lowPolyLogo)
paths << poly.toPolygon();
QTest::newRow("list of QPolygon (integer points)") << QVariant::fromValue(paths);
}
{
QList<QPolygon> paths;
for (const auto &poly : m_lowPolyLogo)
paths << poly.toPolygon();
QTest::newRow("vector of QPolygon (integer points)") << QVariant::fromValue(paths);
}
*/
{
QList<QList<QPoint>> paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPoint> points;
for (const auto &point : poly)
points << point.toPoint();
paths << points;
}
QTest::newRow("list of integer point lists") << QVariant::fromValue(paths);
}
{
QList<QList<QPoint>> paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPoint> points;
for (const auto &point : poly)
points << point.toPoint();
paths << points;
}
QTest::newRow("vector of integer point lists") << QVariant::fromValue(paths);
}
{
QList<QList<QPoint>> paths;
for (const auto &poly : m_lowPolyLogo) {
QList<QPoint> points;
for (const auto &point : poly)
points << point.toPoint();
paths << points;
}
QTest::newRow("list of integer point vectors") << QVariant::fromValue(paths);
}
}
void tst_QQuickShape::multilineDataTypes()
{
SKIP_IF_NO_WINDOW_GRAB;
QFETCH(QVariant, paths);
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("multiline.qml"));
QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject());
QVERIFY(shape);
shape->setProperty("paths", paths);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("multiline.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
const bool res = QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage);
if (!res) { // For visual inspection purposes.
QTest::qWait(5000);
const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
actualImg.save(tempLocation + QLatin1String("/multiline.png"));
}
QVERIFY2(res, qPrintable(errorMessage));
QList<QList<QPointF>> pointVectors;
for (auto v : m_lowPolyLogo)
pointVectors << v;
QCOMPARE(shape->property("paths").value<QList<QList<QPointF>>>(), pointVectors);
// Verify that QML sees it as an array of arrays of points
int i = 0;
for (auto pv : m_lowPolyLogo) {
int j = 0;
for (QPointF p : pv) {
QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++)));
QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p);
}
++i;
}
}
void tst_QQuickShape::multilineStronglyTyped()
{
SKIP_IF_NO_WINDOW_GRAB;
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("multilineStronglyTyped.qml"));
QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject());
QVERIFY(shape);
PolygonProvider *provider = shape->findChild<PolygonProvider*>("provider");
QVERIFY(provider);
window->show();
QVERIFY(QTest::qWaitForWindowExposed(window.data()));
provider->setPaths(m_lowPolyLogo);
QImage img = window->grabWindow();
QVERIFY(!img.isNull());
QImage refImg(testFile("multiline.png"));
QVERIFY(!refImg.isNull());
QString errorMessage;
const QImage actualImg = img.convertToFormat(refImg.format());
const bool res = QQuickVisualTestUtils::compareImages(actualImg, refImg, &errorMessage);
if (!res) { // For visual inspection purposes.
QTest::qWait(5000);
const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
actualImg.save(tempLocation + QLatin1String("/multilineStronglyTyped.png"));
}
QVERIFY2(res, qPrintable(errorMessage));
QList<QList<QPointF>> pointVectors;
for (auto v : m_lowPolyLogo)
pointVectors << v;
QCOMPARE(shape->property("paths").value<QList<QList<QPointF>>>(), pointVectors);
// Verify that QML sees it as an array of arrays of points
int i = 0;
for (auto pv : m_lowPolyLogo) {
int j = 0;
for (QPointF p : pv) {
QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++)));
QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p);
}
++i;
}
}
void tst_QQuickShape::fillTransform()
{
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("filltransform.qml"));
qApp->processEvents();
QQuickShape *obj = findItem<QQuickShape>(window->rootObject(), "shape1");
QVERIFY(obj != nullptr);
QQmlListReference list(obj, "data");
QCOMPARE(list.count(), 2);
QQuickShapePath *p1 = qobject_cast<QQuickShapePath *>(list.at(0));
QVERIFY(p1 != nullptr);
QVERIFY(p1->objectName() == "path1");
QVERIFY(p1->fillTransform() == QMatrix4x4(2,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1));
QQuickShapePath *p2 = qobject_cast<QQuickShapePath *>(list.at(1));
QVERIFY(p2 != nullptr);
QVERIFY(p2->objectName() == "path2");
QVERIFY(p2->fillTransform().isIdentity());
QMatrix4x4 xf(QTransform::fromTranslate(-36, 0).shear(0.35, 0));
p1->setFillTransform(xf);
QVERIFY(p1->fillTransform() == xf);
QVERIFY(p2->fillTransform().isIdentity());
p2->setFillTransform(xf);
QVERIFY(p2->fillTransform() == xf);
p1->setFillTransform(QMatrix4x4{});
QVERIFY(p1->fillTransform().isIdentity());
}
void tst_QQuickShape::changeElementsImperatively()
{
QQuickView window;
// Shouldn't crash.
QVERIFY(QQuickTest::showView(window, testFileUrl("changeElementsImperatively.qml")));
}
QTEST_MAIN(tst_QQuickShape)
#include "tst_qquickshape.moc"