summaryrefslogtreecommitdiffstats
path: root/testfiles/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testfiles/src/2geom-characterization-test.cpp31
-rw-r--r--testfiles/src/attributes-test.cpp646
-rw-r--r--testfiles/src/color-profile-test.cpp127
-rw-r--r--testfiles/src/curve-test.cpp258
-rw-r--r--testfiles/src/cxxtests-to-migrate/marker-test.h42
-rw-r--r--testfiles/src/cxxtests-to-migrate/mod360-test.h65
-rw-r--r--testfiles/src/cxxtests-to-migrate/preferences-test.h139
-rw-r--r--testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h175
-rw-r--r--testfiles/src/cxxtests-to-migrate/test-helpers.h75
-rw-r--r--testfiles/src/cxxtests-to-migrate/verbs-test.h95
-rw-r--r--testfiles/src/dir-util-test.cpp64
-rw-r--r--testfiles/src/drag-and-drop-svgz.cpp62
-rw-r--r--testfiles/src/extract-uri-test.cpp75
-rw-r--r--testfiles/src/lpe-bool-test.cpp70
-rw-r--r--testfiles/src/object-set-test.cpp636
-rw-r--r--testfiles/src/object-style-test.cpp197
-rw-r--r--testfiles/src/object-test.cpp206
-rw-r--r--testfiles/src/sp-gradient-test.cpp130
-rw-r--r--testfiles/src/sp-item-group-test.cpp46
-rw-r--r--testfiles/src/sp-object-test.cpp121
-rw-r--r--testfiles/src/style-elem-test.cpp73
-rw-r--r--testfiles/src/style-test.cpp572
-rw-r--r--testfiles/src/svg-stringstream-test.cpp156
-rw-r--r--testfiles/src/uri-test.cpp304
24 files changed, 4365 insertions, 0 deletions
diff --git a/testfiles/src/2geom-characterization-test.cpp b/testfiles/src/2geom-characterization-test.cpp
new file mode 100644
index 0000000..d3f099b
--- /dev/null
+++ b/testfiles/src/2geom-characterization-test.cpp
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * 2Geom Lib characterization tests
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2020 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <gtest/gtest.h>
+
+#include <2geom/path.h>
+
+TEST(Characterization2Geom, retrievingBackElementOfAnEmptyClosedPathFails)
+{
+ Geom::Path path(Geom::Point(3, 5));
+ path.close();
+ ASSERT_TRUE(path.closed());
+ ASSERT_EQ(path.size_closed(), 0u);
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp
new file mode 100644
index 0000000..791f140
--- /dev/null
+++ b/testfiles/src/attributes-test.cpp
@@ -0,0 +1,646 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Unit tests for attributes.
+ *
+ * Author:
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2015 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "attributes.h"
+
+namespace {
+
+static const unsigned int FIRST_VALID_ID = 1;
+
+class AttributeInfo
+{
+public:
+ AttributeInfo(std::string attr, bool supported) :
+ attr(std::move(attr)),
+ supported(supported)
+ {
+ }
+
+ std::string attr;
+ bool supported;
+};
+
+typedef std::vector<AttributeInfo>::iterator AttrItr;
+
+std::vector<AttributeInfo> getKnownAttrs()
+{
+/* Originally extracted mechanically from
+ http://www.w3.org/TR/SVG11/attindex.html:
+
+ tidy -wrap 999 -asxml < attindex.html 2>/dev/null |
+ tr -d \\n |
+ sed 's,<tr>,@,g' |
+ tr @ \\n |
+ sed 's,</td>.*,,;s,^<td>,,;1,/^%/d;/^%/d;s,^, {",;s/$/", false},/' |
+ uniq
+
+ attindex.html lacks attributeName, begin, additive, font, marker;
+ I've added these manually.
+
+ SVG 2: white-space, shape-inside, shape-subtrace, shape-padding, shape-margin
+*/
+ AttributeInfo all_attrs[] = {
+ AttributeInfo("attributeName", true),
+ AttributeInfo("begin", true),
+ AttributeInfo("additive", true),
+ AttributeInfo("font", true),
+ AttributeInfo("-inkscape-font-specification", true), // TODO look into this attribute's name
+ AttributeInfo("marker", true),
+ AttributeInfo("line-height", true),
+
+ AttributeInfo("accent-height", true),
+ AttributeInfo("accumulate", true),
+ AttributeInfo("alignment-baseline", true),
+ AttributeInfo("alphabetic", true),
+ AttributeInfo("amplitude", true),
+ AttributeInfo("animate", false),
+ AttributeInfo("arabic-form", true),
+ AttributeInfo("ascent", true),
+ AttributeInfo("attributeType", true),
+ AttributeInfo("azimuth", true),
+ AttributeInfo("baseFrequency", true),
+ AttributeInfo("baseline-shift", true),
+ AttributeInfo("baseProfile", false),
+ AttributeInfo("bbox", true),
+ AttributeInfo("bias", true),
+ AttributeInfo("by", true),
+ AttributeInfo("calcMode", true),
+ AttributeInfo("cap-height", true),
+ AttributeInfo("class", false),
+ AttributeInfo("clip", true),
+ AttributeInfo("clip-path", true),
+ AttributeInfo("clip-rule", true),
+ AttributeInfo("clipPathUnits", true),
+ AttributeInfo("color", true),
+ AttributeInfo("color-interpolation", true),
+ AttributeInfo("color-interpolation-filters", true),
+ AttributeInfo("color-profile", true),
+ AttributeInfo("color-rendering", true),
+ AttributeInfo("contentScriptType", false),
+ AttributeInfo("contentStyleType", false),
+ AttributeInfo("cursor", true),
+ AttributeInfo("cx", true),
+ AttributeInfo("cy", true),
+ AttributeInfo("d", true),
+ AttributeInfo("descent", true),
+ AttributeInfo("diffuseConstant", true),
+ AttributeInfo("direction", true),
+ AttributeInfo("display", true),
+ AttributeInfo("divisor", true),
+ AttributeInfo("dominant-baseline", true),
+ AttributeInfo("dur", true),
+ AttributeInfo("dx", true),
+ AttributeInfo("dy", true),
+ AttributeInfo("edgeMode", true),
+ AttributeInfo("elevation", true),
+ AttributeInfo("enable-background", true),
+ AttributeInfo("end", true),
+ AttributeInfo("exponent", true),
+ AttributeInfo("externalResourcesRequired", false),
+ AttributeInfo("feBlend", false),
+ AttributeInfo("feColorMatrix", false),
+ AttributeInfo("feComponentTransfer", false),
+ AttributeInfo("feComposite", false),
+ AttributeInfo("feConvolveMatrix", false),
+ AttributeInfo("feDiffuseLighting", false),
+ AttributeInfo("feDisplacementMap", false),
+ AttributeInfo("feFlood", false),
+ AttributeInfo("feGaussianBlur", false),
+ AttributeInfo("feImage", false),
+ AttributeInfo("feMerge", false),
+ AttributeInfo("feMorphology", false),
+ AttributeInfo("feOffset", false),
+ AttributeInfo("feSpecularLighting", false),
+ AttributeInfo("feTile", false),
+ AttributeInfo("fill", true),
+ AttributeInfo("fill-opacity", true),
+ AttributeInfo("fill-rule", true),
+ AttributeInfo("filter", true),
+ AttributeInfo("filterRes", true),
+ AttributeInfo("filterUnits", true),
+ AttributeInfo("flood-color", true),
+ AttributeInfo("flood-opacity", true),
+ AttributeInfo("font-family", true),
+ AttributeInfo("font-feature-settings", true),
+ AttributeInfo("font-size", true),
+ AttributeInfo("font-size-adjust", true),
+ AttributeInfo("font-stretch", true),
+ AttributeInfo("font-style", true),
+ AttributeInfo("font-variant", true),
+ AttributeInfo("font-variant-ligatures", true),
+ AttributeInfo("font-variant-position", true),
+ AttributeInfo("font-variant-caps", true),
+ AttributeInfo("font-variant-numeric", true),
+ AttributeInfo("font-variant-east-asian", true),
+ AttributeInfo("font-variant-alternates", true),
+ AttributeInfo("font-variation-settings", true),
+ AttributeInfo("font-weight", true),
+ AttributeInfo("format", false),
+ AttributeInfo("from", true),
+ AttributeInfo("fx", true),
+ AttributeInfo("fr", true),
+ AttributeInfo("fy", true),
+ AttributeInfo("g1", true),
+ AttributeInfo("g2", true),
+ AttributeInfo("glyph-name", true),
+ AttributeInfo("glyph-orientation-horizontal", true),
+ AttributeInfo("glyph-orientation-vertical", true),
+ AttributeInfo("glyphRef", false),
+ AttributeInfo("gradientTransform", true),
+ AttributeInfo("gradientUnits", true),
+ AttributeInfo("hanging", true),
+ AttributeInfo("hatchContentUnits", true), // SVG 2.0
+ AttributeInfo("hatchTransform", true), // SVG 2.0 TODO renamed to transform
+ AttributeInfo("hatchUnits", true), // SVG 2.0
+ AttributeInfo("height", true),
+ AttributeInfo("horiz-adv-x", true),
+ AttributeInfo("horiz-origin-x", true),
+ AttributeInfo("horiz-origin-y", true),
+ AttributeInfo("ideographic", true),
+ AttributeInfo("image-rendering", true),
+ AttributeInfo("in", true),
+ AttributeInfo("in2", true),
+ AttributeInfo("inline-size", true),
+ AttributeInfo("intercept", true),
+ AttributeInfo("isolation", true),
+ AttributeInfo("k", true),
+ AttributeInfo("k1", true),
+ AttributeInfo("k2", true),
+ AttributeInfo("k3", true),
+ AttributeInfo("k4", true),
+ AttributeInfo("kernelMatrix", true),
+ AttributeInfo("kernelUnitLength", true),
+ AttributeInfo("kerning", true),
+ AttributeInfo("keyPoints", false),
+ AttributeInfo("keySplines", true),
+ AttributeInfo("keyTimes", true),
+ AttributeInfo("lang", true),
+ AttributeInfo("lengthAdjust", true),
+ AttributeInfo("letter-spacing", true),
+ AttributeInfo("lighting-color", true),
+ AttributeInfo("limitingConeAngle", true),
+ AttributeInfo("local", true),
+ AttributeInfo("marker-end", true),
+ AttributeInfo("marker-mid", true),
+ AttributeInfo("marker-start", true),
+ AttributeInfo("markerHeight", true),
+ AttributeInfo("markerUnits", true),
+ AttributeInfo("markerWidth", true),
+ AttributeInfo("mask", true),
+ AttributeInfo("maskContentUnits", true),
+ AttributeInfo("maskUnits", true),
+ AttributeInfo("mathematical", true),
+ AttributeInfo("max", true),
+ AttributeInfo("media", false),
+ AttributeInfo("method", false),
+ AttributeInfo("min", true),
+ AttributeInfo("mix-blend-mode", true),
+ AttributeInfo("mode", true),
+ AttributeInfo("name", true),
+ AttributeInfo("numOctaves", true),
+ AttributeInfo("offset", true),
+ AttributeInfo("onabort", false),
+ AttributeInfo("onactivate", false),
+ AttributeInfo("onbegin", false),
+ AttributeInfo("onclick", false),
+ AttributeInfo("onend", false),
+ AttributeInfo("onerror", false),
+ AttributeInfo("onfocusin", false),
+ AttributeInfo("onfocusout", false),
+ AttributeInfo("onload", true),
+ AttributeInfo("onmousedown", false),
+ AttributeInfo("onmousemove", false),
+ AttributeInfo("onmouseout", false),
+ AttributeInfo("onmouseover", false),
+ AttributeInfo("onmouseup", false),
+ AttributeInfo("onrepeat", false),
+ AttributeInfo("onresize", false),
+ AttributeInfo("onscroll", false),
+ AttributeInfo("onunload", false),
+ AttributeInfo("onzoom", false),
+ AttributeInfo("opacity", true),
+ AttributeInfo("operator", true),
+ AttributeInfo("order", true),
+ AttributeInfo("orient", true),
+ AttributeInfo("orientation", true),
+ AttributeInfo("origin", false),
+ AttributeInfo("overflow", true),
+ AttributeInfo("overline-position", true),
+ AttributeInfo("overline-thickness", true),
+ AttributeInfo("paint-order", true),
+ AttributeInfo("panose-1", true),
+ AttributeInfo("path", true),
+ AttributeInfo("pathLength", false),
+ AttributeInfo("patternContentUnits", true),
+ AttributeInfo("patternTransform", true),
+ AttributeInfo("patternUnits", true),
+ AttributeInfo("pitch", true), // SVG 2.-
+ AttributeInfo("pointer-events", true),
+ AttributeInfo("points", true),
+ AttributeInfo("pointsAtX", true),
+ AttributeInfo("pointsAtY", true),
+ AttributeInfo("pointsAtZ", true),
+ AttributeInfo("preserveAlpha", true),
+ AttributeInfo("preserveAspectRatio", true),
+ AttributeInfo("primitiveUnits", true),
+ AttributeInfo("r", true),
+ AttributeInfo("radius", true),
+ AttributeInfo("refX", true),
+ AttributeInfo("refY", true),
+ AttributeInfo("rendering-intent", true),
+ AttributeInfo("repeatCount", true),
+ AttributeInfo("repeatDur", true),
+ AttributeInfo("requiredFeatures", true),
+ AttributeInfo("requiredExtensions", true),
+ AttributeInfo("restart", true),
+ AttributeInfo("result", true),
+ AttributeInfo("rotate", true),
+ AttributeInfo("rx", true),
+ AttributeInfo("ry", true),
+ AttributeInfo("scale", true),
+ AttributeInfo("seed", true),
+ AttributeInfo("shape-inside", true),
+ AttributeInfo("shape-margin", true),
+ AttributeInfo("shape-subtract", true),
+ AttributeInfo("shape-padding", true),
+ AttributeInfo("shape-rendering", true),
+ AttributeInfo("side", true),
+ AttributeInfo("slope", true),
+ AttributeInfo("solid-color", true), // SVG 2.0
+ AttributeInfo("solid-opacity", true), // SVG 2.0
+ AttributeInfo("spacing", false),
+ AttributeInfo("specularConstant", true),
+ AttributeInfo("specularExponent", true),
+ AttributeInfo("spreadMethod", true),
+ AttributeInfo("startOffset", true),
+ AttributeInfo("stdDeviation", true),
+ AttributeInfo("stemh", true),
+ AttributeInfo("stemv", true),
+ AttributeInfo("stitchTiles", true),
+ AttributeInfo("stop-color", true),
+ AttributeInfo("stop-opacity", true),
+ AttributeInfo("strikethrough-position", true),
+ AttributeInfo("strikethrough-thickness", true),
+ AttributeInfo("stroke", true),
+ AttributeInfo("stroke-dasharray", true),
+ AttributeInfo("stroke-dashoffset", true),
+ AttributeInfo("stroke-linecap", true),
+ AttributeInfo("stroke-linejoin", true),
+ AttributeInfo("stroke-miterlimit", true),
+ AttributeInfo("stroke-opacity", true),
+ AttributeInfo("stroke-width", true),
+ AttributeInfo("style", true),
+ AttributeInfo("surfaceScale", true),
+ AttributeInfo("systemLanguage", true),
+ AttributeInfo("tableValues", true),
+ AttributeInfo("target", true),
+ AttributeInfo("targetX", true),
+ AttributeInfo("targetY", true),
+ AttributeInfo("text-align", true),
+ AttributeInfo("text-anchor", true),
+ AttributeInfo("text-decoration", true),
+ AttributeInfo("text-decoration-color", true),
+ AttributeInfo("text-decoration-fill", true),
+ AttributeInfo("text-decoration-line", true),
+ AttributeInfo("text-decoration-stroke", true),
+ AttributeInfo("text-decoration-style", true),
+ AttributeInfo("text-indent", true),
+ AttributeInfo("text-orientation", true),
+ AttributeInfo("text-rendering", true),
+ AttributeInfo("text-transform", true),
+ AttributeInfo("textLength", true),
+ AttributeInfo("title", false),
+ AttributeInfo("to", true),
+ AttributeInfo("transform", true),
+ AttributeInfo("type", true),
+ AttributeInfo("u1", true),
+ AttributeInfo("u2", true),
+ AttributeInfo("underline-position", true),
+ AttributeInfo("underline-thickness", true),
+ AttributeInfo("unicode", true),
+ AttributeInfo("unicode-bidi", true),
+ AttributeInfo("unicode-range", true),
+ AttributeInfo("units-per-em", true),
+ AttributeInfo("v-alphabetic", true),
+ AttributeInfo("v-hanging", true),
+ AttributeInfo("v-ideographic", true),
+ AttributeInfo("v-mathematical", true),
+ AttributeInfo("values", true),
+ AttributeInfo("vector-effect", true),
+ AttributeInfo("version", true),
+ AttributeInfo("vert-adv-y", true),
+ AttributeInfo("vert-origin-x", true),
+ AttributeInfo("vert-origin-y", true),
+ AttributeInfo("viewBox", true),
+ AttributeInfo("viewTarget", false),
+ AttributeInfo("visibility", true),
+ AttributeInfo("white-space", true),
+ AttributeInfo("width", true),
+ AttributeInfo("widths", true),
+ AttributeInfo("word-spacing", true),
+ AttributeInfo("writing-mode", true),
+ AttributeInfo("x", true),
+ AttributeInfo("x-height", true),
+ AttributeInfo("x1", true),
+ AttributeInfo("x2", true),
+ AttributeInfo("xChannelSelector", true),
+ AttributeInfo("xlink:actuate", true),
+ AttributeInfo("xlink:arcrole", true),
+ AttributeInfo("xlink:href", true),
+ AttributeInfo("xlink:role", true),
+ AttributeInfo("xlink:show", true),
+ AttributeInfo("xlink:title", true),
+ AttributeInfo("xlink:type", true),
+ AttributeInfo("xml:base", false),
+ AttributeInfo("xml:lang", true),
+ AttributeInfo("xml:space", true),
+ AttributeInfo("xmlns", false),
+ AttributeInfo("xmlns:xlink", false),
+ AttributeInfo("y", true),
+ AttributeInfo("y1", true),
+ AttributeInfo("y2", true),
+ AttributeInfo("yChannelSelector", true),
+ AttributeInfo("z", true),
+ AttributeInfo("zoomAndPan", false),
+
+ // Extra attributes.
+ AttributeInfo("id", true),
+ AttributeInfo("inkscape:bbox-nodes", true),
+ AttributeInfo("inkscape:bbox-paths", true),
+ AttributeInfo("inkscape:box3dsidetype", true),
+ AttributeInfo("inkscape:collect", true),
+ AttributeInfo("inkscape:color", true),
+ AttributeInfo("inkscape:connection-end", true),
+ AttributeInfo("inkscape:connection-end-point", true),
+ AttributeInfo("inkscape:connection-points", true),
+ AttributeInfo("inkscape:connection-start", true),
+ AttributeInfo("inkscape:connection-start-point", true),
+ AttributeInfo("inkscape:connector-avoid", true),
+ AttributeInfo("inkscape:connector-curvature", true),
+ AttributeInfo("inkscape:connector-spacing", true),
+ AttributeInfo("inkscape:connector-type", true),
+ AttributeInfo("inkscape:corner0", true),
+ AttributeInfo("inkscape:corner7", true),
+ AttributeInfo("inkscape:current-layer", true),
+ AttributeInfo("inkscape:cx", true),
+ AttributeInfo("inkscape:cy", true),
+ AttributeInfo("inkscape:document-units", true),
+ AttributeInfo("inkscape:dstBox", true),
+ AttributeInfo("inkscape:dstColumn", true),
+ AttributeInfo("inkscape:dstPath", true),
+ AttributeInfo("inkscape:dstShape", true),
+ AttributeInfo("inkscape:excludeShape", true),
+ AttributeInfo("inkscape:expanded", true),
+ AttributeInfo("inkscape:flatsided", true),
+ AttributeInfo("inkscape:groupmode", true),
+ AttributeInfo("inkscape:highlight-color", true),
+ AttributeInfo("inkscape:href", true),
+ AttributeInfo("inkscape:label", true),
+ AttributeInfo("inkscape:layoutOptions", true),
+ AttributeInfo("inkscape:lockguides", true),
+ AttributeInfo("inkscape:locked", true),
+ AttributeInfo("inkscape:object-nodes", true),
+ AttributeInfo("inkscape:object-paths", true),
+ AttributeInfo("inkscape:original", true),
+ AttributeInfo("inkscape:original-d", true),
+ AttributeInfo("inkscape:pagecheckerboard", true),
+ AttributeInfo("inkscape:pageopacity", true),
+ AttributeInfo("inkscape:pageshadow", true),
+ AttributeInfo("inkscape:path-effect", true),
+ AttributeInfo("inkscape:persp3d", true),
+ AttributeInfo("inkscape:persp3d-origin", true),
+ AttributeInfo("inkscape:perspectiveID", true),
+ AttributeInfo("inkscape:radius", true),
+ AttributeInfo("inkscape:randomized", true),
+ AttributeInfo("inkscape:rounded", true),
+ AttributeInfo("inkscape:snap-bbox", true),
+ AttributeInfo("inkscape:snap-bbox-edge-midpoints", true),
+ AttributeInfo("inkscape:snap-bbox-midpoints", true),
+ AttributeInfo("inkscape:snap-center", true),
+ AttributeInfo("inkscape:snap-global", true),
+ AttributeInfo("inkscape:snap-grids", true),
+ AttributeInfo("inkscape:snap-intersection-paths", true),
+ AttributeInfo("inkscape:snap-midpoints", true),
+ AttributeInfo("inkscape:snap-nodes", true),
+ AttributeInfo("inkscape:snap-object-midpoints", true),
+ AttributeInfo("inkscape:snap-others", true),
+ AttributeInfo("inkscape:snap-from-guide", true),
+ AttributeInfo("inkscape:snap-page", true),
+ AttributeInfo("inkscape:snap-path-clip", true),
+ AttributeInfo("inkscape:snap-path-mask", true),
+ AttributeInfo("inkscape:snap-perpendicular", true),
+ AttributeInfo("inkscape:snap-smooth-nodes", true),
+ AttributeInfo("inkscape:snap-tangential", true),
+ AttributeInfo("inkscape:snap-text-baseline", true),
+ AttributeInfo("inkscape:snap-to-guides", true),
+ AttributeInfo("inkscape:spray-origin", true),
+ AttributeInfo("inkscape:srcNoMarkup", true),
+ AttributeInfo("inkscape:srcPango", true),
+ AttributeInfo("inkscape:transform-center-x", true),
+ AttributeInfo("inkscape:transform-center-y", true),
+ AttributeInfo("inkscape:version", true),
+ AttributeInfo("inkscape:vp_x", true),
+ AttributeInfo("inkscape:vp_y", true),
+ AttributeInfo("inkscape:vp_z", true),
+ AttributeInfo("inkscape:window-height", true),
+ AttributeInfo("inkscape:window-maximized", true),
+ AttributeInfo("inkscape:window-width", true),
+ AttributeInfo("inkscape:window-x", true),
+ AttributeInfo("inkscape:window-y", true),
+ AttributeInfo("inkscape:zoom", true),
+ AttributeInfo("inkscape:svg-dpi", true),
+ AttributeInfo("osb:paint", true),
+ AttributeInfo("sodipodi:arc-type", true),
+ AttributeInfo("sodipodi:arg1", true),
+ AttributeInfo("sodipodi:arg2", true),
+ AttributeInfo("sodipodi:argument", true),
+ AttributeInfo("sodipodi:cx", true),
+ AttributeInfo("sodipodi:cy", true),
+ AttributeInfo("sodipodi:docname", true),
+ AttributeInfo("sodipodi:end", true),
+ AttributeInfo("sodipodi:expansion", true),
+ AttributeInfo("sodipodi:insensitive", true),
+ AttributeInfo("sodipodi:linespacing", true),
+ AttributeInfo("sodipodi:open", true),
+ AttributeInfo("sodipodi:original", true),
+ AttributeInfo("sodipodi:r1", true),
+ AttributeInfo("sodipodi:r2", true),
+ AttributeInfo("sodipodi:radius", true),
+ AttributeInfo("sodipodi:revolution", true),
+ AttributeInfo("sodipodi:role", true),
+ AttributeInfo("sodipodi:rx", true),
+ AttributeInfo("sodipodi:ry", true),
+ AttributeInfo("sodipodi:sides", true),
+ AttributeInfo("sodipodi:start", true),
+ AttributeInfo("sodipodi:t0", true),
+ AttributeInfo("sodipodi:type", true),
+ AttributeInfo("sodipodi:version", false),
+
+ // SPMeshPatch
+ AttributeInfo("tensor", true),
+
+ // SPNamedView
+ AttributeInfo("fit-margin-top", true),
+ AttributeInfo("fit-margin-left", true),
+ AttributeInfo("fit-margin-right", true),
+ AttributeInfo("fit-margin-bottom", true),
+ AttributeInfo("units", true),
+ AttributeInfo("viewonly", true),
+ AttributeInfo("showgrid", true),
+// AttributeInfo("gridtype", true),
+ AttributeInfo("showguides", true),
+ AttributeInfo("gridtolerance", true),
+ AttributeInfo("guidetolerance", true),
+ AttributeInfo("objecttolerance", true),
+/* AttributeInfo("gridoriginx", true),
+ AttributeInfo("gridoriginy", true),
+ AttributeInfo("gridspacingx", true),
+ AttributeInfo("gridspacingy", true),
+ AttributeInfo("gridanglex", true),
+ AttributeInfo("gridanglez", true),
+ AttributeInfo("gridcolor", true),
+ AttributeInfo("gridopacity", true),
+ AttributeInfo("gridempcolor", true),
+ AttributeInfo("gridempopacity", true),
+ AttributeInfo("gridempspacing", true), */
+ AttributeInfo("guidecolor", true),
+ AttributeInfo("guideopacity", true),
+ AttributeInfo("guidehicolor", true),
+ AttributeInfo("guidehiopacity", true),
+ AttributeInfo("showborder", true),
+ AttributeInfo("inkscape:showpageshadow", true),
+ AttributeInfo("borderlayer", true),
+ AttributeInfo("bordercolor", true),
+ AttributeInfo("borderopacity", true),
+ AttributeInfo("pagecolor", true),
+
+ // SPGuide
+ AttributeInfo("position", true)
+ };
+
+ size_t count = sizeof(all_attrs) / sizeof(all_attrs[0]);
+ std::vector<AttributeInfo> vect(all_attrs, all_attrs + count);
+ EXPECT_GT(vect.size(), size_t(100)); // should be more than
+ return vect;
+}
+
+/**
+ * Returns a vector with counts for all IDs up to the highest known value.
+ *
+ * The index is the ID, and the value is the number of times that ID is seen.
+ */
+std::vector<size_t> getIdIds()
+{
+ std::vector<size_t> ids;
+ std::vector<AttributeInfo> all_attrs = getKnownAttrs();
+ ids.reserve(all_attrs.size()); // minimize memory thrashing
+ for (auto & all_attr : all_attrs) {
+ auto id = sp_attribute_lookup(all_attr.attr.c_str());
+ if (id >= ids.size()) {
+ ids.resize(id + 1);
+ }
+ ids[id]++;
+ }
+
+ return ids;
+}
+
+// Ensure 'supported' value for each known attribute is correct.
+TEST(AttributesTest, SupportedKnown)
+{
+ std::vector<AttributeInfo> all_attrs = getKnownAttrs();
+ for (AttrItr it(all_attrs.begin()); it != all_attrs.end(); ++it) {
+ auto id = sp_attribute_lookup(it->attr.c_str());
+ EXPECT_EQ(it->supported, id != 0u) << "Matching for attribute '" << it->attr << "'";
+ }
+}
+
+// Ensure names of known attributes are preserved when converted to id and back.
+TEST(AttributesTest, NameRoundTrip)
+{
+ std::vector<AttributeInfo> all_attrs = getKnownAttrs();
+ for (AttrItr it(all_attrs.begin()); it != all_attrs.end(); ++it) {
+ if (it->supported) {
+ auto id = sp_attribute_lookup(it->attr.c_str());
+ char const *redoneName = sp_attribute_name(id);
+ EXPECT_TRUE(redoneName != NULL) << "For attribute '" << it->attr << "'";
+ if (redoneName) {
+ EXPECT_EQ(it->attr, redoneName);
+ }
+ }
+ }
+}
+
+/* Test for any attributes that this test program doesn't know about.
+ *
+ * If any are found, then:
+ *
+ * If it is in the `inkscape:' namespace then simply add it to all_attrs with
+ * `true' as the second field (`supported').
+ *
+ * If it is in the `sodipodi:' namespace then check the spelling against sodipodi
+ * sources. If you don't have sodipodi sources, then don't add it: leave to someone
+ * else.
+ *
+ * Otherwise, it's probably a bug: ~all SVG 1.1 attributes should already be
+ * in the all_attrs table. However, the comment above all_attrs does mention
+ * some things missing from attindex.html, so there may be more. Check the SVG
+ * spec. Another possibility is that the attribute is new in SVG 1.2. In this case,
+ * check the spelling against the [draft] SVG 1.2 spec before adding to all_attrs.
+ * (If you can't be bothered checking the spec, then don't update all_attrs.)
+ *
+ * If the attribute isn't in either SVG 1.1 or 1.2 then it's probably a mistake
+ * for it not to be in the inkscape namespace. (Not sure about attributes used only
+ * on elements in the inkscape namespace though.)
+ *
+ * In any case, make sure that the attribute's source is documented accordingly.
+ */
+TEST(AttributesTest, ValuesAreKnown)
+{
+ std::vector<size_t> ids = getIdIds();
+ for (size_t i = FIRST_VALID_ID; i < ids.size(); ++i) {
+ if (!ids[i]) {
+ char const *name = sp_attribute_name((SPAttributeEnum)i);
+ EXPECT_TRUE(ids[i] > 0) << "Attribute string with enum " << i << " {" << name << "} not handled";
+ }
+ }
+}
+
+// Ensure two different names aren't mapped to the same enum value.
+TEST(AttributesTest, ValuesUnique)
+{
+ std::vector<size_t> ids = getIdIds();
+ for (size_t i = FIRST_VALID_ID; i < ids.size(); ++i) {
+ EXPECT_LE(ids[i], size_t(1)) << "Attribute enum " << i << " used for multiple strings"
+ << " including {" << sp_attribute_name((SPAttributeEnum)i) << "}";
+ }
+}
+
+} // namespace
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/testfiles/src/color-profile-test.cpp b/testfiles/src/color-profile-test.cpp
new file mode 100644
index 0000000..4804763
--- /dev/null
+++ b/testfiles/src/color-profile-test.cpp
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Unit tests for color profile.
+ *
+ * Author:
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2015 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "gtest/gtest.h"
+
+#include "attributes.h"
+#include "cms-system.h"
+#include "object/color-profile.h"
+#include "doc-per-case-test.h"
+
+namespace {
+
+/**
+ * Test fixture to inherit a shared doc and create a color profile instance per test.
+ */
+class ProfTest : public DocPerCaseTest
+{
+public:
+ ProfTest() :
+ DocPerCaseTest(),
+ _prof(0)
+ {
+ }
+
+protected:
+ void SetUp() override
+ {
+ DocPerCaseTest::SetUp();
+ _prof = new Inkscape::ColorProfile();
+ ASSERT_TRUE( _prof != NULL );
+ _prof->document = _doc;
+ }
+
+ void TearDown() override
+ {
+ if (_prof) {
+ delete _prof;
+ _prof = NULL;
+ }
+ DocPerCaseTest::TearDown();
+ }
+
+ Inkscape::ColorProfile *_prof;
+};
+
+typedef ProfTest ColorProfileTest;
+
+TEST_F(ColorProfileTest, SetRenderingIntent)
+{
+ struct {
+ gchar const *attr;
+ guint intVal;
+ }
+ const cases[] = {
+ {"auto", (guint)Inkscape::RENDERING_INTENT_AUTO},
+ {"perceptual", (guint)Inkscape::RENDERING_INTENT_PERCEPTUAL},
+ {"relative-colorimetric", (guint)Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC},
+ {"saturation", (guint)Inkscape::RENDERING_INTENT_SATURATION},
+ {"absolute-colorimetric", (guint)Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC},
+ {"something-else", (guint)Inkscape::RENDERING_INTENT_UNKNOWN},
+ {"auto2", (guint)Inkscape::RENDERING_INTENT_UNKNOWN},
+ };
+
+ for (auto i : cases) {
+ _prof->setKeyValue( SP_ATTR_RENDERING_INTENT, i.attr);
+ ASSERT_EQ( (guint)i.intVal, _prof->rendering_intent ) << i.attr;
+ }
+}
+
+TEST_F(ColorProfileTest, SetLocal)
+{
+ gchar const* cases[] = {
+ "local",
+ "something",
+ };
+
+ for (auto & i : cases) {
+ _prof->setKeyValue( SP_ATTR_LOCAL, i);
+ ASSERT_TRUE( _prof->local != NULL );
+ if ( _prof->local ) {
+ ASSERT_EQ( std::string(i), _prof->local );
+ }
+ }
+ _prof->setKeyValue( SP_ATTR_LOCAL, NULL);
+ ASSERT_EQ( (gchar*)0, _prof->local );
+}
+
+TEST_F(ColorProfileTest, SetName)
+{
+ gchar const* cases[] = {
+ "name",
+ "something",
+ };
+
+ for (auto & i : cases) {
+ _prof->setKeyValue( SP_ATTR_NAME, i);
+ ASSERT_TRUE( _prof->name != NULL );
+ if ( _prof->name ) {
+ ASSERT_EQ( std::string(i), _prof->name );
+ }
+ }
+ _prof->setKeyValue( SP_ATTR_NAME, NULL );
+ ASSERT_EQ( (gchar*)0, _prof->name );
+}
+
+
+} // namespace
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/testfiles/src/curve-test.cpp b/testfiles/src/curve-test.cpp
new file mode 100644
index 0000000..8c94b77
--- /dev/null
+++ b/testfiles/src/curve-test.cpp
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Curve test
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2020 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <gtest/gtest.h>
+
+#include "display/curve.h"
+#include <2geom/curves.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+
+class CurveTest : public ::testing::Test {
+ public:
+ Geom::Path path1;
+ Geom::Path path2;
+ Geom::Path path3;
+ Geom::Path path4;
+
+ protected:
+ CurveTest()
+ : path4(Geom::Point(3, 5)) // Just a moveto
+ {
+ // Closed path
+ path1.append(Geom::LineSegment(Geom::Point(0, 0), Geom::Point(1, 0)));
+ path1.append(Geom::LineSegment(Geom::Point(1, 0), Geom::Point(1, 1)));
+ path1.close();
+ // Closed path (ClosingSegment is zero length)
+ path2.append(Geom::LineSegment(Geom::Point(2, 0), Geom::Point(3, 0)));
+ path2.append(Geom::CubicBezier(Geom::Point(3, 0), Geom::Point(2, 1), Geom::Point(1, 1), Geom::Point(2, 0)));
+ path2.close();
+ // Open path
+ path3.setStitching(true);
+ path3.append(Geom::EllipticalArc(Geom::Point(4, 0), 1, 2, M_PI, false, false, Geom::Point(5, 1)));
+ path3.append(Geom::LineSegment(Geom::Point(5, 1), Geom::Point(5, 2)));
+ path3.append(Geom::LineSegment(Geom::Point(6, 4), Geom::Point(2, 4)));
+ }
+};
+
+TEST_F(CurveTest, testGetSegmentCount)
+{
+ { // Zero segments
+ Geom::PathVector pv;
+ SPCurve curve(pv);
+ ASSERT_EQ(curve.get_segment_count(), 0u);
+ }
+ { // Zero segments
+ Geom::PathVector pv;
+ pv.push_back(Geom::Path());
+ SPCurve curve(pv);
+ ASSERT_EQ(curve.get_segment_count(), 0u);
+ }
+ { // Individual paths
+ Geom::PathVector pv((Geom::Path()));
+ pv[0] = path1;
+ ASSERT_EQ(SPCurve(pv).get_segment_count(), 3u);
+ pv[0] = path2;
+ ASSERT_EQ(SPCurve(pv).get_segment_count(), 2u);
+ pv[0] = path3;
+ ASSERT_EQ(SPCurve(pv).get_segment_count(), 4u);
+ pv[0] = path4;
+ ASSERT_EQ(SPCurve(pv).get_segment_count(), 0u);
+ pv[0].close();
+ ASSERT_EQ(SPCurve(pv).get_segment_count(), 0u);
+ }
+ { // Combination
+ Geom::PathVector pv;
+ pv.push_back(path1);
+ pv.push_back(path2);
+ pv.push_back(path3);
+ pv.push_back(path4);
+ SPCurve curve(pv);
+ ASSERT_EQ(curve.get_segment_count(), 9u);
+ }
+}
+
+TEST_F(CurveTest, testNodesInPathForZeroSegments)
+{
+ { // Zero segments
+ Geom::PathVector pv;
+ SPCurve curve(pv);
+ ASSERT_EQ(curve.nodes_in_path(), 0u);
+ }
+ { // Zero segments
+ Geom::PathVector pv;
+ pv.push_back(Geom::Path());
+ SPCurve curve(pv);
+ ASSERT_EQ(curve.nodes_in_path(), 1u);
+ }
+}
+
+TEST_F(CurveTest, testNodesInPathForIndividualPaths)
+{
+ Geom::PathVector pv((Geom::Path()));
+ pv[0] = path1;
+ ASSERT_EQ(SPCurve(pv).nodes_in_path(), 3u);
+ pv[0] = path2;
+ ASSERT_EQ(SPCurve(pv).nodes_in_path(), 2u); // zero length closing segments do not increase the nodecount.
+ pv[0] = path3;
+ ASSERT_EQ(SPCurve(pv).nodes_in_path(), 5u);
+ pv[0] = path4;
+ ASSERT_EQ(SPCurve(pv).nodes_in_path(), 1u);
+}
+
+TEST_F(CurveTest, testNodesInPathForNakedMoveToClosedPath)
+{
+ Geom::PathVector pv((Geom::Path()));
+ pv[0] = path4; // just a MoveTo
+ pv[0].close();
+ ASSERT_EQ(SPCurve(pv).nodes_in_path(), 1u);
+}
+
+/*
+TEST_F(CurveTest, testNodesInPathForPathsCombination)
+{
+ Geom::PathVector pv;
+ pv.push_back(path1);
+ pv.push_back(path2);
+ pv.push_back(path3);
+ pv.push_back(path4);
+ SPCurve curve(pv);
+ ASSERT_EQ(curve.nodes_in_path(), 12u);
+}
+*/
+
+TEST_F(CurveTest, testIsEmpty)
+{
+ ASSERT_TRUE(SPCurve(Geom::PathVector()).is_empty());
+ ASSERT_FALSE(SPCurve(path1).is_empty());
+ ASSERT_FALSE(SPCurve(path2).is_empty());
+ ASSERT_FALSE(SPCurve(path3).is_empty());
+ ASSERT_FALSE(SPCurve(path4).is_empty());
+}
+
+TEST_F(CurveTest, testIsClosed)
+{
+ ASSERT_FALSE(SPCurve(Geom::PathVector()).is_closed());
+ Geom::PathVector pv((Geom::Path()));
+ ASSERT_FALSE(SPCurve(pv).is_closed());
+ pv[0].close();
+ ASSERT_TRUE(SPCurve(pv).is_closed());
+ ASSERT_TRUE(SPCurve(path1).is_closed());
+ ASSERT_TRUE(SPCurve(path2).is_closed());
+ ASSERT_FALSE(SPCurve(path3).is_closed());
+ ASSERT_FALSE(SPCurve(path4).is_closed());
+}
+
+/*
+TEST_F(CurveTest, testLastFirstSegment)
+{
+ Geom::PathVector pv(path4);
+ ASSERT_EQ(SPCurve(pv).first_segment(), (void *)0);
+ ASSERT_EQ(SPCurve(pv).last_segment(), (void *)0);
+ pv[0].close();
+ ASSERT_NE(SPCurve(pv).first_segment(), (void *)0);
+ ASSERT_NE(SPCurve(pv).last_segment(), (void *)0);
+}
+*/
+
+TEST_F(CurveTest, testLastFirstPath)
+{
+ Geom::PathVector pv;
+ ASSERT_EQ(SPCurve(pv).first_path(), (void *)0);
+ ASSERT_EQ(SPCurve(pv).last_path(), (void *)0);
+ pv.push_back(path1);
+ ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]);
+ ASSERT_EQ(*SPCurve(pv).last_path(), pv[0]);
+ pv.push_back(path2);
+ ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]);
+ ASSERT_EQ(*SPCurve(pv).last_path(), pv[1]);
+ pv.push_back(path3);
+ ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]);
+ ASSERT_EQ(*SPCurve(pv).last_path(), pv[2]);
+ pv.push_back(path4);
+ ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]);
+ ASSERT_EQ(*SPCurve(pv).last_path(), pv[3]);
+}
+
+TEST_F(CurveTest, testFirstPoint)
+{
+ ASSERT_EQ(*(SPCurve(path1).first_point()), Geom::Point(0, 0));
+ ASSERT_EQ(*(SPCurve(path2).first_point()), Geom::Point(2, 0));
+ ASSERT_EQ(*(SPCurve(path3).first_point()), Geom::Point(4, 0));
+ ASSERT_EQ(*(SPCurve(path4).first_point()), Geom::Point(3, 5));
+ Geom::PathVector pv;
+ ASSERT_FALSE(SPCurve(pv).first_point());
+ pv.push_back(path1);
+ pv.push_back(path2);
+ pv.push_back(path3);
+ ASSERT_EQ(*(SPCurve(pv).first_point()), Geom::Point(0, 0));
+ pv.insert(pv.begin(), path4);
+ ASSERT_EQ(*(SPCurve(pv).first_point()), Geom::Point(3, 5));
+}
+
+/*
+TEST_F(CurveTest, testLastPoint)
+{
+ ASSERT_EQ(*(SPCurve(path1).last_point()), Geom::Point(0, 0));
+ ASSERT_EQ(*(SPCurve(path2).last_point()), Geom::Point(2, 0));
+ ASSERT_EQ(*(SPCurve(path3).last_point()), Geom::Point(8, 4));
+ ASSERT_EQ(*(SPCurve(path4).last_point()), Geom::Point(3, 5));
+ Geom::PathVector pv;
+ ASSERT_FALSE(SPCurve(pv).last_point());
+ pv.push_back(path1);
+ pv.push_back(path2);
+ pv.push_back(path3);
+ ASSERT_EQ(*(SPCurve(pv).last_point()), Geom::Point(8, 4));
+ pv.push_back(path4);
+ ASSERT_EQ(*(SPCurve(pv).last_point()), Geom::Point(3, 5));
+}
+*/
+
+TEST_F(CurveTest, testSecondPoint)
+{
+ ASSERT_EQ(*(SPCurve(path1).second_point()), Geom::Point(1, 0));
+ ASSERT_EQ(*(SPCurve(path2).second_point()), Geom::Point(3, 0));
+ ASSERT_EQ(*(SPCurve(path3).second_point()), Geom::Point(5, 1));
+ ASSERT_EQ(*(SPCurve(path4).second_point()), Geom::Point(3, 5));
+ Geom::PathVector pv;
+ pv.push_back(path1);
+ pv.push_back(path2);
+ pv.push_back(path3);
+ ASSERT_EQ(*(SPCurve(pv).second_point()), Geom::Point(1, 0));
+ pv.insert(pv.begin(), path4);
+ ASSERT_EQ(*SPCurve(pv).second_point(), Geom::Point(0, 0));
+}
+
+/*
+TEST_F(CurveTest, testPenultimatePoint)
+{
+ ASSERT_EQ(*(SPCurve(Geom::PathVector(path1)).penultimate_point()), Geom::Point(1, 1));
+ ASSERT_EQ(*(SPCurve(Geom::PathVector(path2)).penultimate_point()), Geom::Point(3, 0));
+ ASSERT_EQ(*(SPCurve(Geom::PathVector(path3)).penultimate_point()), Geom::Point(6, 4));
+ ASSERT_EQ(*(SPCurve(Geom::PathVector(path4)).penultimate_point()), Geom::Point(3, 5));
+ Geom::PathVector pv;
+ pv.push_back(path1);
+ pv.push_back(path2);
+ pv.push_back(path3);
+ ASSERT_EQ(*(SPCurve(pv).penultimate_point()), Geom::Point(6, 4));
+ pv.push_back(path4);
+ ASSERT_EQ(*(SPCurve(pv).penultimate_point()), Geom::Point(8, 4));
+}
+*/
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/cxxtests-to-migrate/marker-test.h b/testfiles/src/cxxtests-to-migrate/marker-test.h
new file mode 100644
index 0000000..1a77aff
--- /dev/null
+++ b/testfiles/src/cxxtests-to-migrate/marker-test.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * @brief Unit tests for SVG marker handling
+ *//*
+ * Authors:
+ * see git history
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ *
+ * Copyright (C) 2016 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cxxtest/TestSuite.h>
+
+#include "sp-marker-loc.h"
+
+class MarkerTest : public CxxTest::TestSuite
+{
+public:
+
+ void testMarkerLoc()
+ {
+ // code depends on these *exact* values, so check them here.
+ TS_ASSERT_EQUALS(SP_MARKER_LOC, 0);
+ TS_ASSERT_EQUALS(SP_MARKER_LOC_START, 1);
+ TS_ASSERT_EQUALS(SP_MARKER_LOC_MID, 2);
+ TS_ASSERT_EQUALS(SP_MARKER_LOC_END, 3);
+ TS_ASSERT_EQUALS(SP_MARKER_LOC_QTY, 4);
+ }
+
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/cxxtests-to-migrate/mod360-test.h b/testfiles/src/cxxtests-to-migrate/mod360-test.h
new file mode 100644
index 0000000..12ee994
--- /dev/null
+++ b/testfiles/src/cxxtests-to-migrate/mod360-test.h
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2016 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SEEN_MOD_360_TEST_H
+#define SEEN_MOD_360_TEST_H
+
+#include <cxxtest/TestSuite.h>
+#include <2geom/math-utils.h>
+#include "mod360.h"
+
+
+class Mod360Test : public CxxTest::TestSuite
+{
+public:
+ static double inf() { return INFINITY; }
+ static double nan() { return ((double)INFINITY) - ((double)INFINITY); }
+
+ void testMod360()
+ {
+ double cases[][2] = {
+ {0, 0},
+ {10, 10},
+ {360, 0},
+ {361, 1},
+ {-1, 359},
+ {-359, 1},
+ {-360, -0},
+ {-361, 359},
+ {inf(), 0},
+ {-inf(), 0},
+ {nan(), 0},
+ {720, 0},
+ {-721, 359},
+ {-1000, 80}
+ };
+
+ for ( unsigned i = 0; i < G_N_ELEMENTS(cases); i++ ) {
+ double result = mod360( cases[i][0] );
+ TS_ASSERT_EQUALS( cases[i][1], result );
+ }
+ }
+
+};
+
+
+#endif // SEEN_MOD_360_TEST_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+
diff --git a/testfiles/src/cxxtests-to-migrate/preferences-test.h b/testfiles/src/cxxtests-to-migrate/preferences-test.h
new file mode 100644
index 0000000..0dc04b8
--- /dev/null
+++ b/testfiles/src/cxxtests-to-migrate/preferences-test.h
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * @brief Unit tests for the Preferences object
+ *//*
+ * Authors:
+ * see git history
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2016 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cxxtest/TestSuite.h>
+#include "preferences.h"
+
+#include <glibmm/ustring.h>
+
+// test observer
+class TestObserver : public Inkscape::Preferences::Observer {
+public:
+ TestObserver(Glib::ustring const &path) :
+ Inkscape::Preferences::Observer(path),
+ value(0) {}
+
+ virtual void notify(Inkscape::Preferences::Entry const &val)
+ {
+ value = val.getInt();
+ }
+ int value;
+};
+
+class PreferencesTest : public CxxTest::TestSuite {
+public:
+ void setUp() {
+ prefs = Inkscape::Preferences::get();
+ }
+ void tearDown() {
+ prefs = NULL;
+ Inkscape::Preferences::unload();
+ }
+
+ void testStartingState()
+ {
+ TS_ASSERT_DIFFERS(prefs, static_cast<void*>(0));
+ TS_ASSERT_EQUALS(prefs->isWritable(), true);
+ }
+
+ void testOverwrite()
+ {
+ prefs->setInt("/test/intvalue", 123);
+ prefs->setInt("/test/intvalue", 321);
+ TS_ASSERT_EQUALS(prefs->getInt("/test/intvalue"), 321);
+ }
+
+ void testDefaultReturn()
+ {
+ TS_ASSERT_EQUALS(prefs->getInt("/this/path/does/not/exist", 123), 123);
+ }
+
+ void testLimitedReturn()
+ {
+ prefs->setInt("/test/intvalue", 1000);
+
+ // simple case
+ TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 0, 500), 123);
+ // the below may seem quirky but this behaviour is intended
+ TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 1001, 5000), 123);
+ // corner cases
+ TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 0, 1000), 1000);
+ TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 1000, 5000), 1000);
+ }
+
+ void testKeyObserverNotification()
+ {
+ Glib::ustring const path = "/some/random/path";
+ TestObserver obs("/some/random");
+ obs.value = 1;
+ prefs->setInt(path, 5);
+ TS_ASSERT_EQUALS(obs.value, 1); // no notifications sent before adding
+
+ prefs->addObserver(obs);
+ prefs->setInt(path, 10);
+ TS_ASSERT_EQUALS(obs.value, 10);
+ prefs->setInt("/some/other/random/path", 10);
+ TS_ASSERT_EQUALS(obs.value, 10); // value should not change
+
+ prefs->removeObserver(obs);
+ prefs->setInt(path, 15);
+ TS_ASSERT_EQUALS(obs.value, 10); // no notifications sent after removal
+ }
+
+ void testEntryObserverNotification()
+ {
+ Glib::ustring const path = "/some/random/path";
+ TestObserver obs(path);
+ obs.value = 1;
+ prefs->setInt(path, 5);
+ TS_ASSERT_EQUALS(obs.value, 1); // no notifications sent before adding
+
+ prefs->addObserver(obs);
+ prefs->setInt(path, 10);
+ TS_ASSERT_EQUALS(obs.value, 10);
+
+ // test that filtering works properly
+ prefs->setInt("/some/random/value", 1234);
+ TS_ASSERT_EQUALS(obs.value, 10);
+ prefs->setInt("/some/randomvalue", 1234);
+ TS_ASSERT_EQUALS(obs.value, 10);
+ prefs->setInt("/some/random/path2", 1234);
+ TS_ASSERT_EQUALS(obs.value, 10);
+
+ prefs->removeObserver(obs);
+ prefs->setInt(path, 15);
+ TS_ASSERT_EQUALS(obs.value, 10); // no notifications sent after removal
+ }
+
+ void testPreferencesEntryMethods()
+ {
+ prefs->setInt("/test/prefentry", 100);
+ Inkscape::Preferences::Entry val = prefs->getEntry("/test/prefentry");
+ TS_ASSERT(val.isValid());
+ TS_ASSERT_EQUALS(val.getPath(), "/test/prefentry");
+ TS_ASSERT_EQUALS(val.getEntryName(), "prefentry");
+ TS_ASSERT_EQUALS(val.getInt(), 100);
+ }
+private:
+ Inkscape::Preferences *prefs;
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h b/testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h
new file mode 100644
index 0000000..271f319
--- /dev/null
+++ b/testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2016 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SEEN_SP_STYLE_ELEM_TEST_H
+#define SEEN_SP_STYLE_ELEM_TEST_H
+
+#include <cxxtest/TestSuite.h>
+
+#include "test-helpers.h"
+
+#include "sp-style-elem.h"
+#include "xml/repr.h"
+
+class SPStyleElemTest : public CxxTest::TestSuite
+{
+public:
+ SPDocument* _doc;
+
+ SPStyleElemTest() :
+ _doc(0)
+ {
+ }
+
+ virtual ~SPStyleElemTest()
+ {
+ if ( _doc )
+ {
+ _doc->doUnref();
+ }
+ }
+
+ static void createSuiteSubclass( SPStyleElemTest *& dst )
+ {
+ SPStyleElem *style_elem = new SPStyleElem();
+
+ if ( style_elem ) {
+ TS_ASSERT(!style_elem->is_css);
+ TS_ASSERT(style_elem->media.print);
+ TS_ASSERT(style_elem->media.screen);
+ delete style_elem;
+
+ dst = new SPStyleElemTest();
+ }
+ }
+
+ static SPStyleElemTest *createSuite()
+ {
+ return Inkscape::createSuiteAndDocument<SPStyleElemTest>( createSuiteSubclass );
+ }
+
+ static void destroySuite( SPStyleElemTest *suite ) { delete suite; }
+
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
+
+
+ void testSetType()
+ {
+ SPStyleElem *style_elem = new SPStyleElem();
+ SP_OBJECT(style_elem)->document = _doc;
+
+ SP_OBJECT(style_elem)->setKeyValue( SP_ATTR_TYPE, "something unrecognized");
+ TS_ASSERT( !style_elem->is_css );
+
+ SP_OBJECT(style_elem)->setKeyValue( SP_ATTR_TYPE, "text/css");
+ TS_ASSERT( style_elem->is_css );
+
+ SP_OBJECT(style_elem)->setKeyValue( SP_ATTR_TYPE, "atext/css");
+ TS_ASSERT( !style_elem->is_css );
+
+ SP_OBJECT(style_elem)->setKeyValue( SP_ATTR_TYPE, "text/cssx");
+ TS_ASSERT( !style_elem->is_css );
+
+ delete style_elem;
+ }
+
+ void testWrite()
+ {
+ TS_ASSERT( _doc );
+ TS_ASSERT( _doc->getReprDoc() );
+ if ( !_doc->getReprDoc() ) {
+ return; // evil early return
+ }
+
+ SPStyleElem *style_elem = new SPStyleElem();
+ SP_OBJECT(style_elem)->document = _doc;
+
+ SP_OBJECT(style_elem)->setKeyValue( SP_ATTR_TYPE, "text/css");
+ Inkscape::XML::Node *repr = _doc->getReprDoc()->createElement("svg:style");
+ SP_OBJECT(style_elem)->updateRepr(_doc->getReprDoc(), repr, SP_OBJECT_WRITE_ALL);
+ {
+ gchar const *typ = repr->attribute("type");
+ TS_ASSERT( typ != NULL );
+ if ( typ )
+ {
+ TS_ASSERT_EQUALS( std::string(typ), std::string("text/css") );
+ }
+ }
+
+ delete style_elem;
+ }
+
+ void testBuild()
+ {
+ TS_ASSERT( _doc );
+ TS_ASSERT( _doc->getReprDoc() );
+ if ( !_doc->getReprDoc() ) {
+ return; // evil early return
+ }
+
+ SPStyleElem *style_elem = new SPStyleElem();
+ Inkscape::XML::Node *const repr = _doc->getReprDoc()->createElement("svg:style");
+ repr->setAttribute("type", "text/css");
+ style_elem->invoke_build( _doc, repr, false);
+ TS_ASSERT( style_elem->is_css );
+ TS_ASSERT( style_elem->media.print );
+ TS_ASSERT( style_elem->media.screen );
+
+ /* Some checks relevant to the read_content test below. */
+ {
+ g_assert(_doc->style_cascade);
+ CRStyleSheet const *const stylesheet = cr_cascade_get_sheet(_doc->style_cascade, ORIGIN_AUTHOR);
+ g_assert(stylesheet);
+ g_assert(stylesheet->statements == NULL);
+ }
+
+ delete style_elem;
+ Inkscape::GC::release(repr);
+ }
+
+ void testReadContent()
+ {
+ TS_ASSERT( _doc );
+ TS_ASSERT( _doc->getReprDoc() );
+ if ( !_doc->getReprDoc() ) {
+ return; // evil early return
+ }
+
+ SPStyleElem *style_elem = new SPStyleElem();
+ Inkscape::XML::Node *const repr = _doc->getReprDoc()->createElement("svg:style");
+ repr->setAttribute("type", "text/css");
+ Inkscape::XML::Node *const content_repr = _doc->getReprDoc()->createTextNode(".myclass { }");
+ repr->addChild(content_repr, NULL);
+ style_elem->invoke_build(_doc, repr, false);
+ TS_ASSERT( style_elem->is_css );
+ TS_ASSERT( _doc->style_cascade );
+ CRStyleSheet const *const stylesheet = cr_cascade_get_sheet(_doc->style_cascade, ORIGIN_AUTHOR);
+ TS_ASSERT(stylesheet != NULL);
+ TS_ASSERT(stylesheet->statements != NULL);
+
+ delete style_elem;
+ Inkscape::GC::release(repr);
+ }
+
+};
+
+
+#endif // SEEN_SP_STYLE_ELEM_TEST_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/cxxtests-to-migrate/test-helpers.h b/testfiles/src/cxxtests-to-migrate/test-helpers.h
new file mode 100644
index 0000000..96bf5b4
--- /dev/null
+++ b/testfiles/src/cxxtests-to-migrate/test-helpers.h
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2016 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SEEN_TEST_HELPERS_H
+#define SEEN_TEST_HELPERS_H
+
+
+#include <cxxtest/TestSuite.h>
+
+#include "document.h"
+#include "inkscape.h"
+
+
+// Dummy functions to keep linker happy
+#if !defined(DUMMY_MAIN_TEST_CALLS_SEEN)
+#define DUMMY_MAIN_TEST_CALLS_SEEN
+int sp_main_gui (int, char const**) { return 0; }
+int sp_main_console (int, char const**) { return 0; }
+#endif // DUMMY_MAIN_TEST_CALLS_SEEN
+
+namespace Inkscape
+{
+
+template <class T>
+T* createSuiteAndDocument( void (*fun)(T*&) )
+{
+ T* suite = 0;
+
+#if !GLIB_CHECK_VERSION(2,36,0)
+ g_type_init();
+#endif
+
+ Inkscape::GC::init();
+ if ( !Inkscape::Application::exists() )
+ {
+ // Create the global inkscape object.
+ Inkscape::Application::create(false);
+ }
+
+ SPDocument* tmp = SPDocument::createNewDoc( NULL, TRUE, true );
+ if ( tmp ) {
+ fun( suite );
+ if ( suite )
+ {
+ suite->_doc = tmp;
+ }
+ else
+ {
+ tmp->doUnref();
+ }
+ }
+
+ return suite;
+}
+
+} // namespace Inkscape
+
+#endif // SEEN_TEST_HELPERS_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/cxxtests-to-migrate/verbs-test.h b/testfiles/src/cxxtests-to-migrate/verbs-test.h
new file mode 100644
index 0000000..b8fd299
--- /dev/null
+++ b/testfiles/src/cxxtests-to-migrate/verbs-test.h
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2016 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+
+#include <cxxtest/TestSuite.h>
+
+#include "verbs.h"
+
+class VerbsTest : public CxxTest::TestSuite
+{
+public:
+
+ class TestHook : public Inkscape::Verb {
+ public:
+ static int getInternalTableSize() { return _getBaseListSize(); }
+
+ private:
+ TestHook();
+ };
+
+ void testEnumLength()
+ {
+ TS_ASSERT_DIFFERS( 0, static_cast<int>(SP_VERB_LAST) );
+ TS_ASSERT_EQUALS( static_cast<int>(SP_VERB_LAST) + 1, TestHook::getInternalTableSize() );
+ }
+
+ void testEnumFixed()
+ {
+ TS_ASSERT_EQUALS( 0, static_cast<int>(SP_VERB_INVALID) );
+ TS_ASSERT_EQUALS( 1, static_cast<int>(SP_VERB_NONE) );
+
+ TS_ASSERT_DIFFERS( 0, static_cast<int>(SP_VERB_LAST) );
+ TS_ASSERT_DIFFERS( 1, static_cast<int>(SP_VERB_LAST) );
+ }
+
+ void testFetch()
+ {
+ for ( int i = 0; i < static_cast<int>(SP_VERB_LAST); i++ )
+ {
+ char tmp[16];
+ snprintf( tmp, sizeof(tmp), "Verb# %d", i );
+ tmp[sizeof(tmp)-1] = 0;
+ std::string descr(tmp);
+
+ Inkscape::Verb* verb = Inkscape::Verb::get(i);
+ TSM_ASSERT( descr, verb );
+ if ( verb )
+ {
+ TSM_ASSERT_EQUALS( descr, verb->get_code(), static_cast<unsigned int>(i) );
+
+ if ( i != static_cast<int>(SP_VERB_INVALID) )
+ {
+ TSM_ASSERT( descr, verb->get_id() );
+ TSM_ASSERT( descr, verb->get_name() );
+
+ Inkscape::Verb* bounced = verb->getbyid( verb->get_id() );
+ // TODO - put this back once verbs are fixed
+ //TSM_ASSERT( descr, bounced );
+ if ( bounced )
+ {
+ TSM_ASSERT_EQUALS( descr, bounced->get_code(), static_cast<unsigned int>(i) );
+ }
+ else
+ {
+ TS_FAIL( std::string("Unable to getbyid() for ") + descr + std::string(" ID: '") + std::string(verb->get_id()) + std::string("'") );
+ }
+ }
+ else
+ {
+ TSM_ASSERT( std::string("SP_VERB_INVALID"), !verb->get_id() );
+ TSM_ASSERT( std::string("SP_VERB_INVALID"), !verb->get_name() );
+ }
+ }
+ }
+ }
+
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/dir-util-test.cpp b/testfiles/src/dir-util-test.cpp
new file mode 100644
index 0000000..bac5e3c
--- /dev/null
+++ b/testfiles/src/dir-util-test.cpp
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Unit tests for dir utils.
+ *
+ * Author:
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 2015 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "gtest/gtest.h"
+
+#include <glib.h>
+
+#include "io/dir-util.h"
+
+namespace {
+
+
+TEST(DirUtilTest, Base)
+{
+ char const* cases[][3] = {
+#if defined(WIN32) || defined(__WIN32__)
+ {"\\foo\\bar", "\\foo", "bar"},
+ {"\\foo\\barney", "\\foo\\bar", "\\foo\\barney"},
+ {"\\foo\\bar\\baz", "\\foo\\", "bar\\baz"},
+ {"\\foo\\bar\\baz", "\\", "foo\\bar\\baz"},
+ {"\\foo\\bar\\baz", "\\foo\\qux", "\\foo\\bar\\baz"},
+#else
+ {"/foo/bar", "/foo", "bar"},
+ {"/foo/barney", "/foo/bar", "/foo/barney"},
+ {"/foo/bar/baz", "/foo/", "bar/baz"},
+ {"/foo/bar/baz", "/", "foo/bar/baz"},
+ {"/foo/bar/baz", "/foo/qux", "/foo/bar/baz"},
+#endif
+ };
+
+ for (auto & i : cases)
+ {
+ if ( i[0] && i[1] ) { // std::string can't use null.
+ std::string result = sp_relative_path_from_path( i[0], i[1] );
+ ASSERT_FALSE( result.empty() );
+ if ( !result.empty() )
+ {
+ ASSERT_EQ( std::string(i[2]), result );
+ }
+ }
+ }
+}
+
+} // namespace
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/testfiles/src/drag-and-drop-svgz.cpp b/testfiles/src/drag-and-drop-svgz.cpp
new file mode 100644
index 0000000..5ea7c0b
--- /dev/null
+++ b/testfiles/src/drag-and-drop-svgz.cpp
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+/**
+ * @file
+ * Test that svgz (= compressed SVG) import/drag-and-drop
+ * is working: https://gitlab.com/inkscape/inkscape/issues/906 .
+ *
+ */
+/*
+ * Authors:
+ * Shlomi Fish
+ *
+ * Copyright (C) 2020 Authors
+ */
+
+#include "doc-per-case-test.h"
+#include <glibmm.h>
+
+#include "extension/db.h"
+#include "extension/find_extension_by_mime.h"
+#include "extension/internal/svgz.h"
+#include "path-prefix.h"
+#include "preferences.h"
+
+#include "gtest/gtest.h"
+
+
+class SvgzImportTest : public DocPerCaseTest {
+ public:
+ SvgzImportTest() {}
+ void TestBody()
+ {
+ ASSERT_TRUE(_doc != nullptr);
+ ASSERT_TRUE(_doc->getRoot() != nullptr);
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool("/dialogs/import/ask_svg", true);
+ prefs->setBool("/options/onimport", true);
+ auto ext = Inkscape::Extension::find_by_mime("image/svg+xml-compressed");
+ ext->set_gui(true);
+ auto fn = Glib::build_filename(INKSCAPE_EXAMPLESDIR, "tiger.svgz");
+ auto imod = dynamic_cast<Inkscape::Extension::Input *>(ext);
+ auto svg_mod = (new Inkscape::Extension::Internal::Svg);
+ ASSERT_TRUE(svg_mod->open(imod, fn.c_str()) != nullptr);
+ }
+ ~SvgzImportTest() override {}
+};
+
+TEST_F(SvgzImportTest, Eq)
+{
+ SvgzImportTest foo;
+ foo.TestBody();
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/extract-uri-test.cpp b/testfiles/src/extract-uri-test.cpp
new file mode 100644
index 0000000..acff966
--- /dev/null
+++ b/testfiles/src/extract-uri-test.cpp
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Test extract_uri
+ */
+/*
+ * Authors:
+ * Thomas Holder
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "extract-uri.h"
+#include "gtest/gtest.h"
+
+TEST(ExtractUriTest, valid)
+{
+ ASSERT_EQ(extract_uri("url(#foo)"), "#foo");
+ ASSERT_EQ(extract_uri("url( \t #foo \t )"), "#foo");
+ ASSERT_EQ(extract_uri("url( '#foo' )"), "#foo");
+ ASSERT_EQ(extract_uri("url('url(foo)')"), "url(foo)");
+ ASSERT_EQ(extract_uri("url(\"foo(url)\")"), "foo(url)");
+ ASSERT_EQ(extract_uri("url()bar"), "");
+ ASSERT_EQ(extract_uri("url( )bar"), "");
+ ASSERT_EQ(extract_uri("url(a b)"), "a b");
+}
+
+TEST(ExtractUriTest, legacy)
+{
+ ASSERT_EQ(extract_uri("url (foo)"), "foo");
+}
+
+TEST(ExtractUriTest, invalid)
+{
+ ASSERT_EQ(extract_uri("#foo"), "");
+ ASSERT_EQ(extract_uri(" url(foo)"), "");
+ ASSERT_EQ(extract_uri("url(#foo"), "");
+ ASSERT_EQ(extract_uri("url('#foo'"), "");
+ ASSERT_EQ(extract_uri("url('#foo)"), "");
+ ASSERT_EQ(extract_uri("url #foo)"), "");
+}
+
+static char const *extract_end(char const *s)
+{
+ char const *end = nullptr;
+ extract_uri(s, &end);
+ return end;
+}
+
+TEST(ExtractUriTest, endptr)
+{
+ ASSERT_STREQ(extract_end(""), nullptr);
+ ASSERT_STREQ(extract_end("url(invalid"), nullptr);
+ ASSERT_STREQ(extract_end("url('invalid)"), nullptr);
+ ASSERT_STREQ(extract_end("url(valid)"), "");
+ ASSERT_STREQ(extract_end("url(valid)foo"), "foo");
+ ASSERT_STREQ(extract_end("url('valid')bar"), "bar");
+ ASSERT_STREQ(extract_end("url( 'valid' )bar"), "bar");
+ ASSERT_STREQ(extract_end("url( valid ) bar "), " bar ");
+ ASSERT_STREQ(extract_end("url()bar"), "bar");
+ ASSERT_STREQ(extract_end("url( )bar"), "bar");
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/lpe-bool-test.cpp b/testfiles/src/lpe-bool-test.cpp
new file mode 100644
index 0000000..31bc371
--- /dev/null
+++ b/testfiles/src/lpe-bool-test.cpp
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * LPE Boolean operation test
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2020 Authors
+ *
+ * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information
+ */
+
+#include <gtest/gtest.h>
+#include <src/document.h>
+#include <src/inkscape.h>
+#include <src/live_effects/lpe-bool.h>
+#include <src/object/sp-ellipse.h>
+#include <src/object/sp-lpe-item.h>
+
+
+
+using namespace Inkscape;
+using namespace Inkscape::LivePathEffect;
+
+class LPEBoolTest : public ::testing::Test {
+ protected:
+ void SetUp() override
+ {
+ // setup hidden dependency
+ Application::create(false);
+ }
+};
+
+TEST_F(LPEBoolTest, canBeApplyedToNonSiblingPaths)
+{
+ std::string svg("\
+<svg width='100' height='100'\
+ xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'\
+ xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape'>\
+ <defs>\
+ <inkscape:path-effect\
+ id='path-effect1'\
+ effect='bool_op'\
+ operation='diff'\
+ operand-path='#circle1'\
+ lpeversion='1'\
+ hide-linked='true' />\
+ </defs>\
+ <path id='rect1'\
+ inkscape:path-effect='#path-effect1'\
+ sodipodi:type='rect'\
+ width='100' height='100' fill='#ff0000' />\
+ <g id='group1'>\
+ <circle id='circle1'\
+ r='40' cy='50' cx='50' fill='#ffffff' style='display:inline'/>\
+ </g>\
+</svg>");
+
+ SPDocument *doc = SPDocument::createNewDocFromMem(svg.c_str(), svg.size(), true);
+ doc->ensureUpToDate();
+
+ auto lpe_item = dynamic_cast<SPLPEItem *>(doc->getObjectById("rect1"));
+ ASSERT_TRUE(lpe_item != nullptr);
+
+ auto lpe_bool_op_effect = dynamic_cast<LPEBool *>(lpe_item->getPathEffectOfType(EffectType::BOOL_OP));
+ ASSERT_TRUE(lpe_bool_op_effect != nullptr);
+
+ auto operand_path = lpe_bool_op_effect->getParameter("operand-path")->param_getSVGValue();
+ auto circle = dynamic_cast<SPGenericEllipse *>(doc->getObjectById(operand_path.substr(1)));
+ ASSERT_TRUE(circle != nullptr);
+} \ No newline at end of file
diff --git a/testfiles/src/object-set-test.cpp b/testfiles/src/object-set-test.cpp
new file mode 100644
index 0000000..5df9738
--- /dev/null
+++ b/testfiles/src/object-set-test.cpp
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Multiindex container for selection
+ *
+ * Authors:
+ * Adrian Boguszewski
+ *
+ * Copyright (C) 2016 Adrian Boguszewski
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <gtest/gtest.h>
+#include <doc-per-case-test.h>
+#include <src/object/sp-factory.h>
+#include <src/object/sp-rect.h>
+#include <src/object/sp-path.h>
+#include <src/object/sp-use.h>
+#include <src/object/sp-root.h>
+#include <src/object/object-set.h>
+#include <xml/node.h>
+#include <src/xml/text-node.h>
+#include <src/xml/simple-document.h>
+//#include <unistd.h>
+#include <2geom/transforms.h>
+using namespace Inkscape;
+using namespace Inkscape::XML;
+
+class ObjectSetTest: public DocPerCaseTest {
+public:
+ ObjectSetTest() {
+ A = new SPObject();
+ B = new SPObject();
+ C = new SPObject();
+ D = new SPObject();
+ E = new SPObject();
+ F = new SPObject();
+ G = new SPObject();
+ H = new SPObject();
+ X = new SPObject();
+ set = new ObjectSet(_doc);
+ set2 = new ObjectSet(_doc);
+ auto sd = _doc->getReprDoc();
+ auto xt = new TextNode(Util::share_string("x"), sd);
+ auto ht = new TextNode(Util::share_string("h"), sd);
+ auto gt = new TextNode(Util::share_string("g"), sd);
+ auto ft = new TextNode(Util::share_string("f"), sd);
+ auto et = new TextNode(Util::share_string("e"), sd);
+ auto dt = new TextNode(Util::share_string("d"), sd);
+ auto ct = new TextNode(Util::share_string("c"), sd);
+ auto bt = new TextNode(Util::share_string("b"), sd);
+ auto at = new TextNode(Util::share_string("a"), sd);
+ X->invoke_build(_doc, xt, 0);
+ H->invoke_build(_doc, ht, 0);
+ G->invoke_build(_doc, gt, 0);
+ F->invoke_build(_doc, ft, 0);
+ E->invoke_build(_doc, et, 0);
+ D->invoke_build(_doc, dt, 0);
+ C->invoke_build(_doc, ct, 0);
+ B->invoke_build(_doc, bt, 0);
+ A->invoke_build(_doc, at, 0);
+
+ //create 3 rects at root of document
+ Inkscape::XML::Node *repr = _doc->getReprDoc()->createElement("svg:rect");
+ _doc->getRoot()->appendChild(repr);
+ r1.reset(dynamic_cast<SPRect*>(_doc->getObjectByRepr(repr)));
+ repr = _doc->getReprDoc()->createElement("svg:rect");
+ _doc->getRoot()->appendChild(repr);
+ r2.reset(dynamic_cast<SPRect*>(_doc->getObjectByRepr(repr)));
+ repr = _doc->getReprDoc()->createElement("svg:rect");
+ _doc->getRoot()->appendChild(repr);
+ r3.reset(dynamic_cast<SPRect*>(_doc->getObjectByRepr(repr)));
+ EXPECT_EQ(6, _doc->getRoot()->children.size());//metadata, defs, namedview, and those three rects.
+ r1->x = r1->y = r2->x = r2->y = r3->x = r3->y = 0;
+ r1->width = r1->height = r2->width = r2->height = r3->width = r3->height = 10;
+ r1->set_shape();
+ r2->set_shape();
+ r3->set_shape();
+
+ }
+ ~ObjectSetTest() override {
+ delete set;
+ delete set2;
+ delete X;
+ delete H;
+ delete G;
+ delete F;
+ delete E;
+ delete D;
+ delete C;
+ delete B;
+ delete A;
+ }
+ SPObject* A;
+ SPObject* B;
+ SPObject* C;
+ SPObject* D;
+ SPObject* E;
+ SPObject* F;
+ SPObject* G;
+ SPObject* H;
+ SPObject* X;
+ std::unique_ptr<SPRect> r1;
+ std::unique_ptr<SPRect> r2;
+ std::unique_ptr<SPRect> r3;
+ ObjectSet* set;
+ ObjectSet* set2;
+};
+
+#define SP_IS_CLONE(obj) (dynamic_cast<const SPUse*>(obj) != NULL)
+
+bool containsClone(ObjectSet* set) {
+ for (auto it : set->items()) {
+ if (SP_IS_CLONE(it)) {
+ return true;
+ }
+ if (SP_IS_GROUP(it)) {
+ ObjectSet tmp_set(set->document());
+ std::vector<SPObject*> c = it->childList(false);
+ tmp_set.setList(c);
+ if (containsClone(&tmp_set)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+TEST_F(ObjectSetTest, Basics) {
+ EXPECT_EQ(0, set->size());
+ set->add(A);
+ EXPECT_EQ(1, set->size());
+ EXPECT_TRUE(set->includes(A));
+ set->add(B);
+ set->add(C);
+ EXPECT_EQ(3, set->size());
+ EXPECT_TRUE(set->includes(B));
+ EXPECT_TRUE(set->includes(C));
+ EXPECT_FALSE(set->includes(D));
+ EXPECT_FALSE(set->includes(X));
+ EXPECT_FALSE(set->includes(nullptr));
+ set->remove(A);
+ EXPECT_EQ(2, set->size());
+ EXPECT_FALSE(set->includes(A));
+ set->clear();
+ EXPECT_EQ(0, set->size());
+ bool resultNull = set->add((SPObject*)nullptr);
+ EXPECT_FALSE(resultNull);
+ EXPECT_EQ(0, set->size());
+ bool resultNull2 = set->remove(nullptr);
+ EXPECT_FALSE(resultNull2);
+}
+
+TEST_F(ObjectSetTest, Advanced) {
+ set->add(A);
+ set->add(B);
+ set->add(C);
+ EXPECT_TRUE(set->includes(C));
+ set->toggle(C);
+ EXPECT_EQ(2, set->size());
+ EXPECT_FALSE(set->includes(C));
+ set->toggle(D);
+ EXPECT_EQ(3, set->size());
+ EXPECT_TRUE(set->includes(D));
+ set->toggle(D);
+ EXPECT_EQ(2, set->size());
+ EXPECT_FALSE(set->includes(D));
+ EXPECT_EQ(nullptr, set->single());
+ set->set(X);
+ EXPECT_EQ(1, set->size());
+ EXPECT_TRUE(set->includes(X));
+ EXPECT_EQ(X, set->single());
+ EXPECT_FALSE(set->isEmpty());
+ set->clear();
+ EXPECT_TRUE(set->isEmpty());
+ std::vector<SPObject*> list1 {A, B, C, D};
+ std::vector<SPObject*> list2 {E, F};
+ set->addList(list1);
+ EXPECT_EQ(4, set->size());
+ set->addList(list2);
+ EXPECT_EQ(6, set->size());
+ EXPECT_TRUE(set->includes(A));
+ EXPECT_TRUE(set->includes(B));
+ EXPECT_TRUE(set->includes(C));
+ EXPECT_TRUE(set->includes(D));
+ EXPECT_TRUE(set->includes(E));
+ EXPECT_TRUE(set->includes(F));
+ set->setList(list2);
+ EXPECT_EQ(2, set->size());
+ EXPECT_TRUE(set->includes(E));
+ EXPECT_TRUE(set->includes(F));
+}
+
+TEST_F(ObjectSetTest, Items) {
+ // cannot test smallestItem and largestItem functions due to too many dependencies
+ // uncomment if the problem is fixed
+
+ SPRect* rect10x100 = &*r1;
+ rect10x100->x = rect10x100->x = 0;
+ rect10x100->width = 10;
+ rect10x100->height = 100;
+ rect10x100->set_shape();
+
+ SPRect* rect20x40 = &*r2;
+ rect20x40->x = rect20x40->x = 0;
+ rect20x40->width = 20;
+ rect20x40->height = 40;
+ rect20x40->set_shape();
+
+ SPRect* rect30x30 = &*r3;
+ rect30x30->x = rect30x30->x = 0;
+ rect30x30->width = 30;
+ rect30x30->height = 30;
+ rect30x30->set_shape();
+
+
+ set->add(rect10x100);
+ EXPECT_EQ(rect10x100, set->singleItem());
+ EXPECT_EQ(rect10x100->getRepr(), set->singleRepr());
+ set->add(rect20x40);
+ EXPECT_EQ(nullptr, set->singleItem());
+ EXPECT_EQ(nullptr, set->singleRepr());
+ set->add(rect30x30);
+ EXPECT_EQ(3, set->size());
+ EXPECT_EQ(rect10x100, set->smallestItem(ObjectSet::CompareSize::HORIZONTAL));
+ EXPECT_EQ(rect30x30, set->smallestItem(ObjectSet::CompareSize::VERTICAL));
+ EXPECT_EQ(rect20x40, set->smallestItem(ObjectSet::CompareSize::AREA));
+ EXPECT_EQ(rect30x30, set->largestItem(ObjectSet::CompareSize::HORIZONTAL));
+ EXPECT_EQ(rect10x100, set->largestItem(ObjectSet::CompareSize::VERTICAL));
+ EXPECT_EQ(rect10x100, set->largestItem(ObjectSet::CompareSize::AREA));
+}
+
+TEST_F(ObjectSetTest, Ranges) {
+ std::vector<SPObject*> objs {A, D, B, E, C, F};
+ set->add(objs.begin() + 1, objs.end() - 1);
+ EXPECT_EQ(4, set->size());
+ auto it = set->objects().begin();
+ EXPECT_EQ(D, *it++);
+ EXPECT_EQ(B, *it++);
+ EXPECT_EQ(E, *it++);
+ EXPECT_EQ(C, *it++);
+ EXPECT_EQ(set->objects().end(), it);
+ SPObject* rect1 = SPFactory::createObject("svg:rect");
+ SPObject* rect2 = SPFactory::createObject("svg:rect");
+ SPObject* rect3 = SPFactory::createObject("svg:rect");
+ set->add(rect1);
+ set->add(rect2);
+ set->add(rect3);
+ EXPECT_EQ(7, set->size());
+ auto xmlNode = set->xmlNodes().begin();
+ EXPECT_EQ(3, boost::distance(set->xmlNodes()));
+ EXPECT_EQ(rect1->getRepr(), *xmlNode++);
+ EXPECT_EQ(rect2->getRepr(), *xmlNode++);
+ EXPECT_EQ(rect3->getRepr(), *xmlNode++);
+ EXPECT_EQ(set->xmlNodes().end(), xmlNode);
+ auto item = set->items().begin();
+ EXPECT_EQ(3, boost::distance(set->items()));
+ EXPECT_EQ(rect1, *item++);
+ EXPECT_EQ(rect2, *item++);
+ EXPECT_EQ(rect3, *item++);
+ EXPECT_EQ(set->items().end(), item);
+}
+
+TEST_F(ObjectSetTest, Autoremoving) {
+ set->add(A);
+ EXPECT_TRUE(set->includes(A));
+ EXPECT_EQ(1, set->size());
+ A->releaseReferences();
+ EXPECT_EQ(0, set->size());
+}
+
+TEST_F(ObjectSetTest, BasicDescendants) {
+ A->attach(B, nullptr);
+ B->attach(C, nullptr);
+ A->attach(D, nullptr);
+ bool resultB = set->add(B);
+ bool resultB2 = set->add(B);
+ EXPECT_TRUE(resultB);
+ EXPECT_FALSE(resultB2);
+ EXPECT_TRUE(set->includes(B));
+ bool resultC = set->add(C);
+ EXPECT_FALSE(resultC);
+ EXPECT_FALSE(set->includes(C));
+ EXPECT_EQ(1, set->size());
+ bool resultA = set->add(A);
+ EXPECT_TRUE(resultA);
+ EXPECT_EQ(1, set->size());
+ EXPECT_TRUE(set->includes(A));
+ EXPECT_FALSE(set->includes(B));
+}
+
+TEST_F(ObjectSetTest, AdvancedDescendants) {
+ A->attach(B, nullptr);
+ A->attach(C, nullptr);
+ A->attach(X, nullptr);
+ B->attach(D, nullptr);
+ B->attach(E, nullptr);
+ C->attach(F, nullptr);
+ C->attach(G, nullptr);
+ C->attach(H, nullptr);
+ set->add(A);
+ bool resultF = set->remove(F);
+ EXPECT_TRUE(resultF);
+ EXPECT_EQ(4, set->size());
+ EXPECT_FALSE(set->includes(F));
+ EXPECT_TRUE(set->includes(B));
+ EXPECT_TRUE(set->includes(G));
+ EXPECT_TRUE(set->includes(H));
+ EXPECT_TRUE(set->includes(X));
+ bool resultF2 = set->add(F);
+ EXPECT_TRUE(resultF2);
+ EXPECT_EQ(5, set->size());
+ EXPECT_TRUE(set->includes(F));
+}
+
+TEST_F(ObjectSetTest, Removing) {
+ A->attach(B, nullptr);
+ A->attach(C, nullptr);
+ A->attach(X, nullptr);
+ B->attach(D, nullptr);
+ B->attach(E, nullptr);
+ C->attach(F, nullptr);
+ C->attach(G, nullptr);
+ C->attach(H, nullptr);
+ bool removeH = set->remove(H);
+ EXPECT_FALSE(removeH);
+ set->add(A);
+ bool removeX = set->remove(X);
+ EXPECT_TRUE(removeX);
+ EXPECT_EQ(2, set->size());
+ EXPECT_TRUE(set->includes(B));
+ EXPECT_TRUE(set->includes(C));
+ EXPECT_FALSE(set->includes(X));
+ EXPECT_FALSE(set->includes(A));
+ bool removeX2 = set->remove(X);
+ EXPECT_FALSE(removeX2);
+ EXPECT_EQ(2, set->size());
+ bool removeA = set->remove(A);
+ EXPECT_FALSE(removeA);
+ EXPECT_EQ(2, set->size());
+ bool removeC = set->remove(C);
+ EXPECT_TRUE(removeC);
+ EXPECT_EQ(1, set->size());
+ EXPECT_TRUE(set->includes(B));
+ EXPECT_FALSE(set->includes(C));
+}
+
+TEST_F(ObjectSetTest, TwoSets) {
+ A->attach(B, nullptr);
+ A->attach(C, nullptr);
+ set->add(A);
+ set2->add(A);
+ EXPECT_EQ(1, set->size());
+ EXPECT_EQ(1, set2->size());
+ set->remove(B);
+ EXPECT_EQ(1, set->size());
+ EXPECT_TRUE(set->includes(C));
+ EXPECT_EQ(1, set2->size());
+ EXPECT_TRUE(set2->includes(A));
+ C->releaseReferences();
+ EXPECT_EQ(0, set->size());
+ EXPECT_EQ(1, set2->size());
+ EXPECT_TRUE(set2->includes(A));
+}
+
+TEST_F(ObjectSetTest, SetRemoving) {
+ ObjectSet *objectSet = new ObjectSet(_doc);
+ A->attach(B, nullptr);
+ objectSet->add(A);
+ objectSet->add(C);
+ EXPECT_EQ(2, objectSet->size());
+ delete objectSet;
+ EXPECT_STREQ(nullptr, A->getId());
+ EXPECT_STREQ(nullptr, C->getId());
+}
+
+TEST_F(ObjectSetTest, Delete) {
+ //we cannot use the same item as in other tests since it will be freed at the test destructor
+
+ EXPECT_EQ(_doc->getRoot(), r1->parent);
+ set->add(r1.get());
+ set->deleteItems();
+ r1.release();
+ EXPECT_EQ(0, set->size());
+ //EXPECT_EQ(nullptr, r1->parent);
+}
+
+TEST_F(ObjectSetTest, Ops) {
+ set->add(r1.get());
+ set->add(r2.get());
+ set->add(r3.get());
+ set->duplicate();
+ EXPECT_EQ(9, _doc->getRoot()->children.size());//metadata, defs, namedview, and those 3x2 rects.
+ EXPECT_EQ(3, set->size());
+ EXPECT_FALSE(set->includes(r1.get()));
+ set->deleteItems();
+ EXPECT_TRUE(set->isEmpty());
+ set->add(r1.get());
+ set->add(r2.get());
+ set->add(r3.get());
+ set->group();//r1-3 are now invalid (grouping makes copies)
+ r1.release();
+ r2.release();
+ r3.release();
+ EXPECT_EQ(4, _doc->getRoot()->children.size());
+ EXPECT_EQ(1, set->size());
+ set->ungroup();
+ EXPECT_EQ(6, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ /* Uncomment this when toNextLayer is made desktop-independent
+ set->group();
+ set2->add(set->singleItem()->childList(false)[0]);
+ EXPECT_EQ(3, set->singleItem()->children.size());
+ EXPECT_EQ(4, _doc->getRoot()->children.size());
+ set2->popFromGroup();
+ EXPECT_EQ(2, set->singleItem()->children.size());
+ EXPECT_EQ(5, _doc->getRoot()->children.size());
+ set->ungroup();
+ set->add(set2->singleItem());
+ */
+ set->clone();
+ EXPECT_EQ(9, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ EXPECT_NE(nullptr,dynamic_cast<SPUse*>(*(set->items().begin())));
+ EXPECT_EQ(nullptr,dynamic_cast<SPRect*>(*(set->items().begin())));
+ set->unlink();
+ EXPECT_EQ(9, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ EXPECT_EQ(nullptr,dynamic_cast<SPUse*>(*(set->items().begin())));
+ EXPECT_NE(nullptr,dynamic_cast<SPRect*>(*(set->items().begin())));
+ set->clone(); //creates 3 clones
+ set->clone(); //creates 3 clones of clones
+ EXPECT_EQ(15, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ EXPECT_NE(nullptr,dynamic_cast<SPUse*>( ((SPUse*)(*(set->items().begin())))->get_original()));//"original is a Use"
+ set->unlink(); //clone of clone of rect -> rect
+ EXPECT_EQ(nullptr,dynamic_cast<SPUse*>(*(set->items().begin())));
+ EXPECT_NE(nullptr,dynamic_cast<SPRect*>(*(set->items().begin())));
+ set->clone();
+ set->set(*(set->items().begin()));
+ set->cloneOriginal();//get clone original
+ EXPECT_EQ(18, _doc->getRoot()->children.size());
+ EXPECT_EQ(1, set->size());
+ EXPECT_NE(nullptr,dynamic_cast<SPRect*>(*(set->items().begin())));
+ //let's stop here.
+ // TODO: write a hundred more tests to check clone (non-)displacement when grouping, ungrouping and unlinking...
+ TearDownTestCase();
+ SetUpTestCase();
+}
+
+TEST_F(ObjectSetTest, unlinkRecursiveBasic) {
+ // This is the same as the test (ObjectSetTest, Ops), but with unlinkRecursive instead of unlink.
+ set->set(r1.get());
+ set->add(r2.get());
+ set->add(r3.get());
+ EXPECT_FALSE(containsClone(set));
+ set->duplicate();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(9, _doc->getRoot()->children.size());//metadata, defs, namedview, and those 3x2 rects.
+ EXPECT_EQ(3, set->size());
+ EXPECT_FALSE(set->includes(r1.get()));
+ set->deleteItems();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_TRUE(set->isEmpty());
+ set->add(r1.get());
+ set->add(r2.get());
+ set->add(r3.get());
+ EXPECT_FALSE(containsClone(set));
+ set->group();//r1-3 are now invalid (grouping makes copies)
+ r1.release();
+ r2.release();
+ r3.release();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(4, _doc->getRoot()->children.size());
+ EXPECT_EQ(1, set->size());
+ set->ungroup();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(6, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ /* Uncomment this when toNextLayer is made desktop-independent
+ set->group();
+ set2->add(set->singleItem()->childList(false)[0]);
+ EXPECT_EQ(3, set->singleItem()->children.size());
+ EXPECT_EQ(4, _doc->getRoot()->children.size());
+ set2->popFromGroup();
+ EXPECT_EQ(2, set->singleItem()->children.size());
+ EXPECT_EQ(5, _doc->getRoot()->children.size());
+ set->ungroup();
+ set->add(set2->singleItem());
+ */
+ set->clone();
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(9, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ EXPECT_NE(nullptr, dynamic_cast<SPUse*>(*(set->items().begin())));
+ EXPECT_EQ(nullptr, dynamic_cast<SPRect*>(*(set->items().begin())));
+ set->unlinkRecursive();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(9, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ EXPECT_EQ(nullptr, dynamic_cast<SPUse*>(*(set->items().begin())));
+ EXPECT_NE(nullptr, dynamic_cast<SPRect*>(*(set->items().begin())));
+ set->clone(); //creates 3 clones
+ EXPECT_TRUE(containsClone(set));
+ set->clone(); //creates 3 clones of clones
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(15, _doc->getRoot()->children.size());
+ EXPECT_EQ(3, set->size());
+ EXPECT_NE(nullptr, dynamic_cast<SPUse*>( ((SPUse*)(*(set->items().begin())))->get_original()));//"original is a Use"
+ set->unlinkRecursive(); //clone of clone of rect -> rect
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(nullptr, dynamic_cast<SPUse*>(*(set->items().begin())));
+ EXPECT_NE(nullptr, dynamic_cast<SPRect*>(*(set->items().begin())));
+ set->clone();
+ EXPECT_TRUE(containsClone(set));
+ set->set(*(set->items().begin()));
+ set->cloneOriginal();//get clone original
+ EXPECT_EQ(18, _doc->getRoot()->children.size());
+ EXPECT_EQ(1, set->size());
+ EXPECT_NE(nullptr, dynamic_cast<SPRect*>(*(set->items().begin())));
+ TearDownTestCase();
+ SetUpTestCase();
+}
+
+TEST_F(ObjectSetTest, unlinkRecursiveAdvanced) {
+ set->set(r1.get());
+ set->add(r2.get());
+ set->add(r3.get());
+ set->group();//r1-3 are now invalid (grouping makes copies)
+ r1.release();
+ r2.release();
+ r3.release();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(1, set->size());
+ SPItem* original = set->singleItem();
+ set->clone();
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(1, set->size());
+ set->add(original);
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(2, set->size());
+ set->group();
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(1, set->size());
+ original = set->singleItem();
+ set->clone();
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(1, set->size());
+ set->add(original);
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(2, set->size());
+ set->group();
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(1, set->size());
+ original = set->singleItem();
+ set->clone();
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(1, set->size());
+ set->add(original);
+ EXPECT_TRUE(containsClone(set));
+ EXPECT_EQ(2, set->size());
+ set->unlinkRecursive();
+ EXPECT_FALSE(containsClone(set));
+ EXPECT_EQ(2, set->size());
+
+ TearDownTestCase();
+ SetUpTestCase();
+}
+
+TEST_F(ObjectSetTest, ZOrder) {
+ //sp_object_compare_position_bool == true iff "r1<r2" iff r1 is "before" r2 in the file, ie r1 is lower than r2
+ EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get()));
+ EXPECT_TRUE(sp_object_compare_position_bool(r2.get(),r3.get()));
+ EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r3.get()));
+ EXPECT_FALSE(sp_object_compare_position_bool(r2.get(),r1.get()));
+ EXPECT_FALSE(sp_object_compare_position_bool(r3.get(),r1.get()));
+ EXPECT_FALSE(sp_object_compare_position_bool(r3.get(),r2.get()));
+ //1 2 3
+ set->set(r2.get());
+ set->raise();
+ //1 3 2
+ EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r3.get()));
+ EXPECT_TRUE(sp_object_compare_position_bool(r3.get(),r2.get()));//!
+ set->set(r3.get());
+ set->lower();
+ //3 1 2
+ EXPECT_TRUE(sp_object_compare_position_bool(r3.get(),r1.get()));
+ EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get()));
+ set->raiseToTop();
+ //1 2 3
+ EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get()));
+ EXPECT_TRUE(sp_object_compare_position_bool(r2.get(),r3.get()));
+ set->lowerToBottom();
+ //3 1 2
+ EXPECT_TRUE(sp_object_compare_position_bool(r3.get(),r1.get()));
+ EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get()));
+}
+
+TEST_F(ObjectSetTest, Combine) {
+ set->add(r1.get());
+ set->add(r2.get());
+ set->combine();
+ r1.release();
+ r2.release();
+ EXPECT_EQ(1, set->size());
+ EXPECT_EQ(5, _doc->getRoot()->children.size());
+ set->breakApart();
+ EXPECT_EQ(2, set->size());
+ EXPECT_EQ(6, _doc->getRoot()->children.size());
+ set->deleteItems();
+ set->set(r3.get());
+ set->toCurves();
+ r3.release();
+ auto x = set->singleItem();
+ EXPECT_NE(nullptr,dynamic_cast<SPPath*>(x));
+ EXPECT_EQ(nullptr,dynamic_cast<SPRect*>(x));
+ set->deleteItems();
+}
+
+TEST_F(ObjectSetTest, Moves) {
+ set->add(r1.get());
+ set->moveRelative(15,15);
+ EXPECT_EQ(15,r1->x.value);
+ Geom::Point p(20,20);
+ Geom::Scale s(2);
+ set->setScaleRelative(p,s);
+ EXPECT_EQ(10,r1->x.value);
+ EXPECT_EQ(20,r1->width.value);
+ set->toCurves();
+ r1.release();
+ auto x = set->singleItem();
+ EXPECT_EQ(20,(*(x->documentVisualBounds()))[0].extent());
+ set->rotate90(true);
+ set->rotate90(true);
+ EXPECT_EQ(20,(*(x->documentVisualBounds()))[0].extent());
+ set->deleteItems();
+}
diff --git a/testfiles/src/object-style-test.cpp b/testfiles/src/object-style-test.cpp
new file mode 100644
index 0000000..25d9268
--- /dev/null
+++ b/testfiles/src/object-style-test.cpp
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Combination style and object testing for cascading and flags.
+ *//*
+ *
+ * Authors:
+ * Martin Owens
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information
+ */
+
+#include <gtest/gtest.h>
+#include <doc-per-case-test.h>
+
+#include <src/style.h>
+#include <src/object/sp-root.h>
+#include <src/object/sp-rect.h>
+
+using namespace Inkscape;
+using namespace Inkscape::XML;
+
+class ObjectTest: public DocPerCaseTest {
+public:
+ ObjectTest() {
+ char const *docString = "\
+<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>\
+<style>\
+rect { fill: #808080; opacity:0.5; }\
+.extra { opacity:1.0; }\
+.overload { fill: #d0d0d0 !important; stroke: #c0c0c0 !important; }\
+.font { font: italic bold 12px/30px Georgia, serif; }\
+.exsize { stroke-width: 1ex; }\
+.fosize { font-size: 15px; }\
+</style>\
+<g style='fill:blue; stroke-width:2px;font-size: 14px;'>\
+ <rect id='one' style='fill:red; stroke:green;'/>\
+ <rect id='two' style='stroke:green; stroke-width:4px;'/>\
+ <rect id='three' class='extra' style='fill: #cccccc;'/>\
+ <rect id='four' class='overload' style='fill:green;stroke:red !important;'/>\
+ <rect id='five' class='font' style='font: 15px arial, sans-serif;'/>/\
+ <rect id='six' style='stroke-width:1em;'/>\
+ <rect id='seven' class='exsize'/>\
+ <rect id='eight' class='fosize' style='stroke-width: 50%;'/>\
+</g>\
+</svg>";
+ doc = SPDocument::createNewDocFromMem(docString, static_cast<int>(strlen(docString)), false);
+ }
+
+ ~ObjectTest() override {
+ doc->doUnref();
+ }
+
+ SPDocument *doc;
+};
+
+/*
+ * Test basic cascade values, that they are set correctly as we'd want to see them.
+ */
+TEST_F(ObjectTest, Styles) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPRect *one = dynamic_cast<SPRect *>(doc->getObjectById("one"));
+ ASSERT_TRUE(one != nullptr);
+
+ // TODO: Fix when Inkscape preserves colour names (i.e. 'red')
+ EXPECT_EQ(one->style->fill.get_value(), Glib::ustring("#ff0000"));
+ EXPECT_EQ(one->style->stroke.get_value(), Glib::ustring("#008000"));
+ EXPECT_EQ(one->style->opacity.get_value(), Glib::ustring("0.5"));
+ EXPECT_EQ(one->style->stroke_width.get_value(), Glib::ustring("2px"));
+
+ SPRect *two = dynamic_cast<SPRect *>(doc->getObjectById("two"));
+ ASSERT_TRUE(two != nullptr);
+
+ EXPECT_EQ(two->style->fill.get_value(), Glib::ustring("#808080"));
+ EXPECT_EQ(two->style->stroke.get_value(), Glib::ustring("#008000"));
+ EXPECT_EQ(two->style->opacity.get_value(), Glib::ustring("0.5"));
+ EXPECT_EQ(two->style->stroke_width.get_value(), Glib::ustring("4px"));
+
+ SPRect *three = dynamic_cast<SPRect *>(doc->getObjectById("three"));
+ ASSERT_TRUE(three != nullptr);
+
+ EXPECT_EQ(three->style->fill.get_value(), Glib::ustring("#cccccc"));
+ EXPECT_EQ(three->style->stroke.get_value(), Glib::ustring(""));
+ EXPECT_EQ(three->style->opacity.get_value(), Glib::ustring("1"));
+ EXPECT_EQ(three->style->stroke_width.get_value(), Glib::ustring("2px"));
+
+ SPRect *four = dynamic_cast<SPRect *>(doc->getObjectById("four"));
+ ASSERT_TRUE(four != nullptr);
+
+ EXPECT_EQ(four->style->fill.get_value(), Glib::ustring("#d0d0d0"));
+ EXPECT_EQ(four->style->stroke.get_value(), Glib::ustring("#ff0000"));
+ EXPECT_EQ(four->style->opacity.get_value(), Glib::ustring("0.5"));
+ EXPECT_EQ(four->style->stroke_width.get_value(), Glib::ustring("2px"));
+}
+
+/*
+ * Test the origin flag for each of the values, should indicate where it came from.
+ */
+TEST_F(ObjectTest, StyleSource) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPRect *one = dynamic_cast<SPRect *>(doc->getObjectById("one"));
+ ASSERT_TRUE(one != nullptr);
+
+ EXPECT_EQ(one->style->fill.style_src, SP_STYLE_SRC_STYLE_PROP);
+ EXPECT_EQ(one->style->stroke.style_src, SP_STYLE_SRC_STYLE_PROP);
+ EXPECT_EQ(one->style->opacity.style_src, SP_STYLE_SRC_STYLE_SHEET);
+ EXPECT_EQ(one->style->stroke_width.style_src, SP_STYLE_SRC_STYLE_PROP);
+
+ SPRect *two = dynamic_cast<SPRect *>(doc->getObjectById("two"));
+ ASSERT_TRUE(two != nullptr);
+
+ EXPECT_EQ(two->style->fill.style_src, SP_STYLE_SRC_STYLE_SHEET);
+ EXPECT_EQ(two->style->stroke.style_src, SP_STYLE_SRC_STYLE_PROP);
+ EXPECT_EQ(two->style->opacity.style_src, SP_STYLE_SRC_STYLE_SHEET);
+ EXPECT_EQ(two->style->stroke_width.style_src, SP_STYLE_SRC_STYLE_PROP);
+
+ SPRect *three = dynamic_cast<SPRect *>(doc->getObjectById("three"));
+ ASSERT_TRUE(three != nullptr);
+
+ EXPECT_EQ(three->style->fill.style_src, SP_STYLE_SRC_STYLE_PROP);
+ EXPECT_EQ(three->style->stroke.style_src, SP_STYLE_SRC_STYLE_PROP);
+ EXPECT_EQ(three->style->opacity.style_src, SP_STYLE_SRC_STYLE_SHEET);
+ EXPECT_EQ(three->style->stroke_width.style_src, SP_STYLE_SRC_STYLE_PROP);
+
+ SPRect *four = dynamic_cast<SPRect *>(doc->getObjectById("four"));
+ ASSERT_TRUE(four != nullptr);
+
+ EXPECT_EQ(four->style->fill.style_src, SP_STYLE_SRC_STYLE_SHEET);
+ EXPECT_EQ(four->style->stroke.style_src, SP_STYLE_SRC_STYLE_PROP);
+ EXPECT_EQ(four->style->opacity.style_src, SP_STYLE_SRC_STYLE_SHEET);
+ EXPECT_EQ(four->style->stroke_width.style_src, SP_STYLE_SRC_STYLE_PROP);
+}
+
+/*
+ * Test the breaking up of the font property and recreation into separate properties.
+ */
+TEST_F(ObjectTest, StyleFont) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPRect *five = dynamic_cast<SPRect *>(doc->getObjectById("five"));
+ ASSERT_TRUE(five != nullptr);
+
+ // Font property is ALWAYS unset as it's converted into specific font css properties
+ EXPECT_EQ(five->style->font.get_value(), Glib::ustring(""));
+ EXPECT_EQ(five->style->font_size.get_value(), Glib::ustring("12px"));
+ EXPECT_EQ(five->style->font_weight.get_value(), Glib::ustring("bold"));
+ EXPECT_EQ(five->style->font_style.get_value(), Glib::ustring("italic"));
+ EXPECT_EQ(five->style->font_family.get_value(), Glib::ustring("arial, sans-serif"));
+}
+
+/*
+ * Test the consumption of font dependent lengths in SPILength, e.g. EM, EX and % units
+ */
+TEST_F(ObjectTest, StyleFontSizes) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPRect *six = dynamic_cast<SPRect *>(doc->getObjectById("six"));
+ ASSERT_TRUE(six != nullptr);
+
+ EXPECT_EQ(six->style->stroke_width.get_value(), Glib::ustring("1em"));
+ EXPECT_EQ(six->style->stroke_width.computed, 14);
+
+ SPRect *seven = dynamic_cast<SPRect *>(doc->getObjectById("seven"));
+ ASSERT_TRUE(seven != nullptr);
+
+ EXPECT_EQ(seven->style->stroke_width.get_value(), Glib::ustring("1ex"));
+ EXPECT_EQ(seven->style->stroke_width.computed, 7);
+
+ SPRect *eight = dynamic_cast<SPRect *>(doc->getObjectById("eight"));
+ ASSERT_TRUE(eight != nullptr);
+
+ EXPECT_EQ(eight->style->stroke_width.get_value(), Glib::ustring("50%"));
+ EXPECT_EQ(eight->style->stroke_width.computed, 1); // Is this right?
+}
diff --git a/testfiles/src/object-test.cpp b/testfiles/src/object-test.cpp
new file mode 100644
index 0000000..ac42b30
--- /dev/null
+++ b/testfiles/src/object-test.cpp
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Unit tests migrated from cxxtest
+ *
+ * Authors:
+ * Adrian Boguszewski
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtest/gtest.h>
+#include <doc-per-case-test.h>
+#include <src/object/sp-root.h>
+#include <src/object/sp-path.h>
+
+using namespace Inkscape;
+using namespace Inkscape::XML;
+
+class ObjectTest: public DocPerCaseTest {
+public:
+ ObjectTest() {
+ // Sample document
+ // svg:svg
+ // svg:defs
+ // svg:path
+ // svg:linearGradient
+ // svg:stop
+ // svg:filter
+ // svg:feGaussianBlur (feel free to implement for other filters)
+ // svg:clipPath
+ // svg:rect
+ // svg:g
+ // svg:use
+ // svg:circle
+ // svg:ellipse
+ // svg:text
+ // svg:polygon
+ // svg:polyline
+ // svg:image
+ // svg:line
+ char const *docString = R"A(
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- just a comment -->
+ <title id="title">SVG test</title>
+ <defs>
+ <path id="P" d="M -21,-4 -5,0 -18,12 -3,4 -4,21 0,5 12,17 4,2 21,3 5,-1 17,-12 2,-4 3,-21 -1,-5 -12,-18 -4,-3z"/>
+ <linearGradient id="LG" x1="0%" y1="0%" x2="100%" y2="0%">
+ <stop offset="0%" style="stop-color:#ffff00;stop-opacity:1"/>
+ <stop offset="100%" style="stop-color:red;stop-opacity:1"/>
+ </linearGradient>
+ <clipPath id="clip" clipPathUnits="userSpaceOnUse">
+ <rect x="10" y="10" width="100" height="100"/>
+ </clipPath>
+ <filter style="color-interpolation-filters:sRGB" id="filter" x="-0.15" width="1.34" y="0" height="1">
+ <feGaussianBlur stdDeviation="4.26"/>
+ </filter>
+ </defs>
+
+ <g id="G" transform="skewX(10.5) translate(9,5)">
+ <use id="U" xlink:href="#P" opacity="0.5" fill="#1dace3" transform="rotate(4)"/>
+ <circle id="C" cx="45.5" cy="67" r="23" fill="#000"/>
+ <ellipse id="E" cx="200" cy="70" rx="85" ry="55" fill="url(#LG)"/>
+ <text id="T" fill="#fff" style="font-size:45;font-family:Verdana" x="150" y="86">TEST</text>
+ <polygon id="PG" points="60,20 100,40 100,80 60,100 20,80 20,40" clip-path="url(#clip)" filter="url(#filter)"/>
+ <polyline id="PL" points="0,40 40,40 40,80 80,80 80,120 120,120 120,160" style="fill:none;stroke:red;stroke-width:4"/>
+ <image id="I" xlink:href="data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE4MCIgd2lkdGg9IjUwMCI+PHBhdGggZD0iTTAsNDAgNDAsNDAgNDAsODAgODAsODAgODAsMTIwIDEyMCwxMjAgMTIwLDE2MCIgc3R5bGU9ImZpbGw6d2hpdGU7c3Ryb2tlOnJlZDtzdHJva2Utd2lkdGg6NCIvPjwvc3ZnPgo="/>
+ <line id="L" x1="20" y1="100" x2="100" y2="20" stroke="black" stroke-width="2"/>
+ </g>
+</svg>
+ )A";
+ doc = SPDocument::createNewDocFromMem(docString, static_cast<int>(strlen(docString)), false);
+ }
+
+ ~ObjectTest() override {
+ doc->doUnref();
+ }
+
+ SPDocument *doc;
+};
+
+TEST_F(ObjectTest, Clones) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPPath *path = dynamic_cast<SPPath *>(doc->getObjectById("P"));
+ ASSERT_TRUE(path != nullptr);
+
+ Node *node = path->getRepr();
+ ASSERT_TRUE(node != nullptr);
+
+ Document *xml_doc = node->document();
+ ASSERT_TRUE(xml_doc != nullptr);
+
+ Node *parent = node->parent();
+ ASSERT_TRUE(parent != nullptr);
+
+ const size_t num_clones = 1000;
+ std::string href = std::string("#") + std::string(path->getId());
+ std::vector<Node *> clones(num_clones, nullptr);
+
+ // Create num_clones clones of this path and stick them in the document
+ for (size_t i = 0; i < num_clones; ++i) {
+ Node *clone = xml_doc->createElement("svg:use");
+ Inkscape::GC::release(clone);
+ clone->setAttribute("xlink:href", href);
+ parent->addChild(clone, node);
+ clones[i] = clone;
+ }
+
+ // Remove those clones
+ for (size_t i = 0; i < num_clones; ++i) {
+ parent->removeChild(clones[i]);
+ }
+}
+
+TEST_F(ObjectTest, Grouping) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPGroup *group = dynamic_cast<SPGroup *>(doc->getObjectById("G"));
+
+ ASSERT_TRUE(group != nullptr);
+
+ Node *node = group->getRepr();
+ ASSERT_TRUE(node != nullptr);
+
+ Document *xml_doc = node->document();
+ ASSERT_TRUE(xml_doc != nullptr);
+
+ const size_t num_elements = 1000;
+
+ Node *new_group = xml_doc->createElement("svg:g");
+ Inkscape::GC::release(new_group);
+ node->addChild(new_group, nullptr);
+
+ std::vector<Node *> elements(num_elements, nullptr);
+
+ for (size_t i = 0; i < num_elements; ++i) {
+ Node *circle = xml_doc->createElement("svg:circle");
+ Inkscape::GC::release(circle);
+ circle->setAttribute("cx", "2048");
+ circle->setAttribute("cy", "1024");
+ circle->setAttribute("r", "1.5");
+ new_group->addChild(circle, nullptr);
+ elements[i] = circle;
+ }
+
+ SPGroup *n_group = dynamic_cast<SPGroup *>(group->get_child_by_repr(new_group));
+ ASSERT_TRUE(n_group != nullptr);
+
+ std::vector<SPItem*> ch;
+ sp_item_group_ungroup(n_group, ch, false);
+
+ // Remove those elements
+ for (size_t i = 0; i < num_elements; ++i) {
+ elements[i]->parent()->removeChild(elements[i]);
+ }
+
+}
+
+TEST_F(ObjectTest, Objects) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+ ASSERT_TRUE(root->hasChildren());
+
+ SPPath *path = dynamic_cast<SPPath *>(doc->getObjectById("P"));
+ ASSERT_TRUE(path != nullptr);
+
+ // Test parent behavior
+ SPObject *child = root->firstChild();
+ ASSERT_TRUE(child != nullptr);
+
+ EXPECT_EQ(root, child->parent);
+ EXPECT_EQ(doc, child->document);
+ EXPECT_TRUE(root->isAncestorOf(child));
+
+ // Test list behavior
+ SPObject *next = child->getNext();
+ SPObject *prev = next;
+ EXPECT_EQ(child, next->getPrev());
+
+ prev = next;
+ next = next->getNext();
+ while (next != nullptr) {
+ // Walk the list
+ EXPECT_EQ(prev, next->getPrev());
+ prev = next;
+ next = next->getNext();
+ }
+
+ // Test hrefcount
+ EXPECT_TRUE(path->isReferenced());
+}
diff --git a/testfiles/src/sp-gradient-test.cpp b/testfiles/src/sp-gradient-test.cpp
new file mode 100644
index 0000000..d190f1c
--- /dev/null
+++ b/testfiles/src/sp-gradient-test.cpp
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Unit tests migrated from cxxtest
+ *
+ * Authors:
+ * Adrian Boguszewski
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtest/gtest.h>
+#include <doc-per-case-test.h>
+#include <src/object/sp-gradient.h>
+#include <src/attributes.h>
+#include <2geom/transforms.h>
+#include <src/xml/node.h>
+#include <src/xml/simple-document.h>
+#include <src/svg/svg.h>
+
+using namespace Inkscape;
+using namespace Inkscape::XML;
+
+class SPGradientTest: public DocPerCaseTest {
+public:
+ SPGradientTest() {
+ DocPerCaseTest::SetUpTestCase();
+ gr = new SPGradient();
+ }
+
+ ~SPGradientTest() override {
+ delete gr;
+ DocPerCaseTest::TearDownTestCase();
+ }
+
+ SPGradient *gr;
+};
+
+TEST_F(SPGradientTest, Init) {
+ ASSERT_TRUE(gr != nullptr);
+ EXPECT_TRUE(gr->gradientTransform.isIdentity());
+ EXPECT_TRUE(Geom::are_near(Geom::identity(), gr->gradientTransform));
+}
+
+TEST_F(SPGradientTest, SetGradientTransform) {
+ SP_OBJECT(gr)->document = _doc;
+
+ SP_OBJECT(gr)->setKeyValue(SP_ATTR_GRADIENTTRANSFORM, "translate(5, 8)");
+ EXPECT_TRUE(Geom::are_near(Geom::Affine(Geom::Translate(5.0, 8.0)), gr->gradientTransform));
+
+ SP_OBJECT(gr)->setKeyValue(SP_ATTR_GRADIENTTRANSFORM, "");
+ EXPECT_TRUE(Geom::are_near(Geom::identity(), gr->gradientTransform));
+
+ SP_OBJECT(gr)->setKeyValue(SP_ATTR_GRADIENTTRANSFORM, "rotate(90)");
+ EXPECT_TRUE(Geom::are_near(Geom::Affine(Geom::Rotate::from_degrees(90.0)), gr->gradientTransform));
+}
+
+TEST_F(SPGradientTest, Write) {
+ SP_OBJECT(gr)->document = _doc;
+
+ SP_OBJECT(gr)->setKeyValue(SP_ATTR_GRADIENTTRANSFORM, "matrix(0, 1, -1, 0, 0, 0)");
+ Document *xml_doc = _doc->getReprDoc();
+
+ ASSERT_TRUE(xml_doc != nullptr);
+
+ Node *repr = xml_doc->createElement("svg:radialGradient");
+ SP_OBJECT(gr)->updateRepr(xml_doc, repr, SP_OBJECT_WRITE_ALL);
+
+ gchar const *tr = repr->attribute("gradientTransform");
+ Geom::Affine svd;
+ bool const valid = sp_svg_transform_read(tr, &svd);
+
+ EXPECT_TRUE(valid);
+ EXPECT_TRUE(Geom::are_near(Geom::Affine(Geom::Rotate::from_degrees(90.0)), svd));
+}
+
+TEST_F(SPGradientTest, GetG2dGetGs2dSetGs2) {
+ SP_OBJECT(gr)->document = _doc;
+
+ Geom::Affine grXform(2, 1,
+ 1, 3,
+ 4, 6);
+ gr->gradientTransform = grXform;
+
+ Geom::Rect unit_rect(Geom::Point(0, 0), Geom::Point(1, 1));
+ {
+ Geom::Affine g2d(gr->get_g2d_matrix(Geom::identity(), unit_rect));
+ Geom::Affine gs2d(gr->get_gs2d_matrix(Geom::identity(), unit_rect));
+ EXPECT_TRUE(Geom::are_near(Geom::identity(), g2d));
+ EXPECT_TRUE(Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12));
+
+ gr->set_gs2d_matrix(Geom::identity(), unit_rect, gs2d);
+ EXPECT_TRUE(Geom::are_near(gr->gradientTransform, grXform, 1e-12));
+ }
+
+ gr->gradientTransform = grXform;
+ Geom::Affine funny(2, 3,
+ 4, 5,
+ 6, 7);
+ {
+ Geom::Affine g2d(gr->get_g2d_matrix(funny, unit_rect));
+ Geom::Affine gs2d(gr->get_gs2d_matrix(funny, unit_rect));
+ EXPECT_TRUE(Geom::are_near(funny, g2d));
+ EXPECT_TRUE(Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12));
+
+ gr->set_gs2d_matrix(funny, unit_rect, gs2d);
+ EXPECT_TRUE(Geom::are_near(gr->gradientTransform, grXform, 1e-12));
+ }
+
+ gr->gradientTransform = grXform;
+ Geom::Rect larger_rect(Geom::Point(5, 6), Geom::Point(8, 10));
+ {
+ Geom::Affine g2d(gr->get_g2d_matrix(funny, larger_rect));
+ Geom::Affine gs2d(gr->get_gs2d_matrix(funny, larger_rect));
+ EXPECT_TRUE(Geom::are_near(Geom::Affine(3, 0,
+ 0, 4,
+ 5, 6) * funny, g2d ));
+ EXPECT_TRUE(Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12));
+
+ gr->set_gs2d_matrix(funny, larger_rect, gs2d);
+ EXPECT_TRUE(Geom::are_near(gr->gradientTransform, grXform, 1e-12));
+
+ SP_OBJECT(gr)->setKeyValue( SP_ATTR_GRADIENTUNITS, "userSpaceOnUse");
+ Geom::Affine user_g2d(gr->get_g2d_matrix(funny, larger_rect));
+ Geom::Affine user_gs2d(gr->get_gs2d_matrix(funny, larger_rect));
+ EXPECT_TRUE(Geom::are_near(funny, user_g2d));
+ EXPECT_TRUE(Geom::are_near(user_gs2d, gr->gradientTransform * user_g2d, 1e-12));
+ }
+}
diff --git a/testfiles/src/sp-item-group-test.cpp b/testfiles/src/sp-item-group-test.cpp
new file mode 100644
index 0000000..3439f54
--- /dev/null
+++ b/testfiles/src/sp-item-group-test.cpp
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * SPGroup test
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2020 Authors
+ *
+ * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information
+ */
+
+#include <gtest/gtest.h>
+#include <src/document.h>
+#include <src/inkscape.h>
+#include <src/live_effects/effect.h>
+#include <src/object/sp-lpe-item.h>
+
+using namespace Inkscape;
+using namespace Inkscape::LivePathEffect;
+
+class SPGroupTest : public ::testing::Test {
+ protected:
+ void SetUp() override
+ {
+ // setup hidden dependency
+ Application::create(false);
+ }
+};
+
+TEST_F(SPGroupTest, applyingPowerClipEffectToGroupWithoutClipIsIgnored)
+{
+ std::string svg("\
+<svg width='100' height='100'>\
+ <g id='group1'>\
+ <rect id='rect1' width='100' height='50' />\
+ <rect id='rect2' y='50' width='100' height='50' />\
+ </g>\
+</svg>");
+
+ SPDocument *doc = SPDocument::createNewDocFromMem(svg.c_str(), svg.size(), true);
+
+ auto group = dynamic_cast<SPGroup *>(doc->getObjectById("group1"));
+ Effect::createAndApply(POWERCLIP, doc, group);
+
+ ASSERT_FALSE(group->hasPathEffect());
+}
diff --git a/testfiles/src/sp-object-test.cpp b/testfiles/src/sp-object-test.cpp
new file mode 100644
index 0000000..713b4b0
--- /dev/null
+++ b/testfiles/src/sp-object-test.cpp
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Multiindex container for selection
+ *
+ * Authors:
+ * Adrian Boguszewski
+ *
+ * Copyright (C) 2016 Adrian Boguszewski
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <gtest/gtest.h>
+#include <src/object/sp-object.h>
+#include <src/object/sp-item.h>
+#include <src/xml/node.h>
+#include <src/xml/text-node.h>
+#include <doc-per-case-test.h>
+#include <src/xml/simple-document.h>
+
+using namespace Inkscape;
+using namespace Inkscape::XML;
+
+class SPObjectTest: public DocPerCaseTest {
+public:
+ SPObjectTest() {
+ a = new SPItem();
+ b = new SPItem();
+ c = new SPItem();
+ d = new SPItem();
+ e = new SPItem();
+ auto sd = new SimpleDocument();
+ auto et = new TextNode(Util::share_string("e"), sd);
+ auto dt = new TextNode(Util::share_string("d"), sd);
+ auto ct = new TextNode(Util::share_string("c"), sd);
+ auto bt = new TextNode(Util::share_string("b"), sd);
+ auto at = new TextNode(Util::share_string("a"), sd);
+ e->invoke_build(_doc, et, 0);
+ d->invoke_build(_doc, dt, 0);
+ c->invoke_build(_doc, ct, 0);
+ b->invoke_build(_doc, bt, 0);
+ a->invoke_build(_doc, at, 0);
+ }
+ ~SPObjectTest() override {
+ delete e;
+ delete d;
+ delete c;
+ delete b;
+ delete a;
+ }
+ SPObject* a;
+ SPObject* b;
+ SPObject* c;
+ SPObject* d;
+ SPObject* e;
+};
+
+TEST_F(SPObjectTest, Basics) {
+ a->attach(c, a->lastChild());
+ a->attach(b, nullptr);
+ a->attach(d, c);
+ EXPECT_TRUE(a->hasChildren());
+ EXPECT_EQ(b, a->firstChild());
+ EXPECT_EQ(d, a->lastChild());
+ auto children = a->childList(false);
+ EXPECT_EQ(3, children.size());
+ EXPECT_EQ(b, children[0]);
+ EXPECT_EQ(c, children[1]);
+ EXPECT_EQ(d, children[2]);
+ a->attach(b, a->lastChild());
+ EXPECT_EQ(3, a->children.size());
+ a->reorder(b, b);
+ EXPECT_EQ(3, a->children.size());
+ EXPECT_EQ(b, &a->children.front());
+ EXPECT_EQ(d, &a->children.back());
+ a->reorder(b, d);
+ EXPECT_EQ(3, a->children.size());
+ EXPECT_EQ(c, &a->children.front());
+ EXPECT_EQ(b, &a->children.back());
+ a->reorder(d, nullptr);
+ EXPECT_EQ(3, a->children.size());
+ EXPECT_EQ(d, &a->children.front());
+ EXPECT_EQ(b, &a->children.back());
+ a->reorder(c, b);
+ EXPECT_EQ(3, a->children.size());
+ EXPECT_EQ(d, &a->children.front());
+ EXPECT_EQ(c, &a->children.back());
+ a->detach(b);
+ EXPECT_EQ(c, a->lastChild());
+ children = a->childList(false);
+ EXPECT_EQ(2, children.size());
+ EXPECT_EQ(d, children[0]);
+ EXPECT_EQ(c, children[1]);
+ a->detach(b);
+ EXPECT_EQ(2, a->childList(false).size());
+ a->releaseReferences();
+ EXPECT_FALSE(a->hasChildren());
+ EXPECT_EQ(nullptr, a->firstChild());
+ EXPECT_EQ(nullptr, a->lastChild());
+}
+
+TEST_F(SPObjectTest, Advanced) {
+ a->attach(b, a->lastChild());
+ a->attach(c, a->lastChild());
+ a->attach(d, a->lastChild());
+ a->attach(e, a->lastChild());
+ EXPECT_EQ(e, a->get_child_by_repr(e->getRepr()));
+ EXPECT_EQ(c, a->get_child_by_repr(c->getRepr()));
+ EXPECT_EQ(d, e->getPrev());
+ EXPECT_EQ(c, d->getPrev());
+ EXPECT_EQ(b, c->getPrev());
+ EXPECT_EQ(nullptr, b->getPrev());
+ EXPECT_EQ(nullptr, e->getNext());
+ EXPECT_EQ(e, d->getNext());
+ EXPECT_EQ(d, c->getNext());
+ EXPECT_EQ(c, b->getNext());
+ std::vector<SPObject*> tmp = {b, c, d, e};
+ int index = 0;
+ for(auto& child: a->children) {
+ EXPECT_EQ(tmp[index++], &child);
+ }
+}
diff --git a/testfiles/src/style-elem-test.cpp b/testfiles/src/style-elem-test.cpp
new file mode 100644
index 0000000..c003e7b
--- /dev/null
+++ b/testfiles/src/style-elem-test.cpp
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Test the API to the style element, access, read and write functions.
+ *//*
+ *
+ * Authors:
+ * Martin Owens
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information
+ */
+
+#include <gtest/gtest.h>
+#include <doc-per-case-test.h>
+
+#include <src/style.h>
+#include <src/object/sp-root.h>
+#include <src/object/sp-style-elem.h>
+
+using namespace Inkscape;
+using namespace Inkscape::XML;
+
+class ObjectTest: public DocPerCaseTest {
+public:
+ ObjectTest() {
+ char const *docString = "\
+<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>\
+<style id='style01'>\
+rect { fill: red; opacity:0.5; }\
+#id1, #id2 { fill: red; stroke: #c0c0c0; }\
+.cls1 { fill: red; opacity:1.0; }\
+</style>\
+<style id='style02'>\
+rect { fill: green; opacity:1.0; }\
+#id3, #id4 { fill: green; stroke: #606060; }\
+.cls2 { fill: green; opacity:0.5; }\
+</style>\
+</svg>";
+ doc = SPDocument::createNewDocFromMem(docString, static_cast<int>(strlen(docString)), false);
+ }
+
+ ~ObjectTest() override {
+ doc->doUnref();
+ }
+
+ SPDocument *doc;
+};
+
+/*
+ * Test sp-style-element objects created in document.
+ */
+TEST_F(ObjectTest, StyleElems) {
+ ASSERT_TRUE(doc != nullptr);
+ ASSERT_TRUE(doc->getRoot() != nullptr);
+
+ SPRoot *root = doc->getRoot();
+ ASSERT_TRUE(root->getRepr() != nullptr);
+
+ SPStyleElem *one = dynamic_cast<SPStyleElem *>(doc->getObjectById("style01"));
+ ASSERT_TRUE(one != nullptr);
+
+ for(auto style: one->styles) {
+ EXPECT_EQ(style->fill.get_value(), Glib::ustring("#ff0000"));
+ }
+
+ SPStyleElem *two = dynamic_cast<SPStyleElem *>(doc->getObjectById("style02"));
+ ASSERT_TRUE(one != nullptr);
+
+ for(auto style: two->styles) {
+ EXPECT_EQ(style->fill.get_value(), Glib::ustring("#008000"));
+ }
+}
diff --git a/testfiles/src/style-test.cpp b/testfiles/src/style-test.cpp
new file mode 100644
index 0000000..fea2901
--- /dev/null
+++ b/testfiles/src/style-test.cpp
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Unit test for style properties.
+ *
+ * Author:
+ * Tavmjong Bah <tavjong@free.fr>
+ *
+ * Copyright (C) 2017 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "style.h"
+
+namespace {
+
+class StyleRead {
+
+public:
+ StyleRead(std::string src, std::string dst, std::string uri) :
+ src(std::move(src)), dst(std::move(dst)), uri(std::move(uri))
+ {
+ }
+
+ StyleRead(std::string src, std::string dst) :
+ src(std::move(src)), dst(std::move(dst)), uri("")
+ {
+ }
+
+ StyleRead(std::string const &src) :
+ src(src), dst(src), uri("")
+ {
+ }
+
+ std::string src;
+ std::string dst;
+ std::string uri;
+
+};
+
+std::vector<StyleRead> getStyleData()
+{
+ StyleRead all_style_data[] = {
+
+ // Paint -----------------------------------------------
+ StyleRead("fill:none"), StyleRead("fill:currentColor"), StyleRead("fill:#ff00ff"),
+ StyleRead("fill:rgb(100%, 0%, 100%)", "fill:#ff00ff"), StyleRead("fill:rgb(255, 0, 255)", "fill:#ff00ff"),
+
+ // TODO - fix this to preserve the string
+ // StyleRead("fill:url(#painter) rgb(100%, 0%, 100%)",
+ // "fill:url(#painter) #ff00ff", "#painter" ),
+
+ // TODO - fix this to preserve the string
+ // StyleRead("fill:url(#painter) rgb(255, 0, 255)",
+ // "fill:url(#painter) #ff00ff", "#painter"),
+
+
+ StyleRead("fill:#ff00ff icc-color(colorChange, 0.1, 0.5, 0.1)"),
+
+ // StyleRead("fill:url(#painter)", "", "#painter"),
+ // StyleRead("fill:url(#painter) none", "", "#painter"),
+ // StyleRead("fill:url(#painter) currentColor", "", "#painter"),
+ // StyleRead("fill:url(#painter) #ff00ff", "", "#painter"),
+ // StyleRead("fill:url(#painter) rgb(100%, 0%, 100%)", "", "#painter"),
+ // StyleRead("fill:url(#painter) rgb(255, 0, 255)", "", "#painter"),
+
+ // StyleRead("fill:url(#painter) #ff00ff icc-color(colorChange, 0.1, 0.5, 0.1)", "", "#painter"),
+
+ // StyleRead("fill:url(#painter) inherit", "", "#painter"),
+
+ StyleRead("fill:inherit"),
+
+
+ // General tests (in general order of appearance in sp_style_read), SPIPaint tested above
+ StyleRead("visibility:hidden"), // SPIEnum
+ StyleRead("visibility:collapse"), StyleRead("visibility:visible"),
+ StyleRead("display:none"), // SPIEnum
+ StyleRead("overflow:visible"), // SPIEnum
+ StyleRead("overflow:auto"), // SPIEnum
+
+ StyleRead("color:#ff0000"), StyleRead("color:blue", "color:#0000ff"),
+ // StyleRead("color:currentColor"), SVG 1.1 does not allow color value 'currentColor'
+
+ // Font shorthand
+ StyleRead("font:bold 12px Arial", "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;"
+ "font-size:12px;line-height:normal;font-family:Arial"),
+ StyleRead("font:bold 12px/24px 'Times New Roman'",
+ "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12px;line-"
+ "height:24px;font-family:\'Times New Roman\'"),
+
+ // From CSS 3 Fonts (examples):
+ StyleRead("font: 12pt/15pt sans-serif", "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:"
+ "normal;font-size:16px;line-height:15pt;font-family:sans-serif"),
+ // StyleRead("font: 80% sans-serif",
+ // "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80%;line-height:normal;font-family:sans-serif"),
+ // StyleRead("font: x-large/110% 'new century schoolbook', serif",
+ // "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:x-large;line-height:110%;font-family:\'new
+ //century schoolbook\', serif"),
+ StyleRead("font: bold italic large Palatino, serif",
+ "font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:large;line-"
+ "height:normal;font-family:Palatino, serif"),
+ // StyleRead("font: normal small-caps 120%/120% fantasy",
+ // "font-style:normal;font-variant:small-caps;font-weight:normal;font-stretch:normal;font-size:120%;line-height:120%;font-family:fantasy"),
+ StyleRead("font: condensed oblique 12pt 'Helvetica Neue', serif;",
+ "font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:16px;"
+ "line-height:normal;font-family:\'Helvetica Neue\', serif"),
+
+ StyleRead("font-family:sans-serif"), // SPIString, text_private
+ StyleRead("font-family:Arial"),
+ // StyleRead("font-variant:normal;font-stretch:normal;-inkscape-font-specification:Nimbus Roman No9 L Bold
+ // Italic"),
+
+ // Needs to be fixed (quotes should be around each font-family):
+ StyleRead("font-family:Georgia, 'Minion Web'", "font-family:Georgia, \'Minion Web\'"),
+ StyleRead("font-size:12", "font-size:12px"), // SPIFontSize
+ StyleRead("font-size:12px"), StyleRead("font-size:12pt", "font-size:16px"), StyleRead("font-size:medium"),
+ StyleRead("font-size:smaller"),
+ StyleRead("font-style:italic"), // SPIEnum
+ StyleRead("font-variant:small-caps"), // SPIEnum
+ StyleRead("font-weight:100"), // SPIEnum
+ StyleRead("font-weight:normal"), StyleRead("font-weight:bolder"),
+ StyleRead("font-stretch:condensed"), // SPIEnum
+
+ StyleRead("font-variant-ligatures:none"), // SPILigatures
+ StyleRead("font-variant-ligatures:normal"), StyleRead("font-variant-ligatures:no-common-ligatures"),
+ StyleRead("font-variant-ligatures:discretionary-ligatures"),
+ StyleRead("font-variant-ligatures:historical-ligatures"), StyleRead("font-variant-ligatures:no-contextual"),
+ StyleRead("font-variant-ligatures:common-ligatures", "font-variant-ligatures:normal"),
+ StyleRead("font-variant-ligatures:contextual", "font-variant-ligatures:normal"),
+ StyleRead("font-variant-ligatures:no-common-ligatures historical-ligatures"),
+ StyleRead("font-variant-ligatures:historical-ligatures no-contextual"),
+ StyleRead("font-variant-position:normal"), StyleRead("font-variant-position:sub"),
+ StyleRead("font-variant-position:super"), StyleRead("font-variant-caps:normal"),
+ StyleRead("font-variant-caps:small-caps"), StyleRead("font-variant-caps:all-small-caps"),
+ StyleRead("font-variant-numeric:normal"), StyleRead("font-variant-numeric:lining-nums"),
+ StyleRead("font-variant-numeric:oldstyle-nums"), StyleRead("font-variant-numeric:proportional-nums"),
+ StyleRead("font-variant-numeric:tabular-nums"), StyleRead("font-variant-numeric:diagonal-fractions"),
+ StyleRead("font-variant-numeric:stacked-fractions"), StyleRead("font-variant-numeric:ordinal"),
+ StyleRead("font-variant-numeric:slashed-zero"), StyleRead("font-variant-numeric:tabular-nums slashed-zero"),
+ StyleRead("font-variant-numeric:tabular-nums proportional-nums", "font-variant-numeric:proportional-nums"),
+
+ StyleRead("font-variation-settings:'wght' 400"),
+ StyleRead("font-variation-settings:'wght' 400", "font-variation-settings:'wght' 400"),
+ StyleRead("font-variation-settings:'wght' 400, 'slnt' 0.5", "font-variation-settings:'slnt' 0.5, 'wght' 400"),
+
+ // Should be moved down
+ StyleRead("text-indent:12em"), // SPILength?
+ StyleRead("text-align:center"), // SPIEnum
+
+ // SPITextDecoration
+ // The default value for 'text-decoration-color' is 'currentColor', but
+ // we cannot set the default to that value yet. (We need to switch
+ // SPIPaint to SPIColor and then add the ability to set default.)
+ // StyleRead("text-decoration: underline",
+ // "text-decoration: underline;text-decoration-line: underline;text-decoration-color:currentColor"),
+ // StyleRead("text-decoration: overline underline",
+ // "text-decoration: underline overline;text-decoration-line: underline
+ // overline;text-decoration-color:currentColor"),
+
+ StyleRead("text-decoration: underline wavy #0000ff",
+ "text-decoration: underline;text-decoration-line: "
+ "underline;text-decoration-style:wavy;text-decoration-color:#0000ff"),
+ StyleRead("text-decoration: double overline underline #ff0000",
+ "text-decoration: underline overline;text-decoration-line: underline "
+ "overline;text-decoration-style:double;text-decoration-color:#ff0000"),
+
+ // SPITextDecorationLine
+ StyleRead("text-decoration-line: underline", "text-decoration: underline;text-decoration-line: underline"),
+
+ // SPITextDecorationStyle
+ StyleRead("text-decoration-style:solid"), StyleRead("text-decoration-style:dotted"),
+
+ // SPITextDecorationColor
+ StyleRead("text-decoration-color:#ff00ff"),
+
+ // Should be moved up
+ StyleRead("line-height:24px"), // SPILengthOrNormal
+ StyleRead("line-height:1.5"),
+ StyleRead("letter-spacing:2px"), // SPILengthOrNormal
+ StyleRead("word-spacing:2px"), // SPILengthOrNormal
+ StyleRead("word-spacing:normal"),
+ StyleRead("text-transform:lowercase"), // SPIEnum
+ // ...
+ StyleRead("baseline-shift:baseline"), // SPIBaselineShift
+ StyleRead("baseline-shift:sub"), StyleRead("baseline-shift:12.5%"), StyleRead("baseline-shift:2px"),
+
+ StyleRead("opacity:0.1"), // SPIScale24
+ // ...
+ StyleRead("stroke-width:2px"), // SPILength
+ StyleRead("stroke-linecap:round"), // SPIEnum
+ StyleRead("stroke-linejoin:round"), // SPIEnum
+ StyleRead("stroke-miterlimit:4"), // SPIFloat
+ StyleRead("marker:url(#Arrow)"), // SPIString
+ StyleRead("marker-start:url(#Arrow)"), StyleRead("marker-mid:url(#Arrow)"), StyleRead("marker-end:url(#Arrow)"),
+ StyleRead("stroke-opacity:0.5"), // SPIScale24
+ // Currently inkscape handle unit conversion in dasharray but need
+ // a active document to do it, so we can't include in any test
+ StyleRead("stroke-dasharray:0, 1, 0, 1"), // SPIDashArray
+ StyleRead("stroke-dasharray:0 1 0 1", "stroke-dasharray:0, 1, 0, 1"),
+ StyleRead("stroke-dasharray:0 1 2 3", "stroke-dasharray:0, 1, 2, 3"),
+ StyleRead("stroke-dashoffset:13"), // SPILength
+ StyleRead("stroke-dashoffset:10px"),
+ // ...
+ // StyleRead("filter:url(#myfilter)"), // SPIFilter segfault in read
+ StyleRead("filter:inherit"),
+
+ StyleRead("opacity:0.1;fill:#ff0000;stroke:#0000ff;stroke-width:2px"),
+ StyleRead("opacity:0.1;fill:#ff0000;stroke:#0000ff;stroke-width:2px;stroke-dasharray:1, 2, 3, "
+ "4;stroke-dashoffset:15"),
+
+ StyleRead("paint-order:stroke"), // SPIPaintOrder
+ StyleRead("paint-order:normal"),
+ StyleRead("paint-order: markers stroke fill", "paint-order:markers stroke fill"),
+
+ // !important (in order of appearance in style-internal.h)
+ StyleRead("stroke-miterlimit:4 !important"), // SPIFloat
+ StyleRead("stroke-opacity:0.5 !important"), // SPIScale24
+ StyleRead("stroke-width:2px !important"), // SPILength
+ StyleRead("line-height:24px !important"), // SPILengthOrNormal
+ StyleRead("line-height:normal !important"),
+ StyleRead("font-stretch:condensed !important"), // SPIEnum
+ StyleRead("marker:url(#Arrow) !important"), // SPIString
+ StyleRead("color:#0000ff !important"), // SPIColor
+ StyleRead("fill:none !important"), // SPIPaint
+ StyleRead("fill:currentColor !important"), StyleRead("fill:#ff00ff !important"),
+ StyleRead("paint-order:stroke !important"), // SPIPaintOrder
+ StyleRead("paint-order:normal !important"),
+ StyleRead("stroke-dasharray:0, 1, 0, 1 !important"), // SPIDashArray
+ StyleRead("font-size:12px !important"), // SPIFontSize
+ StyleRead("baseline-shift:baseline !important"), // SPIBaselineShift
+ StyleRead("baseline-shift:sub !important"),
+ // StyleRead("text-decoration-line: underline !important"), // SPITextDecorationLine
+
+ };
+
+ size_t count = sizeof(all_style_data) / sizeof(all_style_data[0]);
+ std::vector<StyleRead> vect(all_style_data, all_style_data + count);
+ return vect;
+}
+
+TEST(StyleTest, Read) {
+ std::vector<StyleRead> all_style = getStyleData();
+ EXPECT_GT(all_style.size(), 0);
+ for (auto i : all_style) {
+
+ SPStyle style;
+ style.mergeString (i.src.c_str());
+
+ if (!i.uri.empty()) {
+ //EXPECT_EQ (style.fill.value.href->getURI()->toString(), i.uri);
+ }
+
+ std::string out = style.write();
+ if (i.dst.empty()) {
+ // std::cout << "out: " << out << std::endl;
+ // std::cout << "i.src: " << i.src << std::endl;
+ EXPECT_EQ (out, i.src);
+ } else {
+ // std::cout << "out: " << out << std::endl;
+ // std::cout << "i.dst: " << i.dst << std::endl;
+ EXPECT_EQ (out, i.dst);
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------------------------
+
+class StyleMatch {
+
+public:
+ StyleMatch(std::string src, std::string dst, bool const &match) :
+ src(std::move(src)), dst(std::move(dst)), match(match)
+ {
+ }
+
+ std::string src;
+ std::string dst;
+ bool match;
+
+};
+
+std::vector<StyleMatch> getStyleMatchData()
+{
+ StyleMatch all_style_data[] = {
+
+ // SPIFloat
+ StyleMatch("stroke-miterlimit:4", "stroke-miterlimit:4", true ),
+ StyleMatch("stroke-miterlimit:4", "stroke-miterlimit:2", false),
+ StyleMatch("stroke-miterlimit:4", "", true ), // Default
+
+ // SPIScale24
+ StyleMatch("opacity:0.3", "opacity:0.3", true ),
+ StyleMatch("opacity:0.3", "opacity:0.6", false),
+ StyleMatch("opacity:1.0", "", true ), // Default
+
+ // SPILength
+ StyleMatch("text-indent:3", "text-indent:3", true ),
+ StyleMatch("text-indent:6", "text-indent:3", false),
+ StyleMatch("text-indent:6px", "text-indent:3", false),
+ StyleMatch("text-indent:1px", "text-indent:12pc", false),
+ StyleMatch("text-indent:2ex", "text-indent:2ex", false),
+
+ // SPILengthOrNormal
+ StyleMatch("letter-spacing:normal", "letter-spacing:normal", true ),
+ StyleMatch("letter-spacing:2", "letter-spacing:normal", false),
+ StyleMatch("letter-spacing:normal", "letter-spacing:2", false),
+ StyleMatch("letter-spacing:5px", "letter-spacing:5px", true ),
+ StyleMatch("letter-spacing:10px", "letter-spacing:5px", false),
+ StyleMatch("letter-spacing:10em", "letter-spacing:10em", false),
+
+ // SPIEnum
+ StyleMatch("text-anchor:start", "text-anchor:start", true ),
+ StyleMatch("text-anchor:start", "text-anchor:middle", false),
+ StyleMatch("text-anchor:start", "", true ), // Default
+ StyleMatch("text-anchor:start", "text-anchor:junk", true ), // Bad value
+
+ StyleMatch("font-weight:normal", "font-weight:400", true ),
+ StyleMatch("font-weight:bold", "font-weight:700", true ),
+
+
+ // SPIString and SPIFontString
+ StyleMatch("font-family:Arial", "font-family:Arial", true ),
+ StyleMatch("font-family:A B", "font-family:A B", true ),
+ StyleMatch("font-family:A B", "font-family:A C", false),
+ // Default is not set by class... value is NULL which cannot be compared
+ // StyleMatch("font-family:sans-serif", "", true ), // Default
+
+ // SPIColor
+ StyleMatch("color:blue", "color:blue", true ),
+ StyleMatch("color:blue", "color:red", false),
+ StyleMatch("color:red", "color:#ff0000", true ),
+
+ // SPIPaint
+ StyleMatch("fill:blue", "fill:blue", true ),
+ StyleMatch("fill:blue", "fill:red", false),
+ StyleMatch("fill:currentColor", "fill:currentColor", true ),
+ StyleMatch("fill:url(#xxx)", "fill:url(#xxx)", true ),
+ // Needs URL defined as in test 1
+ //StyleMatch("fill:url(#xxx)", "fill:url(#yyy)", false),
+
+ // SPIPaintOrder
+ StyleMatch("paint-order:markers", "paint-order:markers", true ),
+ StyleMatch("paint-order:markers", "paint-order:stroke", false),
+ //StyleMatch("paint-order:fill stroke markers", "", true ), // Default
+ StyleMatch("paint-order:normal", "paint-order:normal", true ),
+ //StyleMatch("paint-order:fill stroke markers", "paint-order:normal", true ),
+
+ // SPIDashArray
+ StyleMatch("stroke-dasharray:0 1 2 3","stroke-dasharray:0 1 2 3",true ),
+ StyleMatch("stroke-dasharray:0 1", "stroke-dasharray:0 2", false),
+
+ // SPIFilter
+
+ // SPIFontSize
+ StyleMatch("font-size:12px", "font-size:12px", true ),
+ StyleMatch("font-size:12px", "font-size:24px", false),
+ StyleMatch("font-size:12ex", "font-size:24ex", false),
+ StyleMatch("font-size:medium", "font-size:medium", true ),
+ StyleMatch("font-size:medium", "font-size:large", false),
+
+ // SPIBaselineShift
+ StyleMatch("baseline-shift:baseline", "baseline-shift:baseline", true ),
+ StyleMatch("baseline-shift:sub", "baseline-shift:sub", true ),
+ StyleMatch("baseline-shift:sub", "baseline-shift:super", false),
+ StyleMatch("baseline-shift:baseline", "baseline-shift:sub", false),
+ StyleMatch("baseline-shift:10px", "baseline-shift:10px", true ),
+ StyleMatch("baseline-shift:10px", "baseline-shift:12px", false),
+
+
+ // SPITextDecorationLine
+ StyleMatch("text-decoration-line:underline", "text-decoration-line:underline", true ),
+ StyleMatch("text-decoration-line:underline", "text-decoration-line:overline", false),
+ StyleMatch("text-decoration-line:underline overline", "text-decoration-line:underline overline", true ),
+ StyleMatch("text-decoration-line:none", "", true ), // Default
+
+
+ // SPITextDecorationStyle
+ StyleMatch("text-decoration-style:solid", "text-decoration-style:solid", true ),
+ StyleMatch("text-decoration-style:dotted", "text-decoration-style:solid", false),
+ StyleMatch("text-decoration-style:solid", "", true ), // Default
+
+ // SPITextDecoration
+ StyleMatch("text-decoration:underline", "text-decoration:underline", true ),
+ StyleMatch("text-decoration:underline", "text-decoration:overline", false),
+ StyleMatch("text-decoration:underline overline","text-decoration:underline overline",true ),
+ StyleMatch("text-decoration:overline underline","text-decoration:underline overline",true ),
+ // StyleMatch("text-decoration:none", "text-decoration-color:currentColor", true ), // Default
+
+ };
+
+ size_t count = sizeof(all_style_data) / sizeof(all_style_data[0]);
+ std::vector<StyleMatch> vect(all_style_data, all_style_data + count);
+ return vect;
+}
+
+TEST(StyleTest, Match) {
+ std::vector<StyleMatch> all_style = getStyleMatchData();
+ EXPECT_GT(all_style.size(), 0);
+ for (auto i : all_style) {
+
+ SPStyle style_src;
+ SPStyle style_dst;
+
+ style_src.mergeString( i.src.c_str() );
+ style_dst.mergeString( i.dst.c_str() );
+
+ // std::cout << "Test:" << std::endl;
+ // std::cout << " C: |" << i.src
+ // << "| |" << i.dst << "|" << std::endl;
+ // std::cout << " S: |" << style_src.write( SP_STYLE_FLAG_IFSET )
+ // << "| |" << style_dst.write( SP_STYLE_FLAG_IFSET ) << "|" <<std::endl;
+
+ EXPECT_TRUE( (style_src == style_dst) == i.match );
+ }
+}
+
+// ------------------------------------------------------------------------------------
+
+class StyleCascade {
+
+public:
+ StyleCascade(std::string parent, std::string child, std::string result) :
+ parent(std::move(parent)), child(std::move(child)), result(std::move(result))
+ {
+ }
+
+ std::string parent;
+ std::string child;
+ std::string result;
+
+};
+
+std::vector<StyleCascade> getStyleCascadeData()
+{
+
+ StyleCascade all_style_data[] = {
+
+ // SPIFloat
+ StyleCascade("stroke-miterlimit:6", "stroke-miterlimit:2", "stroke-miterlimit:2" ),
+ StyleCascade("stroke-miterlimit:6", "", "stroke-miterlimit:6" ),
+ StyleCascade("", "stroke-miterlimit:2", "stroke-miterlimit:2" ),
+
+ // SPIScale24
+ StyleCascade("opacity:0.3", "opacity:0.3", "opacity:0.3" ),
+ StyleCascade("opacity:0.3", "opacity:0.6", "opacity:0.6" ),
+ // 'opacity' does not inherit
+ StyleCascade("opacity:0.3", "", "opacity:1.0" ),
+ StyleCascade("", "opacity:0.3", "opacity:0.3" ),
+ StyleCascade("opacity:0.5", "opacity:inherit", "opacity:0.5" ),
+ StyleCascade("", "", "opacity:1.0" ),
+
+ // SPILength
+ StyleCascade("text-indent:3", "text-indent:3", "text-indent:3" ),
+ StyleCascade("text-indent:6", "text-indent:3", "text-indent:3" ),
+ StyleCascade("text-indent:6px", "text-indent:3", "text-indent:3" ),
+ StyleCascade("text-indent:1px", "text-indent:12pc", "text-indent:12pc" ),
+ // ex, em cannot be equal
+ //StyleCascade("text-indent:2ex", "text-indent:2ex", "text-indent:2ex" ),
+ StyleCascade("text-indent:3", "", "text-indent:3" ),
+ StyleCascade("text-indent:3", "text-indent:inherit", "text-indent:3" ),
+
+ // SPILengthOrNormal
+ StyleCascade("letter-spacing:normal", "letter-spacing:normal", "letter-spacing:normal" ),
+ StyleCascade("letter-spacing:2", "letter-spacing:normal", "letter-spacing:normal" ),
+ StyleCascade("letter-spacing:normal", "letter-spacing:2", "letter-spacing:2" ),
+ StyleCascade("letter-spacing:5px", "letter-spacing:5px", "letter-spacing:5px" ),
+ StyleCascade("letter-spacing:10px", "letter-spacing:5px", "letter-spacing:5px" ),
+ // ex, em cannot be equal
+ // StyleCascade("letter-spacing:10em", "letter-spacing:10em", "letter-spacing:10em" ),
+
+ // SPIEnum
+ StyleCascade("text-anchor:start", "text-anchor:start", "text-anchor:start" ),
+ StyleCascade("text-anchor:start", "text-anchor:middle", "text-anchor:middle" ),
+ StyleCascade("text-anchor:start", "", "text-anchor:start" ),
+ StyleCascade("text-anchor:start", "text-anchor:junk", "text-anchor:start" ),
+ StyleCascade("text-anchor:end", "text-anchor:inherit", "text-anchor:end" ),
+
+ StyleCascade("font-weight:400", "font-weight:400", "font-weight:400" ),
+ StyleCascade("font-weight:400", "font-weight:700", "font-weight:700" ),
+ StyleCascade("font-weight:400", "font-weight:bolder", "font-weight:700" ),
+ StyleCascade("font-weight:700", "font-weight:bolder", "font-weight:900" ),
+ StyleCascade("font-weight:400", "font-weight:lighter", "font-weight:100" ),
+ StyleCascade("font-weight:200", "font-weight:lighter", "font-weight:100" ),
+
+ StyleCascade("font-stretch:condensed","font-stretch:expanded", "font-stretch:expanded" ),
+ StyleCascade("font-stretch:condensed","font-stretch:wider", "font-stretch:semi-condensed" ),
+
+ // SPIString and SPIFontString
+
+ StyleCascade("font-variation-settings:'wght' 400", "", "font-variation-settings:'wght' 400"),
+ StyleCascade("font-variation-settings:'wght' 100",
+ "font-variation-settings:'wght' 400",
+ "font-variation-settings:'wght' 400"),
+
+ // SPIPaint
+
+ // SPIPaintOrder
+
+ // SPIDashArray
+
+ // SPIFilter
+
+ // SPIFontSize
+
+ // SPIBaselineShift
+
+
+ // SPITextDecorationLine
+ StyleCascade("text-decoration-line:overline", "text-decoration-line:underline",
+ "text-decoration-line:underline" ),
+
+ // SPITextDecorationStyle
+
+ // SPITextDecoration
+ };
+
+ size_t count = sizeof(all_style_data) / sizeof(all_style_data[0]);
+ std::vector<StyleCascade> vect(all_style_data, all_style_data + count);
+ return vect;
+
+}
+
+TEST(StyleTest, Cascade) {
+ std::vector<StyleCascade> all_style = getStyleCascadeData();
+ EXPECT_GT(all_style.size(), 0);
+ for (auto i : all_style) {
+
+ SPStyle style_parent;
+ SPStyle style_child;
+ SPStyle style_result;
+
+ style_parent.mergeString( i.parent.c_str() );
+ style_child.mergeString( i.child.c_str() );
+ style_result.mergeString( i.result.c_str() );
+
+ // std::cout << "Test:" << std::endl;
+ // std::cout << " Input: ";
+ // std::cout << " Parent: " << i.parent
+ // << " Child: " << i.child
+ // << " Result: " << i.result << std::endl;
+ // std::cout << " Write: ";
+ // std::cout << " Parent: " << style_parent.write( SP_STYLE_FLAG_IFSET )
+ // << " Child: " << style_child.write( SP_STYLE_FLAG_IFSET )
+ // << " Result: " << style_result.write( SP_STYLE_FLAG_IFSET ) << std::endl;
+
+ style_child.cascade( &style_parent );
+
+ EXPECT_TRUE(style_child == style_result );
+ }
+}
+
+
+} // namespace
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/testfiles/src/svg-stringstream-test.cpp b/testfiles/src/svg-stringstream-test.cpp
new file mode 100644
index 0000000..636b018
--- /dev/null
+++ b/testfiles/src/svg-stringstream-test.cpp
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Test CSSOStringStream and SVGOStringStream
+ */
+/*
+ * Authors:
+ * Thomas Holder
+ *
+ * Copyright (C) 2019 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "2geom/point.h"
+#include "svg/css-ostringstream.h"
+#include "svg/stringstream.h"
+
+#include "gtest/gtest.h"
+#include <glibmm/ustring.h>
+
+template <typename S, typename T>
+static void assert_tostring_eq(T value, const char *expected)
+{
+ S os;
+
+ // default of /options/svgoutput/numericprecision
+ os.precision(8);
+
+ os << value;
+ ASSERT_EQ(os.str(), expected);
+}
+
+#define TEST_STRING "Hello & <World>"
+
+template <typename S>
+void test_tostring()
+{
+ assert_tostring_eq<S, char>('A', "A");
+ assert_tostring_eq<S, signed char>('A', "A");
+ assert_tostring_eq<S, unsigned char>('A', "A");
+
+ assert_tostring_eq<S, short>(0x7FFF, "32767");
+ assert_tostring_eq<S, short>(-30000, "-30000");
+ assert_tostring_eq<S, unsigned short>(0xFFFFu, "65535");
+ assert_tostring_eq<S, int>(0x7FFFFFFF, "2147483647");
+ assert_tostring_eq<S, int>(-2000000000, "-2000000000");
+ assert_tostring_eq<S, unsigned int>(0xFFFFFFFFu, "4294967295");
+
+ // long is 32bit on Windows, 64bit on Linux
+ assert_tostring_eq<S, long>(0x7FFFFFFFL, "2147483647");
+ assert_tostring_eq<S, long>(-2000000000L, "-2000000000");
+ assert_tostring_eq<S, unsigned long>(0xFFFFFFFFuL, "4294967295");
+
+ assert_tostring_eq<S>((char const *)TEST_STRING, TEST_STRING);
+ assert_tostring_eq<S>((signed char const *)TEST_STRING, TEST_STRING);
+ assert_tostring_eq<S>((unsigned char const *)TEST_STRING, TEST_STRING);
+ assert_tostring_eq<S, std::string>(TEST_STRING, TEST_STRING);
+ assert_tostring_eq<S, Glib::ustring>(TEST_STRING, TEST_STRING);
+}
+
+TEST(CSSOStringStreamTest, tostring)
+{
+ using S = Inkscape::CSSOStringStream;
+
+ test_tostring<S>();
+
+ // float has 6 significant digits
+ assert_tostring_eq<S, float>(0.0, "0");
+ assert_tostring_eq<S, float>(4.5, "4.5");
+ assert_tostring_eq<S, float>(-4.0, "-4");
+ assert_tostring_eq<S, float>(0.001, "0.001");
+ assert_tostring_eq<S, float>(0.00123456, "0.00123456");
+ assert_tostring_eq<S, float>(-0.00123456, "-0.00123456");
+ assert_tostring_eq<S, float>(-1234560.0, "-1234560");
+
+ // double has 15 significant digits
+ assert_tostring_eq<S, double>(0.0, "0");
+ assert_tostring_eq<S, double>(4.5, "4.5");
+ assert_tostring_eq<S, double>(-4.0, "-4");
+ assert_tostring_eq<S, double>(0.001, "0.001");
+
+ // 9 significant digits
+ assert_tostring_eq<S, double>(1.23456789, "1.23456789");
+ assert_tostring_eq<S, double>(-1.23456789, "-1.23456789");
+ assert_tostring_eq<S, double>(12345678.9, "12345678.9");
+ assert_tostring_eq<S, double>(-12345678.9, "-12345678.9");
+
+ assert_tostring_eq<S, double>(1.234e-12, "0");
+ assert_tostring_eq<S, double>(3e9, "3000000000");
+ assert_tostring_eq<S, double>(-3.5e9, "-3500000000");
+}
+
+TEST(SVGOStringStreamTest, tostring)
+{
+ using S = Inkscape::SVGOStringStream;
+
+ test_tostring<S>();
+
+ assert_tostring_eq<S>(Geom::Point(12, 3.4), "12,3.4");
+
+ // float has 6 significant digits
+ assert_tostring_eq<S, float>(0.0, "0");
+ assert_tostring_eq<S, float>(4.5, "4.5");
+ assert_tostring_eq<S, float>(-4.0, "-4");
+ assert_tostring_eq<S, float>(0.001, "0.001");
+ assert_tostring_eq<S, float>(0.00123456, "0.00123456");
+ assert_tostring_eq<S, float>(-0.00123456, "-0.00123456");
+ assert_tostring_eq<S, float>(-1234560.0, "-1234560");
+
+ // double has 15 significant digits
+ assert_tostring_eq<S, double>(0.0, "0");
+ assert_tostring_eq<S, double>(4.5, "4.5");
+ assert_tostring_eq<S, double>(-4.0, "-4");
+ assert_tostring_eq<S, double>(0.001, "0.001");
+
+ // 8 significant digits
+ assert_tostring_eq<S, double>(1.23456789, "1.2345679");
+ assert_tostring_eq<S, double>(-1.23456789, "-1.2345679");
+ assert_tostring_eq<S, double>(12345678.9, "12345679");
+ assert_tostring_eq<S, double>(-12345678.9, "-12345679");
+
+ assert_tostring_eq<S, double>(1.234e-12, "1.234e-12");
+ assert_tostring_eq<S, double>(3e9, "3e+09");
+ assert_tostring_eq<S, double>(-3.5e9, "-3.5e+09");
+}
+
+template <typename S>
+void test_concat()
+{
+ S s;
+ s << "hello, ";
+ s << -53.5;
+ ASSERT_EQ(s.str(), std::string("hello, -53.5"));
+}
+
+TEST(CSSOStringStreamTest, concat)
+{ //
+ test_concat<Inkscape::CSSOStringStream>();
+}
+
+TEST(SVGOStringStreamTest, concat)
+{ //
+ test_concat<Inkscape::SVGOStringStream>();
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/testfiles/src/uri-test.cpp b/testfiles/src/uri-test.cpp
new file mode 100644
index 0000000..91af2e7
--- /dev/null
+++ b/testfiles/src/uri-test.cpp
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Test Inkscape::URI
+ */
+/*
+ * Authors:
+ * Thomas Holder
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "object/uri.h"
+#include "gtest/gtest.h"
+
+using Inkscape::URI;
+
+#define BASE64_HELLO_WORLD_P1 "SGVsbG8g"
+#define BASE64_HELLO_WORLD_P2 "V29ybGQ="
+#define DATA_BASE64_HEADER "data:text/plain;charset=utf-8;base64,"
+char const *DATA_BASE64_HELLO_WORLD = DATA_BASE64_HEADER BASE64_HELLO_WORLD_P1 BASE64_HELLO_WORLD_P2;
+char const *DATA_BASE64_HELLO_WORLD_WRAPPED = DATA_BASE64_HEADER BASE64_HELLO_WORLD_P1 "\n" BASE64_HELLO_WORLD_P2;
+
+char const *win_url_unc = "file://laptop/My%20Documents/FileSchemeURIs.doc";
+char const *win_url_local = "file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc";
+char const *win_filename_local = "C:\\Documents and Settings\\davris\\FileSchemeURIs.doc";
+
+TEST(UriTest, Malformed)
+{
+ ASSERT_ANY_THROW(URI(nullptr));
+ ASSERT_ANY_THROW(URI("nonhex-%XX"));
+}
+
+TEST(UriTest, GetPath)
+{
+ ASSERT_STREQ(URI().getPath(), nullptr);
+ ASSERT_STREQ(URI("foo.svg").getPath(), "foo.svg");
+ ASSERT_STREQ(URI("foo.svg#bar").getPath(), "foo.svg");
+ ASSERT_STREQ(URI("#bar").getPath(), nullptr);
+ ASSERT_STREQ(URI("scheme://host").getPath(), nullptr);
+ ASSERT_STREQ(URI("scheme://host/path").getPath(), "/path");
+ ASSERT_STREQ(URI("scheme://host/path?query").getPath(), "/path");
+ ASSERT_STREQ(URI("scheme:/path").getPath(), "/path");
+}
+
+TEST(UriTest, FromDir)
+{
+#ifdef _WIN32
+ ASSERT_EQ(URI::from_dirname("C:\\tmp").str(), "file:///C:/tmp/");
+ ASSERT_EQ(URI::from_dirname("C:\\").str(), "file:///C:/");
+ ASSERT_EQ(URI::from_href_and_basedir("uri.svg", "C:\\tmp").str(), "file:///C:/tmp/uri.svg");
+#else
+ ASSERT_EQ(URI::from_dirname("/").str(), "file:///");
+ ASSERT_EQ(URI::from_dirname("/tmp").str(), "file:///tmp/");
+ ASSERT_EQ(URI::from_href_and_basedir("uri.svg", "/tmp").str(), "file:///tmp/uri.svg");
+#endif
+}
+
+TEST(UriTest, Str)
+{
+ ASSERT_EQ(URI().str(), "");
+ ASSERT_EQ(URI("").str(), "");
+ ASSERT_EQ(URI("", "http://a/b").str(), "http://a/b");
+
+ ASSERT_EQ(URI("uri.svg").str(), "uri.svg");
+ ASSERT_EQ(URI("tmp/uri.svg").str(), "tmp/uri.svg");
+ ASSERT_EQ(URI("/tmp/uri.svg").str(), "/tmp/uri.svg");
+ ASSERT_EQ(URI("../uri.svg").str(), "../uri.svg");
+
+ ASSERT_EQ(URI("file:///tmp/uri.svg").str(), "file:///tmp/uri.svg");
+ ASSERT_EQ(URI("uri.svg", "file:///tmp/").str(), "file:///tmp/uri.svg");
+ ASSERT_EQ(URI("file:///tmp/uri.svg").str("file:///tmp/"), "uri.svg");
+ ASSERT_EQ(URI("file:///tmp/up/uri.svg").str("file:///tmp/"), "up/uri.svg");
+ ASSERT_EQ(URI("file:///tmp/uri.svg").str("file:///tmp/up/"), "../uri.svg");
+ ASSERT_EQ(URI("file:///tmp/uri.svg").str("http://web/url"), "file:///tmp/uri.svg");
+ ASSERT_EQ(URI("file:///tmp/uri.svg").str("http://web/url"), "file:///tmp/uri.svg");
+ ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str(), "http://web/a/b/foo/uri.svg");
+ ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str("http://web/a/"), "b/foo/uri.svg");
+ ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str("http://other/a/"), "http://web/a/b/foo/uri.svg");
+
+ ASSERT_EQ(URI("http://web/").str("http://web/"), "");
+ ASSERT_EQ(URI("http://web/").str("http://web/url"), "./");
+
+ // special case: don't cross filesystem root
+ ASSERT_EQ(URI("file:///a").str("file:///"), "a");
+ ASSERT_EQ(URI("file:///ax/b").str("file:///ay/"), "file:///ax/b"); // special case
+ ASSERT_EQ(URI("file:///C:/b").str("file:///D:/"), "file:///C:/b"); // special case
+ ASSERT_EQ(URI("file:///C:/a/b").str("file:///C:/b/"), "../a/b");
+
+ ASSERT_EQ(URI(win_url_unc).str(), win_url_unc);
+ ASSERT_EQ(URI(win_url_unc).str("file://laptop/My%20Documents/"), "FileSchemeURIs.doc");
+ ASSERT_EQ(URI(win_url_local).str(), win_url_local);
+ ASSERT_EQ(URI(win_url_local).str("file:///C:/Documents%20and%20Settings/"), "davris/FileSchemeURIs.doc");
+ ASSERT_EQ(URI(win_url_local).str(win_url_unc), win_url_local);
+#ifdef _WIN32
+ ASSERT_EQ(URI(win_url_local).toNativeFilename(), win_filename_local);
+#else
+ ASSERT_EQ(URI("file:///tmp/uri.svg").toNativeFilename(), "/tmp/uri.svg");
+ ASSERT_EQ(URI("file:///tmp/x%20y.svg").toNativeFilename(), "/tmp/x y.svg");
+ ASSERT_EQ(URI("file:///a/b#hash").toNativeFilename(), "/a/b");
+#endif
+
+ ASSERT_ANY_THROW(URI("http://a/b").toNativeFilename());
+}
+
+TEST(UriTest, StrDataScheme)
+{
+ ASSERT_EQ(URI("data:,text").str(), "data:,text");
+ ASSERT_EQ(URI("data:,white%20space").str(), "data:,white%20space");
+ ASSERT_EQ(URI("data:,umlaut-%C3%96").str(), "data:,umlaut-%C3%96");
+ ASSERT_EQ(URI(DATA_BASE64_HELLO_WORLD).str(), DATA_BASE64_HELLO_WORLD);
+}
+
+TEST(UriTest, Escape)
+{
+ ASSERT_EQ(URI("data:,white space").str(), "data:,white%20space");
+ ASSERT_EQ(URI("data:,white\nspace").str(), "data:,white%0Aspace");
+ ASSERT_EQ(URI("data:,umlaut-\xC3\x96").str(), "data:,umlaut-%C3%96");
+}
+
+TEST(UriTest, GetContents)
+{
+ ASSERT_EQ(URI("data:,white space").getContents(), "white space");
+ ASSERT_EQ(URI("data:,white%20space").getContents(), "white space");
+ ASSERT_EQ(URI("data:,white\nspace").getContents(), "white\nspace");
+ ASSERT_EQ(URI("data:,white%0Aspace").getContents(), "white\nspace");
+ ASSERT_EQ(URI("data:,umlaut-%C3%96").getContents(), "umlaut-\xC3\x96");
+ ASSERT_EQ(URI(DATA_BASE64_HELLO_WORLD).getContents(), "Hello World");
+ ASSERT_EQ(URI(DATA_BASE64_HELLO_WORLD_WRAPPED).getContents(), "Hello World");
+
+ ASSERT_ANY_THROW(URI().getContents());
+}
+
+TEST(UriTest, CssStr)
+{
+ ASSERT_EQ(URI("file:///tmp/uri.svg").cssStr(), "url(file:///tmp/uri.svg)");
+ ASSERT_EQ(URI("uri.svg").cssStr(), "url(uri.svg)");
+}
+
+TEST(UriTest, GetMimeType)
+{
+ ASSERT_EQ(URI("data:image/png;base64,").getMimeType(), "image/png");
+ ASSERT_EQ(URI("data:text/plain,xxx").getMimeType(), "text/plain");
+ ASSERT_EQ(URI("file:///tmp/uri.png").getMimeType(), "image/png");
+ ASSERT_EQ(URI("uri.png").getMimeType(), "image/png");
+ ASSERT_EQ(URI("uri.svg").getMimeType(), "image/svg+xml");
+
+ // can be "text/plain" or "text/*"
+ ASSERT_EQ(URI("file:///tmp/uri.txt").getMimeType().substr(0, 5), "text/");
+}
+
+TEST(UriTest, HasScheme)
+{
+ ASSERT_FALSE(URI().hasScheme("file"));
+ ASSERT_FALSE(URI("uri.svg").hasScheme("file"));
+ ASSERT_FALSE(URI("uri.svg").hasScheme("data"));
+
+ ASSERT_TRUE(URI("file:///uri.svg").hasScheme("file"));
+ ASSERT_TRUE(URI("FILE:///uri.svg").hasScheme("file"));
+ ASSERT_FALSE(URI("file:///uri.svg").hasScheme("data"));
+
+ ASSERT_TRUE(URI("data:,").hasScheme("data"));
+ ASSERT_TRUE(URI("DaTa:,").hasScheme("data"));
+ ASSERT_FALSE(URI("data:,").hasScheme("file"));
+
+ ASSERT_TRUE(URI("http://web/").hasScheme("http"));
+ ASSERT_FALSE(URI("http://web/").hasScheme("file"));
+
+ ASSERT_TRUE(URI::from_href_and_basedir("data:,white\nspace", "/tmp").hasScheme("data"));
+}
+
+TEST(UriTest, isOpaque)
+{
+ ASSERT_FALSE(URI().isOpaque());
+ ASSERT_FALSE(URI("file:///uri.svg").isOpaque());
+ ASSERT_FALSE(URI("/uri.svg").isOpaque());
+ ASSERT_FALSE(URI("uri.svg").isOpaque());
+ ASSERT_FALSE(URI("foo://bar/baz").isOpaque());
+ ASSERT_FALSE(URI("foo://bar").isOpaque());
+ ASSERT_FALSE(URI("foo:/bar").isOpaque());
+
+ ASSERT_TRUE(URI("foo:bar").isOpaque());
+ ASSERT_TRUE(URI("mailto:user@host.xy").isOpaque());
+ ASSERT_TRUE(URI("news:comp.lang.java").isOpaque());
+}
+
+TEST(UriTest, isRelative)
+{
+ ASSERT_TRUE(URI().isRelative());
+
+ ASSERT_FALSE(URI("http://web/uri.svg").isRelative());
+ ASSERT_FALSE(URI("file:///uri.svg").isRelative());
+ ASSERT_FALSE(URI("mailto:user@host.xy").isRelative());
+ ASSERT_FALSE(URI("data:,").isRelative());
+
+ ASSERT_TRUE(URI("//web/uri.svg").isRelative());
+ ASSERT_TRUE(URI("/uri.svg").isRelative());
+ ASSERT_TRUE(URI("uri.svg").isRelative());
+ ASSERT_TRUE(URI("./uri.svg").isRelative());
+ ASSERT_TRUE(URI("../uri.svg").isRelative());
+}
+
+TEST(UriTest, isNetPath)
+{
+ ASSERT_FALSE(URI().isNetPath());
+ ASSERT_FALSE(URI("http://web/uri.svg").isNetPath());
+ ASSERT_FALSE(URI("file:///uri.svg").isNetPath());
+ ASSERT_FALSE(URI("/uri.svg").isNetPath());
+ ASSERT_FALSE(URI("uri.svg").isNetPath());
+
+ ASSERT_TRUE(URI("//web/uri.svg").isNetPath());
+}
+
+TEST(UriTest, isRelativePath)
+{
+ ASSERT_FALSE(URI("foo:bar").isRelativePath());
+ ASSERT_TRUE(URI("foo%3Abar").isRelativePath());
+
+ ASSERT_FALSE(URI("http://web/uri.svg").isRelativePath());
+ ASSERT_FALSE(URI("//web/uri.svg").isRelativePath());
+ ASSERT_FALSE(URI("/uri.svg").isRelativePath());
+
+ ASSERT_TRUE(URI("uri.svg").isRelativePath());
+ ASSERT_TRUE(URI("./uri.svg").isRelativePath());
+ ASSERT_TRUE(URI("../uri.svg").isRelativePath());
+}
+
+TEST(UriTest, isAbsolutePath)
+{
+ ASSERT_FALSE(URI().isAbsolutePath());
+ ASSERT_FALSE(URI("http://web/uri.svg").isAbsolutePath());
+ ASSERT_FALSE(URI("//web/uri.svg").isAbsolutePath());
+ ASSERT_FALSE(URI("uri.svg").isAbsolutePath());
+ ASSERT_FALSE(URI("../uri.svg").isAbsolutePath());
+
+ ASSERT_TRUE(URI("/uri.svg").isAbsolutePath());
+}
+
+TEST(UriTest, getScheme)
+{
+ ASSERT_STREQ(URI().getScheme(), nullptr);
+
+ ASSERT_STREQ(URI("https://web/uri.svg").getScheme(), "https");
+ ASSERT_STREQ(URI("file:///uri.svg").getScheme(), "file");
+ ASSERT_STREQ(URI("data:,").getScheme(), "data");
+
+ ASSERT_STREQ(URI("data").getScheme(), nullptr);
+}
+
+TEST(UriTest, getQuery)
+{
+ ASSERT_STREQ(URI().getQuery(), nullptr);
+ ASSERT_STREQ(URI("uri.svg?a=b&c=d").getQuery(), "a=b&c=d");
+ ASSERT_STREQ(URI("?a=b&c=d#hash").getQuery(), "a=b&c=d");
+}
+
+TEST(UriTest, getFragment)
+{
+ ASSERT_STREQ(URI().getFragment(), nullptr);
+ ASSERT_STREQ(URI("uri.svg").getFragment(), nullptr);
+ ASSERT_STREQ(URI("uri.svg#hash").getFragment(), "hash");
+ ASSERT_STREQ(URI("?a=b&c=d#hash").getFragment(), "hash");
+ ASSERT_STREQ(URI("urn:isbn:096139210x#hash").getFragment(), "hash");
+}
+
+TEST(UriTest, getOpaque)
+{
+ ASSERT_STREQ(URI().getOpaque(), nullptr);
+ ASSERT_STREQ(URI("urn:isbn:096139210x#hash").getOpaque(), "isbn:096139210x");
+ ASSERT_STREQ(URI("data:,foo").getOpaque(), ",foo");
+}
+
+TEST(UriTest, from_native_filename)
+{
+#ifdef _WIN32
+ ASSERT_EQ(URI::from_native_filename(win_filename_local).str(), win_url_local);
+#else
+ ASSERT_EQ(URI::from_native_filename("/tmp/uri.svg").str(), "file:///tmp/uri.svg");
+ ASSERT_EQ(URI::from_native_filename("/tmp/x y.svg").str(), "file:///tmp/x%20y.svg");
+#endif
+}
+
+TEST(UriTest, uri_to_iri)
+{
+ // unescape UTF-8 (U+00D6)
+ ASSERT_EQ(Inkscape::uri_to_iri("data:,umlaut-%C3%96"), "data:,umlaut-\xC3\x96");
+ // don't unescape ASCII (U+003A)
+ ASSERT_EQ(Inkscape::uri_to_iri("foo%3Abar"), "foo%3Abar");
+ // sequence (U+00D6 U+1F37A U+003A)
+ ASSERT_EQ(Inkscape::uri_to_iri("%C3%96%F0%9F%8D%BA%3A"), "\xC3\x96\xF0\x9F\x8D\xBA%3A");
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :