summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2019-01-14 10:20:37 +0100
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2019-01-21 14:13:01 +0000
commit888fb0ca5d5e18fe5d8b88ed721544bf204ac158 (patch)
tree694ce06751637d2a15dbfe4d3a8260c0e6938769 /tests
parent7d089843c32296ba1d48713388c809996149138d (diff)
Initial commit for Qt Lottie!
This implements a QML API to display graphics and animations exported by the Bodymovin plugin in AfterEffects. Done-by: Kari Hautamäki <kari.hautamaki@qt.io> Change-Id: Idbcfc5c947e97c1a5a70947a262fe1be17de7504 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/auto.pro2
-rw-r--r--tests/auto/bodymovin/bodymovin.pro2
-rw-r--r--tests/auto/bodymovin/shape/ellipse/ellipse.pro5
-rw-r--r--tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json1
-rw-r--r--tests/auto/bodymovin/shape/ellipse/ellipse_direction.json1
-rw-r--r--tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json1
-rw-r--r--tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp315
-rw-r--r--tests/auto/bodymovin/shape/fill/.gitignore73
-rw-r--r--tests/auto/bodymovin/shape/fill/fill.pro5
-rw-r--r--tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json1
-rw-r--r--tests/auto/bodymovin/shape/fill/fill_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/fill/fill_static_red_100.json1
-rw-r--r--tests/auto/bodymovin/shape/fill/tst_bmfill.cpp236
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_curve_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_curve_static.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_direction.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_roto_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_roto_static.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_triangle_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/path/freeform_triangle_static.json1
-rw-r--r--tests/auto/bodymovin/shape/path/path.pro5
-rw-r--r--tests/auto/bodymovin/shape/path/tst_bmpath.cpp1164
-rw-r--r--tests/auto/bodymovin/shape/rect/rect.pro5
-rw-r--r--tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json1
-rw-r--r--tests/auto/bodymovin/shape/rect/rect_direction.json1
-rw-r--r--tests/auto/bodymovin/shape/rect/rect_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json1
-rw-r--r--tests/auto/bodymovin/shape/rect/tst_bmrect.cpp349
-rw-r--r--tests/auto/bodymovin/shape/repeater/repeater.pro5
-rw-r--r--tests/auto/bodymovin/shape/repeater/repeater_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/repeater/repeater_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/repeater/repeater_static.json1
-rw-r--r--tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp231
-rw-r--r--tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json1
-rw-r--r--tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro6
-rw-r--r--tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp461
-rw-r--r--tests/auto/bodymovin/shape/shape.pro13
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json1
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json1
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json1
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json1
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json1
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/shapelayer.pro5
-rw-r--r--tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp197
-rw-r--r--tests/auto/bodymovin/shape/shapetransform/shapetransform.pro5
-rw-r--r--tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json1
-rw-r--r--tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp508
-rw-r--r--tests/auto/bodymovin/shape/stroke/stroke.pro5
-rw-r--r--tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json213
-rw-r--r--tests/auto/bodymovin/shape/stroke/stroke_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json1
-rw-r--r--tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp349
-rw-r--r--tests/auto/bodymovin/shape/transform/transform.pro6
-rw-r--r--tests/auto/bodymovin/shape/transform/transform_animated.json1
-rw-r--r--tests/auto/bodymovin/shape/transform/transform_static.json1
-rw-r--r--tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp413
-rw-r--r--tests/auto/bodymovin/shape/trimpath/trimpath.pro5
-rw-r--r--tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json1
-rw-r--r--tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json1
-rw-r--r--tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json1
-rw-r--r--tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp311
-rw-r--r--tests/auto/imports/imports.pro30
-rw-r--r--tests/auto/imports/main.cpp3
-rw-r--r--tests/auto/imports/qml.qrc6
-rw-r--r--tests/auto/imports/rec_pos_col_opa.json1
-rw-r--r--tests/auto/imports/tst_main.qml88
-rw-r--r--tests/manual/featcheck/.gitignore73
-rw-r--r--tests/manual/featcheck/featcheck.pro22
-rw-r--r--tests/manual/featcheck/howtofind.txt7
-rw-r--r--tests/manual/featcheck/main.cpp82
-rw-r--r--tests/manual/html/content.json1
-rw-r--r--tests/manual/html/index.html47
-rw-r--r--tests/manual/manual.pro2
-rw-r--r--tests/manual/testApp/.gitignore73
-rw-r--r--tests/manual/testApp/easing_rect_translate.json1
-rw-r--r--tests/manual/testApp/gradient_fill.json1
-rw-r--r--tests/manual/testApp/layer_effect_fill.json1
-rw-r--r--tests/manual/testApp/layer_effect_slider.json1
-rw-r--r--tests/manual/testApp/linked_layers.json1
-rw-r--r--tests/manual/testApp/main.cpp50
-rw-r--r--tests/manual/testApp/main.qml132
-rw-r--r--tests/manual/testApp/path_and_ellipse.json1
-rw-r--r--tests/manual/testApp/qml.qrc45
-rw-r--r--tests/manual/testApp/rect_anchor_moved_static2.json1
-rw-r--r--tests/manual/testApp/rect_anchor_std_anim.json1
-rw-r--r--tests/manual/testApp/rect_anim_easing.json2
-rw-r--r--tests/manual/testApp/rect_gfill_linear.json1
-rw-r--r--tests/manual/testApp/rect_gfill_radial.json1
-rw-r--r--tests/manual/testApp/rect_move_bezier.json1
-rw-r--r--tests/manual/testApp/rect_negative_scale_anchors.json1
-rw-r--r--tests/manual/testApp/rect_negative_scale_pos.json1
-rw-r--r--tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json2
-rw-r--r--tests/manual/testApp/rect_rotate.json2
-rw-r--r--tests/manual/testApp/rect_rotate_layer.json1
-rw-r--r--tests/manual/testApp/rect_rotate_layer2.json1
-rw-r--r--tests/manual/testApp/rect_rotate_shape.json1
-rw-r--r--tests/manual/testApp/rect_scale.json1
-rw-r--r--tests/manual/testApp/rect_scale_anchors_on_layer.json1
-rw-r--r--tests/manual/testApp/rect_scale_anchors_on_rect.json1
-rw-r--r--tests/manual/testApp/rect_skew.json1
-rw-r--r--tests/manual/testApp/rect_skew_axis.json1
-rw-r--r--tests/manual/testApp/repeater1.json1
-rw-r--r--tests/manual/testApp/repeater_group1.json1
-rw-r--r--tests/manual/testApp/repeater_group2.json1
-rw-r--r--tests/manual/testApp/shape_bezier_simple.json1
-rw-r--r--tests/manual/testApp/shape_circle.json1
-rw-r--r--tests/manual/testApp/shape_complex.json1
-rw-r--r--tests/manual/testApp/shape_complex2.json1
-rw-r--r--tests/manual/testApp/shape_static.json1
-rw-r--r--tests/manual/testApp/stroke.json1
-rw-r--r--tests/manual/testApp/testApp.pro34
-rw-r--r--tests/manual/testApp/trim_path1.json1
-rw-r--r--tests/manual/testApp/trim_path2.json1
-rw-r--r--tests/manual/testApp/trim_path2_individual.json1
-rw-r--r--tests/manual/testApp/trim_path2_offset.json1
-rw-r--r--tests/manual/testApp/trim_path_multiple1.json1
-rw-r--r--tests/manual/testApp/trim_path_multiple2.json1
-rw-r--r--tests/manual/testApp/trim_path_multiple3.json1
-rw-r--r--tests/manual/testApp/trim_path_multiple4.json1
-rw-r--r--tests/manual/testApp/trim_path_simultaneous2.json1
-rw-r--r--tests/tests.pro4
125 files changed, 5677 insertions, 0 deletions
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
new file mode 100644
index 0000000..da39e21
--- /dev/null
+++ b/tests/auto/auto.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += bodymovin
diff --git a/tests/auto/bodymovin/bodymovin.pro b/tests/auto/bodymovin/bodymovin.pro
new file mode 100644
index 0000000..9af152d
--- /dev/null
+++ b/tests/auto/bodymovin/bodymovin.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += shape
diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse.pro b/tests/auto/bodymovin/shape/ellipse/ellipse.pro
new file mode 100644
index 0000000..fab1bd8
--- /dev/null
+++ b/tests/auto/bodymovin/shape/ellipse/ellipse.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmellipse.cpp
diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json b/tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json
new file mode 100644
index 0000000..3883853
--- /dev/null
+++ b/tests/auto/bodymovin/shape/ellipse/ellipse_animated_100x80at00to200x40at50100.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"AnimatedRoundShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[100,80],"e":[200,40]},{"t":179}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[50,100],"to":[8.33333301544189,16.6666660308838],"ti":[-8.33333301544189,-16.6666660308838]},{"t":179}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_direction.json b/tests/auto/bodymovin/shape/ellipse/ellipse_direction.json
new file mode 100644
index 0000000..48faacc
--- /dev/null
+++ b/tests/auto/bodymovin/shape/ellipse/ellipse_direction.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticEllipseShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":3,"ty":"el","s":{"a":0,"k":[100,80],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json b/tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json
new file mode 100644
index 0000000..9463cb6
--- /dev/null
+++ b/tests/auto/bodymovin/shape/ellipse/ellipse_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticEllipseShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,80],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json b/tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json
new file mode 100644
index 0000000..9158d27
--- /dev/null
+++ b/tests/auto/bodymovin/shape/ellipse/ellipse_static_100x80.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticRoundShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,80],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp b/tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp
new file mode 100644
index 0000000..c8dd644
--- /dev/null
+++ b/tests/auto/bodymovin/shape/ellipse/tst_bmellipse.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmellipse_p.h"
+
+class tst_BMEllipse: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMEllipse();
+ ~tst_BMEllipse();
+
+private:
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialX();
+ void testStaticInitialY();
+ void testStaticInitialWidth();
+ void testStaticInitialHeight();
+ void testStaticUpdatedX();
+ void testStaticUpdatedY();
+ void testStaticUpdatedWidth();
+ void testStaticUpdatedHeight();
+
+ void testAnimatedInitialX();
+ void testAnimatedInitialY();
+ void testAnimatedInitialWidth();
+ void testAnimatedInitialHeight();
+ void testAnimatedUpdatedX();
+ void testAnimatedUpdatedY();
+ void testAnimatedUpdatedWidth();
+ void testAnimatedUpdatedHeight();
+
+ void testName();
+ void testType();
+ void testActive();
+ void testHidden();
+ void testDirection();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMEllipse *m_ellipse = nullptr;
+};
+
+tst_BMEllipse::tst_BMEllipse()
+{
+
+}
+
+tst_BMEllipse::~tst_BMEllipse()
+{
+
+}
+
+void tst_BMEllipse::initTestCase()
+{
+}
+
+void tst_BMEllipse::cleanupTestCase()
+{
+ if (m_ellipse)
+ delete m_ellipse;
+}
+
+void tst_BMEllipse::testStaticInitialX()
+{
+ loadTestData("ellipse_static_100x80.json");
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 0.0));
+}
+
+void tst_BMEllipse:: testStaticInitialY()
+{
+ loadTestData("ellipse_static_100x80.json");
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 0.0));
+}
+
+void tst_BMEllipse:: testStaticInitialWidth()
+{
+ loadTestData("ellipse_static_100x80.json");
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 100));
+}
+
+void tst_BMEllipse:: testStaticInitialHeight()
+{
+ loadTestData("ellipse_static_100x80.json");
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 80));
+}
+
+void tst_BMEllipse::testStaticUpdatedX()
+{
+ loadTestData("ellipse_static_100x80.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 0.0));
+}
+
+void tst_BMEllipse:: testStaticUpdatedY()
+{
+ loadTestData("ellipse_static_100x80.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 0.0));
+}
+
+void tst_BMEllipse:: testStaticUpdatedWidth()
+{
+ loadTestData("ellipse_static_100x80.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 100));
+}
+
+void tst_BMEllipse:: testStaticUpdatedHeight()
+{
+ loadTestData("ellipse_static_100x80.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 80));
+}
+
+void tst_BMEllipse::testAnimatedInitialX()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 0.0));
+}
+
+void tst_BMEllipse::testAnimatedInitialY()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 0.0));
+}
+
+void tst_BMEllipse::testAnimatedInitialWidth()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 100.0));
+}
+
+void tst_BMEllipse::testAnimatedInitialHeight()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 80.0));
+}
+
+void tst_BMEllipse::testAnimatedUpdatedX()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().x(), 50.0));
+}
+
+void tst_BMEllipse::testAnimatedUpdatedY()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->position().y(), 100.0));
+}
+
+void tst_BMEllipse::testAnimatedUpdatedWidth()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().width(), 200.0));
+}
+
+void tst_BMEllipse::testAnimatedUpdatedHeight()
+{
+ loadTestData("ellipse_animated_100x80at00to200x40at50100.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_ellipse->size().height(), 40.0));
+}
+
+void tst_BMEllipse::testName()
+{
+ loadTestData("ellipse_static_100x80.json");
+ QVERIFY(m_ellipse->name() == QString("Ellipse Path 1"));
+}
+
+void tst_BMEllipse::testType()
+{
+ loadTestData("ellipse_static_100x80.json");
+ QVERIFY(m_ellipse->type() == BM_SHAPE_ELLIPSE_IX);
+}
+
+void tst_BMEllipse::testActive()
+{
+ loadTestData("ellipse_static_100x80.json");
+ QVERIFY(m_ellipse->active(100) == true);
+
+ loadTestData("ellipse_hidden.json");
+ QVERIFY(m_ellipse->active(100) == false);
+}
+
+void tst_BMEllipse::testHidden()
+{
+ loadTestData("ellipse_hidden.json");
+ QVERIFY(m_ellipse->hidden() == true);
+}
+
+void tst_BMEllipse::testDirection()
+{
+ loadTestData("ellipse_hidden.json");
+ QVERIFY(m_ellipse->direction() == 0);
+ loadTestData("ellipse_direction.json");
+ QVERIFY(m_ellipse->direction() == 0);
+}
+
+void tst_BMEllipse::loadTestData(const QByteArray &filename)
+{
+ if (m_ellipse) {
+ delete m_ellipse;
+ m_ellipse = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_ELLIPSE_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_ellipse = static_cast<BMEllipse*>(shape);
+
+ QVERIFY(m_ellipse != nullptr);
+}
+
+void tst_BMEllipse::updateProperty(int frame)
+{
+ m_ellipse->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMEllipse)
+#include "tst_bmellipse.moc"
diff --git a/tests/auto/bodymovin/shape/fill/.gitignore b/tests/auto/bodymovin/shape/fill/.gitignore
new file mode 100644
index 0000000..fab7372
--- /dev/null
+++ b/tests/auto/bodymovin/shape/fill/.gitignore
@@ -0,0 +1,73 @@
+# This file is used to ignore files which are generated
+# ----------------------------------------------------------------------------
+
+*~
+*.autosave
+*.a
+*.core
+*.moc
+*.o
+*.obj
+*.orig
+*.rej
+*.so
+*.so.*
+*_pch.h.cpp
+*_resource.rc
+*.qm
+.#*
+*.*#
+core
+!core/
+tags
+.DS_Store
+.directory
+*.debug
+Makefile*
+*.prl
+*.app
+moc_*.cpp
+ui_*.h
+qrc_*.cpp
+Thumbs.db
+*.res
+*.rc
+/.qmake.cache
+/.qmake.stash
+
+# qtcreator generated files
+*.pro.user*
+
+# xemacs temporary files
+*.flc
+
+# Vim temporary files
+.*.swp
+
+# Visual Studio generated files
+*.ib_pdb_index
+*.idb
+*.ilk
+*.pdb
+*.sln
+*.suo
+*.vcproj
+*vcproj.*.*.user
+*.ncb
+*.sdf
+*.opensdf
+*.vcxproj
+*vcxproj.*
+
+# MinGW generated files
+*.Debug
+*.Release
+
+# Python byte code
+*.pyc
+
+# Binaries
+# --------
+*.dll
+*.exe
+
diff --git a/tests/auto/bodymovin/shape/fill/fill.pro b/tests/auto/bodymovin/shape/fill/fill.pro
new file mode 100644
index 0000000..f026bb3
--- /dev/null
+++ b/tests/auto/bodymovin/shape/fill/fill.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmfill.cpp
diff --git a/tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json b/tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json
new file mode 100644
index 0000000..0175b95
--- /dev/null
+++ b/tests/auto/bodymovin/shape/fill/fill_animated_red100_green0.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"AnimatedFillShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[1,0,0,1],"e":[0,1,0,1]},{"t":179}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[0]},{"t":179}],"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/fill/fill_hidden.json b/tests/auto/bodymovin/shape/fill/fill_hidden.json
new file mode 100644
index 0000000..ab7c5da
--- /dev/null
+++ b/tests/auto/bodymovin/shape/fill/fill_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticFillShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/fill/fill_static_red_100.json b/tests/auto/bodymovin/shape/fill/fill_static_red_100.json
new file mode 100644
index 0000000..2583b5e
--- /dev/null
+++ b/tests/auto/bodymovin/shape/fill/fill_static_red_100.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":320,"h":320,"nm":"StaticFillShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[160,160,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/fill/tst_bmfill.cpp b/tests/auto/bodymovin/shape/fill/tst_bmfill.cpp
new file mode 100644
index 0000000..4ea557e
--- /dev/null
+++ b/tests/auto/bodymovin/shape/fill/tst_bmfill.cpp
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmfill_p.h"
+
+class tst_BMFill: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMFill();
+ ~tst_BMFill();
+
+private:
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialColor();
+ void testStaticeInitialOpacity();
+ void testStaticUpdatedColor();
+ void testStaticeUpdatedOpacity();
+
+ void testAnimatedInitialColor();
+ void testAnimatedInitialOpacity();
+ void testAnimatedUpdatedColor();
+ void testAnimatedUpdatedOpacity();
+
+ void testName();
+ void testType();
+ void testActive();
+ void testHidden();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMFill *m_fill = nullptr;
+};
+
+tst_BMFill::tst_BMFill()
+{
+
+}
+
+tst_BMFill::~tst_BMFill()
+{
+
+}
+
+void tst_BMFill::initTestCase()
+{
+}
+
+void tst_BMFill::cleanupTestCase()
+{
+ if (m_fill)
+ delete m_fill;
+}
+
+void tst_BMFill::testStaticInitialColor()
+{
+ loadTestData("fill_static_red_100.json");
+
+ QVERIFY(m_fill->color() == QColor(Qt::red));
+}
+
+void tst_BMFill:: testStaticeInitialOpacity()
+{
+ loadTestData("fill_static_red_100.json");
+
+ QVERIFY(qFuzzyCompare(m_fill->opacity(), 100.0));
+}
+
+void tst_BMFill::testStaticUpdatedColor()
+{
+ loadTestData("fill_static_red_100.json");
+ updateProperty(179);
+
+ QVERIFY(m_fill->color() == QColor(Qt::red));
+}
+
+void tst_BMFill:: testStaticeUpdatedOpacity()
+{
+ loadTestData("fill_static_red_100.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_fill->opacity(), 100.0));
+}
+
+void tst_BMFill::testAnimatedInitialColor()
+{
+ loadTestData("fill_animated_red100_green0.json");
+ updateProperty(0);
+
+ QVERIFY(m_fill->color() == QColor(Qt::red));
+}
+
+void tst_BMFill::testAnimatedInitialOpacity()
+{
+ loadTestData("fill_animated_red100_green0.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_fill->opacity(), 100.0));
+}
+
+void tst_BMFill::testAnimatedUpdatedColor()
+{
+ loadTestData("fill_animated_red100_green0.json");
+ updateProperty(179);
+
+ QVERIFY(m_fill->color() == QColor(Qt::green));
+}
+
+void tst_BMFill::testAnimatedUpdatedOpacity()
+{
+ loadTestData("fill_animated_red100_green0.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_fill->opacity(), 0.0));
+}
+
+void tst_BMFill::testName()
+{
+ loadTestData("fill_static_red_100.json");
+ QVERIFY(m_fill->name() == QString("Fill 1"));
+}
+
+void tst_BMFill::testType()
+{
+ loadTestData("fill_static_red_100.json");
+ QVERIFY(m_fill->type() == BM_SHAPE_FILL_IX);
+}
+
+void tst_BMFill::testActive()
+{
+ loadTestData("fill_static_red_100.json");
+ QVERIFY(m_fill->active(100) == true);
+
+ loadTestData("fill_hidden.json");
+ QVERIFY(m_fill->active(100) == false);
+}
+
+void tst_BMFill::testHidden()
+{
+ loadTestData("fill_hidden.json");
+ QVERIFY(m_fill->hidden() == true);
+}
+
+void tst_BMFill::loadTestData(const QByteArray &filename)
+{
+ if (m_fill) {
+ delete m_fill;
+ m_fill = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_FILL_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_fill = static_cast<BMFill*>(shape);
+
+ QVERIFY(m_fill != nullptr);
+}
+
+void tst_BMFill::updateProperty(int frame)
+{
+ m_fill->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMFill)
+#include "tst_bmfill.moc"
diff --git a/tests/auto/bodymovin/shape/path/freeform_curve_animated.json b/tests/auto/bodymovin/shape/path/freeform_curve_animated.json
new file mode 100644
index 0000000..619ef39
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_curve_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[-17.2,34.503],[5.008,-25.055]],"o":[[0,0],[5.999,-12.033],[-5.149,25.76]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false}],"e":[{"i":[[0,0],[-16.893,-45.111],[-20,-38.07]],"o":[[0,0],[14.999,40.053],[12.217,23.255]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false}]},{"t":179}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_curve_static.json b/tests/auto/bodymovin/shape/path/freeform_curve_static.json
new file mode 100644
index 0000000..d313686
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_curve_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-17.2,34.503],[5.008,-25.055]],"o":[[0,0],[5.999,-12.033],[-5.149,25.76]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_direction.json b/tests/auto/bodymovin/shape/path/freeform_direction.json
new file mode 100644
index 0000000..f0503c2
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_direction.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.149,25.76],[5.999,-12.033],[0,0]],"o":[[5.008,-25.055],[-17.2,34.503],[0,0]],"v":[[85,50.07],[45.001,50.072],[15.001,50.066]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_hidden.json b/tests/auto/bodymovin/shape/path/freeform_hidden.json
new file mode 100644
index 0000000..f11376a
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformCurveShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-17.2,34.503],[5.008,-25.055]],"o":[[0,0],[5.999,-12.033],[-5.149,25.76]],"v":[[15.001,50.066],[45.001,50.072],[85,50.07]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_roto_animated.json b/tests/auto/bodymovin/shape/path/freeform_roto_animated.json
new file mode 100644
index 0000000..0aa14ea
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_roto_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedFreeformRotoShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.031,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[-4.727,-4.705],[-4.718,4.714],[4.709,4.687],[4.727,-4.723]],"o":[[4.727,4.705],[4.701,-4.696],[-4.736,-4.714],[-4.718,4.714]],"v":[[40.008,60],[60.016,59.953],[60.007,40.02],[39.96,39.991]],"c":true}],"e":[{"i":[[-3.342,-3.311],[-4.718,4.714],[4.72,4.676],[3.338,-3.335]],"o":[[4.738,4.694],[4.701,-4.696],[-3.352,-3.321],[-3.328,3.325]],"v":[[40.008,60],[60.016,59.953],[60.007,40.02],[49.96,49.991]],"c":true}]},{"t":179}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_roto_static.json b/tests/auto/bodymovin/shape/path/freeform_roto_static.json
new file mode 100644
index 0000000..94d394a
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_roto_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformRotoShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.031,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.727,-4.705],[-4.718,4.714],[4.709,4.687],[4.727,-4.723]],"o":[[4.727,4.705],[4.701,-4.696],[-4.736,-4.714],[-4.718,4.714]],"v":[[40.008,60],[60.016,59.953],[60.007,40.02],[39.96,39.991]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_triangle_animated.json b/tests/auto/bodymovin/shape/path/freeform_triangle_animated.json
new file mode 100644
index 0000000..595eaf6
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_triangle_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedFreeformTriangleShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.984,30.024],[34.996,50.013],[64.995,49.999]],"c":true}],"e":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.734,79.274],[34.996,59.951],[64.995,60.124]],"c":true}]},{"t":179}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/freeform_triangle_static.json b/tests/auto/bodymovin/shape/path/freeform_triangle_static.json
new file mode 100644
index 0000000..c35c27c
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/freeform_triangle_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticFreeformTriangleShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.984,30.024],[34.996,50.013],[64.995,49.999]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/path/path.pro b/tests/auto/bodymovin/shape/path/path.pro
new file mode 100644
index 0000000..1ce75c6
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/path.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmpath.cpp
diff --git a/tests/auto/bodymovin/shape/path/tst_bmpath.cpp b/tests/auto/bodymovin/shape/path/tst_bmpath.cpp
new file mode 100644
index 0000000..47f6e7c
--- /dev/null
+++ b/tests/auto/bodymovin/shape/path/tst_bmpath.cpp
@@ -0,0 +1,1164 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmfreeformshape_p.h"
+
+class tst_BMPath: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMPath();
+ ~tst_BMPath();
+
+private:
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticCurveInitialStartPoint();
+ void testStaticCurveInitialSecondPoint();
+ void testStaticCurveInitialThirdPoint();
+ void testStaticCurveInitialFourthPoint();
+ void testStaticCurveInitialFifthPoint();
+ void testStaticCurveInitialSixthPoint();
+ void testStaticCurveUpdatedStartPoint();
+ void testStaticCurveUpdatedSecondPoint();
+ void testStaticCurveUpdatedThirdPoint();
+ void testStaticCurveUpdatedFourthPoint();
+ void testStaticCurveUpdatedFifthPoint();
+ void testStaticCurveUpdatedSixthPoint();
+
+ void testStaticTriangleInitialStartPoint();
+ void testStaticTriangleInitialSecondPoint();
+ void testStaticTriangleInitialThirdPoint();
+ void testStaticTriangleInitialClosed();
+ void testStaticTriangleUpdatedStartPoint();
+ void testStaticTriangleUpdatedSecondPoint();
+ void testStaticTriangleUpdatedThirdPoint();
+ void testStaticTriangleUpdatedClosed();
+
+ void testStaticRotoInitialStartPoint();
+ void testStaticRotoInitialSecondPoint();
+ void testStaticRotoInitialThirdPoint();
+ void testStaticRotoInitialFourthPoint();
+ void testStaticRotoInitialFifthPoint();
+ void testStaticRotoInitialSixthPoint();
+ void testStaticRotoInitialSeventhPoint();
+ void testStaticRotoInitialEighthPoint();
+ void testStaticRotoInitialNinthPoint();
+ void testStaticRotoInitialTenthPoint();
+ void testStaticRotoInitialEleventhPoint();
+ void testStaticRotoInitialTwelvthPoint();
+ void testStaticRotoInitialClosed();
+ void testStaticRotoUpdatedStartPoint();
+ void testStaticRotoUpdatedSecondPoint();
+ void testStaticRotoUpdatedThirdPoint();
+ void testStaticRotoUpdatedFourthPoint();
+ void testStaticRotoUpdatedFifthPoint();
+ void testStaticRotoUpdatedSixthPoint();
+ void testStaticRotoUpdatedSeventhPoint();
+ void testStaticRotoUpdatedEighthPoint();
+ void testStaticRotoUpdatedNinthPoint();
+ void testStaticRotoUpdatedTenthPoint();
+ void testStaticRotoUpdatedEleventhPoint();
+ void testStaticRotoUpdatedTwelvthPoint();
+ void testStaticRotoUpdatedClosed();
+
+ void testAnimatedCurveInitialStartPoint();
+ void testAnimatedCurveInitialSecondPoint();
+ void testAnimatedCurveInitialThirdPoint();
+ void testAnimatedCurveInitialFourthPoint();
+ void testAnimatedCurveInitialFifthPoint();
+ void testAnimatedCurveInitialSixthPoint();
+ void testAnimatedCurveUpdatedStartPoint();
+ void testAnimatedCurveUpdatedSecondPoint();
+ void testAnimatedCurveUpdatedThirdPoint();
+ void testAnimatedCurveUpdatedFourthPoint();
+ void testAnimatedCurveUpdatedFifthPoint();
+ void testAnimatedCurveUpdatedSixthPoint();
+
+ void testAnimatedTriangleInitialStartPoint();
+ void testAnimatedTriangleInitialSecondPoint();
+ void testAnimatedTriangleInitialThirdPoint();
+ void testAnimatedTriangleInitialClosed();
+ void testAnimatedTriangleUpdatedStartPoint();
+ void testAnimatedTriangleUpdatedSecondPoint();
+ void testAnimatedTriangleUpdatedThirdPoint();
+ void testAnimatedTriangleUpdatedClosed();
+
+ void testAnimatedRotoInitialStartPoint();
+ void testAnimatedRotoInitialSecondPoint();
+ void testAnimatedRotoInitialThirdPoint();
+ void testAnimatedRotoInitialFourthPoint();
+ void testAnimatedRotoInitialFifthPoint();
+ void testAnimatedRotoInitialSixthPoint();
+ void testAnimatedRotoInitialSeventhPoint();
+ void testAnimatedRotoInitialEighthPoint();
+ void testAnimatedRotoInitialNinthPoint();
+ void testAnimatedRotoInitialTenthPoint();
+ void testAnimatedRotoInitialEleventhPoint();
+ void testAnimatedRotoInitialTwelvthPoint();
+ void testAnimatedRotoInitialClosed();
+ void testAnimatedRotoUpdatedStartPoint();
+ void testAnimatedRotoUpdatedSecondPoint();
+ void testAnimatedRotoUpdatedThirdPoint();
+ void testAnimatedRotoUpdatedFourthPoint();
+ void testAnimatedRotoUpdatedFifthPoint();
+ void testAnimatedRotoUpdatedSixthPoint();
+ void testAnimatedRotoUpdatedSeventhPoint();
+ void testAnimatedRotoUpdatedEighthPoint();
+ void testAnimatedRotoUpdatedNinthPoint();
+ void testAnimatedRotoUpdatedTenthPoint();
+ void testAnimatedRotoUpdatedEleventhPoint();
+ void testAnimatedRotoUpdatedTwelvthPoint();
+ void testAnimatedRotoUpdatedClosed();
+
+ void testName();
+ void testType();
+ void testActive();
+ void testHidden();
+ void testDirection();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMFreeFormShape *m_path = nullptr;
+};
+
+tst_BMPath::tst_BMPath()
+{
+
+}
+
+tst_BMPath::~tst_BMPath()
+{
+
+}
+
+void tst_BMPath::initTestCase()
+{
+}
+
+void tst_BMPath::cleanupTestCase()
+{
+ if (m_path)
+ delete m_path;
+}
+
+void tst_BMPath::testStaticCurveInitialStartPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 15);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticCurveInitialSecondPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 28);
+ QVERIFY(qRound(el.y) == 85);
+}
+
+void tst_BMPath::testStaticCurveInitialThirdPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticCurveInitialFourthPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 51);
+ QVERIFY(qRound(el.y) == 38);
+}
+
+void tst_BMPath::testStaticCurveInitialFifthPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 90);
+ QVERIFY(qRound(el.y) == 25);
+}
+
+void tst_BMPath::testStaticCurveInitialSixthPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 85);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticCurveUpdatedStartPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 15);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticCurveUpdatedSecondPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 28);
+ QVERIFY(qRound(el.y) == 85);
+}
+
+void tst_BMPath::testStaticCurveUpdatedThirdPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticCurveUpdatedFourthPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 51);
+ QVERIFY(qRound(el.y) == 38);
+}
+
+void tst_BMPath::testStaticCurveUpdatedFifthPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 90);
+ QVERIFY(qRound(el.y) == 25);
+}
+
+void tst_BMPath::testStaticCurveUpdatedSixthPoint()
+{
+ loadTestData("freeform_curve_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 85);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticTriangleInitialStartPoint()
+{
+ loadTestData("freeform_triangle_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 50);
+ QVERIFY(qRound(el.y) == 30);
+}
+
+void tst_BMPath::testStaticTriangleInitialSecondPoint()
+{
+ loadTestData("freeform_triangle_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticTriangleInitialThirdPoint()
+{
+ loadTestData("freeform_triangle_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticTriangleInitialClosed()
+{
+ loadTestData("freeform_triangle_static.json");
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testStaticTriangleUpdatedStartPoint()
+{
+ loadTestData("freeform_triangle_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 50);
+ QVERIFY(qRound(el.y) == 30);
+}
+
+void tst_BMPath::testStaticTriangleUpdatedSecondPoint()
+{
+ loadTestData("freeform_triangle_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticTriangleUpdatedThirdPoint()
+{
+ loadTestData("freeform_triangle_static.json");
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testStaticTriangleUpdatedClosed()
+{
+ loadTestData("freeform_triangle_static.json");
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testStaticRotoInitialStartPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testStaticRotoInitialSecondPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(1);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testStaticRotoInitialThirdPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testStaticRotoInitialFourthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testStaticRotoInitialFifthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testStaticRotoInitialSixthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testStaticRotoInitialSeventhPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testStaticRotoInitialEighthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(7);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 35);
+}
+
+void tst_BMPath::testStaticRotoInitialNinthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(8);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 35);
+}
+
+void tst_BMPath::testStaticRotoInitialTenthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(9);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testStaticRotoInitialEleventhPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(10);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testStaticRotoInitialTwelvthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el = m_path->path().elementAt(11);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testStaticRotoInitialClosed()
+{
+ loadTestData("freeform_roto_static.json");
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testStaticRotoUpdatedStartPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testStaticRotoUpdatedSecondPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(1);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testStaticRotoUpdatedThirdPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testStaticRotoUpdatedFourthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testStaticRotoUpdatedFifthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testStaticRotoUpdatedSixthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testStaticRotoUpdatedSeventhPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testStaticRotoUpdatedEighthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(7);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 35);
+}
+
+void tst_BMPath::testStaticRotoUpdatedNinthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(8);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 35);
+}
+
+void tst_BMPath::testStaticRotoUpdatedTenthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(9);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testStaticRotoUpdatedEleventhPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(10);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testStaticRotoUpdatedTwelvthPoint()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(11);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testStaticRotoUpdatedClosed()
+{
+ loadTestData("freeform_roto_static.json");
+ updateProperty(179);
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testAnimatedCurveInitialStartPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 15);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedCurveInitialSecondPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 28);
+ QVERIFY(qRound(el.y) == 85);
+}
+
+void tst_BMPath::testAnimatedCurveInitialThirdPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedCurveInitialFourthPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 51);
+ QVERIFY(qRound(el.y) == 38);
+}
+
+void tst_BMPath::testAnimatedCurveInitialFifthPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 90);
+ QVERIFY(qRound(el.y) == 25);
+}
+
+void tst_BMPath::testAnimatedCurveInitialSixthPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 85);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedCurveUpdatedStartPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 15);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedCurveUpdatedSecondPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 28);
+ QVERIFY(qRound(el.y) == 5);
+}
+
+void tst_BMPath::testAnimatedCurveUpdatedThirdPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedCurveUpdatedFourthPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 90);
+}
+
+void tst_BMPath::testAnimatedCurveUpdatedFifthPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 12);
+}
+
+void tst_BMPath::testAnimatedCurveUpdatedSixthPoint()
+{
+ loadTestData("freeform_curve_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 85);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedTriangleInitialStartPoint()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 50);
+ QVERIFY(qRound(el.y) == 30);
+}
+
+void tst_BMPath::testAnimatedTriangleInitialSecondPoint()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedTriangleInitialThirdPoint()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedTriangleInitialClosed()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testAnimatedTriangleUpdatedStartPoint()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 50);
+ QVERIFY(qRound(el.y) == 79);
+}
+
+void tst_BMPath::testAnimatedTriangleUpdatedSecondPoint()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testAnimatedTriangleUpdatedThirdPoint()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testAnimatedTriangleUpdatedClosed()
+{
+ loadTestData("freeform_triangle_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testAnimatedRotoInitialStartPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testAnimatedRotoInitialSecondPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(1);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testAnimatedRotoInitialThirdPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testAnimatedRotoInitialFourthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testAnimatedRotoInitialFifthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testAnimatedRotoInitialSixthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testAnimatedRotoInitialSeventhPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testAnimatedRotoInitialEighthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(7);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 35);
+}
+
+void tst_BMPath::testAnimatedRotoInitialNinthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(8);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 35);
+}
+
+void tst_BMPath::testAnimatedRotoInitialTenthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(9);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testAnimatedRotoInitialEleventhPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(10);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testAnimatedRotoInitialTwelvthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el = m_path->path().elementAt(11);
+ QVERIFY(qRound(el.x) == 35);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testAnimatedRotoInitialClosed()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(0);
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedStartPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(0);
+ QVERIFY(qRound(el.x) == 40);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedSecondPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(1);
+ QVERIFY(qRound(el.x) == 45);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedThirdPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(2);
+ QVERIFY(qRound(el.x) == 55);
+ QVERIFY(qRound(el.y) == 65);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedFourthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(3);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 60);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedFifthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(4);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 55);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedSixthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(5);
+ QVERIFY(qRound(el.x) == 65);
+ QVERIFY(qRound(el.y) == 45);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedSeventhPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(6);
+ QVERIFY(qRound(el.x) == 60);
+ QVERIFY(qRound(el.y) == 40);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedEighthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(7);
+ QVERIFY(qRound(el.x) == 57);
+ QVERIFY(qRound(el.y) == 37);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedNinthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(8);
+ QVERIFY(qRound(el.x) == 53);
+ QVERIFY(qRound(el.y) == 47);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedTenthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(9);
+ QVERIFY(qRound(el.x) == 50);
+ QVERIFY(qRound(el.y) == 50);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedEleventhPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(10);
+ QVERIFY(qRound(el.x) == 47);
+ QVERIFY(qRound(el.y) == 53);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedTwelvthPoint()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el = m_path->path().elementAt(11);
+ QVERIFY(qRound(el.x) == 37);
+ QVERIFY(qRound(el.y) == 57);
+}
+
+void tst_BMPath::testAnimatedRotoUpdatedClosed()
+{
+ loadTestData("freeform_roto_animated.json");
+ updateProperty(179);
+
+ QPainterPath::Element el1 = m_path->path().elementAt(0);
+ QPainterPath::Element el2 = m_path->path().elementAt(m_path->path().elementCount()-1);
+ QVERIFY(qFuzzyCompare(el1.x, el2.x));
+ QVERIFY(qFuzzyCompare(el1.y, el2.y));
+}
+
+void tst_BMPath::testName()
+{
+ loadTestData("freeform_curve_static.json");
+ QVERIFY(m_path->name() == QString("Path 1"));
+}
+
+void tst_BMPath::testType()
+{
+ loadTestData("freeform_curve_static.json");
+ QVERIFY(m_path->type() == BM_SHAPE_SHAPE_IX);
+}
+
+void tst_BMPath::testActive()
+{
+ loadTestData("freeform_curve_static.json");
+ QVERIFY(m_path->active(100) == true);
+
+ loadTestData("freeform_hidden.json");
+ QVERIFY(m_path->active(100) == false);
+}
+
+void tst_BMPath::testHidden()
+{
+ loadTestData("freeform_hidden.json");
+ QVERIFY(m_path->hidden() == true);
+}
+
+void tst_BMPath::testDirection()
+{
+ loadTestData("freeform_hidden.json");
+ QVERIFY(m_path->direction() == 0);
+ loadTestData("freeform_direction.json");
+ QVERIFY(m_path->direction() == 0);
+}
+
+void tst_BMPath::loadTestData(const QByteArray &filename)
+{
+ if (m_path) {
+ delete m_path;
+ m_path = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_SHAPE_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_path = static_cast<BMFreeFormShape*>(shape);
+
+ QVERIFY(m_path != nullptr);
+}
+
+void tst_BMPath::updateProperty(int frame)
+{
+ m_path->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMPath)
+#include "tst_bmpath.moc"
diff --git a/tests/auto/bodymovin/shape/rect/rect.pro b/tests/auto/bodymovin/shape/rect/rect.pro
new file mode 100644
index 0000000..75a356e
--- /dev/null
+++ b/tests/auto/bodymovin/shape/rect/rect.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmrect.cpp
diff --git a/tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json b/tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json
new file mode 100644
index 0000000..c2fc75e
--- /dev/null
+++ b/tests/auto/bodymovin/shape/rect/rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[30,30],"e":[50,50]},{"t":179}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[15,15],"e":[75,75],"to":[10,10],"ti":[-10,-10]},{"t":179}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"t":179}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/rect/rect_direction.json b/tests/auto/bodymovin/shape/rect/rect_direction.json
new file mode 100644
index 0000000..102834a
--- /dev/null
+++ b/tests/auto/bodymovin/shape/rect/rect_direction.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":3,"s":{"a":0,"k":[30,30],"ix":2},"p":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false}],"ip":60,"op":120,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/rect/rect_hidden.json b/tests/auto/bodymovin/shape/rect/rect_hidden.json
new file mode 100644
index 0000000..902190d
--- /dev/null
+++ b/tests/auto/bodymovin/shape/rect/rect_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"HiddenRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[30,30],"ix":2},"p":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json b/tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json
new file mode 100644
index 0000000..58d82cf
--- /dev/null
+++ b/tests/auto/bodymovin/shape/rect/rect_static_30x30_5050_rad0.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRectShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[30,30],"ix":2},"p":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/rect/tst_bmrect.cpp b/tests/auto/bodymovin/shape/rect/tst_bmrect.cpp
new file mode 100644
index 0000000..abec892
--- /dev/null
+++ b/tests/auto/bodymovin/shape/rect/tst_bmrect.cpp
@@ -0,0 +1,349 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmrect_p.h"
+
+class tst_BMRect: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMRect();
+ ~tst_BMRect();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialX();
+ void testStaticInitialY();
+ void testStaticInitialWidth();
+ void testStaticInitialHeight();
+ void testStaticInitialRoundness();
+ void testStaticUpdatedX();
+ void testStaticUpdatedY();
+ void testStaticUpdatedWidth();
+ void testStaticUpdatedHeight();
+ void testStaticUpdatedRoundness();
+
+ void testAnimatedInitialX();
+ void testAnimatedInitialY();
+ void testAnimatedInitialWidth();
+ void testAnimatedInitialHeight();
+ void testAnimatedInitialRoundness();
+ void testAnimatedUpdatedX();
+ void testAnimatedUpdatedY();
+ void testAnimatedUpdatedWidth();
+ void testAnimatedUpdatedHeight();
+ void testAnimatedUpdatedRoundness();
+
+ void testName();
+ void testType();
+ void testHidden();
+ void testActive();
+ void testDirection();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMRect *m_rect = nullptr;
+};
+
+tst_BMRect::tst_BMRect()
+{
+
+}
+
+tst_BMRect::~tst_BMRect()
+{
+
+}
+
+void tst_BMRect::initTestCase()
+{
+}
+
+void tst_BMRect::cleanupTestCase()
+{
+ if (m_rect)
+ delete m_rect;
+}
+
+void tst_BMRect::testStaticInitialX()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+
+ QVERIFY(qFuzzyCompare(m_rect->position().x(), 50.0));
+}
+
+void tst_BMRect:: testStaticInitialY()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+
+ QVERIFY(qFuzzyCompare(m_rect->position().y(), 50.0));
+}
+
+void tst_BMRect::testStaticInitialWidth()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+
+ QVERIFY(qFuzzyCompare(m_rect->size().width(), 30.0));
+}
+
+void tst_BMRect::testStaticInitialHeight()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+
+ QVERIFY(qFuzzyCompare(m_rect->size().height(), 30.0));
+}
+
+void tst_BMRect:: testStaticInitialRoundness()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+
+ QVERIFY(qFuzzyCompare(m_rect->roundness(), 0.0));
+}
+
+void tst_BMRect::testStaticUpdatedX()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->position().x(), 50.0));
+}
+
+void tst_BMRect:: testStaticUpdatedY()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->position().y(), 50.0));
+}
+
+void tst_BMRect::testStaticUpdatedWidth()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->size().width(), 30.0));
+}
+
+void tst_BMRect::testStaticUpdatedHeight()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->size().height(), 30.0));
+}
+
+void tst_BMRect:: testStaticUpdatedRoundness()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->roundness(), 0.0));
+}
+
+void tst_BMRect::testAnimatedInitialX()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_rect->position().x(), 15.0));
+}
+
+void tst_BMRect:: testAnimatedInitialY()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_rect->position().y(), 15.0));
+}
+
+void tst_BMRect::testAnimatedInitialWidth()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_rect->size().width(), 30.0));
+}
+
+void tst_BMRect::testAnimatedInitialHeight()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_rect->size().height(), 30.0));
+}
+
+void tst_BMRect::testAnimatedInitialRoundness()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_rect->roundness(), 0.0));
+}
+
+void tst_BMRect::testAnimatedUpdatedX()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->position().x(), 75.0));
+}
+
+void tst_BMRect:: testAnimatedUpdatedY()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->position().y(), 75.0));
+}
+
+void tst_BMRect::testAnimatedUpdatedWidth()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->size().width(), 50.0));
+}
+
+void tst_BMRect::testAnimatedUpdatedHeight()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->size().height(), 50.0));
+}
+
+void tst_BMRect::testAnimatedUpdatedRoundness()
+{
+ loadTestData("rect_animated_30x30_1515_rad0_to_50x50_7575_rad25.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_rect->roundness(), 25.0));
+}
+
+void tst_BMRect::testName()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ QVERIFY(m_rect->name() == QString("Rectangle Path 1"));
+}
+
+void tst_BMRect::testType()
+{
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ QVERIFY(m_rect->type() == BM_SHAPE_RECT_IX);
+}
+
+void tst_BMRect::testHidden()
+{
+ loadTestData("rect_hidden.json");
+ QVERIFY(m_rect->hidden() == true);
+}
+
+void tst_BMRect::testActive()
+{
+ loadTestData("rect_hidden.json");
+ QVERIFY(m_rect->active(100) == false);
+
+ loadTestData("rect_static_30x30_5050_rad0.json");
+ QVERIFY(m_rect->active(100) == true);
+}
+
+void tst_BMRect::testDirection()
+{
+ loadTestData("rect_hidden.json");
+ QVERIFY(m_rect->direction() == 0);
+
+ loadTestData("rect_direction.json");
+ QVERIFY(m_rect->direction() == 3);
+}
+
+void tst_BMRect::loadTestData(const QByteArray &filename)
+{
+ if (m_rect) {
+ delete m_rect;
+ m_rect = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_RECT_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_rect = static_cast<BMRect*>(shape);
+
+ QVERIFY(m_rect != nullptr);
+}
+
+void tst_BMRect::updateProperty(int frame)
+{
+ m_rect->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMRect)
+#include "tst_bmrect.moc"
diff --git a/tests/auto/bodymovin/shape/repeater/repeater.pro b/tests/auto/bodymovin/shape/repeater/repeater.pro
new file mode 100644
index 0000000..646838b
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeater/repeater.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmrepeater.cpp
diff --git a/tests/auto/bodymovin/shape/repeater/repeater_animated.json b/tests/auto/bodymovin/shape/repeater/repeater_animated.json
new file mode 100644
index 0000000..70a6378
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeater/repeater_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedRepeaterShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[3],"e":[30]},{"t":179}],"ix":1},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[15]},{"t":179}],"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-20,"op":184,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/repeater/repeater_hidden.json b/tests/auto/bodymovin/shape/repeater/repeater_hidden.json
new file mode 100644
index 0000000..c5ee464
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeater/repeater_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRepeaterShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":true}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/repeater/repeater_static.json b/tests/auto/bodymovin/shape/repeater/repeater_static.json
new file mode 100644
index 0000000..a7f5067
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeater/repeater_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRepeaterShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp b/tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp
new file mode 100644
index 0000000..bbb49cb
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeater/tst_bmrepeater.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmrepeater_p.h"
+
+class tst_BMRepeater : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMRepeater();
+ ~tst_BMRepeater();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialCopy();
+ void testStaticInitialOffset();
+ void testStaticUpdatedCopy();
+ void testStaticUpdatedOffset();
+
+ void testAnimatedInitialCopy();
+ void testAnimatedInitialOffset();
+ void testAnimatedUpdatedCopy();
+ void testAnimatedUpdatedOffset();
+
+ void testName();
+ void testType();
+ void testHidden();
+ void testActive();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMRepeater *m_repeater = nullptr;
+
+};
+
+tst_BMRepeater::tst_BMRepeater()
+{
+
+}
+
+tst_BMRepeater::~tst_BMRepeater()
+{
+
+}
+
+void tst_BMRepeater::initTestCase()
+{
+
+}
+
+void tst_BMRepeater::cleanupTestCase()
+{
+
+}
+
+void tst_BMRepeater::testStaticInitialCopy()
+{
+ loadTestData("repeater_static.json");
+ QVERIFY(m_repeater->copies() == 3);
+}
+
+void tst_BMRepeater::testStaticInitialOffset()
+{
+ loadTestData("repeater_static.json");
+ QVERIFY(qFuzzyCompare(m_repeater->offset(), 0.0));
+}
+
+void tst_BMRepeater::testStaticUpdatedCopy()
+{
+ loadTestData("repeater_static.json");
+ updateProperty(180);
+ QVERIFY(m_repeater->copies() == 3);
+}
+
+void tst_BMRepeater::testStaticUpdatedOffset()
+{
+ loadTestData("repeater_static.json");
+ updateProperty(180);
+ QVERIFY(qFuzzyCompare(m_repeater->offset(), 0.0));
+}
+
+void tst_BMRepeater::testAnimatedInitialCopy()
+{
+ loadTestData("repeater_animated.json");
+ updateProperty(0);
+ QVERIFY(m_repeater->copies() == 3);
+}
+
+void tst_BMRepeater::testAnimatedInitialOffset()
+{
+ loadTestData("repeater_animated.json");
+ updateProperty(0);
+ QVERIFY(qFuzzyCompare(m_repeater->offset(), 0.0));
+}
+
+void tst_BMRepeater::testAnimatedUpdatedCopy()
+{
+ loadTestData("repeater_animated.json");
+ updateProperty(180);
+ QVERIFY(m_repeater->copies() == 30);
+}
+
+void tst_BMRepeater::testAnimatedUpdatedOffset()
+{
+ loadTestData("repeater_animated.json");
+ updateProperty(180);
+ QVERIFY(qFuzzyCompare(m_repeater->offset(), 15.0));
+}
+
+void tst_BMRepeater::testName()
+{
+ loadTestData("repeater_static.json");
+ QVERIFY(m_repeater->name() == QString("Repeater 1"));
+}
+
+void tst_BMRepeater::testType()
+{
+ loadTestData("repeater_static.json");
+ QVERIFY(m_repeater->type() == BM_SHAPE_REPEATER_IX);
+}
+
+void tst_BMRepeater::testActive()
+{
+ loadTestData("repeater_static.json");
+ QVERIFY(m_repeater->active(100) == true);
+
+ loadTestData("repeater_hidden.json");
+ QVERIFY(m_repeater->active(100) == false);
+}
+
+void tst_BMRepeater::testHidden()
+{
+ loadTestData("repeater_static.json");
+ QVERIFY(m_repeater->hidden() == false);
+
+ loadTestData("repeater_hidden.json");
+ QVERIFY(m_repeater->hidden() == true);
+}
+
+
+void tst_BMRepeater::loadTestData(const QByteArray &filename)
+{
+ if (m_repeater) {
+ delete m_repeater;
+ m_repeater = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_REPEATER_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_repeater = static_cast<BMRepeater*>(shape);
+
+ QVERIFY(m_repeater != nullptr);
+}
+
+void tst_BMRepeater::updateProperty(int frame)
+{
+ m_repeater->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMRepeater)
+#include "tst_bmrepeater.moc"
diff --git a/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json
new file mode 100644
index 0000000..ddaf233
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedRepeaterTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[75,75],"to":[4.16666650772095,4.16666650772095],"ti":[-4.16666650772095,-4.16666650772095]},{"t":179}],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[25,25],"to":[-4.16666650772095,-4.16666650772095],"ti":[4.16666650772095,4.16666650772095]},{"t":179}],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50],"e":[100,100]},{"t":179}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":4},"so":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[25]},{"t":179}],"ix":5},"eo":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[75]},{"t":179}],"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json
new file mode 100644
index 0000000..87d9206
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeatertransform/repeater_transform_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticRepeaterTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":1,"tr":{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[50,50],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":-50,"op":184,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro b/tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro
new file mode 100644
index 0000000..5306888
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeatertransform/repeatertransform.pro
@@ -0,0 +1,6 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmrepeatertransform.cpp
+
diff --git a/tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp b/tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp
new file mode 100644
index 0000000..fb0ef52
--- /dev/null
+++ b/tests/auto/bodymovin/shape/repeatertransform/tst_bmrepeatertransform.cpp
@@ -0,0 +1,461 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmrepeater_p.h"
+#include "private/bmrepeatertransform_p.h"
+
+class tst_BMRepeaterTransform: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMRepeaterTransform();
+ ~tst_BMRepeaterTransform();
+
+private:
+
+ // void testParseStaticRect();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialAnchorX();
+ void testStaticInitialAnchorY();
+ void testStaticInitialPositionX();
+ void testStaticInitialPositionY();
+ void testStaticInitialScaleX();
+ void testStaticInitialScaleY();
+ void testStaticInitialRotation();
+ void testStaticInitialStartOpacity();
+ void testStaticInitialEndOpacity();
+ void testStaticUpdatedAnchorX();
+ void testStaticUpdatedAnchorY();
+ void testStaticUpdatedPositionX();
+ void testStaticUpdatedPositionY();
+ void testStaticUpdatedScaleX();
+ void testStaticUpdatedScaleY();
+ void testStaticUpdatedRotation();
+ void testStaticUpdatedStartOpacity();
+ void testStaticUpdatedEndOpacity();
+
+ void testAnimatedInitialAnchorX();
+ void testAnimatedInitialAnchorY();
+ void testAnimatedInitialPositionX();
+ void testAnimatedInitialPositionY();
+ void testAnimatedInitialScaleX();
+ void testAnimatedInitialScaleY();
+ void testAnimatedInitialRotation();
+ void testAnimatedInitialStartOpacity();
+ void testAnimatedInitialEndOpacity();
+ void testAnimatedUpdatedAnchorX();
+ void testAnimatedUpdatedAnchorY();
+ void testAnimatedUpdatedPositionX();
+ void testAnimatedUpdatedPositionY();
+ void testAnimatedUpdatedScaleX();
+ void testAnimatedUpdatedScaleY();
+ void testAnimatedUpdatedRotation();
+ void testAnimatedUpdatedStartOpacity();
+ void testAnimatedUpdatedEndOpacity();
+
+ void testActive();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMRepeaterTransform *m_transform = nullptr;
+};
+
+tst_BMRepeaterTransform::tst_BMRepeaterTransform()
+{
+
+}
+
+tst_BMRepeaterTransform::~tst_BMRepeaterTransform()
+{
+
+}
+
+void tst_BMRepeaterTransform::initTestCase()
+{
+}
+
+void tst_BMRepeaterTransform::cleanupTestCase()
+{
+ if (m_transform)
+ delete m_transform;
+}
+
+void tst_BMRepeaterTransform::testStaticInitialAnchorX()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialAnchorY()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialPositionX()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialPositionY()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialScaleX()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialScaleY()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialRotation()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialStartOpacity()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 100.0));
+}
+
+void tst_BMRepeaterTransform::testStaticInitialEndOpacity()
+{
+ loadTestData("repeater_transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 100.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedAnchorX()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedAnchorY()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedPositionX()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedPositionY()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedScaleX()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedScaleY()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedRotation()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedStartOpacity()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 100.0));
+}
+
+void tst_BMRepeaterTransform::testStaticUpdatedEndOpacity()
+{
+ loadTestData("repeater_transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 100.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialAnchorX()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialAnchorY()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialPositionX()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialPositionY()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialScaleX()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialScaleY()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialRotation()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialStartOpacity()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 0.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedInitialEndOpacity()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 100.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedAnchorX()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 25.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedAnchorY()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 25.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedPositionX()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 75.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedPositionY()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 75.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedScaleX()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 1.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedScaleY()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 1.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedRotation()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), (3 * 360 + 30.0)));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedStartOpacity()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->startOpacity(), 25.0));
+}
+
+void tst_BMRepeaterTransform::testAnimatedUpdatedEndOpacity()
+{
+ loadTestData("repeater_transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->endOpacity(), 75.0));
+}
+
+void tst_BMRepeaterTransform::testActive()
+{
+ loadTestData("repeater_transform_static.json");
+ QVERIFY(m_transform->active(100) == true);
+}
+
+void tst_BMRepeaterTransform::loadTestData(const QByteArray &filename)
+{
+ if (m_transform) {
+ delete m_transform;
+ m_transform = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_REPEATER_IX)
+ break;
+ shapesIt++;
+ }
+
+ BMRepeater *repeater = static_cast<BMRepeater*>(shape);
+ m_transform = static_cast<BMRepeaterTransform*>(repeater->transform().clone());
+
+ QVERIFY(m_transform != nullptr);
+}
+
+void tst_BMRepeaterTransform::updateProperty(int frame)
+{
+ m_transform->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMRepeaterTransform)
+#include "tst_bmrepeatertransform.moc"
diff --git a/tests/auto/bodymovin/shape/shape.pro b/tests/auto/bodymovin/shape/shape.pro
new file mode 100644
index 0000000..dab6f46
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shape.pro
@@ -0,0 +1,13 @@
+TEMPLATE = subdirs
+SUBDIRS += \
+ shapelayer \
+ rect \
+ ellipse \
+ fill \
+ stroke \
+ path \
+ trimpath \
+ transform \
+ shapetransform \
+ repeater \
+ repeatertransform
diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json b/tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json
new file mode 100644
index 0000000..8d1da56
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/shape_active_60to120.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json
new file mode 100644
index 0000000..bd913ae
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphaclip.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json
new file mode 100644
index 0000000..d5854e9
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_alphainvclip.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json
new file mode 100644
index 0000000..706fbea
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumaclip.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":3,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json
new file mode 100644
index 0000000..a4faa7d
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/shape_mask_lumainvclip.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"EmptyMaskClipShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":60,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","tt":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[],"ip":-4,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapelayer/shapelayer.pro b/tests/auto/bodymovin/shape/shapelayer/shapelayer.pro
new file mode 100644
index 0000000..478f5c9
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/shapelayer.pro
@@ -0,0 +1,5 @@
+QT += testlib bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmshapelayer.cpp
diff --git a/tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp b/tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp
new file mode 100644
index 0000000..0099b41
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapelayer/tst_bmshapelayer.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmshapelayer_p.h"
+
+class tst_BMShapeLayer: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMShapeLayer();
+ ~tst_BMShapeLayer();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testName();
+ void testType();
+ void testWidth();
+ void testHeight();
+ void testActive();
+ void testIsClippedLayer();
+ void testIsMaskLayer();
+ void testClipMode();
+
+private:
+ void loadTestData(const QByteArray &filename);
+
+ BMShapeLayer *m_layer;
+ BMShapeLayer *m_clippedlayer;
+ qreal m_width;
+ qreal m_height;
+};
+
+tst_BMShapeLayer::tst_BMShapeLayer()
+{
+ m_layer = nullptr;
+ m_clippedlayer = nullptr;
+}
+
+tst_BMShapeLayer::~tst_BMShapeLayer()
+{
+
+}
+
+void tst_BMShapeLayer::initTestCase()
+{
+
+}
+
+void tst_BMShapeLayer::cleanupTestCase()
+{
+
+}
+
+void tst_BMShapeLayer::testName()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_layer->name() == QString("Shape Layer 1"));
+}
+
+void tst_BMShapeLayer::testType()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_layer->type() == BM_LAYER_SHAPE_IX);
+}
+
+void tst_BMShapeLayer::testActive()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_layer->active(0) == false);
+ QVERIFY(m_layer->active(100) == true);
+ QVERIFY(m_layer->active(150) == false);
+}
+
+void tst_BMShapeLayer::testWidth()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_width == 100.0);
+}
+
+void tst_BMShapeLayer::testHeight()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_height == 100.0);
+}
+
+void tst_BMShapeLayer::testIsClippedLayer()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_layer->isClippedLayer() == false);
+ loadTestData("shape_mask_alphaclip.json");
+ QVERIFY(m_clippedlayer->isClippedLayer() == true);
+}
+
+void tst_BMShapeLayer::testIsMaskLayer()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_layer->isMaskLayer() == false);
+ loadTestData("shape_mask_alphaclip.json");
+ QVERIFY(m_layer->isMaskLayer() == true);
+}
+
+void tst_BMShapeLayer::testClipMode()
+{
+ loadTestData("shape_active_60to120.json");
+ QVERIFY(m_layer->clipMode() == BMLayer::MatteClipMode::NoClip);
+ loadTestData("shape_mask_alphaclip.json");
+ QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::Alpha);
+ loadTestData("shape_mask_alphainvclip.json");
+ QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::InvertedAlpha);
+ loadTestData("shape_mask_lumaclip.json");
+ QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::Luminence);
+ loadTestData("shape_mask_lumainvclip.json");
+ QVERIFY(m_clippedlayer->clipMode() == BMLayer::MatteClipMode::InvertedLuminence);
+}
+
+void tst_BMShapeLayer::loadTestData(const QByteArray &filename)
+{
+ if (m_layer) {
+ delete m_layer;
+ m_layer = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ m_width = rootObj.value(QLatin1String("w")).toVariant().toReal();
+ m_height = rootObj.value(QLatin1String("h")).toVariant().toReal();
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+ m_layer = new BMShapeLayer(layerObj);
+ QVERIFY(m_layer != nullptr);
+
+ if (layers.size() > 1) {
+ layerObj = layers[1].toObject();
+ type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("it's not shape layer");
+ m_clippedlayer = new BMShapeLayer(layerObj);
+ QVERIFY(m_clippedlayer != nullptr);
+ }
+}
+
+QTEST_MAIN(tst_BMShapeLayer)
+#include "tst_bmshapelayer.moc"
diff --git a/tests/auto/bodymovin/shape/shapetransform/shapetransform.pro b/tests/auto/bodymovin/shape/shapetransform/shapetransform.pro
new file mode 100644
index 0000000..92ae225
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapetransform/shapetransform.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmshapetransform.cpp
diff --git a/tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json b/tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json
new file mode 100644
index 0000000..ed650bf
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapetransform/shapetransform_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedShapeTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[75,75],"to":[4.16666650772095,4.16666650772095],"ti":[-4.16666650772095,-4.16666650772095]},{"t":179}],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50],"e":[25,25],"to":[-4.16666650772095,-4.16666650772095],"ti":[4.16666650772095,4.16666650772095]},{"t":179}],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50],"e":[100,100]},{"t":179}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[25]},{"t":179}],"ix":7},"sk":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[4]},{"t":179}],"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[745]},{"t":179}],"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json b/tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json
new file mode 100644
index 0000000..5d60293
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapetransform/shapetransform_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticShapeTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[50,50],"ix":1},"s":{"a":0,"k":[50,50],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp b/tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp
new file mode 100644
index 0000000..4a5e78a
--- /dev/null
+++ b/tests/auto/bodymovin/shape/shapetransform/tst_bmshapetransform.cpp
@@ -0,0 +1,508 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmgroup_p.h"
+#include "private/bmshapetransform_p.h"
+
+class tst_BMShapeTransform: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMShapeTransform();
+ ~tst_BMShapeTransform();
+
+private:
+
+ // void testParseStaticRect();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialAnchorX();
+ void testStaticInitialAnchorY();
+ void testStaticInitialPositionX();
+ void testStaticInitialPositionY();
+ void testStaticInitialScaleX();
+ void testStaticInitialScaleY();
+ void testStaticInitialRotation();
+ void testStaticInitialOpacity();
+ void testStaticInitialSkew();
+ void testStaticInitialSkewAxis();
+ void testStaticUpdatedAnchorX();
+ void testStaticUpdatedAnchorY();
+ void testStaticUpdatedPositionX();
+ void testStaticUpdatedPositionY();
+ void testStaticUpdatedScaleX();
+ void testStaticUpdatedScaleY();
+ void testStaticUpdatedRotation();
+ void testStaticUpdatedOpacity();
+ void testStaticUpdatedSkew();
+ void testStaticUpdatedSkewAxis();
+
+
+ void testAnimatedInitialAnchorX();
+ void testAnimatedInitialAnchorY();
+ void testAnimatedInitialPositionX();
+ void testAnimatedInitialPositionY();
+ void testAnimatedInitialScaleX();
+ void testAnimatedInitialScaleY();
+ void testAnimatedInitialRotation();
+ void testAnimatedInitialOpacity();
+ void testAnimatedInitialSkew();
+ void testAnimatedInitialSkewAxis();
+ void testAnimatedUpdatedAnchorX();
+ void testAnimatedUpdatedAnchorY();
+ void testAnimatedUpdatedPositionX();
+ void testAnimatedUpdatedPositionY();
+ void testAnimatedUpdatedScaleX();
+ void testAnimatedUpdatedScaleY();
+ void testAnimatedUpdatedRotation();
+ void testAnimatedUpdatedOpacity();
+ void testAnimatedUpdatedSkew();
+ void testAnimatedUpdatedSkewAxis();
+
+ void testName();
+ void testType();
+ void testActive();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMShapeTransform *m_transform = nullptr;
+};
+
+tst_BMShapeTransform::tst_BMShapeTransform()
+{
+
+}
+
+tst_BMShapeTransform::~tst_BMShapeTransform()
+{
+
+}
+
+void tst_BMShapeTransform::initTestCase()
+{
+}
+
+void tst_BMShapeTransform::cleanupTestCase()
+{
+ if (m_transform)
+ delete m_transform;
+}
+
+void tst_BMShapeTransform::testStaticInitialAnchorX()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialAnchorY()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialPositionX()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialPositionY()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialScaleX()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMShapeTransform::testStaticInitialScaleY()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMShapeTransform::testStaticInitialRotation()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialOpacity()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialSkew()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->skew(), 0.0));
+}
+
+void tst_BMShapeTransform::testStaticInitialSkewAxis()
+{
+ loadTestData("shapetransform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->skewAxis(), 0.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedAnchorX()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedAnchorY()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedPositionX()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedPositionY()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedScaleX()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedScaleY()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedRotation()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedOpacity()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedSkew()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->skew(), 0.0));
+}
+
+void tst_BMShapeTransform::testStaticUpdatedSkewAxis()
+{
+ loadTestData("shapetransform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->skewAxis(), 0.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialAnchorX()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialAnchorY()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialPositionX()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialPositionY()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialScaleX()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialScaleY()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialRotation()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialOpacity()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialSkew()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->skew(), 0.0));
+}
+
+void tst_BMShapeTransform::testAnimatedInitialSkewAxis()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->skewAxis(), 0.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedAnchorX()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 25.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedAnchorY()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 25.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedPositionX()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 75.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedPositionY()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 75.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedScaleX()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 1.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedScaleY()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 1.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedRotation()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), (3 * 360 + 30.0)));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedOpacity()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 0.25));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedSkew()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->skew(), 4.0));
+}
+
+void tst_BMShapeTransform::testAnimatedUpdatedSkewAxis()
+{
+ loadTestData("shapetransform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->skewAxis(), (2 * 360 + 25.0)));
+}
+
+void tst_BMShapeTransform::testName()
+{
+ loadTestData("shapetransform_static.json");
+ QVERIFY(m_transform->name() == QString("Transform"));
+}
+
+void tst_BMShapeTransform::testType()
+{
+ loadTestData("shapetransform_static.json");
+ QVERIFY(m_transform->type() == BM_SHAPE_TRANS_IX);
+}
+
+void tst_BMShapeTransform::testActive()
+{
+ loadTestData("shapetransform_static.json");
+ QVERIFY(m_transform->active(100) == true);
+}
+
+void tst_BMShapeTransform::loadTestData(const QByteArray &filename)
+{
+ if (m_transform) {
+ delete m_transform;
+ m_transform = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMGroup* group = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ QByteArray type = childObj.value(QLatin1String("ty")).toVariant().toByteArray();
+ if (QLatin1String(type.data()) == QLatin1String(BM_SHAPE_GROUP_STR))
+ group = new BMGroup(childObj);
+ shapesIt++;
+ }
+ m_transform = static_cast<BMShapeTransform*>(group->findChild("Transform"));
+
+ QVERIFY(m_transform != nullptr);
+}
+
+void tst_BMShapeTransform::updateProperty(int frame)
+{
+ m_transform->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMShapeTransform)
+#include "tst_bmshapetransform.moc"
diff --git a/tests/auto/bodymovin/shape/stroke/stroke.pro b/tests/auto/bodymovin/shape/stroke/stroke.pro
new file mode 100644
index 0000000..ce75670
--- /dev/null
+++ b/tests/auto/bodymovin/shape/stroke/stroke.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmstroke.cpp
diff --git a/tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json b/tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json
new file mode 100644
index 0000000..b9f3445
--- /dev/null
+++ b/tests/auto/bodymovin/shape/stroke/stroke_animated_blue5_white1.json
@@ -0,0 +1,213 @@
+{
+ "assets" : [],
+ "ddd" : 0,
+ "fr" : 60,
+ "h" : 100,
+ "ip" : 0,
+ "layers" : [
+ {
+ "ao" : 0,
+ "bm" : 0,
+ "ddd" : 0,
+ "ind" : 1,
+ "ip" : 0,
+ "ks" : {
+ "a" : {
+ "a" : 0,
+ "ix" : 1,
+ "k" : [ 0, 0, 0 ]
+ },
+ "o" : {
+ "a" : 0,
+ "ix" : 11,
+ "k" : 100
+ },
+ "p" : {
+ "a" : 0,
+ "ix" : 2,
+ "k" : [ 0, 0, 0 ]
+ },
+ "r" : {
+ "a" : 0,
+ "ix" : 10,
+ "k" : 0
+ },
+ "s" : {
+ "a" : 0,
+ "ix" : 6,
+ "k" : [ 100, 100, 100 ]
+ }
+ },
+ "nm" : "Shape Layer 1",
+ "op" : 180,
+ "shapes" : [
+ {
+ "c" : {
+ "a" : 1,
+ "ix" : 3,
+ "k" : [
+ {
+ "e" : [ 1, 1, 1, 1 ],
+ "i" : {
+ "x" : [ 0.8330 ],
+ "y" : [ 0.8330 ]
+ },
+ "n" : [ "0p833_0p833_0p167_0p167" ],
+ "o" : {
+ "x" : [ 0.1670 ],
+ "y" : [ 0.1670 ]
+ },
+ "s" : [ 0, 0, 1, 1 ],
+ "t" : 0
+ },
+ {
+ "t" : 179
+ }
+ ]
+ },
+ "d" : [
+ {
+ "n" : "d",
+ "nm" : "dash",
+ "v" : {
+ "a" : 1,
+ "ix" : 1,
+ "k" : [
+ {
+ "e" : [ 30 ],
+ "i" : {
+ "x" : [ 0.8330 ],
+ "y" : [ 0.8330 ]
+ },
+ "n" : [ "0p833_0p833_0p167_0p167" ],
+ "o" : {
+ "x" : [ 0.1670 ],
+ "y" : [ 0.1670 ]
+ },
+ "s" : [ 10 ],
+ "t" : 0
+ },
+ {
+ "t" : 179
+ }
+ ]
+ }
+ },
+ {
+ "n" : "o",
+ "nm" : "offset",
+ "v" : {
+ "a" : 1,
+ "ix" : 7,
+ "k" : [
+ {
+ "e" : [ 10 ],
+ "i" : {
+ "x" : [ 0.8330 ],
+ "y" : [ 0.8330 ]
+ },
+ "n" : [ "0p833_0p833_0p167_0p167" ],
+ "o" : {
+ "x" : [ 0.1670 ],
+ "y" : [ 0.1670 ]
+ },
+ "s" : [ 0 ],
+ "t" : 0
+ },
+ {
+ "t" : 179
+ }
+ ]
+ }
+ }
+ ],
+ "hd" : false,
+ "lc" : 1,
+ "lj" : 1,
+ "ml" : 4,
+ "ml2" : {
+ "a" : 1,
+ "ix" : 8,
+ "k" : [
+ {
+ "e" : [ 8 ],
+ "i" : {
+ "x" : [ 0.8330 ],
+ "y" : [ 0.8330 ]
+ },
+ "n" : [ "0p833_0p833_0p167_0p167" ],
+ "o" : {
+ "x" : [ 0.1670 ],
+ "y" : [ 0.1670 ]
+ },
+ "s" : [ 4 ],
+ "t" : 0
+ },
+ {
+ "t" : 179
+ }
+ ]
+ },
+ "mn" : "ADBE Vector Graphic - Stroke",
+ "nm" : "Stroke 1",
+ "o" : {
+ "a" : 1,
+ "ix" : 4,
+ "k" : [
+ {
+ "e" : [ 10 ],
+ "i" : {
+ "x" : [ 0.8330 ],
+ "y" : [ 0.8330 ]
+ },
+ "n" : [ "0p833_0p833_0p167_0p167" ],
+ "o" : {
+ "x" : [ 0.1670 ],
+ "y" : [ 0.1670 ]
+ },
+ "s" : [ 100 ],
+ "t" : 0
+ },
+ {
+ "t" : 179
+ }
+ ]
+ },
+ "ty" : "st",
+ "w" : {
+ "a" : 1,
+ "ix" : 5,
+ "k" : [
+ {
+ "e" : [ 1 ],
+ "i" : {
+ "x" : [ 0.8330 ],
+ "y" : [ 0.8330 ]
+ },
+ "n" : [ "0p833_0p833_0p167_0p167" ],
+ "o" : {
+ "x" : [ 0.1670 ],
+ "y" : [ 0.1670 ]
+ },
+ "s" : [ 5 ],
+ "t" : 0
+ },
+ {
+ "t" : 179
+ }
+ ]
+ }
+ }
+ ],
+ "sr" : 1,
+ "st" : 0,
+ "ty" : 4
+ }
+ ],
+ "markers" : [],
+ "nm" : "AnimatedStrokeShapeLayer",
+ "op" : 180,
+ "v" : "5.3.4",
+ "w" : 100
+}
+
diff --git a/tests/auto/bodymovin/shape/stroke/stroke_hidden.json b/tests/auto/bodymovin/shape/stroke/stroke_hidden.json
new file mode 100644
index 0000000..d49699d
--- /dev/null
+++ b/tests/auto/bodymovin/shape/stroke/stroke_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticStrokeShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"st","c":{"a":0,"k":[0,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":true}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json b/tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json
new file mode 100644
index 0000000..f8ff748
--- /dev/null
+++ b/tests/auto/bodymovin/shape/stroke/stroke_static_blue_2.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticStrokeShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"st","c":{"a":0,"k":[0,0,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp b/tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp
new file mode 100644
index 0000000..c32284a
--- /dev/null
+++ b/tests/auto/bodymovin/shape/stroke/tst_bmstroke.cpp
@@ -0,0 +1,349 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmstroke_p.h"
+
+class tst_BMStroke: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMStroke();
+ ~tst_BMStroke();
+
+private:
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialColor();
+ void testStaticInitialOpacity();
+ void testStaticInitialWidth();
+ void testStaticInitialCapStyle();
+ void testStaticInitialJoinStyle();
+ void testStaticInitialMiterLimit();
+ void testStaticUpdatedColor();
+ void testStaticUpdatedOpacity();
+ void testStaticUpdatedWidth();
+ void testStaticUpdatedCapStyle();
+ void testStaticUpdatedJoinStyle();
+ void testStaticUpdatedMiterLimit();
+
+ void testAnimatedInitialColor();
+ void testAnimatedInitialOpacity();
+ void testAnimatedInitialWidth();
+ void testAnimatedInitialCapStyle();
+ void testAnimatedInitialJoinStyle();
+ void testAnimatedInitialMiterLimit();
+ void testAnimatedUpdatedColor();
+ void testAnimatedUpdatedOpacity();
+ void testAnimatedUpdatedWidth();
+
+ void testName();
+ void testType();
+ void testActive();
+ void testHidden();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMStroke *m_stroke = nullptr;
+};
+
+tst_BMStroke::tst_BMStroke()
+{
+
+}
+
+tst_BMStroke::~tst_BMStroke()
+{
+
+}
+
+void tst_BMStroke::initTestCase()
+{
+}
+
+void tst_BMStroke::cleanupTestCase()
+{
+ if (m_stroke)
+ delete m_stroke;
+}
+
+void tst_BMStroke::testStaticInitialColor()
+{
+ loadTestData("stroke_static_blue_2.json");
+
+ QVERIFY(m_stroke->pen().color() == QColor(Qt::blue));
+}
+
+void tst_BMStroke::testStaticInitialOpacity()
+{
+ loadTestData("stroke_static_blue_2.json");
+
+ QVERIFY(qFuzzyCompare(m_stroke->opacity(), 100.0));
+}
+
+void tst_BMStroke::testStaticInitialWidth()
+{
+ loadTestData("stroke_static_blue_2.json");
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 2.0));
+}
+
+void tst_BMStroke::testStaticInitialCapStyle()
+{
+ loadTestData("stroke_static_blue_2.json");
+
+ QVERIFY(m_stroke->pen().capStyle() == Qt::FlatCap);
+}
+
+void tst_BMStroke::testStaticInitialJoinStyle()
+{
+ loadTestData("stroke_static_blue_2.json");
+
+ QVERIFY(m_stroke->pen().joinStyle() == Qt::MiterJoin);
+}
+
+void tst_BMStroke::testStaticInitialMiterLimit()
+{
+ loadTestData("stroke_static_blue_2.json");
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().miterLimit(), 4.0));
+}
+
+void tst_BMStroke::testStaticUpdatedColor()
+{
+ loadTestData("stroke_static_blue_2.json");
+ updateProperty(179);
+
+ QVERIFY(m_stroke->pen().color() == QColor(Qt::blue));
+}
+
+void tst_BMStroke::testStaticUpdatedOpacity()
+{
+ loadTestData("stroke_static_blue_2.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_stroke->opacity(), 100.0));
+}
+
+void tst_BMStroke::testStaticUpdatedWidth()
+{
+ loadTestData("stroke_static_blue_2.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 2.0));
+}
+
+void tst_BMStroke::testStaticUpdatedCapStyle()
+{
+ loadTestData("stroke_static_blue_2.json");
+ updateProperty(179);
+
+ QVERIFY(m_stroke->pen().capStyle() == Qt::FlatCap);
+}
+
+void tst_BMStroke::testStaticUpdatedJoinStyle()
+{
+ loadTestData("stroke_static_blue_2.json");
+ updateProperty(179);
+
+ QVERIFY(m_stroke->pen().joinStyle() == Qt::MiterJoin);
+}
+
+void tst_BMStroke::testStaticUpdatedMiterLimit()
+{
+ loadTestData("stroke_static_blue_2.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().miterLimit(), 4.0));
+}
+
+void tst_BMStroke::testAnimatedInitialColor()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(0);
+
+ QVERIFY(m_stroke->pen().color() == QColor(Qt::blue));
+}
+
+void tst_BMStroke::testAnimatedInitialOpacity()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_stroke->opacity(), 100.0));
+}
+
+void tst_BMStroke::testAnimatedInitialWidth()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 5.0));
+}
+
+void tst_BMStroke::testAnimatedInitialCapStyle()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(0);
+
+ QVERIFY(m_stroke->pen().capStyle() == Qt::FlatCap);
+}
+
+void tst_BMStroke::testAnimatedInitialJoinStyle()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(0);
+
+ QVERIFY(m_stroke->pen().joinStyle() == Qt::MiterJoin);
+}
+
+void tst_BMStroke::testAnimatedInitialMiterLimit()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().miterLimit(), 4.0));
+}
+
+void tst_BMStroke::testAnimatedUpdatedColor()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(179);
+
+ QVERIFY(m_stroke->pen().color() == QColor(Qt::white));
+}
+
+void tst_BMStroke::testAnimatedUpdatedOpacity()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_stroke->opacity(), 10.0));
+}
+
+void tst_BMStroke::testAnimatedUpdatedWidth()
+{
+ loadTestData("stroke_animated_blue5_white1.json");
+ updateProperty(179);
+
+ QVERIFY(qFuzzyCompare(m_stroke->pen().width(), 1.0));
+}
+
+void tst_BMStroke::testName()
+{
+ loadTestData("stroke_static_blue_2.json");
+ QVERIFY(m_stroke->name() == QString("Stroke 1"));
+}
+
+void tst_BMStroke::testType()
+{
+ loadTestData("stroke_static_blue_2.json");
+ QVERIFY(m_stroke->type() == BM_SHAPE_STROKE_IX);
+}
+
+void tst_BMStroke::testActive()
+{
+ loadTestData("stroke_static_blue_2.json");
+ QVERIFY(m_stroke->active(100) == true);
+
+ loadTestData("stroke_hidden.json");
+ QVERIFY(m_stroke->active(100) == false);
+}
+
+void tst_BMStroke::testHidden()
+{
+ loadTestData("stroke_hidden.json");
+ QVERIFY(m_stroke->hidden() == true);
+}
+
+void tst_BMStroke::loadTestData(const QByteArray &filename)
+{
+ if (m_stroke) {
+ delete m_stroke;
+ m_stroke = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_STROKE_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_stroke = static_cast<BMStroke*>(shape);
+
+ QVERIFY(m_stroke != nullptr);
+}
+
+void tst_BMStroke::updateProperty(int frame)
+{
+ m_stroke->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMStroke)
+#include "tst_bmstroke.moc"
diff --git a/tests/auto/bodymovin/shape/transform/transform.pro b/tests/auto/bodymovin/shape/transform/transform.pro
new file mode 100644
index 0000000..cdb0a83
--- /dev/null
+++ b/tests/auto/bodymovin/shape/transform/transform.pro
@@ -0,0 +1,6 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmbasictransform.cpp
+
diff --git a/tests/auto/bodymovin/shape/transform/transform_animated.json b/tests/auto/bodymovin/shape/transform/transform_animated.json
new file mode 100644
index 0000000..c06f5e3
--- /dev/null
+++ b/tests/auto/bodymovin/shape/transform/transform_animated.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[25]},{"t":179}],"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50,0],"e":[75,75,0],"to":[4.16666650772095,4.16666650772095,0],"ti":[-4.16666650772095,-4.16666650772095,0]},{"t":179}],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[50,50,0],"e":[25,25,0],"to":[-4.16666650772095,-4.16666650772095,0],"ti":[4.16666650772095,4.16666650772095,0]},{"t":179}],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50,100],"e":[100,100,100]},{"t":179}],"ix":6}},"ao":0,"shapes":[],"ip":-179,"op":184,"st":-179,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/transform/transform_static.json b/tests/auto/bodymovin/shape/transform/transform_static.json
new file mode 100644
index 0000000..d1d7edf
--- /dev/null
+++ b/tests/auto/bodymovin/shape/transform/transform_static.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedTransformShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[],"ip":-179,"op":184,"st":-179,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp b/tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp
new file mode 100644
index 0000000..d29501c
--- /dev/null
+++ b/tests/auto/bodymovin/shape/transform/tst_bmbasictransform.cpp
@@ -0,0 +1,413 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmbasictransform_p.h"
+
+class tst_BMBasicTransform: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMBasicTransform();
+ ~tst_BMBasicTransform();
+
+private:
+
+ // void testParseStaticRect();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialAnchorX();
+ void testStaticInitialAnchorY();
+ void testStaticInitialPositionX();
+ void testStaticInitialPositionY();
+ void testStaticInitialScaleX();
+ void testStaticInitialScaleY();
+ void testStaticInitialRotation();
+ void testStaticInitialOpacity();
+ void testStaticUpdatedAnchorX();
+ void testStaticUpdatedAnchorY();
+ void testStaticUpdatedPositionX();
+ void testStaticUpdatedPositionY();
+ void testStaticUpdatedScaleX();
+ void testStaticUpdatedScaleY();
+ void testStaticUpdatedRotation();
+ void testStaticUpdatedOpacity();
+
+ void testAnimatedInitialAnchorX();
+ void testAnimatedInitialAnchorY();
+ void testAnimatedInitialPositionX();
+ void testAnimatedInitialPositionY();
+ void testAnimatedInitialScaleX();
+ void testAnimatedInitialScaleY();
+ void testAnimatedInitialRotation();
+ void testAnimatedInitialOpacity();
+ void testAnimatedUpdatedAnchorX();
+ void testAnimatedUpdatedAnchorY();
+ void testAnimatedUpdatedPositionX();
+ void testAnimatedUpdatedPositionY();
+ void testAnimatedUpdatedScaleX();
+ void testAnimatedUpdatedScaleY();
+ void testAnimatedUpdatedRotation();
+ void testAnimatedUpdatedOpacity();
+
+ void testActive();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMBasicTransform *m_transform = nullptr;
+};
+
+tst_BMBasicTransform::tst_BMBasicTransform()
+{
+
+}
+
+tst_BMBasicTransform::~tst_BMBasicTransform()
+{
+
+}
+
+void tst_BMBasicTransform::initTestCase()
+{
+}
+
+void tst_BMBasicTransform::cleanupTestCase()
+{
+ if (m_transform)
+ delete m_transform;
+}
+
+void tst_BMBasicTransform::testStaticInitialAnchorX()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticInitialAnchorY()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticInitialPositionX()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticInitialPositionY()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticInitialScaleX()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMBasicTransform::testStaticInitialScaleY()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMBasicTransform::testStaticInitialRotation()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMBasicTransform::testStaticInitialOpacity()
+{
+ loadTestData("transform_static.json");
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedAnchorX()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedAnchorY()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedPositionX()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedPositionY()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedScaleX()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedScaleY()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedRotation()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMBasicTransform::testStaticUpdatedOpacity()
+{
+ loadTestData("transform_static.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialAnchorX()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 50.0));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialAnchorY()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 50.0));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialPositionX()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 50.0));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialPositionY()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 50.0));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialScaleX()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 0.5));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialScaleY()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 0.5));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialRotation()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), 0.0));
+}
+
+void tst_BMBasicTransform::testAnimatedInitialOpacity()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 1.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedAnchorX()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().x(), 25.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedAnchorY()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->anchorPoint().y(), 25.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedPositionX()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().x(), 75.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedPositionY()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->position().y(), 75.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedScaleX()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().x(), 1.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedScaleY()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->scale().y(), 1.0));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedRotation()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->rotation(), (3 * 360 + 30.0)));
+}
+
+void tst_BMBasicTransform::testAnimatedUpdatedOpacity()
+{
+ loadTestData("transform_animated.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_transform->opacity(), 0.25));
+}
+
+void tst_BMBasicTransform::testActive()
+{
+ loadTestData("transform_static.json");
+ QVERIFY(m_transform->active(100) == true);
+}
+
+void tst_BMBasicTransform::loadTestData(const QByteArray &filename)
+{
+ if (m_transform) {
+ delete m_transform;
+ m_transform = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonObject transformObj = layerObj.value(QLatin1String("ks")).toObject();
+ m_transform = new BMBasicTransform(transformObj);
+
+ QVERIFY(m_transform != nullptr);
+}
+
+void tst_BMBasicTransform::updateProperty(int frame)
+{
+ m_transform->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMBasicTransform)
+#include "tst_bmbasictransform.moc"
diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath.pro b/tests/auto/bodymovin/shape/trimpath/trimpath.pro
new file mode 100644
index 0000000..94ed1be
--- /dev/null
+++ b/tests/auto/bodymovin/shape/trimpath/trimpath.pro
@@ -0,0 +1,5 @@
+QT += testlib gui-private bodymovin-private
+CONFIG += c++11
+
+SOURCES = \
+ tst_bmtrimpath.cpp
diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json b/tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json
new file mode 100644
index 0000000..2532123
--- /dev/null
+++ b/tests/auto/bodymovin/shape/trimpath/trimpath_animated_2080_0_to_0060_3x30.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"AnimatedTrimPathShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-29.992,10.008],[30.008,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-12,25.75],[-14.75,-26.75]],"o":[[12,-25.75],[14.75,26.75]],"v":[[-31.25,-15.25],[28.5,-16]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[20],"e":[0]},{"t":179}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[80],"e":[60]},{"t":179}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[1110]},{"t":179}],"ix":3},"m":2,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-179,"op":184,"st":-179,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json b/tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json
new file mode 100644
index 0000000..7689dcc
--- /dev/null
+++ b/tests/auto/bodymovin/shape/trimpath/trimpath_hidden.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticTrimPathShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-29.992,10.008],[30.008,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":20,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":true}],"ip":-179,"op":1,"st":-179,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json b/tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json
new file mode 100644
index 0000000..bb7ac08
--- /dev/null
+++ b/tests/auto/bodymovin/shape/trimpath/trimpath_static_20to80.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":0,"op":180,"w":100,"h":100,"nm":"StaticTrimPathShapeLayer","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-29.992,10.008],[30.008,10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":20,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-179,"op":1,"st":-179,"bm":0}],"markers":[]}
diff --git a/tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp b/tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp
new file mode 100644
index 0000000..6210c09
--- /dev/null
+++ b/tests/auto/bodymovin/shape/trimpath/tst_bmtrimpath.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmlayer_p.h"
+#include "private/bmtrimpath_p.h"
+
+class tst_BMTrimPath: public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_BMTrimPath();
+ ~tst_BMTrimPath();
+
+private:
+
+ // void testParseStaticRect();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void testStaticInitialStart();
+ void testStaticInitialEnd();
+ void testStaticInitialOffset();
+ void testStaticInitialSimultaneous();
+ void testStaticUpdatedStart();
+ void testStaticUpdatedEnd();
+ void testStaticUpdatedOffset();
+ void testStaticUpdatedSimultaneous();
+
+ void testAnimatedInitialStart();
+ void testAnimatedInitialEnd();
+ void testAnimatedInitialOffset();
+ void testAnimatedInitialSimultaneous();
+ void testAnimatedUpdatedStart();
+ void testAnimatedUpdatedEnd();
+ void testAnimatedUpdatedOffset();
+ void testAnimatedUpdatedSimultaneous();
+
+ void testName();
+ void testType();
+ void testHidden();
+ void testActive();
+
+private:
+ void loadTestData(const QByteArray &filename);
+ void updateProperty(int frame);
+
+ BMTrimPath *m_trimpath = nullptr;
+};
+
+tst_BMTrimPath::tst_BMTrimPath()
+{
+
+}
+
+tst_BMTrimPath::~tst_BMTrimPath()
+{
+
+}
+
+void tst_BMTrimPath::initTestCase()
+{
+}
+
+void tst_BMTrimPath::cleanupTestCase()
+{
+ if (m_trimpath)
+ delete m_trimpath;
+}
+
+void tst_BMTrimPath::testStaticInitialStart()
+{
+ loadTestData("trimpath_static_20to80.json");
+
+ QVERIFY(qFuzzyCompare(m_trimpath->start(), 20.0));
+}
+
+void tst_BMTrimPath::testStaticInitialEnd()
+{
+ loadTestData("trimpath_static_20to80.json");
+
+ QVERIFY(qFuzzyCompare(m_trimpath->end(), 80.0));
+}
+
+void tst_BMTrimPath::testStaticInitialOffset()
+{
+ loadTestData("trimpath_static_20to80.json");
+
+ QVERIFY(qFuzzyCompare(m_trimpath->offset(), 0.0));
+}
+
+void tst_BMTrimPath::testStaticInitialSimultaneous()
+{
+ loadTestData("trimpath_static_20to80.json");
+
+ QVERIFY(m_trimpath->simultaneous() == true);
+}
+
+void tst_BMTrimPath::testStaticUpdatedStart()
+{
+ loadTestData("trimpath_static_20to80.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->start(), 20.0));
+}
+
+void tst_BMTrimPath::testStaticUpdatedEnd()
+{
+ loadTestData("trimpath_static_20to80.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->end(), 80.0));
+}
+
+void tst_BMTrimPath::testStaticUpdatedOffset()
+{
+ loadTestData("trimpath_static_20to80.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->offset(), 0.0));
+}
+
+void tst_BMTrimPath::testStaticUpdatedSimultaneous()
+{
+ loadTestData("trimpath_static_20to80.json");
+ updateProperty(180);
+
+ QVERIFY(m_trimpath->simultaneous() == true);
+}
+
+void tst_BMTrimPath::testAnimatedInitialStart()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->start(), 20.0));
+}
+
+void tst_BMTrimPath::testAnimatedInitialEnd()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->end(), 80.0));
+}
+
+void tst_BMTrimPath::testAnimatedInitialOffset()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(0);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->offset(), 0.0));
+}
+
+void tst_BMTrimPath::testAnimatedInitialSimultaneous()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(0);
+
+ QVERIFY(m_trimpath->simultaneous() == false);
+}
+
+void tst_BMTrimPath::testAnimatedUpdatedStart()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->start(), 0.0));
+}
+
+void tst_BMTrimPath::testAnimatedUpdatedEnd()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->end(), 60.0));
+}
+
+void tst_BMTrimPath::testAnimatedUpdatedOffset()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(180);
+
+ QVERIFY(qFuzzyCompare(m_trimpath->offset(), (360 * 3 + 30.0)));
+}
+
+void tst_BMTrimPath::testAnimatedUpdatedSimultaneous()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ updateProperty(180);
+
+ QVERIFY(m_trimpath->simultaneous() == false);
+}
+
+void tst_BMTrimPath::testName()
+{
+ loadTestData("trimpath_hidden.json");
+
+ QVERIFY(m_trimpath->name() == QString("Trim Paths 1"));
+}
+
+void tst_BMTrimPath::testType()
+{
+ loadTestData("trimpath_hidden.json");
+
+ QVERIFY(m_trimpath->type() == BM_SHAPE_TRIM_IX);
+}
+
+void tst_BMTrimPath::testHidden()
+{
+ loadTestData("trimpath_hidden.json");
+
+ QVERIFY(m_trimpath->hidden() == true);
+}
+
+void tst_BMTrimPath::testActive()
+{
+ loadTestData("trimpath_animated_2080_0_to_0060_3x30.json");
+ QVERIFY(m_trimpath->active(100) == true);
+
+ loadTestData("trimpath_hidden.json");
+ QVERIFY(m_trimpath->active(100) == false);
+}
+
+void tst_BMTrimPath::loadTestData(const QByteArray &filename)
+{
+ if (m_trimpath) {
+ delete m_trimpath;
+ m_trimpath = nullptr;
+ }
+
+ QFile sourceFile(QFINDTESTDATA(filename.constData()));
+ if (!sourceFile.exists())
+ QFAIL("File does not exist");
+ if (!sourceFile.open(QIODevice::ReadOnly))
+ QFAIL("Cannot read test file");
+
+ QByteArray json = sourceFile.readAll();
+
+ sourceFile.close();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+ if (rootObj.empty())
+ QFAIL("Cannot parse test file");
+
+ QJsonArray layers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonObject layerObj = layers[0].toObject();
+ int type = layerObj.value(QLatin1String("ty")).toInt();
+ if (type != 4)
+ QFAIL("It's not shape layer");
+
+ QJsonArray shapes = layerObj.value(QLatin1String("shapes")).toArray();
+ QJsonArray::const_iterator shapesIt = shapes.constBegin();
+ BMShape* shape = nullptr;
+ while (shapesIt != shapes.end()) {
+ QJsonObject childObj = (*shapesIt).toObject();
+ shape = BMShape::construct(childObj);
+ QVERIFY(shape != nullptr);
+ if (shape->type() == BM_SHAPE_TRIM_IX)
+ break;
+ shapesIt++;
+ }
+
+ m_trimpath = static_cast<BMTrimPath*>(shape);
+
+ QVERIFY(m_trimpath != nullptr);
+}
+
+void tst_BMTrimPath::updateProperty(int frame)
+{
+ m_trimpath->updateProperties(frame);
+}
+
+QTEST_MAIN(tst_BMTrimPath)
+#include "tst_bmtrimpath.moc"
diff --git a/tests/auto/imports/imports.pro b/tests/auto/imports/imports.pro
new file mode 100644
index 0000000..6b78b4c
--- /dev/null
+++ b/tests/auto/imports/imports.pro
@@ -0,0 +1,30 @@
+QT += quick
+CONFIG += c++11 qmltestcase
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += qml.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
diff --git a/tests/auto/imports/main.cpp b/tests/auto/imports/main.cpp
new file mode 100644
index 0000000..a7e37a8
--- /dev/null
+++ b/tests/auto/imports/main.cpp
@@ -0,0 +1,3 @@
+#include <QtQuickTest/quicktest.h>
+
+QUICK_TEST_MAIN("tst_main.qml");
diff --git a/tests/auto/imports/qml.qrc b/tests/auto/imports/qml.qrc
new file mode 100644
index 0000000..c821f84
--- /dev/null
+++ b/tests/auto/imports/qml.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>rec_pos_col_opa.json</file>
+ <file>tst_main.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/imports/rec_pos_col_opa.json b/tests/auto/imports/rec_pos_col_opa.json
new file mode 100644
index 0000000..35245f4
--- /dev/null
+++ b/tests/auto/imports/rec_pos_col_opa.json
@@ -0,0 +1 @@
+{"v":"5.3.4","fr":60,"ip":60,"op":180,"w":100,"h":100,"nm":"Test","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":60,"s":[0,0],"e":[25,-25],"to":[4.16666650772095,-4.16666650772095],"ti":[-4.16666650772095,4.16666650772095]},{"t":119}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[1,0,0,1],"e":[0,0,1,1]},{"t":119}],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[100],"e":[25]},{"t":119}],"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/auto/imports/tst_main.qml b/tests/auto/imports/tst_main.qml
new file mode 100644
index 0000000..788d385
--- /dev/null
+++ b/tests/auto/imports/tst_main.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.9
+import QtQuick.Window 2.2
+import QtTest 1.0
+import Qt.labs.lottieqt 1.0
+
+Item {
+ id: animContainer
+ width: childrenRect.width
+ height: childrenRect.height
+
+ LottieAnimation {
+ id: bmAnim
+
+ x: 0
+ y: 0
+ quality: LottieAnimation.HighQuality
+ source: ":/rec_pos_col_opa.json"
+
+ onFinished: {
+ bmAnim.start();
+ }
+
+
+ }
+
+ TestCase{
+ id: testID
+ name: "testAnimation"
+
+ function test_1_loadingStatus(){
+ compare(bmAnim.status, 2); /* status: Null(0), Loading(1), Ready(2), Error(3)
+ if the source has been loaded successfully, the status
+ must be "Ready"
+ */
+ }
+
+ function test_2_initialFrameRate(){
+ compare(bmAnim.frameRate, 60); /* frame rate of the source used in this test is 60 */
+ }
+
+ function test_3_changeFrameRate(){
+ bmAnim.frameRate = 30;
+ compare(bmAnim.frameRate, 30);
+ }
+
+ function test_4_initialQuality(){
+ compare(bmAnim.quality, 2); /* quality: LowQuality(0), MediumQuality(1), HighQuality(2)
+ lottieanimation's initial quality is set to HighQuality
+ */
+ }
+
+ function test_5_changeQuality(){
+ bmAnim.quality = 1; /* change quality to MediumQuality */
+ compare(bmAnim.quality, 1);
+ }
+
+
+ }
+}
diff --git a/tests/manual/featcheck/.gitignore b/tests/manual/featcheck/.gitignore
new file mode 100644
index 0000000..fab7372
--- /dev/null
+++ b/tests/manual/featcheck/.gitignore
@@ -0,0 +1,73 @@
+# This file is used to ignore files which are generated
+# ----------------------------------------------------------------------------
+
+*~
+*.autosave
+*.a
+*.core
+*.moc
+*.o
+*.obj
+*.orig
+*.rej
+*.so
+*.so.*
+*_pch.h.cpp
+*_resource.rc
+*.qm
+.#*
+*.*#
+core
+!core/
+tags
+.DS_Store
+.directory
+*.debug
+Makefile*
+*.prl
+*.app
+moc_*.cpp
+ui_*.h
+qrc_*.cpp
+Thumbs.db
+*.res
+*.rc
+/.qmake.cache
+/.qmake.stash
+
+# qtcreator generated files
+*.pro.user*
+
+# xemacs temporary files
+*.flc
+
+# Vim temporary files
+.*.swp
+
+# Visual Studio generated files
+*.ib_pdb_index
+*.idb
+*.ilk
+*.pdb
+*.sln
+*.suo
+*.vcproj
+*vcproj.*.*.user
+*.ncb
+*.sdf
+*.opensdf
+*.vcxproj
+*vcxproj.*
+
+# MinGW generated files
+*.Debug
+*.Release
+
+# Python byte code
+*.pyc
+
+# Binaries
+# --------
+*.dll
+*.exe
+
diff --git a/tests/manual/featcheck/featcheck.pro b/tests/manual/featcheck/featcheck.pro
new file mode 100644
index 0000000..2a35170
--- /dev/null
+++ b/tests/manual/featcheck/featcheck.pro
@@ -0,0 +1,22 @@
+QT += bodymovin-private
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main.cpp
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
diff --git a/tests/manual/featcheck/howtofind.txt b/tests/manual/featcheck/howtofind.txt
new file mode 100644
index 0000000..6879b85
--- /dev/null
+++ b/tests/manual/featcheck/howtofind.txt
@@ -0,0 +1,7 @@
+
+1. Build featcheck
+2. Add build directory to path
+3. Switch to the directory containing json files (in subdirectories)
+4. Enter command
+ find * -name *.json -printf '\n\n%p:\n\n' -exec featcheck {} \; &>log
+5. The results are now found in the log file
diff --git a/tests/manual/featcheck/main.cpp b/tests/manual/featcheck/main.cpp
new file mode 100644
index 0000000..cdd7f3a
--- /dev/null
+++ b/tests/manual/featcheck/main.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include "private/bmbase_p.h"
+#include "private/bmlayer_p.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ if (argc < 2) {
+ printf("Filename missing\n");
+ return -1;
+ }
+
+ QFile sourceFile(argv[1]);
+
+ if (!sourceFile.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, "Cannot open file\n");
+ return -1;
+ }
+
+ QByteArray json = sourceFile.readAll();
+
+ QJsonDocument doc = QJsonDocument::fromJson(json);
+ QJsonObject rootObj = doc.object();
+
+ if (rootObj.empty()) {
+ fprintf(stderr, "Json document not found\n");
+ return -1;
+ }
+
+ if (rootObj.value(QLatin1String("assets")).toArray().count())
+ qWarning() << "assets not supported";
+
+ if (rootObj.value(QLatin1String("chars")).toArray().count())
+ qWarning() << "chars not supported";
+
+ if (rootObj.value(QLatin1String("markers")).toArray().count())
+ qWarning() << "markers not supported";
+
+ QJsonArray jsonLayers = rootObj.value(QLatin1String("layers")).toArray();
+ QJsonArray::const_iterator jsonLayerIt = jsonLayers.constBegin();
+ while (jsonLayerIt != jsonLayers.constEnd()) {
+ QJsonObject jsonLayer = (*jsonLayerIt).toObject();
+ BMLayer *layer = BMLayer::construct(jsonLayer);
+ if (layer)
+ delete layer;
+ jsonLayerIt++;
+ }
+}
diff --git a/tests/manual/html/content.json b/tests/manual/html/content.json
new file mode 100644
index 0000000..0a49e5e
--- /dev/null
+++ b/tests/manual/html/content.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":114.000004643315,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[63]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[63],"e":[0]},{"t":90.0000036657751}],"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[54.5,66.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":114.000004643315,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/html/index.html b/tests/manual/html/index.html
new file mode 100644
index 0000000..b0f6ee6
--- /dev/null
+++ b/tests/manual/html/index.html
@@ -0,0 +1,47 @@
+<html xmlns="http://www.w3.org/1999/xhtml" />
+<meta charset="UTF-8">
+
+<head>
+ <style>
+ body,
+ html {
+ background-color: #ffffff;
+ color: #000000;
+ margin: 0px;
+ height: 100%;
+ width: 100%;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ font-family: Arial;
+ font-weight: bold;
+ }
+ </style>
+ <script src=https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.4.1/lottie.js type="text/javascript"></script>
+</head>
+
+<body>
+
+<div class="position">
+ <div class="title">CONTENT</div>
+ <div class="bodymovin" id="content"></div>
+</div>
+
+ <script>
+ var content = {
+ container: document.getElementById('content'),
+ renderer: 'svg',
+ loop: true,
+ autoplay: true,
+ rendererSettings: {
+ progressiveLoad: false
+ },
+ path: 'content.json'
+ };
+
+ var anim = bodymovin.loadAnimation(content);
+ </script>
+
+
+</body>
+
+</html>
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
new file mode 100644
index 0000000..b2c0eb1
--- /dev/null
+++ b/tests/manual/manual.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += testApp
diff --git a/tests/manual/testApp/.gitignore b/tests/manual/testApp/.gitignore
new file mode 100644
index 0000000..fab7372
--- /dev/null
+++ b/tests/manual/testApp/.gitignore
@@ -0,0 +1,73 @@
+# This file is used to ignore files which are generated
+# ----------------------------------------------------------------------------
+
+*~
+*.autosave
+*.a
+*.core
+*.moc
+*.o
+*.obj
+*.orig
+*.rej
+*.so
+*.so.*
+*_pch.h.cpp
+*_resource.rc
+*.qm
+.#*
+*.*#
+core
+!core/
+tags
+.DS_Store
+.directory
+*.debug
+Makefile*
+*.prl
+*.app
+moc_*.cpp
+ui_*.h
+qrc_*.cpp
+Thumbs.db
+*.res
+*.rc
+/.qmake.cache
+/.qmake.stash
+
+# qtcreator generated files
+*.pro.user*
+
+# xemacs temporary files
+*.flc
+
+# Vim temporary files
+.*.swp
+
+# Visual Studio generated files
+*.ib_pdb_index
+*.idb
+*.ilk
+*.pdb
+*.sln
+*.suo
+*.vcproj
+*vcproj.*.*.user
+*.ncb
+*.sdf
+*.opensdf
+*.vcxproj
+*vcxproj.*
+
+# MinGW generated files
+*.Debug
+*.Release
+
+# Python byte code
+*.pyc
+
+# Binaries
+# --------
+*.dll
+*.exe
+
diff --git a/tests/manual/testApp/easing_rect_translate.json b/tests/manual/testApp/easing_rect_translate.json
new file mode 100644
index 0000000..2cede5b
--- /dev/null
+++ b/tests/manual/testApp/easing_rect_translate.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"rect translate","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[50,-31.75],[-50,-31.75],[-50,31.75],[50,31.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.939,"y":0},"o":{"x":0.958,"y":0.066},"n":"0p939_0_0p958_0p066","t":0,"s":[-37.5,-1.25],"e":[137.5,-1.25],"to":[29.1666660308838,0],"ti":[-29.1666660308838,0]},{"t":168.00000684278}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/gradient_fill.json b/tests/manual/testApp/gradient_fill.json
new file mode 100644
index 0000000..8351a5b
--- /dev/null
+++ b/tests/manual/testApp/gradient_fill.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[106.5,71.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[103.689,91.954,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":41,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.5,0,0.5,1,0,0,1],"ix":9}},"s":{"a":0,"k":[2,0],"ix":5},"e":{"a":0,"k":[15,6],"ix":6},"t":2,"h":{"a":0,"k":84,"ix":7},"a":{"a":0,"k":-7,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/layer_effect_fill.json b/tests/manual/testApp/layer_effect_fill.json
new file mode 100644
index 0000000..36e4821
--- /dev/null
+++ b/tests/manual/testApp/layer_effect_fill.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":21,"nm":"Fill","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"Fill Mask","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":7,"nm":"All Masks","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":2,"nm":"Color","mn":"ADBE Fill-0002","ix":3,"v":{"a":0,"k":[1,0.223529413342,0.168627455831,1],"ix":3}},{"ty":7,"nm":"Invert","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Horizontal Feather","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Vertical Feather","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Opacity","mn":"ADBE Fill-0005","ix":7,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[1],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[0],"e":[1]},{"t":110.000004480392}],"ix":7}}]},{"ty":5,"nm":"Checkbox Control","np":3,"mn":"ADBE Checkbox Control","ix":2,"en":1,"ef":[{"ty":7,"nm":"Checkbox","mn":"ADBE Checkbox Control-0001","ix":1,"v":{"a":0,"k":1,"ix":1}}]},{"ty":5,"nm":"Slider Control","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[43.5,-53.5],[-43.5,-53.5],[-43.5,53.5],[43.5,53.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[57,-44],[-57,-44],[-57,44],[57,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.235294117647,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-52,-15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":21,"nm":"Fill","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"Fill Mask","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":7,"nm":"All Masks","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":2,"nm":"Color","mn":"ADBE Fill-0002","ix":3,"v":{"a":0,"k":[1,0.823529422283,0.121568627656,1],"ix":3}},{"ty":7,"nm":"Invert","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Horizontal Feather","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Vertical Feather","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Opacity","mn":"ADBE Fill-0005","ix":7,"v":{"a":0,"k":1,"ix":7}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[129,-35.5],[-129,-35.5],[-129,35.5],[129,35.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.027450980619,0.854901969433,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-25,-52.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/layer_effect_slider.json b/tests/manual/testApp/layer_effect_slider.json
new file mode 100644
index 0000000..14b7da7
--- /dev/null
+++ b/tests/manual/testApp/layer_effect_slider.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Slider Control","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[46.696]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":42,"s":[46.696],"e":[100]},{"t":83.0000033806593}],"ix":1}}]},{"ty":5,"nm":"Slider Control 2","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":21,"s":[0],"e":[96.916]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":60,"s":[96.916],"e":[0]},{"t":83.0000033806593}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[95,-10],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3,"x":"var $bm_rt;\n$bm_rt = content('Group 1').transform.scale;"},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7,"x":"var $bm_rt;\n$bm_rt = effect('Slider Control')('Slider');"},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[63.5,-33.5],[-63.5,-33.5],[-63.5,33.5],[63.5,33.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-114.5,-0.5],"ix":2,"x":"var $bm_rt;\nvar temp;\ntemp = effect('Slider Control 2')('Slider');\n$bm_rt = [\n temp,\n temp\n];"},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7,"x":"var $bm_rt;\n$bm_rt = effect('Slider Control')('Slider');"},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/linked_layers.json b/tests/manual/testApp/linked_layers.json
new file mode 100644
index 0000000..44886c9
--- /dev/null
+++ b/tests/manual/testApp/linked_layers.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":10.0000004073083,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":20,"s":[150,0,0],"e":[0,0,0],"to":[-37.2210121154785,-3.5262824391979e-15,0],"ti":[21.4031734466553,2.0277157723221e-15,0]},{"t":110.000004480392}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.874509811401,0.082352943718,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":-15.0000006109625,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":21,"s":[50,112,0],"e":[200,112,0],"to":[37.6828193664551,0,0],"ti":[-21.1501274108887,0,0]},{"t":110.000004480392}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":-17.0000006924242,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/main.cpp b/tests/manual/testApp/main.cpp
new file mode 100644
index 0000000..c07f125
--- /dev/null
+++ b/tests/manual/testApp/main.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ if (qEnvironmentVariableIsEmpty("QTGLESSTREAM_DISPLAY")) {
+ qputenv("QT_QPA_EGLFS_PHYSICAL_WIDTH", QByteArray("213"));
+ qputenv("QT_QPA_EGLFS_PHYSICAL_HEIGHT", QByteArray("120"));
+
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ }
+
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}
diff --git a/tests/manual/testApp/main.qml b/tests/manual/testApp/main.qml
new file mode 100644
index 0000000..fdc1878
--- /dev/null
+++ b/tests/manual/testApp/main.qml
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the lottie-qt module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.9
+import QtQuick.Window 2.2
+import Qt.labs.lottieqt 1.0
+
+Window {
+ visible: true
+ width: animContainer.width
+ height: animContainer.height
+ title: qsTr("Animation test")
+
+ property bool showFps: false
+
+ color: "black"
+
+ Item {
+ id: animContainer
+ width: childrenRect.width
+ height: childrenRect.height
+
+ Repeater {
+ model: 1
+
+ LottieAnimation {
+ id: bmAnim
+
+ x: 10 * index
+ y: 10 * index
+ loops: LottieAnimation.Infinite
+ quality: LottieAnimation.MediumQuality
+ source: ":/rect_rotate.json"
+ }
+ }
+ }
+
+ Text {
+ id: text
+
+ property real t
+ property int frame: 0
+
+ anchors.right: parent.right
+ color: "red"
+ visible: showFps
+ text: "FPS: " + fpsTimer.fps
+
+ Timer {
+ id: fpsTimer
+ property real fps: 0
+ repeat: true
+ interval: 1000
+ running: showFps
+ onTriggered: {
+ parent.text = "FPS: " + fpsTimer.fps
+ fps = text.frame
+ text.frame = 0
+ }
+ }
+
+ NumberAnimation on t {
+ id: tAnim
+ from: 0
+ to: 1000
+ running: showFps
+ loops: Animation.Infinite
+ }
+
+ onTChanged: {
+ update() // force continuous animation
+ text.frame++;
+ }
+ }
+
+ Rectangle {
+ id: rot
+
+ width: 10
+ height: 100
+ anchors.centerIn: parent
+ color: "blue"
+ enabled: showFps
+ visible: enabled
+
+ PropertyAnimation {
+ target: rot
+ property: "rotation"
+ from: 0
+ to: 360
+ duration: 500
+ running: true
+ loops: Animation.Infinite
+ }
+ }
+
+ PropertyAnimation {
+ target: rot
+ property: "rotation"
+ from: 0
+ to: 360
+ duration: 500
+ running: true
+ loops: Animation.Infinite
+ }
+}
diff --git a/tests/manual/testApp/path_and_ellipse.json b/tests/manual/testApp/path_and_ellipse.json
new file mode 100644
index 0000000..7332d97
--- /dev/null
+++ b/tests/manual/testApp/path_and_ellipse.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[460,150.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.027450980619,0.996078431606,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 2","mn":"ADBE Vector Graphic - Fill","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[],"o":[],"v":[],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[96,1],[0,0],[17.091,27.482],[0,0]],"o":[[-96,-1],[0,0],[-17.091,-27.482],[0,0]],"v":[[-311,-13.5],[-258,54.5],[-245.909,9.982],[-236,-26.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/qml.qrc b/tests/manual/testApp/qml.qrc
new file mode 100644
index 0000000..b678a61
--- /dev/null
+++ b/tests/manual/testApp/qml.qrc
@@ -0,0 +1,45 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>rect_anim_easing.json</file>
+ <file>rect_move_bezier.json</file>
+ <file>rect_anchor_moved_static2.json</file>
+ <file>rect_anchor_std_anim.json</file>
+ <file>shape_static.json</file>
+ <file>shape_circle.json</file>
+ <file>rect_rotate.json</file>
+ <file>shape_complex.json</file>
+ <file>shape_complex2.json</file>
+ <file>rect_skew_axis.json</file>
+ <file>rect_skew.json</file>
+ <file>rect_gfill_radial.json</file>
+ <file>rect_gfill_linear.json</file>
+ <file>trim_path1.json</file>
+ <file>trim_path2.json</file>
+ <file>trim_path2_individual.json</file>
+ <file>path_and_ellipse.json</file>
+ <file>trim_path2_offset.json</file>
+ <file>trim_path_multiple1.json</file>
+ <file>trim_path_multiple2.json</file>
+ <file>trim_path_multiple3.json</file>
+ <file>trim_path_multiple4.json</file>
+ <file>rect_negative_scale_anchors.json</file>
+ <file>rect_negative_scale_pos.json</file>
+ <file>rect_negative_scale_pos_anchors_rot.json</file>
+ <file>rect_rotate_shape.json</file>
+ <file>rect_rotate_layer.json</file>
+ <file>rect_rotate_layer2.json</file>
+ <file>trim_path_simultaneous2.json</file>
+ <file>shape_bezier_simple.json</file>
+ <file>layer_effect_fill.json</file>
+ <file>layer_effect_slider.json</file>
+ <file>easing_rect_translate.json</file>
+ <file>repeater1.json</file>
+ <file>repeater_group1.json</file>
+ <file>repeater_group2.json</file>
+ <file>rect_scale.json</file>
+ <file>rect_scale_anchors_on_layer.json</file>
+ <file>rect_scale_anchors_on_rect.json</file>
+ <file>linked_layers.json</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/testApp/rect_anchor_moved_static2.json b/tests/manual/testApp/rect_anchor_moved_static2.json
new file mode 100644
index 0000000..f451d5f
--- /dev/null
+++ b/tests/manual/testApp/rect_anchor_moved_static2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[-43.5,-43.5],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_anchor_std_anim.json b/tests/manual/testApp/rect_anchor_std_anim.json
new file mode 100644
index 0000000..8429b25
--- /dev/null
+++ b/tests/manual/testApp/rect_anchor_std_anim.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[-43.5,-43.5],"to":[-7.25,-7.25],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":70,"s":[-43.5,-43.5],"e":[0,0],"to":[0,0],"ti":[-7.25,-7.25]},{"t":119.000004846969}],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":147.000005987433,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_anim_easing.json b/tests/manual/testApp/rect_anim_easing.json
new file mode 100644
index 0000000..55e6560
--- /dev/null
+++ b/tests/manual/testApp/rect_anim_easing.json
@@ -0,0 +1,2 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":22.0000008960784,"op":517.000021057842,"w":1920,"h":1080,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":22.355,"s":[652.714,831.376,0],"e":[761.705,991.696,0],"to":[-35.3866271972656,-331.209503173828,0],"ti":[421.808654785156,-54.9365158081055,0]},{"i":{"x":0.703,"y":1},"o":{"x":0.333,"y":0},"n":"0p703_1_0p333_0","t":76,"s":[761.705,991.696,0],"e":[1066.115,493.603,0],"to":[-50.1014442443848,6.525230884552,0],"ti":[-401.992126464844,-38.2022514343262,0]},{"t":109.741254469853}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[273.185,168.44],"ix":2,"x":"var $bm_rt;\n$bm_rt = content('Rectangle 1').content('Rectangle Path 1').size;"},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[0,0],"e":[242,242],"to":[40.3333320617676,0],"ti":[-40.3333320617676,0]},{"t":113.000004602584}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-492.189,-63.696],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-17.0000006924242,"op":496.000020202494,"st":0,"bm":0}],"markers":[]}
+
diff --git a/tests/manual/testApp/rect_gfill_linear.json b/tests/manual/testApp/rect_gfill_linear.json
new file mode 100644
index 0000000..f6993f1
--- /dev/null
+++ b/tests/manual/testApp/rect_gfill_linear.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[110.5,70.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,88.682,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[2,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.5,0,0.5,1,0,0,1],"ix":9}},"s":{"a":0,"k":[-50,-50],"ix":5},"e":{"a":0,"k":[0,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_gfill_radial.json b/tests/manual/testApp/rect_gfill_radial.json
new file mode 100644
index 0000000..51b291b
--- /dev/null
+++ b/tests/manual/testApp/rect_gfill_radial.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.5,0,0.5,1,0,0,1],"ix":9}},"s":{"a":0,"k":[-50,-50],"ix":5},"e":{"a":0,"k":[0,0],"ix":6},"t":2,"h":{"a":0,"k":100,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_move_bezier.json b/tests/manual/testApp/rect_move_bezier.json
new file mode 100644
index 0000000..4e5e179
--- /dev/null
+++ b/tests/manual/testApp/rect_move_bezier.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[200,112,0],"e":[331,214,0],"to":[21.8333339691162,17,0],"ti":[-161.33332824707,2.33333325386047,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":35,"s":[331,214,0],"e":[493,89,0],"to":[161.33332824707,-2.33333325386047,0],"ti":[13.3333330154419,12.75,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":83,"s":[493,89,0],"e":[251,137.5,0],"to":[-13.3333330154419,-12.75,0],"ti":[40.3333320617676,-8.08333301544189,0]},{"t":122.000004969162}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[38,-32.5],[-38,-32.5],[-38,32.5],[38,32.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-137.5,-59],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_negative_scale_anchors.json b/tests/manual/testApp/rect_negative_scale_anchors.json
new file mode 100644
index 0000000..3baeb55
--- /dev/null
+++ b/tests/manual/testApp/rect_negative_scale_anchors.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-562,150.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-272,1.5],[276,5.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1,-152.5],[-2,156.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[50,50],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_negative_scale_pos.json b/tests/manual/testApp/rect_negative_scale_pos.json
new file mode 100644
index 0000000..e327813
--- /dev/null
+++ b/tests/manual/testApp/rect_negative_scale_pos.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-562,150.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-272,1.5],[276,5.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1,-152.5],[-2,156.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json b/tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json
new file mode 100644
index 0000000..a88ca9d
--- /dev/null
+++ b/tests/manual/testApp/rect_negative_scale_pos_anchors_rot.json
@@ -0,0 +1,2 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[-562,150.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-272,1.5],[276,5.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1,-152.5],[-2,156.5]],"c":false},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,-100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
+
diff --git a/tests/manual/testApp/rect_rotate.json b/tests/manual/testApp/rect_rotate.json
new file mode 100644
index 0000000..a049549
--- /dev/null
+++ b/tests/manual/testApp/rect_rotate.json
@@ -0,0 +1,2 @@
+{"v":"5.3.1","fr":60,"ip":0,"op":114.000004643315,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[63]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":45,"s":[63],"e":[0]},{"t":90.0000036657751}],"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[54.5,66.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":114.000004643315,"st":0,"bm":0}],"markers":[]}
+
diff --git a/tests/manual/testApp/rect_rotate_layer.json b/tests/manual/testApp/rect_rotate_layer.json
new file mode 100644
index 0000000..f270c31
--- /dev/null
+++ b/tests/manual/testApp/rect_rotate_layer.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[720]},{"t":70.0000028511585}],"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-6,99.5],[267,99.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36,-32],[-36,-32],[-36,32],[36,32]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[50,50],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[],"o":[],"v":[],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_rotate_layer2.json b/tests/manual/testApp/rect_rotate_layer2.json
new file mode 100644
index 0000000..0b70115
--- /dev/null
+++ b/tests/manual/testApp/rect_rotate_layer2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[-100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-6,99.5],[267,99.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36,-32],[-36,-32],[-36,32],[36,32]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,59.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[],"o":[],"v":[],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_rotate_shape.json b/tests/manual/testApp/rect_rotate_shape.json
new file mode 100644
index 0000000..5465c36
--- /dev/null
+++ b/tests/manual/testApp/rect_rotate_shape.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275,154.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[36,-32],[-36,-32],[-36,32],[36,32]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[87,59.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[360],"e":[720]},{"t":70.0000028511585}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_scale.json b/tests/manual/testApp/rect_scale.json
new file mode 100644
index 0000000..6f0559d
--- /dev/null
+++ b/tests/manual/testApp/rect_scale.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"rect_scale","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[100,100,100],"e":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":47,"s":[0,0,100],"e":[100,100,100]},{"t":94.0000038286985}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[47,-41.5],[-47,-41.5],[-47,41.5],[47,41.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":95.0000038694293,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_scale_anchors_on_layer.json b/tests/manual/testApp/rect_scale_anchors_on_layer.json
new file mode 100644
index 0000000..7574a14
--- /dev/null
+++ b/tests/manual/testApp/rect_scale_anchors_on_layer.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":30,"ip":30,"op":60,"w":320,"h":320,"nm":"Star - On","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Star Echo","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128.5,77.5,0],"ix":2},"a":{"a":0,"k":[240,240,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0p25_1_0p001_0","0p25_1_0p001_0","0p25_1_0p001_0"],"t":30,"s":[100,100,100],"e":[70,70,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0_1_0p001_0","0_1_0p001_0","0_1_0p001_0"],"t":36,"s":[70,70,100],"e":[200,200,100]},{"t":56}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[8.843,-21.095],[-23.375,-21.095],[-23.375,13.122],[8.843,13.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.638999968884,0.513999968884,0.380000005984,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[255.393,265.656],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":15,"bm":0}],"markers":[{"tm":30,"cm":"Start","dr":0},{"tm":60,"cm":"End","dr":0}]}
diff --git a/tests/manual/testApp/rect_scale_anchors_on_rect.json b/tests/manual/testApp/rect_scale_anchors_on_rect.json
new file mode 100644
index 0000000..fb54438
--- /dev/null
+++ b/tests/manual/testApp/rect_scale_anchors_on_rect.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":30,"ip":30,"op":60,"w":320,"h":320,"nm":"Star - On","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Star Echo","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[128.5,77.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0p25_1_0p001_0","0p25_1_0p001_0","0p25_1_0p001_0"],"t":30,"s":[100,100,100],"e":[70,70,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"n":["0_1_0p001_0","0_1_0p001_0","0_1_0p001_0"],"t":36,"s":[70,70,100],"e":[200,200,100]},{"t":56}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[8.843,-21.095],[-23.375,-21.095],[-23.375,13.122],[8.843,13.122]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.638999968884,0.513999968884,0.380000005984,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[255.393,265.656],"ix":2},"a":{"a":0,"k":[240,240],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":15,"bm":0}],"markers":[{"tm":30,"cm":"Start","dr":0},{"tm":60,"cm":"End","dr":0}]}
diff --git a/tests/manual/testApp/rect_skew.json b/tests/manual/testApp/rect_skew.json
new file mode 100644
index 0000000..5c89472
--- /dev/null
+++ b/tests/manual/testApp/rect_skew.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[106.5,71.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[41]},{"t":139.000005661586}],"ix":4},"sa":{"a":0,"k":191,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/rect_skew_axis.json b/tests/manual/testApp/rect_skew_axis.json
new file mode 100644
index 0000000..d31b1e6
--- /dev/null
+++ b/tests/manual/testApp/rect_skew_axis.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":241.000009816131,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[106.5,71.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[43.5,43.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":41,"ix":4},"sa":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[191]},{"t":139.000005661586}],"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/repeater1.json b/tests/manual/testApp/repeater1.json
new file mode 100644
index 0000000..9361d4e
--- /dev/null
+++ b/tests/manual/testApp/repeater1.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64,76,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.615686297417,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 2","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gr","it":[{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":6,"tr":{"ty":"tr","p":{"a":0,"k":[100,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/repeater_group1.json b/tests/manual/testApp/repeater_group1.json
new file mode 100644
index 0000000..3bddb7d
--- /dev/null
+++ b/tests/manual/testApp/repeater_group1.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[64,76,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[56,56],"ix":2},"p":{"a":0,"k":[0,86],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.070588238537,0.917647063732,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.615686297417,0.129411771894,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 2","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":6,"tr":{"ty":"tr","p":{"a":0,"k":[105,12],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[73,73],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":0,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[6.25,-5.5],[-6.25,-5.5],[-6.25,5.5],[6.25,5.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23529399797,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-25.75,-66.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":7,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/repeater_group2.json b/tests/manual/testApp/repeater_group2.json
new file mode 100644
index 0000000..9fcf3ec
--- /dev/null
+++ b/tests/manual/testApp/repeater_group2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":50,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-25],"e":[75]},{"t":61.0000024845809}],"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"rp","c":{"a":0,"k":3,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":6,"tr":{"ty":"tr","p":{"a":0,"k":[124,73],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[89,89],"ix":3},"r":{"a":0,"k":0,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/shape_bezier_simple.json b/tests/manual/testApp/shape_bezier_simple.json
new file mode 100644
index 0000000..d002cfd
--- /dev/null
+++ b/tests/manual/testApp/shape_bezier_simple.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-15,0]],"o":[[0,0],[58.502,0]],"v":[[1,0],[-0.5,61]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":174.825007120768,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/shape_circle.json b/tests/manual/testApp/shape_circle.json
new file mode 100644
index 0000000..bd41883
--- /dev/null
+++ b/tests/manual/testApp/shape_circle.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[47.497,-0.511],[-43,0]],"o":[[-46.5,0.5],[50,0]],"v":[[0.5,0.5],[0,69.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/shape_complex.json b/tests/manual/testApp/shape_complex.json
new file mode 100644
index 0000000..f726168
--- /dev/null
+++ b/tests/manual/testApp/shape_complex.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-38.234,34.801],[-32.222,-9.403],[-14.71,0.126],[-21.203,33.16]],"o":[[0,0],[4.82,-4.387],[32.222,9.403],[23.332,-0.199],[21.203,-33.16]],"v":[[81,22],[10.234,8.699],[1.278,-25.403],[2.5,-59],[55.297,-30.84]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":174.825007120768,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/shape_complex2.json b/tests/manual/testApp/shape_complex2.json
new file mode 100644
index 0000000..554dd1f
--- /dev/null
+++ b/tests/manual/testApp/shape_complex2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13,-0.5],[0,0],[12.927,19.168],[0,0]],"o":[[47.465,1.826],[0,0],[-14.5,-21.5],[0,0]],"v":[[3.5,4],[17.5,39.5],[-28.5,29.5],[21,-23]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/shape_static.json b/tests/manual/testApp/shape_static.json
new file mode 100644
index 0000000..6a88e15
--- /dev/null
+++ b/tests/manual/testApp/shape_static.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":356,"h":200,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[178,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[35.368,-37.448],[-9,37.5]],"o":[[0,0],[0,0],[-34,36],[9,-37.5]],"v":[[-16,-29],[-45,-21.5],[-100.5,14.5],[56,3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/stroke.json b/tests/manual/testApp/stroke.json
new file mode 100644
index 0000000..0248085
--- /dev/null
+++ b/tests/manual/testApp/stroke.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0,0,0,1],"e":[1,1,1,1]},{"t":64.0000026067734}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/testApp.pro b/tests/manual/testApp/testApp.pro
new file mode 100644
index 0000000..621f618
--- /dev/null
+++ b/tests/manual/testApp/testApp.pro
@@ -0,0 +1,34 @@
+QT += quick
+CONFIG += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main.cpp \
+
+RESOURCES += qml.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+# Additional import path used to resolve QML modules just for Qt Quick Designer
+QML_DESIGNER_IMPORT_PATH =
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+qmldir.files = C:\users\kahautam\projects\lg\lottie-qt\src\imports\qmldir
+qmldir.path = /usr/lib/qml/Qt/labs/lottieqt
+INSTALLS += qmldir
+
+#HEADERS +=
diff --git a/tests/manual/testApp/trim_path1.json b/tests/manual/testApp/trim_path1.json
new file mode 100644
index 0000000..872c971
--- /dev/null
+++ b/tests/manual/testApp/trim_path1.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":50.0000020365418}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path2.json b/tests/manual/testApp/trim_path2.json
new file mode 100644
index 0000000..8bb22d5
--- /dev/null
+++ b/tests/manual/testApp/trim_path2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[103,158],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":50.0000020365418}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path2_individual.json b/tests/manual/testApp/trim_path2_individual.json
new file mode 100644
index 0000000..bf88c01
--- /dev/null
+++ b/tests/manual/testApp/trim_path2_individual.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[9,-2,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":16,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":50.0000020365418}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":4,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path2_offset.json b/tests/manual/testApp/trim_path2_offset.json
new file mode 100644
index 0000000..36e8804
--- /dev/null
+++ b/tests/manual/testApp/trim_path2_offset.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":50,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[-25],"e":[75]},{"t":61.0000024845809}],"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path_multiple1.json b/tests/manual/testApp/trim_path_multiple1.json
new file mode 100644
index 0000000..113e8c5
--- /dev/null
+++ b/tests/manual/testApp/trim_path_multiple1.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":50,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path_multiple2.json b/tests/manual/testApp/trim_path_multiple2.json
new file mode 100644
index 0000000..4ca4696
--- /dev/null
+++ b/tests/manual/testApp/trim_path_multiple2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":25,"ix":1},"e":{"a":0,"k":75,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 3","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":50,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":6,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path_multiple3.json b/tests/manual/testApp/trim_path_multiple3.json
new file mode 100644
index 0000000..598585d
--- /dev/null
+++ b/tests/manual/testApp/trim_path_multiple3.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":25,"ix":1},"e":{"a":0,"k":75,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 3","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":50,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[100],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":5,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path_multiple4.json b/tests/manual/testApp/trim_path_multiple4.json
new file mode 100644
index 0000000..c3b6946
--- /dev/null
+++ b/tests/manual/testApp/trim_path_multiple4.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":550,"h":309,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[28.5,-4,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":2,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[267,155],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1.1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false,"cl":"1"},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[54,-44],[-54,-44],[-54,44],[54,44]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[190,154],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":53.0000021587343}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":2,"nm":"Trim Paths 2","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1.2","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false,"cl":"2"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/manual/testApp/trim_path_simultaneous2.json b/tests/manual/testApp/trim_path_simultaneous2.json
new file mode 100644
index 0000000..5618821
--- /dev/null
+++ b/tests/manual/testApp/trim_path_simultaneous2.json
@@ -0,0 +1 @@
+{"v":"5.3.1","fr":29.9700012207031,"ip":0,"op":175.000007127896,"w":400,"h":224,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,112,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-5,5.5],[-58,-33],[-118.5,6]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[50],"e":[0]},{"t":44.0000017921567}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[50],"e":[100]},{"t":44.0000017921567}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":175.000007127896,"st":0,"bm":0}],"markers":[]}
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644
index 0000000..b71cdd5
--- /dev/null
+++ b/tests/tests.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+SUBDIRS += \
+ auto \
+ manual