Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt5
-rw-r--r--tests/check_deprecated.py6
-rw-r--r--tests/gtests/CMakeLists.txt8
-rw-r--r--tests/gtests/alembic/CMakeLists.txt52
-rw-r--r--tests/gtests/alembic/abc_export_test.cc120
-rw-r--r--tests/gtests/alembic/abc_matrix_test.cc282
-rw-r--r--tests/gtests/blenlib/BLI_array_store_test.cc68
-rw-r--r--tests/gtests/blenlib/BLI_array_utils_test.cc38
-rw-r--r--tests/gtests/blenlib/BLI_ghash_performance_test.cc18
-rw-r--r--tests/gtests/blenlib/BLI_ghash_test.cc34
-rw-r--r--tests/gtests/blenlib/BLI_hash_mm2a_test.cc16
-rw-r--r--tests/gtests/blenlib/BLI_kdopbvh_test.cc97
-rw-r--r--tests/gtests/blenlib/BLI_listbase_test.cc26
-rw-r--r--tests/gtests/blenlib/BLI_math_geom_test.cc2
-rw-r--r--tests/gtests/blenlib/BLI_path_util_test.cc255
-rw-r--r--tests/gtests/blenlib/BLI_polyfill2d_test.cc87
-rw-r--r--tests/gtests/blenlib/BLI_stack_test.cc32
-rw-r--r--tests/gtests/blenlib/BLI_string_test.cc104
-rw-r--r--tests/gtests/blenlib/BLI_string_utf8_test.cc304
-rw-r--r--tests/gtests/blenlib/CMakeLists.txt29
-rw-r--r--tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h18
-rw-r--r--tests/gtests/bmesh/bmesh_core_test.cc18
-rw-r--r--tests/gtests/guardedalloc/guardedalloc_alignment_test.cc2
-rw-r--r--tests/gtests/testing/CMakeLists.txt16
-rw-r--r--tests/gtests/testing/testing.h23
-rw-r--r--tests/gtests/testing/testing_main.cc2
-rw-r--r--tests/python/CMakeLists.txt304
-rwxr-xr-xtests/python/alembic_tests.py478
-rw-r--r--tests/python/batch_import.py41
-rw-r--r--tests/python/bl_alembic_import_test.py270
-rw-r--r--tests/python/bl_keymap_completeness.py1
-rw-r--r--tests/python/bl_load_py_modules.py69
-rw-r--r--tests/python/bl_mesh_modifiers.py75
-rw-r--r--tests/python/bl_pyapi_bpy_utils_units.py19
-rw-r--r--tests/python/bl_pyapi_idprop.py204
-rw-r--r--tests/python/bl_pyapi_idprop_datablock.py338
-rw-r--r--tests/python/bl_pyapi_mathutils.py9
-rw-r--r--tests/python/bl_run_operators.py18
-rwxr-xr-xtests/python/cycles_render_tests.py408
-rw-r--r--tests/python/pep8.py1
-rw-r--r--tests/python/rna_info_dump.py1
41 files changed, 3417 insertions, 481 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index fa0b86a2637..64326f34377 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,7 +1,8 @@
# Python CTests
-add_subdirectory(python)
+if(WITH_BLENDER)
+ add_subdirectory(python)
+endif()
# GTest
add_subdirectory(gtests)
-
diff --git a/tests/check_deprecated.py b/tests/check_deprecated.py
index cf8f8e0cc35..6e07f8fdb31 100644
--- a/tests/check_deprecated.py
+++ b/tests/check_deprecated.py
@@ -53,10 +53,8 @@ def is_source_any(filename):
def source_list(path, filename_check=None):
for dirpath, dirnames, filenames in os.walk(path):
-
- # skip '.svn'
- if dirpath.startswith("."):
- continue
+ # skip '.git'
+ dirnames[:] = [d for d in dirnames if not d.startswith(".")]
for filename in filenames:
if filename_check is None or filename_check(filename):
diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt
index a3860ce3e67..781da7bf452 100644
--- a/tests/gtests/CMakeLists.txt
+++ b/tests/gtests/CMakeLists.txt
@@ -4,6 +4,10 @@ if(WITH_GTESTS)
Include(GTestTesting)
+ add_definitions(${GFLAGS_DEFINES})
+ add_definitions(${GLOG_DEFINES})
+ add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
+
# Otherwise we get warnings here that we cant fix in external projects
remove_strict_flags()
@@ -11,5 +15,7 @@ if(WITH_GTESTS)
add_subdirectory(blenlib)
add_subdirectory(guardedalloc)
add_subdirectory(bmesh)
+ if(WITH_ALEMBIC)
+ add_subdirectory(alembic)
+ endif()
endif()
-
diff --git a/tests/gtests/alembic/CMakeLists.txt b/tests/gtests/alembic/CMakeLists.txt
new file mode 100644
index 00000000000..fadf549e212
--- /dev/null
+++ b/tests/gtests/alembic/CMakeLists.txt
@@ -0,0 +1,52 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# Contributor(s): Sybren A. Stüvel
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ..
+ ../../../source/blender/blenlib
+ ../../../source/blender/alembic
+ ../../../source/blender/makesdna
+ ${ALEMBIC_INCLUDE_DIRS}
+ ${BOOST_INCLUDE_DIR}
+ ${HDF5_INCLUDE_DIRS}
+ ${OPENEXR_INCLUDE_DIRS}
+)
+
+include_directories(${INC})
+
+setup_libdirs()
+get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP)
+
+if(WITH_BUILDINFO)
+ set(_buildinfo_src "$<TARGET_OBJECTS:buildinfoobj>")
+else()
+ set(_buildinfo_src "")
+endif()
+
+# For motivation on doubling BLENDER_SORTED_LIBS, see ../bmesh/CMakeLists.txt
+BLENDER_SRC_GTEST(alembic "abc_matrix_test.cc;abc_export_test.cc;${_buildinfo_src}" "${BLENDER_SORTED_LIBS};${BLENDER_SORTED_LIBS}")
+
+unset(_buildinfo_src)
+
+setup_liblinks(alembic_test)
diff --git a/tests/gtests/alembic/abc_export_test.cc b/tests/gtests/alembic/abc_export_test.cc
new file mode 100644
index 00000000000..63c1d179e51
--- /dev/null
+++ b/tests/gtests/alembic/abc_export_test.cc
@@ -0,0 +1,120 @@
+#include "testing/testing.h"
+
+// Keep first since utildefines defines AT which conflicts with fucking STL
+#include "intern/abc_util.h"
+#include "intern/abc_exporter.h"
+
+extern "C" {
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "DNA_scene_types.h"
+}
+
+class TestableAbcExporter : public AbcExporter {
+public:
+ TestableAbcExporter(Scene *scene, const char *filename, ExportSettings &settings)
+ : AbcExporter(scene, filename, settings)
+ {}
+
+ void getShutterSamples(unsigned int nr_of_samples,
+ bool time_relative,
+ std::vector<double> &samples)
+ {
+ AbcExporter::getShutterSamples(nr_of_samples, time_relative, samples);
+ }
+
+ void getFrameSet(unsigned int nr_of_samples,
+ std::set<double> &frames) {
+ AbcExporter::getFrameSet(nr_of_samples, frames);
+ }
+
+};
+
+
+TEST(abc_export, TimeSamplesFullShutter) {
+ ExportSettings settings;
+ settings.frame_start = 31.0;
+ settings.frame_end = 223.0;
+ settings.shutter_open = 0.0;
+ settings.shutter_close = 1.0;
+
+ /* Fake a 25 FPS scene with a nonzero base (because that's sometimes forgotten) */
+ Scene scene;
+ scene.r.frs_sec = 50;
+ scene.r.frs_sec_base = 2;
+
+ TestableAbcExporter exporter(&scene, "somefile.abc", settings);
+ std::vector<double> samples;
+
+ /* test 5 samples per frame */
+ exporter.getShutterSamples(5, true, samples);
+ EXPECT_EQ(5, samples.size());
+ EXPECT_NEAR(1.240, samples[0], 1e-5f);
+ EXPECT_NEAR(1.248, samples[1], 1e-5f);
+ EXPECT_NEAR(1.256, samples[2], 1e-5f);
+ EXPECT_NEAR(1.264, samples[3], 1e-5f);
+ EXPECT_NEAR(1.272, samples[4], 1e-5f);
+
+ /* test same, but using frame number offset instead of time */
+ exporter.getShutterSamples(5, false, samples);
+ EXPECT_EQ(5, samples.size());
+ EXPECT_NEAR(0.0, samples[0], 1e-5f);
+ EXPECT_NEAR(0.2, samples[1], 1e-5f);
+ EXPECT_NEAR(0.4, samples[2], 1e-5f);
+ EXPECT_NEAR(0.6, samples[3], 1e-5f);
+ EXPECT_NEAR(0.8, samples[4], 1e-5f);
+
+ /* use the same setup to test getFrameSet() */
+ std::set<double> frames;
+ exporter.getFrameSet(5, frames);
+ EXPECT_EQ(965, frames.size());
+ EXPECT_EQ(1, frames.count(31.0));
+ EXPECT_EQ(1, frames.count(31.2));
+ EXPECT_EQ(1, frames.count(31.4));
+ EXPECT_EQ(1, frames.count(31.6));
+ EXPECT_EQ(1, frames.count(31.8));
+}
+
+
+TEST(abc_export, TimeSamples180degShutter) {
+ ExportSettings settings;
+ settings.frame_start = 31.0;
+ settings.frame_end = 223.0;
+ settings.shutter_open = -0.25;
+ settings.shutter_close = 0.25;
+
+ /* Fake a 25 FPS scene with a nonzero base (because that's sometimes forgotten) */
+ Scene scene;
+ scene.r.frs_sec = 50;
+ scene.r.frs_sec_base = 2;
+
+ TestableAbcExporter exporter(&scene, "somefile.abc", settings);
+ std::vector<double> samples;
+
+ /* test 5 samples per frame */
+ exporter.getShutterSamples(5, true, samples);
+ EXPECT_EQ(5, samples.size());
+ EXPECT_NEAR(1.230, samples[0], 1e-5f);
+ EXPECT_NEAR(1.234, samples[1], 1e-5f);
+ EXPECT_NEAR(1.238, samples[2], 1e-5f);
+ EXPECT_NEAR(1.242, samples[3], 1e-5f);
+ EXPECT_NEAR(1.246, samples[4], 1e-5f);
+
+ /* test same, but using frame number offset instead of time */
+ exporter.getShutterSamples(5, false, samples);
+ EXPECT_EQ(5, samples.size());
+ EXPECT_NEAR(-0.25, samples[0], 1e-5f);
+ EXPECT_NEAR(-0.15, samples[1], 1e-5f);
+ EXPECT_NEAR(-0.05, samples[2], 1e-5f);
+ EXPECT_NEAR( 0.05, samples[3], 1e-5f);
+ EXPECT_NEAR( 0.15, samples[4], 1e-5f);
+
+ /* Use the same setup to test getFrameSet().
+ * Here only a few numbers are tested, due to rounding issues. */
+ std::set<double> frames;
+ exporter.getFrameSet(5, frames);
+ EXPECT_EQ(965, frames.size());
+ EXPECT_EQ(1, frames.count(30.75));
+ EXPECT_EQ(1, frames.count(30.95));
+ EXPECT_EQ(1, frames.count(31.15));
+}
diff --git a/tests/gtests/alembic/abc_matrix_test.cc b/tests/gtests/alembic/abc_matrix_test.cc
new file mode 100644
index 00000000000..08bce1ed50f
--- /dev/null
+++ b/tests/gtests/alembic/abc_matrix_test.cc
@@ -0,0 +1,282 @@
+#include "testing/testing.h"
+
+// Keep first since utildefines defines AT which conflicts with fucking STL
+#include "intern/abc_util.h"
+
+extern "C" {
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+}
+
+
+TEST(abc_matrix, CreateRotationMatrixY_YfromZ) {
+ // Input variables
+ float rot_x_mat[3][3];
+ float rot_y_mat[3][3];
+ float rot_z_mat[3][3];
+ float euler[3] = {0.f, M_PI_4, 0.f};
+
+ // Construct expected matrices
+ float unit[3][3];
+ float rot_z_min_quart_pi[3][3]; // rotation of -pi/4 radians over z-axis
+
+ unit_m3(unit);
+ unit_m3(rot_z_min_quart_pi);
+ rot_z_min_quart_pi[0][0] = M_SQRT1_2;
+ rot_z_min_quart_pi[0][1] = -M_SQRT1_2;
+ rot_z_min_quart_pi[1][0] = M_SQRT1_2;
+ rot_z_min_quart_pi[1][1] = M_SQRT1_2;
+
+ // Run tests
+ create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
+ ABC_YUP_FROM_ZUP);
+
+ EXPECT_M3_NEAR(rot_x_mat, unit, 1e-5f);
+ EXPECT_M3_NEAR(rot_y_mat, unit, 1e-5f);
+ EXPECT_M3_NEAR(rot_z_mat, rot_z_min_quart_pi, 1e-5f);
+}
+
+TEST(abc_matrix, CreateRotationMatrixZ_YfromZ) {
+ // Input variables
+ float rot_x_mat[3][3];
+ float rot_y_mat[3][3];
+ float rot_z_mat[3][3];
+ float euler[3] = {0.f, 0.f, M_PI_4};
+
+ // Construct expected matrices
+ float unit[3][3];
+ float rot_y_quart_pi[3][3]; // rotation of pi/4 radians over y-axis
+
+ unit_m3(unit);
+ unit_m3(rot_y_quart_pi);
+ rot_y_quart_pi[0][0] = M_SQRT1_2;
+ rot_y_quart_pi[0][2] = -M_SQRT1_2;
+ rot_y_quart_pi[2][0] = M_SQRT1_2;
+ rot_y_quart_pi[2][2] = M_SQRT1_2;
+
+ // Run tests
+ create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
+ ABC_YUP_FROM_ZUP);
+
+ EXPECT_M3_NEAR(rot_x_mat, unit, 1e-5f);
+ EXPECT_M3_NEAR(rot_y_mat, rot_y_quart_pi, 1e-5f);
+ EXPECT_M3_NEAR(rot_z_mat, unit, 1e-5f);
+}
+
+TEST(abc_matrix, CreateRotationMatrixXYZ_YfromZ) {
+ // Input variables
+ float rot_x_mat[3][3];
+ float rot_y_mat[3][3];
+ float rot_z_mat[3][3];
+ // in degrees: X=10, Y=20, Z=30
+ float euler[3] = {0.17453292012214f, 0.34906581044197f, 0.52359879016876f};
+
+ // Construct expected matrices
+ float rot_x_p10[3][3]; // rotation of +10 degrees over x-axis
+ float rot_y_p30[3][3]; // rotation of +30 degrees over y-axis
+ float rot_z_m20[3][3]; // rotation of -20 degrees over z-axis
+
+ unit_m3(rot_x_p10);
+ rot_x_p10[1][1] = 0.9848077297210693f;
+ rot_x_p10[1][2] = 0.1736481785774231f;
+ rot_x_p10[2][1] = -0.1736481785774231f;
+ rot_x_p10[2][2] = 0.9848077297210693f;
+
+ unit_m3(rot_y_p30);
+ rot_y_p30[0][0] = 0.8660253882408142f;
+ rot_y_p30[0][2] = -0.5f;
+ rot_y_p30[2][0] = 0.5f;
+ rot_y_p30[2][2] = 0.8660253882408142f;
+
+ unit_m3(rot_z_m20);
+ rot_z_m20[0][0] = 0.9396926164627075f;
+ rot_z_m20[0][1] = -0.3420201241970062f;
+ rot_z_m20[1][0] = 0.3420201241970062f;
+ rot_z_m20[1][1] = 0.9396926164627075f;
+
+ // Run tests
+ create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
+ ABC_YUP_FROM_ZUP);
+
+ EXPECT_M3_NEAR(rot_x_mat, rot_x_p10, 1e-5f);
+ EXPECT_M3_NEAR(rot_y_mat, rot_y_p30, 1e-5f);
+ EXPECT_M3_NEAR(rot_z_mat, rot_z_m20, 1e-5f);
+}
+
+TEST(abc_matrix, CreateRotationMatrixXYZ_ZfromY) {
+ // Input variables
+ float rot_x_mat[3][3];
+ float rot_y_mat[3][3];
+ float rot_z_mat[3][3];
+ // in degrees: X=10, Y=20, Z=30
+ float euler[3] = {0.1745329201221466f, 0.3490658104419708f, 0.5235987901687622f};
+
+ // Construct expected matrices
+ float rot_x_p10[3][3]; // rotation of +10 degrees over x-axis
+ float rot_y_m30[3][3]; // rotation of -30 degrees over y-axis
+ float rot_z_p20[3][3]; // rotation of +20 degrees over z-axis
+
+ unit_m3(rot_x_p10);
+ rot_x_p10[1][1] = 0.9848077297210693f;
+ rot_x_p10[1][2] = 0.1736481785774231f;
+ rot_x_p10[2][1] = -0.1736481785774231f;
+ rot_x_p10[2][2] = 0.9848077297210693f;
+
+ unit_m3(rot_y_m30);
+ rot_y_m30[0][0] = 0.8660253882408142f;
+ rot_y_m30[0][2] = 0.5f;
+ rot_y_m30[2][0] = -0.5f;
+ rot_y_m30[2][2] = 0.8660253882408142f;
+
+ unit_m3(rot_z_p20);
+ rot_z_p20[0][0] = 0.9396926164627075f;
+ rot_z_p20[0][1] = 0.3420201241970062f;
+ rot_z_p20[1][0] = -0.3420201241970062f;
+ rot_z_p20[1][1] = 0.9396926164627075f;
+
+ // Run tests
+ create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler,
+ ABC_ZUP_FROM_YUP);
+
+ EXPECT_M3_NEAR(rot_x_mat, rot_x_p10, 1e-5f);
+ EXPECT_M3_NEAR(rot_y_mat, rot_y_m30, 1e-5f);
+ EXPECT_M3_NEAR(rot_z_mat, rot_z_p20, 1e-5f);
+}
+
+TEST(abc_matrix, CopyM44AxisSwap_YfromZ) {
+ float result[4][4];
+
+ /* Construct an input matrix that performs a rotation like the tests
+ * above. This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=20, Z=30 degrees in XYZ order) and translating over (1, 2, 3) */
+ float input[4][4] = {
+ { 0.81379765272f, 0.4698463380336f, -0.342020124197f, 0.f},
+ {-0.44096961617f, 0.8825641274452f, 0.163175910711f, 0.f},
+ { 0.37852230668f, 0.0180283170193f, 0.925416588783f, 0.f},
+ {1.f, 2.f, 3.f, 1.f},
+ };
+
+ copy_m44_axis_swap(result, input, ABC_YUP_FROM_ZUP);
+
+ /* Check the resulting rotation & translation. */
+ float trans[4] = {1.f, 3.f, -2.f, 1.f};
+ EXPECT_V4_NEAR(trans, result[3], 1e-5f);
+
+ /* This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=30, Z=-20 degrees in XZY order) and translating over (1, 3, -2) */
+ float expect[4][4] = {
+ {0.813797652721f, -0.342020124197f, -0.469846338033f, 0.f},
+ {0.378522306680f, 0.925416588783f, -0.018028317019f, 0.f},
+ {0.440969616174f, -0.163175910711f, 0.882564127445f, 0.f},
+ {1.f, 3.f, -2.f, 1.f},
+ };
+ EXPECT_M4_NEAR(expect, result, 1e-5f);
+}
+
+TEST(abc_matrix, CopyM44AxisSwapWithScale_YfromZ) {
+ float result[4][4];
+
+ /* Construct an input matrix that performs a rotation like the tests
+ * above. This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=20, Z=30 degrees in XYZ order), translating over (1, 2, 3),
+ * and scaling by (4, 5, 6). */
+ float input[4][4] = {
+ { 3.25519061088f, 1.8793853521347f, -1.368080496788f, 0.f},
+ {-2.20484805107f, 4.4128208160400f, 0.815879583358f, 0.f},
+ { 2.27113389968f, 0.1081698983907f, 5.552499771118f, 0.f},
+ {1.f, 2.f, 3.f, 1.f},
+ };
+
+ copy_m44_axis_swap(result, input, ABC_YUP_FROM_ZUP);
+
+ /* This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=30, Z=-20 degrees in XZY order), translating over (1, 3, -2)
+ * and scaling over (4, 6, 5). */
+ float expect[4][4] = {
+ {3.255190610885f, -1.368080496788f, -1.879385352134f, 0.f},
+ {2.271133899688f, 5.552499771118f, -0.108169898390f, 0.f},
+ {2.204848051071f, -0.815879583358f, 4.412820816040f, 0.f},
+ {1.f, 3.f, -2.f, 1.f},
+ };
+ EXPECT_M4_NEAR(expect, result, 1e-5f);
+}
+
+TEST(abc_matrix, CopyM44AxisSwap_ZfromY) {
+ float result[4][4];
+
+ /* This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=30, Z=-20 degrees in XZY order) and translating over (1, 3, -2) */
+ float input[4][4] = {
+ {0.813797652721f, -0.342020124197f, -0.469846338033f, 0.f},
+ {0.378522306680f, 0.925416588783f, -0.018028317019f, 0.f},
+ {0.440969616174f, -0.163175910711f, 0.882564127445f, 0.f},
+ {1.f, 3.f, -2.f, 1.f},
+ };
+
+ copy_m44_axis_swap(result, input, ABC_ZUP_FROM_YUP);
+
+ /* This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=20, Z=30 degrees in XYZ order) and translating over (1, 2, 3) */
+ float expect[4][4] = {
+ {0.813797652721f, 0.469846338033f, -0.342020124197f, 0.f},
+ {-0.44096961617f, 0.882564127445f, 0.163175910711f, 0.f},
+ {0.378522306680f, 0.018028317019f, 0.925416588783f, 0.f},
+ {1.f, 2.f, 3.f, 1.f},
+ };
+
+ EXPECT_M4_NEAR(expect, result, 1e-5f);
+}
+
+TEST(abc_matrix, CopyM44AxisSwapWithScale_ZfromY) {
+ float result[4][4];
+
+ /* This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=30, Z=-20 degrees in XZY order), translating over (1, 3, -2)
+ * and scaling over (4, 6, 5). */
+ float input[4][4] = {
+ {3.2551906108f, -1.36808049678f, -1.879385352134f, 0.f},
+ {2.2711338996f, 5.55249977111f, -0.108169898390f, 0.f},
+ {2.2048480510f, -0.81587958335f, 4.412820816040f, 0.f},
+ {1.f, 3.f, -2.f, 1.f},
+ };
+
+ copy_m44_axis_swap(result, input, ABC_ZUP_FROM_YUP);
+
+ /* This matrix was created by rotating a cube in Blender over
+ * (X=10, Y=20, Z=30 degrees in XYZ order), translating over (1, 2, 3),
+ * and scaling by (4, 5, 6). */
+ float expect[4][4] = {
+ {3.25519061088f, 1.879385352134f, -1.36808049678f, 0.f},
+ {-2.2048480510f, 4.412820816040f, 0.81587958335f, 0.f},
+ {2.27113389968f, 0.108169898390f, 5.55249977111f, 0.f},
+ {1.f, 2.f, 3.f, 1.f},
+ };
+
+ EXPECT_M4_NEAR(expect, result, 1e-5f);
+}
+
+TEST(abc_matrix, CopyM44AxisSwapWithScale_gimbal_ZfromY) {
+ float result[4][4];
+
+ /* This matrix represents a rotation over (-90, -0, -0) degrees,
+ * and a translation over (-0, -0.1, -0). It is in Y=up. */
+ float input[4][4] = {
+ { 1.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, 0.000f,-1.000f, 0.000f},
+ { 0.000f, 1.000f, 0.000f, 0.000f},
+ {-0.000f,-0.100f,-0.000f, 1.000f},
+ };
+
+ copy_m44_axis_swap(result, input, ABC_ZUP_FROM_YUP);
+
+ /* Since the rotation is only over the X-axis, it should not change.
+ * The translation does change. */
+ float expect[4][4] = {
+ { 1.000f, 0.000f, 0.000f, 0.000f},
+ { 0.000f, 0.000f,-1.000f, 0.000f},
+ { 0.000f, 1.000f, 0.000f, 0.000f},
+ {-0.000f, 0.000f,-0.100f, 1.000f},
+ };
+
+ EXPECT_M4_NEAR(expect, result, 1e-5f);
+}
diff --git a/tests/gtests/blenlib/BLI_array_store_test.cc b/tests/gtests/blenlib/BLI_array_store_test.cc
index b71dc4575f1..370a4111bae 100644
--- a/tests/gtests/blenlib/BLI_array_store_test.cc
+++ b/tests/gtests/blenlib/BLI_array_store_test.cc
@@ -36,15 +36,15 @@ static void print_mem_saved(const char *id, const BArrayStore *bs)
/* -------------------------------------------------------------------- */
/* Test Chunks (building data from list of chunks) */
-typedef struct TestChunnk {
- struct TestChunnk *next, *prev;
+typedef struct TestChunk {
+ struct TestChunk *next, *prev;
const void *data;
size_t data_len;
-} TestChunnk;
+} TestChunk;
-static TestChunnk *testchunk_list_add(ListBase *lb, const void *data, size_t data_len)
+static TestChunk *testchunk_list_add(ListBase *lb, const void *data, size_t data_len)
{
- TestChunnk *tc = (TestChunnk *)MEM_mallocN(sizeof(*tc), __func__);
+ TestChunk *tc = (TestChunk *)MEM_mallocN(sizeof(*tc), __func__);
tc->data = data;
tc->data_len = data_len;
BLI_addtail(lb, tc);
@@ -53,7 +53,7 @@ static TestChunnk *testchunk_list_add(ListBase *lb, const void *data, size_t dat
}
#if 0
-static TestChunnk *testchunk_list_add_copydata(ListBase *lb, const void *data, size_t data_len)
+static TestChunk *testchunk_list_add_copydata(ListBase *lb, const void *data, size_t data_len)
{
void *data_copy = MEM_mallocN(data_len, __func__);
memcpy(data_copy, data, data_len);
@@ -63,7 +63,7 @@ static TestChunnk *testchunk_list_add_copydata(ListBase *lb, const void *data, s
static void testchunk_list_free(ListBase *lb)
{
- for (TestChunnk *tc = (TestChunnk *)lb->first, *tb_next; tc; tc = tb_next) {
+ for (TestChunk *tc = (TestChunk *)lb->first, *tb_next; tc; tc = tb_next) {
tb_next = tc->next;
MEM_freeN((void *)tc->data);
MEM_freeN(tc);
@@ -77,12 +77,12 @@ static char *testchunk_as_data(
size_t *r_data_len)
{
size_t data_len = 0;
- for (TestChunnk *tc = (TestChunnk *)lb->first; tc; tc = tc->next) {
+ for (TestChunk *tc = (TestChunk *)lb->first; tc; tc = tc->next) {
data_len += tc->data_len;
}
char *data = (char *)MEM_mallocN(data_len, __func__);
size_t i = 0;
- for (TestChunnk *tc = (TestChunnk *)lb->first; tc; tc = tc->next) {
+ for (TestChunk *tc = (TestChunk *)lb->first; tc; tc = tc->next) {
memcpy(&data[i], tc->data, tc->data_len);
data_len += tc->data_len;
i += tc->data_len;
@@ -95,7 +95,7 @@ static char *testchunk_as_data(
#endif
static char *testchunk_as_data_array(
- TestChunnk **tc_array, int tc_array_len,
+ TestChunk **tc_array, int tc_array_len,
size_t *r_data_len)
{
size_t data_len = 0;
@@ -105,7 +105,7 @@ static char *testchunk_as_data_array(
char *data = (char *)MEM_mallocN(data_len, __func__);
size_t i = 0;
for (int tc_index = 0; tc_index < tc_array_len; tc_index++) {
- TestChunnk *tc = tc_array[tc_index];
+ TestChunk *tc = tc_array[tc_index];
memcpy(&data[i], tc->data, tc->data_len);
i += tc->data_len;
}
@@ -280,8 +280,8 @@ static void testbuffer_run_tests_single(
BArrayStore *bs, ListBase *lb)
{
testbuffer_list_store_populate(bs, lb);
- EXPECT_EQ(true, testbuffer_list_validate(lb));
- EXPECT_EQ(true, BLI_array_store_is_valid(bs));
+ EXPECT_TRUE(testbuffer_list_validate(lb));
+ EXPECT_TRUE(BLI_array_store_is_valid(bs));
#ifdef DEBUG_PRINT
print_mem_saved("data", bs);
#endif
@@ -326,7 +326,7 @@ TEST(array_store, NopState)
BArrayStore *bs = BLI_array_store_create(1, 32);
const unsigned char data[] = "test";
BArrayState *state = BLI_array_store_state_add(bs, data, sizeof(data) - 1, NULL);
- EXPECT_EQ(sizeof(data) - 1, BLI_array_store_state_size_get(state));
+ EXPECT_EQ(BLI_array_store_state_size_get(state), sizeof(data) - 1);
BLI_array_store_state_remove(bs, state);
BLI_array_store_destroy(bs);
}
@@ -340,7 +340,7 @@ TEST(array_store, Single)
size_t data_dst_len;
data_dst = (char *)BLI_array_store_state_data_get_alloc(state, &data_dst_len);
EXPECT_STREQ(data_src, data_dst);
- EXPECT_EQ(sizeof(data_src), data_dst_len);
+ EXPECT_EQ(data_dst_len, sizeof(data_src));
BLI_array_store_destroy(bs);
MEM_freeN((void *)data_dst);
}
@@ -354,8 +354,8 @@ TEST(array_store, DoubleNop)
BArrayState *state_a = BLI_array_store_state_add(bs, data_src, sizeof(data_src), NULL);
BArrayState *state_b = BLI_array_store_state_add(bs, data_src, sizeof(data_src), state_a);
- EXPECT_EQ(sizeof(data_src), BLI_array_store_calc_size_compacted_get(bs));
- EXPECT_EQ(sizeof(data_src) * 2, BLI_array_store_calc_size_expanded_get(bs));
+ EXPECT_EQ(BLI_array_store_calc_size_compacted_get(bs), sizeof(data_src));
+ EXPECT_EQ(BLI_array_store_calc_size_expanded_get(bs), sizeof(data_src) * 2);
size_t data_dst_len;
@@ -367,7 +367,7 @@ TEST(array_store, DoubleNop)
EXPECT_STREQ(data_src, data_dst);
MEM_freeN((void *)data_dst);
- EXPECT_EQ(sizeof(data_src), data_dst_len);
+ EXPECT_EQ(data_dst_len, sizeof(data_src));
BLI_array_store_destroy(bs);
}
@@ -382,8 +382,8 @@ TEST(array_store, DoubleDiff)
BArrayState *state_b = BLI_array_store_state_add(bs, data_src_b, sizeof(data_src_b), state_a);
size_t data_dst_len;
- EXPECT_EQ(sizeof(data_src_a) * 2, BLI_array_store_calc_size_compacted_get(bs));
- EXPECT_EQ(sizeof(data_src_a) * 2, BLI_array_store_calc_size_expanded_get(bs));
+ EXPECT_EQ(BLI_array_store_calc_size_compacted_get(bs), sizeof(data_src_a) * 2);
+ EXPECT_EQ(BLI_array_store_calc_size_expanded_get(bs), sizeof(data_src_a) * 2);
data_dst = (char *)BLI_array_store_state_data_get_alloc(state_a, &data_dst_len);
EXPECT_STREQ(data_src_a, data_dst);
@@ -423,19 +423,19 @@ TEST(array_store, TextDupeIncreaseDecrease)
/* forward */
testbuffer_list_store_populate(bs, &lb);
- EXPECT_EQ(true, testbuffer_list_validate(&lb));
- EXPECT_EQ(true, BLI_array_store_is_valid(bs));
- EXPECT_EQ(strlen(D), BLI_array_store_calc_size_compacted_get(bs));
+ EXPECT_TRUE(testbuffer_list_validate(&lb));
+ EXPECT_TRUE(BLI_array_store_is_valid(bs));
+ EXPECT_EQ(BLI_array_store_calc_size_compacted_get(bs), strlen(D));
testbuffer_list_store_clear(bs, &lb);
BLI_listbase_reverse(&lb);
/* backwards */
testbuffer_list_store_populate(bs, &lb);
- EXPECT_EQ(true, testbuffer_list_validate(&lb));
- EXPECT_EQ(true, BLI_array_store_is_valid(bs));
+ EXPECT_TRUE(testbuffer_list_validate(&lb));
+ EXPECT_TRUE(BLI_array_store_is_valid(bs));
/* larger since first block doesn't de-duplicate */
- EXPECT_EQ(strlen(D) * 4, BLI_array_store_calc_size_compacted_get(bs));
+ EXPECT_EQ(BLI_array_store_calc_size_compacted_get(bs), strlen(D) * 4);
#undef D
testbuffer_list_free(&lb); \
@@ -677,9 +677,9 @@ static void random_chunk_mutate_helper(
ListBase random_chunks;
BLI_listbase_clear(&random_chunks);
random_chunk_generate(&random_chunks, chunks_per_buffer, stride, chunk_count, random_seed);
- TestChunnk **chunks_array = (TestChunnk **)MEM_mallocN(chunks_per_buffer * sizeof(TestChunnk *), __func__);
+ TestChunk **chunks_array = (TestChunk **)MEM_mallocN(chunks_per_buffer * sizeof(TestChunk *), __func__);
{
- TestChunnk *tc = (TestChunnk *)random_chunks.first;
+ TestChunk *tc = (TestChunk *)random_chunks.first;
for (int i = 0; i < chunks_per_buffer; i++, tc = tc->next) {
chunks_array[i] = tc;
}
@@ -692,7 +692,7 @@ static void random_chunk_mutate_helper(
{
RNG *rng = BLI_rng_new(random_seed);
for (int i = 0; i < items_total; i++) {
- BLI_rng_shuffle_array(rng, chunks_array, sizeof(TestChunnk *), chunks_per_buffer);
+ BLI_rng_shuffle_array(rng, chunks_array, sizeof(TestChunk *), chunks_per_buffer);
size_t data_len;
char *data = testchunk_as_data_array(chunks_array, chunks_per_buffer, &data_len);
BLI_assert(data_len == chunks_per_buffer * chunk_count * stride);
@@ -708,7 +708,7 @@ static void random_chunk_mutate_helper(
testbuffer_run_tests_single(bs, &lb);
size_t expected_size = chunks_per_buffer * chunk_count * stride;
- EXPECT_EQ(expected_size, BLI_array_store_calc_size_compacted_get(bs));
+ EXPECT_EQ(BLI_array_store_calc_size_compacted_get(bs), expected_size);
BLI_array_store_destroy(bs);
@@ -782,8 +782,8 @@ TEST(array_store, PlainTextFiles)
/* forwards */
testbuffer_list_store_populate(bs, &lb);
- EXPECT_EQ(true, testbuffer_list_validate(&lb));
- EXPECT_EQ(true, BLI_array_store_is_valid(bs));
+ EXPECT_TRUE(testbuffer_list_validate(&lb));
+ EXPECT_TRUE(BLI_array_store_is_valid(bs));
#ifdef DEBUG_PRINT
print_mem_saved("source code forward", bs);
#endif
@@ -793,8 +793,8 @@ TEST(array_store, PlainTextFiles)
/* backwards */
testbuffer_list_store_populate(bs, &lb);
- EXPECT_EQ(true, testbuffer_list_validate(&lb));
- EXPECT_EQ(true, BLI_array_store_is_valid(bs));
+ EXPECT_TRUE(testbuffer_list_validate(&lb));
+ EXPECT_TRUE(BLI_array_store_is_valid(bs));
#ifdef DEBUG_PRINT
print_mem_saved("source code backwards", bs);
#endif
diff --git a/tests/gtests/blenlib/BLI_array_utils_test.cc b/tests/gtests/blenlib/BLI_array_utils_test.cc
index eabf5bc72cf..c4601e00fbd 100644
--- a/tests/gtests/blenlib/BLI_array_utils_test.cc
+++ b/tests/gtests/blenlib/BLI_array_utils_test.cc
@@ -5,7 +5,7 @@
extern "C" {
#include "BLI_utildefines.h"
#include "BLI_array_utils.h"
-#include "BLI_stackdefines.h"
+#include "BLI_utildefines_stack.h"
}
/* -------------------------------------------------------------------- */
@@ -45,50 +45,50 @@ TEST(array_utils, ReverseInt4)
TEST(array_utils, FindIndexStringEmpty)
{
char data[] = "", find = '0';
- EXPECT_EQ(-1, BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find));
- EXPECT_EQ(-1, BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find), -1);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find), -1);
}
TEST(array_utils, FindIndexStringSingle)
{
char data[] = "0", find = '0';
- EXPECT_EQ(0, BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find));
- EXPECT_EQ(0, BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find), 0);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find), 0);
}
TEST(array_utils, FindIndexStringSingleMissing)
{
char data[] = "1", find = '0';
- EXPECT_EQ(-1, BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find));
- EXPECT_EQ(-1, BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find), -1);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find), -1);
}
TEST(array_utils, FindIndexString4)
{
char data[] = "0123", find = '3';
- EXPECT_EQ(3, BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find));
- EXPECT_EQ(3, BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data) - 1, &find), 3);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data) - 1, &find), 3);
}
TEST(array_utils, FindIndexInt4)
{
int data[] = {0, 1, 2, 3}, find = 3;
- EXPECT_EQ(3, BLI_array_findindex(data, ARRAY_SIZE(data), &find));
- EXPECT_EQ(3, BLI_array_rfindindex(data, ARRAY_SIZE(data), &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data), &find), 3);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data), &find), 3);
}
TEST(array_utils, FindIndexInt4_DupeEnd)
{
int data[] = {0, 1, 2, 0}, find = 0;
- EXPECT_EQ(0, BLI_array_findindex(data, ARRAY_SIZE(data), &find));
- EXPECT_EQ(3, BLI_array_rfindindex(data, ARRAY_SIZE(data), &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data), &find), 0);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data), &find), 3);
}
TEST(array_utils, FindIndexInt4_DupeMid)
{
int data[] = {1, 0, 0, 3}, find = 0;
- EXPECT_EQ(1, BLI_array_findindex(data, ARRAY_SIZE(data), &find));
- EXPECT_EQ(2, BLI_array_rfindindex(data, ARRAY_SIZE(data), &find));
+ EXPECT_EQ(BLI_array_findindex(data, ARRAY_SIZE(data), &find), 1);
+ EXPECT_EQ(BLI_array_rfindindex(data, ARRAY_SIZE(data), &find), 2);
}
TEST(array_utils, FindIndexPointer)
@@ -102,18 +102,18 @@ TEST(array_utils, FindIndexPointer)
#define STACK_PUSH_AND_CHECK_FORWARD(v, i) { \
STACK_PUSH(data, v); \
- EXPECT_EQ(i, BLI_array_findindex(data, STACK_SIZE(data), &(v))); \
+ EXPECT_EQ(BLI_array_findindex(data, STACK_SIZE(data), &(v)), i); \
} ((void)0)
#define STACK_PUSH_AND_CHECK_BACKWARD(v, i) { \
STACK_PUSH(data, v); \
- EXPECT_EQ(i, BLI_array_rfindindex(data, STACK_SIZE(data), &(v))); \
+ EXPECT_EQ(BLI_array_rfindindex(data, STACK_SIZE(data), &(v)), i); \
} ((void)0)
#define STACK_PUSH_AND_CHECK_BOTH(v, i) { \
STACK_PUSH(data, v); \
- EXPECT_EQ(i, BLI_array_findindex(data, STACK_SIZE(data), &(v))); \
- EXPECT_EQ(i, BLI_array_rfindindex(data, STACK_SIZE(data), &(v))); \
+ EXPECT_EQ(BLI_array_findindex(data, STACK_SIZE(data), &(v)), i); \
+ EXPECT_EQ(BLI_array_rfindindex(data, STACK_SIZE(data), &(v)), i); \
} ((void)0)
STACK_PUSH_AND_CHECK_BOTH(a, 0);
diff --git a/tests/gtests/blenlib/BLI_ghash_performance_test.cc b/tests/gtests/blenlib/BLI_ghash_performance_test.cc
index fb32cb3f0a5..924c84d72d0 100644
--- a/tests/gtests/blenlib/BLI_ghash_performance_test.cc
+++ b/tests/gtests/blenlib/BLI_ghash_performance_test.cc
@@ -118,21 +118,21 @@ static void str_ghash_tests(GHash *ghash, const char *id)
TIMEIT_START(string_lookup);
v = BLI_ghash_lookup(ghash, data_bis);
- EXPECT_EQ(data_bis[0], GET_INT_FROM_POINTER(v));
+ EXPECT_EQ(GET_INT_FROM_POINTER(v), data_bis[0]);
for (p = w = c = data_bis; *c; c++) {
if (*c == '.') {
*c = '\0';
v = BLI_ghash_lookup(ghash, w);
- EXPECT_EQ(w[0], GET_INT_FROM_POINTER(v));
+ EXPECT_EQ(GET_INT_FROM_POINTER(v), w[0]);
v = BLI_ghash_lookup(ghash, p);
- EXPECT_EQ(p[0], GET_INT_FROM_POINTER(v));
+ EXPECT_EQ(GET_INT_FROM_POINTER(v), p[0]);
p = w = c + 1;
}
else if (*c == ' ') {
*c = '\0';
v = BLI_ghash_lookup(ghash, w);
- EXPECT_EQ(w[0], GET_INT_FROM_POINTER(v));
+ EXPECT_EQ(GET_INT_FROM_POINTER(v), w[0]);
w = c + 1;
}
}
@@ -195,7 +195,7 @@ static void int_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr
while (i--) {
void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(i));
- EXPECT_EQ(i, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), i);
}
TIMEIT_END(int_lookup);
@@ -214,7 +214,7 @@ static void int_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr
TIMEIT_END(int_pop);
}
- EXPECT_EQ(0, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), 0);
BLI_ghash_free(ghash, NULL, NULL);
@@ -292,7 +292,7 @@ static void randint_ghash_tests(GHash *ghash, const char *id, const unsigned int
for (i = nbr, dt = data; i--; dt++) {
void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(*dt));
- EXPECT_EQ(*dt, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), *dt);
}
TIMEIT_END(int_lookup);
@@ -403,7 +403,7 @@ static void int4_ghash_tests(GHash *ghash, const char *id, const unsigned int nb
for (i = nbr, dt = data; i--; dt++) {
void *v = BLI_ghash_lookup(ghash, (void *)(*dt));
- EXPECT_EQ(i, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), i);
}
TIMEIT_END(int_v4_lookup);
@@ -469,7 +469,7 @@ static void multi_small_ghash_tests_one(GHash *ghash, RNG *rng, const unsigned i
for (i = nbr, dt = data; i--; dt++) {
void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(*dt));
- EXPECT_EQ(*dt, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), *dt);
}
BLI_ghash_clear(ghash, NULL, NULL);
diff --git a/tests/gtests/blenlib/BLI_ghash_test.cc b/tests/gtests/blenlib/BLI_ghash_test.cc
index ffbe5f5547f..6d075e29114 100644
--- a/tests/gtests/blenlib/BLI_ghash_test.cc
+++ b/tests/gtests/blenlib/BLI_ghash_test.cc
@@ -62,11 +62,11 @@ TEST(ghash, InsertLookup)
BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k));
}
- EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), TESTCASE_SIZE);
for (i = TESTCASE_SIZE, k = keys; i--; k++) {
void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(*k));
- EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), *k);
}
BLI_ghash_free(ghash, NULL, NULL);
@@ -85,16 +85,16 @@ TEST(ghash, InsertRemove)
BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k));
}
- EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), TESTCASE_SIZE);
bkt_size = BLI_ghash_buckets_size(ghash);
for (i = TESTCASE_SIZE, k = keys; i--; k++) {
void *v = BLI_ghash_popkey(ghash, SET_UINT_IN_POINTER(*k), NULL);
- EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), *k);
}
- EXPECT_EQ(0, BLI_ghash_size(ghash));
- EXPECT_EQ(bkt_size, BLI_ghash_buckets_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), 0);
+ EXPECT_EQ(BLI_ghash_buckets_size(ghash), bkt_size);
BLI_ghash_free(ghash, NULL, NULL);
}
@@ -113,15 +113,15 @@ TEST(ghash, InsertRemoveShrink)
BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k));
}
- EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), TESTCASE_SIZE);
bkt_size = BLI_ghash_buckets_size(ghash);
for (i = TESTCASE_SIZE, k = keys; i--; k++) {
void *v = BLI_ghash_popkey(ghash, SET_UINT_IN_POINTER(*k), NULL);
- EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), *k);
}
- EXPECT_EQ(0, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), 0);
EXPECT_LT(BLI_ghash_buckets_size(ghash), bkt_size);
BLI_ghash_free(ghash, NULL, NULL);
@@ -141,16 +141,16 @@ TEST(ghash, Copy)
BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k));
}
- EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), TESTCASE_SIZE);
ghash_copy = BLI_ghash_copy(ghash, NULL, NULL);
- EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash_copy));
- EXPECT_EQ(BLI_ghash_buckets_size(ghash), BLI_ghash_buckets_size(ghash_copy));
+ EXPECT_EQ(BLI_ghash_size(ghash_copy), TESTCASE_SIZE);
+ EXPECT_EQ(BLI_ghash_buckets_size(ghash_copy), BLI_ghash_buckets_size(ghash));
for (i = TESTCASE_SIZE, k = keys; i--; k++) {
void *v = BLI_ghash_lookup(ghash_copy, SET_UINT_IN_POINTER(*k));
- EXPECT_EQ(*k, GET_UINT_FROM_POINTER(v));
+ EXPECT_EQ(GET_UINT_FROM_POINTER(v), *k);
}
BLI_ghash_free(ghash, NULL, NULL);
@@ -171,7 +171,7 @@ TEST(ghash, Pop)
BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*k), SET_UINT_IN_POINTER(*k));
}
- EXPECT_EQ(TESTCASE_SIZE, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), TESTCASE_SIZE);
GHashIterState pop_state = {0};
@@ -179,14 +179,14 @@ TEST(ghash, Pop)
void *k, *v;
bool success = BLI_ghash_pop(ghash, &pop_state, &k, &v);
EXPECT_EQ(k, v);
- EXPECT_EQ(success, true);
+ EXPECT_TRUE(success);
if (i % 2) {
BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(i * 4), SET_UINT_IN_POINTER(i * 4));
}
}
- EXPECT_EQ((TESTCASE_SIZE - TESTCASE_SIZE / 2 + TESTCASE_SIZE / 4), BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), (TESTCASE_SIZE - TESTCASE_SIZE / 2 + TESTCASE_SIZE / 4));
{
void *k, *v;
@@ -194,7 +194,7 @@ TEST(ghash, Pop)
EXPECT_EQ(k, v);
}
}
- EXPECT_EQ(0, BLI_ghash_size(ghash));
+ EXPECT_EQ(BLI_ghash_size(ghash), 0);
BLI_ghash_free(ghash, NULL, NULL);
}
diff --git a/tests/gtests/blenlib/BLI_hash_mm2a_test.cc b/tests/gtests/blenlib/BLI_hash_mm2a_test.cc
index b35a1a809d6..109c925af4c 100644
--- a/tests/gtests/blenlib/BLI_hash_mm2a_test.cc
+++ b/tests/gtests/blenlib/BLI_hash_mm2a_test.cc
@@ -19,9 +19,9 @@ TEST(hash_mm2a, MM2ABasic)
BLI_hash_mm2a_init(&mm2, 0);
BLI_hash_mm2a_add(&mm2, (const unsigned char *)data, strlen(data));
#ifdef __LITTLE_ENDIAN__
- EXPECT_EQ(1633988145, BLI_hash_mm2a_end(&mm2));
+ EXPECT_EQ(BLI_hash_mm2a_end(&mm2), 1633988145);
#else
- EXPECT_EQ(959283772, BLI_hash_mm2a_end(&mm2));
+ EXPECT_EQ(BLI_hash_mm2a_end(&mm2), 959283772);
#endif
}
@@ -43,11 +43,11 @@ TEST(hash_mm2a, MM2AConcatenateStrings)
BLI_hash_mm2a_init(&mm2, 0);
BLI_hash_mm2a_add(&mm2, (const unsigned char *)data123, strlen(data123));
#ifdef __LITTLE_ENDIAN__
- EXPECT_EQ(1545105348, hash);
+ EXPECT_EQ(hash, 1545105348);
#else
- EXPECT_EQ(2604964730, hash);
+ EXPECT_EQ(hash, 2604964730);
#endif
- EXPECT_EQ(hash, BLI_hash_mm2a_end(&mm2));
+ EXPECT_EQ(BLI_hash_mm2a_end(&mm2), hash);
}
TEST(hash_mm2a, MM2AIntegers)
@@ -67,9 +67,9 @@ TEST(hash_mm2a, MM2AIntegers)
BLI_hash_mm2a_add(&mm2, (const unsigned char *)ints, sizeof(ints));
/* Yes, same hash here on little and big endian. */
#ifdef __LITTLE_ENDIAN__
- EXPECT_EQ(405493096, hash);
+ EXPECT_EQ(hash, 405493096);
#else
- EXPECT_EQ(405493096, hash);
+ EXPECT_EQ(hash, 405493096);
#endif
- EXPECT_EQ(hash, BLI_hash_mm2a_end(&mm2));
+ EXPECT_EQ(BLI_hash_mm2a_end(&mm2), hash);
}
diff --git a/tests/gtests/blenlib/BLI_kdopbvh_test.cc b/tests/gtests/blenlib/BLI_kdopbvh_test.cc
new file mode 100644
index 00000000000..74db7cf20a0
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_kdopbvh_test.cc
@@ -0,0 +1,97 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+/* TODO: ray intersection, overlap ... etc.*/
+
+extern "C" {
+#include "BLI_compiler_attrs.h"
+#include "BLI_kdopbvh.h"
+#include "BLI_rand.h"
+#include "BLI_math_vector.h"
+#include "MEM_guardedalloc.h"
+}
+
+#include "stubs/bf_intern_eigen_stubs.h"
+
+/* -------------------------------------------------------------------- */
+/* Helper Functions */
+
+static void rng_v3_round(
+ float *coords, int coords_len,
+ struct RNG *rng, int round, float scale)
+{
+ for (int i = 0; i < coords_len; i++) {
+ float f = BLI_rng_get_float(rng) * 2.0f - 1.0f;
+ coords[i] = ((float)((int)(f * round)) / (float)round) * scale;
+ }
+}
+
+/* -------------------------------------------------------------------- */
+/* Tests */
+
+TEST(kdopbvh, Empty)
+{
+ BVHTree *tree = BLI_bvhtree_new(0, 0.0, 8, 8);
+ BLI_bvhtree_balance(tree);
+ EXPECT_EQ(0, BLI_bvhtree_get_size(tree));
+ BLI_bvhtree_free(tree);
+}
+
+TEST(kdopbvh, Single)
+{
+ BVHTree *tree = BLI_bvhtree_new(1, 0.0, 8, 8);
+ {
+ float co[3] = {0};
+ BLI_bvhtree_insert(tree, 0, co, 1);
+ }
+
+ EXPECT_EQ(BLI_bvhtree_get_size(tree), 1);
+
+ BLI_bvhtree_balance(tree);
+ BLI_bvhtree_free(tree);
+}
+
+/**
+ * Note that a small epsilon is added to the BVH nodes bounds, even if we pass in zero.
+ * Use rounding to ensure very close nodes don't cause the wrong node to be found as nearest.
+ */
+static void find_nearest_points_test(int points_len, float scale, int round, int random_seed)
+{
+ struct RNG *rng = BLI_rng_new(random_seed);
+ BVHTree *tree = BLI_bvhtree_new(points_len, 0.0, 8, 8);
+
+ void *mem = MEM_mallocN(sizeof(float[3]) * points_len, __func__);
+ float (*points)[3] = (float (*)[3])mem;
+
+ for (int i = 0; i < points_len; i++) {
+ rng_v3_round(points[i], 3, rng, round, scale);
+ BLI_bvhtree_insert(tree, i, points[i], 1);
+ }
+ BLI_bvhtree_balance(tree);
+ /* first find each point */
+ for (int i = 0; i < points_len; i++) {
+ const int j = BLI_bvhtree_find_nearest(tree, points[i], NULL, NULL, NULL);
+ if (j != i) {
+#if 0
+ const float dist = len_v3v3(points[i], points[j]);
+ if (dist > (1.0f / (float)round)) {
+ printf("%.15f (%d %d)\n", dist, i, j);
+ print_v3_id(points[i]);
+ print_v3_id(points[j]);
+ fflush(stdout);
+ }
+#endif
+ EXPECT_GE(j, 0);
+ EXPECT_LT(j, points_len);
+ EXPECT_EQ_ARRAY(points[i], points[j], 3);
+ }
+ }
+ BLI_bvhtree_free(tree);
+ BLI_rng_free(rng);
+ MEM_freeN(points);
+}
+
+TEST(kdopbvh, FindNearest_1) { find_nearest_points_test(1, 1.0, 1000, 1234); }
+TEST(kdopbvh, FindNearest_2) { find_nearest_points_test(2, 1.0, 1000, 123); }
+TEST(kdopbvh, FindNearest_500) { find_nearest_points_test(500, 1.0, 1000, 12); }
diff --git a/tests/gtests/blenlib/BLI_listbase_test.cc b/tests/gtests/blenlib/BLI_listbase_test.cc
index 994b8f74541..4dac2d05bd8 100644
--- a/tests/gtests/blenlib/BLI_listbase_test.cc
+++ b/tests/gtests/blenlib/BLI_listbase_test.cc
@@ -74,25 +74,25 @@ TEST(listbase, FindLinkOrIndex)
/* Empty list */
BLI_listbase_clear(&lb);
- EXPECT_EQ(NULL, BLI_findlink(&lb, -1));
- EXPECT_EQ(NULL, BLI_findlink(&lb, 0));
- EXPECT_EQ(NULL, BLI_findlink(&lb, 1));
- EXPECT_EQ(NULL, BLI_rfindlink(&lb, -1));
- EXPECT_EQ(NULL, BLI_rfindlink(&lb, 0));
- EXPECT_EQ(NULL, BLI_rfindlink(&lb, 1));
- EXPECT_EQ(-1, BLI_findindex(&lb, link1));
+ EXPECT_EQ(BLI_findlink(&lb, -1), (void*)NULL);
+ EXPECT_EQ(BLI_findlink(&lb, 0), (void*)NULL);
+ EXPECT_EQ(BLI_findlink(&lb, 1), (void*)NULL);
+ EXPECT_EQ(BLI_rfindlink(&lb, -1), (void*)NULL);
+ EXPECT_EQ(BLI_rfindlink(&lb, 0), (void*)NULL);
+ EXPECT_EQ(BLI_rfindlink(&lb, 1), (void*)NULL);
+ EXPECT_EQ(BLI_findindex(&lb, link1), -1);
/* One link */
BLI_addtail(&lb, link1);
- EXPECT_EQ(link1, BLI_findlink(&lb, 0));
- EXPECT_EQ(link1, BLI_rfindlink(&lb, 0));
- EXPECT_EQ(0, BLI_findindex(&lb, link1));
+ EXPECT_EQ(BLI_findlink(&lb, 0), link1);
+ EXPECT_EQ(BLI_rfindlink(&lb, 0), link1);
+ EXPECT_EQ(BLI_findindex(&lb, link1), 0);
/* Two links */
BLI_addtail(&lb, link2);
- EXPECT_EQ(link2, BLI_findlink(&lb, 1));
- EXPECT_EQ(link2, BLI_rfindlink(&lb, 0));
- EXPECT_EQ(1, BLI_findindex(&lb, link2));
+ EXPECT_EQ(BLI_findlink(&lb, 1), link2);
+ EXPECT_EQ(BLI_rfindlink(&lb, 0), link2);
+ EXPECT_EQ(BLI_findindex(&lb, link2), 1);
BLI_freelistN(&lb);
}
diff --git a/tests/gtests/blenlib/BLI_math_geom_test.cc b/tests/gtests/blenlib/BLI_math_geom_test.cc
index cd15a4eb8ff..92e2532392e 100644
--- a/tests/gtests/blenlib/BLI_math_geom_test.cc
+++ b/tests/gtests/blenlib/BLI_math_geom_test.cc
@@ -4,6 +4,8 @@
#include "BLI_math.h"
+#include "stubs/bf_intern_eigen_stubs.h"
+
TEST(math_geom, DistToLine2DSimple)
{
float p[2] = {5.0f, 1.0f},
diff --git a/tests/gtests/blenlib/BLI_path_util_test.cc b/tests/gtests/blenlib/BLI_path_util_test.cc
index c80987c3586..41fad661ea9 100644
--- a/tests/gtests/blenlib/BLI_path_util_test.cc
+++ b/tests/gtests/blenlib/BLI_path_util_test.cc
@@ -5,6 +5,7 @@
extern "C" {
#include "BLI_fileops.h"
#include "BLI_path_util.h"
+#include "BLI_string.h"
#include "../../../source/blender/imbuf/IMB_imbuf.h"
#ifdef _WIN32
@@ -40,7 +41,7 @@ const char *GHOST_getSystemDir(int version, const char *versionstr)
struct ImBuf;
void IMB_freeImBuf(struct ImBuf *ibuf) {}
-struct ImBuf *IMB_dupImBuf(struct ImBuf *ibuf) {return NULL;}
+struct ImBuf *IMB_dupImBuf(const ImBuf *ibuf) {return NULL;}
#ifdef __linux__
char *zLhm65070058860608_br_find_exe(const char *default_exe)
@@ -57,7 +58,7 @@ char *zLhm65070058860608_br_find_exe(const char *default_exe)
/* BLI_cleanup_path */
#ifndef _WIN32
-TEST(path_util, PathUtilClean)
+TEST(path_util, Clean)
{
/* "/./" -> "/" */
{
@@ -113,50 +114,280 @@ TEST(path_util, PathUtilClean)
}
#endif
+
+#define AT_INDEX(str_input, index_input, str_expect) \
+ { \
+ char path[] = str_input; \
+ const char *expect = str_expect; \
+ int index_output, len_output; \
+ const bool ret = BLI_path_name_at_index(path, index_input, &index_output, &len_output); \
+ if (expect == NULL) { \
+ EXPECT_EQ(ret, false); \
+ } \
+ else { \
+ EXPECT_EQ(ret, true); \
+ EXPECT_EQ(strlen(expect), len_output); \
+ path[index_output + len_output] = '\0'; \
+ EXPECT_STREQ(&path[index_output], expect); \
+ } \
+ }((void)0)
+
+/* BLI_path_name_at_index */
+TEST(path_util, NameAtIndex_Single)
+{
+ AT_INDEX("/a", 0, "a");
+ AT_INDEX("/a/", 0, "a");
+ AT_INDEX("a/", 0, "a");
+ AT_INDEX("//a//", 0, "a");
+ AT_INDEX("a/b", 0, "a");
+
+ AT_INDEX("/a", 1, NULL);
+ AT_INDEX("/a/", 1, NULL);
+ AT_INDEX("a/", 1, NULL);
+ AT_INDEX("//a//", 1, NULL);
+}
+TEST(path_util, NameAtIndex_SingleNeg)
+{
+ AT_INDEX("/a", -1, "a");
+ AT_INDEX("/a/", -1, "a");
+ AT_INDEX("a/", -1, "a");
+ AT_INDEX("//a//", -1, "a");
+ AT_INDEX("a/b", -1, "b");
+
+ AT_INDEX("/a", -2, NULL);
+ AT_INDEX("/a/", -2, NULL);
+ AT_INDEX("a/", -2, NULL);
+ AT_INDEX("//a//", -2, NULL);
+}
+
+TEST(path_util, NameAtIndex_Double)
+{
+ AT_INDEX("/ab", 0, "ab");
+ AT_INDEX("/ab/", 0, "ab");
+ AT_INDEX("ab/", 0, "ab");
+ AT_INDEX("//ab//", 0, "ab");
+ AT_INDEX("ab/c", 0, "ab");
+
+ AT_INDEX("/ab", 1, NULL);
+ AT_INDEX("/ab/", 1, NULL);
+ AT_INDEX("ab/", 1, NULL);
+ AT_INDEX("//ab//", 1, NULL);
+}
+
+TEST(path_util, NameAtIndex_DoublNeg)
+{
+ AT_INDEX("/ab", -1, "ab");
+ AT_INDEX("/ab/", -1, "ab");
+ AT_INDEX("ab/", -1, "ab");
+ AT_INDEX("//ab//", -1, "ab");
+ AT_INDEX("ab/c", -1, "c");
+
+ AT_INDEX("/ab", -2, NULL);
+ AT_INDEX("/ab/", -2, NULL);
+ AT_INDEX("ab/", -2, NULL);
+ AT_INDEX("//ab//", -2, NULL);
+}
+
+TEST(path_util, NameAtIndex_Misc)
+{
+ AT_INDEX("/how/now/brown/cow", 0, "how");
+ AT_INDEX("/how/now/brown/cow", 1, "now");
+ AT_INDEX("/how/now/brown/cow", 2, "brown");
+ AT_INDEX("/how/now/brown/cow", 3, "cow");
+ AT_INDEX("/how/now/brown/cow", 4, NULL);
+ AT_INDEX("/how/now/brown/cow/", 4, NULL);
+}
+
+TEST(path_util, NameAtIndex_MiscNeg)
+{
+ AT_INDEX("/how/now/brown/cow", 0, "how");
+ AT_INDEX("/how/now/brown/cow", 1, "now");
+ AT_INDEX("/how/now/brown/cow", 2, "brown");
+ AT_INDEX("/how/now/brown/cow", 3, "cow");
+ AT_INDEX("/how/now/brown/cow", 4, NULL);
+ AT_INDEX("/how/now/brown/cow/", 4, NULL);
+}
+
+TEST(path_util, NameAtIndex_MiscComplex)
+{
+ AT_INDEX("how//now/brown/cow", 0, "how");
+ AT_INDEX("//how///now\\/brown/cow", 1, "now");
+ AT_INDEX("/how/now\\//brown\\/cow", 2, "brown");
+ AT_INDEX("/how/now/brown/cow//\\", 3, "cow");
+ AT_INDEX("/how/now/brown/\\cow", 4, NULL);
+ AT_INDEX("how/now/brown/\\cow\\", 4, NULL);
+}
+
+TEST(path_util, NameAtIndex_MiscComplexNeg)
+{
+ AT_INDEX("how//now/brown/cow", -4, "how");
+ AT_INDEX("//how///now\\/brown/cow", -3, "now");
+ AT_INDEX("/how/now\\//brown\\/cow", -2, "brown");
+ AT_INDEX("/how/now/brown/cow//\\", -1, "cow");
+ AT_INDEX("/how/now/brown/\\cow", -5, NULL);
+ AT_INDEX("how/now/brown/\\cow\\", -5, NULL);
+}
+
+TEST(path_util, NameAtIndex_NoneComplex)
+{
+ AT_INDEX("", 0, NULL);
+ AT_INDEX("/", 0, NULL);
+ AT_INDEX("//", 0, NULL);
+ AT_INDEX("///", 0, NULL);
+}
+
+TEST(path_util, NameAtIndex_NoneComplexNeg)
+{
+ AT_INDEX("", -1, NULL);
+ AT_INDEX("/", -1, NULL);
+ AT_INDEX("//", -1, NULL);
+ AT_INDEX("///", -1, NULL);
+}
+
+#undef AT_INDEX
+
+#define JOIN(str_expect, out_size, ...) \
+ { \
+ const char *expect = str_expect; \
+ char result[(out_size) + 1024]; \
+ /* check we don't write past the last byte */ \
+ result[out_size] = '\0'; \
+ BLI_path_join(result, out_size, __VA_ARGS__, NULL); \
+ /* simplify expected string */ \
+ BLI_str_replace_char(result, '\\', '/'); \
+ EXPECT_STREQ(result, expect); \
+ EXPECT_EQ(result[out_size], '\0'); \
+ } ((void)0)
+
+/* BLI_path_join */
+TEST(path_util, JoinNop)
+{
+ JOIN("", 100, "");
+ JOIN("", 100, "", "");
+ JOIN("", 100, "", "", "");
+ JOIN("/", 100, "/", "", "");
+ JOIN("/", 100, "/", "/");
+ JOIN("/", 100, "/", "", "/");
+ JOIN("/", 100, "/", "", "/", "");
+}
+
+TEST(path_util, JoinSingle)
+{
+ JOIN("test", 100, "test");
+ JOIN("", 100, "");
+ JOIN("a", 100, "a");
+ JOIN("/a", 100, "/a");
+ JOIN("a/", 100, "a/");
+ JOIN("/a/", 100, "/a/");
+ JOIN("/a/", 100, "/a//");
+ JOIN("//a/", 100, "//a//");
+}
+
+TEST(path_util, JoinTriple)
+{
+ JOIN("/a/b/c", 100, "/a", "b", "c");
+ JOIN("/a/b/c", 100, "/a/", "/b/", "/c");
+ JOIN("/a/b/c", 100, "/a/b/", "/c");
+ JOIN("/a/b/c", 100, "/a/b/c");
+ JOIN("/a/b/c", 100, "/", "a/b/c");
+
+ JOIN("/a/b/c/", 100, "/a/", "/b/", "/c/");
+ JOIN("/a/b/c/", 100, "/a/b/c/");
+ JOIN("/a/b/c/", 100, "/a/b/", "/c/");
+ JOIN("/a/b/c/", 100, "/a/b/c", "/");
+ JOIN("/a/b/c/", 100, "/", "a/b/c", "/");
+}
+
+TEST(path_util, JoinTruncateShort)
+{
+ JOIN("", 1, "/");
+ JOIN("/", 2, "/");
+ JOIN("a", 2, "", "aa");
+ JOIN("a", 2, "", "a/");
+ JOIN("a/b", 4, "a", "bc");
+ JOIN("ab/", 4, "ab", "c");
+ JOIN("/a/", 4, "/a", "b");
+ JOIN("/a/", 4, "/a/", "b/");
+ JOIN("/a/", 4, "/a", "/b/");
+ JOIN("/a/", 4, "/", "a/b/");
+ JOIN("//a", 4, "//", "a/b/");
+
+ JOIN("/a/b", 5, "/a", "b", "c");
+}
+
+TEST(path_util, JoinTruncateLong)
+{
+ JOIN("", 1, "//", "//longer", "path");
+ JOIN("/", 2, "//", "//longer", "path");
+ JOIN("//", 3, "//", "//longer", "path");
+ JOIN("//l", 4, "//", "//longer", "path");
+ /* snip */
+ JOIN("//longe", 8, "//", "//longer", "path");
+ JOIN("//longer", 9, "//", "//longer", "path");
+ JOIN("//longer/", 10, "//", "//longer", "path");
+ JOIN("//longer/p", 11, "//", "//longer", "path");
+ JOIN("//longer/pa", 12, "//", "//longer", "path");
+ JOIN("//longer/pat", 13, "//", "//longer", "path");
+ JOIN("//longer/path", 14, "//", "//longer", "path"); // not truncated
+ JOIN("//longer/path", 14, "//", "//longer", "path/");
+ JOIN("//longer/path/", 15, "//", "//longer", "path/"); // not truncated
+ JOIN("//longer/path/", 15, "//", "//longer", "path/", "trunc");
+ JOIN("//longer/path/t", 16, "//", "//longer", "path/", "trunc");
+}
+
+TEST(path_util, JoinComplex)
+{
+ JOIN("/a/b/c/d/e/f/g/", 100, "/", "\\a/b", "//////c/d", "", "e\\\\", "f", "g//");
+ JOIN("/aa/bb/cc/dd/ee/ff/gg/", 100, "/", "\\aa/bb", "//////cc/dd", "", "ee\\\\", "ff", "gg//");
+ JOIN("1/2/3/", 100, "1", "////////", "", "2", "3\\");
+}
+
+#undef JOIN
+
/* BLI_path_frame */
-TEST(path_util, PathUtilFrame)
+TEST(path_util, Frame)
{
bool ret;
{
char path[FILE_MAX] = "";
ret = BLI_path_frame(path, 123, 1);
- EXPECT_EQ(1, ret);
+ EXPECT_EQ(ret, 1);
EXPECT_STREQ("123", path);
}
{
char path[FILE_MAX] = "";
ret = BLI_path_frame(path, 123, 12);
- EXPECT_EQ(1, ret);
+ EXPECT_EQ(ret, 1);
EXPECT_STREQ("000000000123", path);
}
{
char path[FILE_MAX] = "test_";
ret = BLI_path_frame(path, 123, 1);
- EXPECT_EQ(1, ret);
+ EXPECT_EQ(ret, 1);
EXPECT_STREQ("test_123", path);
}
{
char path[FILE_MAX] = "test_";
ret = BLI_path_frame(path, 1, 12);
- EXPECT_EQ(1, ret);
+ EXPECT_EQ(ret, 1);
EXPECT_STREQ("test_000000000001", path);
}
{
char path[FILE_MAX] = "test_############";
ret = BLI_path_frame(path, 1, 0);
- EXPECT_EQ(1, ret);
+ EXPECT_EQ(ret, 1);
EXPECT_STREQ("test_000000000001", path);
}
{
char path[FILE_MAX] = "test_#_#_middle";
ret = BLI_path_frame(path, 123, 0);
- EXPECT_EQ(1, ret);
+ EXPECT_EQ(ret, 1);
EXPECT_STREQ("test_#_123_middle", path);
}
@@ -164,20 +395,20 @@ TEST(path_util, PathUtilFrame)
{
char path[FILE_MAX] = "";
ret = BLI_path_frame(path, 123, 0);
- EXPECT_EQ(0, ret);
+ EXPECT_EQ(ret, 0);
EXPECT_STREQ("", path);
}
{
char path[FILE_MAX] = "test_middle";
ret = BLI_path_frame(path, 123, 0);
- EXPECT_EQ(0, ret);
+ EXPECT_EQ(ret, 0);
EXPECT_STREQ("test_middle", path);
}
}
/* BLI_split_dirfile */
-TEST(path_util, PathUtilSplitDirfile)
+TEST(path_util, SplitDirfile)
{
{
const char *path = "";
diff --git a/tests/gtests/blenlib/BLI_polyfill2d_test.cc b/tests/gtests/blenlib/BLI_polyfill2d_test.cc
index a4ed70fbec9..df98ead4cb9 100644
--- a/tests/gtests/blenlib/BLI_polyfill2d_test.cc
+++ b/tests/gtests/blenlib/BLI_polyfill2d_test.cc
@@ -27,6 +27,8 @@ extern "C" {
#endif
}
+#include "stubs/bf_intern_eigen_stubs.h"
+
static void polyfill_to_obj(
const char *id,
const float poly[][2], const unsigned int poly_tot,
@@ -98,14 +100,14 @@ static void test_polyfill_topology(
}
}
}
- EXPECT_EQ(poly_tot + (poly_tot - 3), BLI_edgehash_size(edgehash));
+ EXPECT_EQ(BLI_edgehash_size(edgehash), poly_tot + (poly_tot - 3));
for (i = 0; i < poly_tot; i++) {
const unsigned int v1 = i;
const unsigned int v2 = (i + 1) % poly_tot;
void **p = BLI_edgehash_lookup_p(edgehash, v1, v2);
- EXPECT_EQ(1, (void *)p != NULL);
- EXPECT_EQ(1, (intptr_t)*p);
+ EXPECT_EQ((void *)p != NULL, 1);
+ EXPECT_EQ((intptr_t)*p, 1);
}
for (ehi = BLI_edgehashIterator_new(edgehash), i = 0;
@@ -113,7 +115,7 @@ static void test_polyfill_topology(
BLI_edgehashIterator_step(ehi), i++)
{
void **p = BLI_edgehashIterator_getValue_p(ehi);
- EXPECT_EQ(true, ELEM((intptr_t)*p, 1, 2));
+ EXPECT_TRUE(ELEM((intptr_t)*p, 1, 2));
}
BLI_edgehashIterator_free(ehi);
@@ -135,7 +137,7 @@ static void test_polyfill_winding(
count[winding_test < 0.0f] += 1;
}
}
- EXPECT_EQ(true, ELEM(0, count[0], count[1]));
+ EXPECT_TRUE(ELEM(0, count[0], count[1]));
}
/**
@@ -208,6 +210,26 @@ static void test_polyfill_template(
#endif
}
+static void test_polyfill_template_flip_sign(
+ const char *id, bool is_degenerate,
+ const float poly[][2], const unsigned int poly_tot,
+ unsigned int tris[][3], const unsigned int tris_tot)
+{
+ float (*poly_copy)[2] = (float (*)[2])MEM_mallocN(sizeof(float[2]) * poly_tot, id);
+ for (int flip_x = 0; flip_x < 2; flip_x++) {
+ for (int flip_y = 0; flip_y < 2; flip_y++) {
+ float sign_x = flip_x ? -1.0f : 1.0f;
+ float sign_y = flip_y ? -1.0f : 1.0f;
+ for (int i = 0; i < poly_tot; i++) {
+ poly_copy[i][0] = poly[i][0] * sign_x;
+ poly_copy[i][1] = poly[i][1] * sign_y;
+ }
+ test_polyfill_template(id, is_degenerate, poly_copy, poly_tot, tris, tris_tot);
+ }
+ }
+ MEM_freeN(poly_copy);
+}
+
#ifdef USE_COMBINATIONS_ALL
static void test_polyfill_template_main(
const char *id, bool is_degenerate,
@@ -230,7 +252,7 @@ static void test_polyfill_template_main(
for (poly_cycle = 0; poly_cycle < poly_tot; poly_cycle++) {
// printf("polytest %s ofs=%d, reverse=%d\n", id, poly_cycle, poly_reverse);
- test_polyfill_template(id, is_degenerate, poly, poly_tot, tris, tris_tot);
+ test_polyfill_template_flip_sign(id, is_degenerate, poly, poly_tot, tris, tris_tot);
/* cycle */
copy_v2_v2(tmp, poly_copy[0]);
@@ -247,7 +269,7 @@ static void test_polyfill_template_main(
const float poly[][2], const unsigned int poly_tot,
unsigned int tris[][3], const unsigned int tris_tot)
{
- test_polyfill_template(id, is_degenerate, poly, poly_tot, tris, tris_tot);
+ test_polyfill_template_flip_sign(id, is_degenerate, poly, poly_tot, tris, tris_tot);
}
#endif /* USE_COMBINATIONS_ALL */
@@ -307,6 +329,43 @@ static void polyfill_to_obj(
/* -------------------------------------------------------------------- */
/* tests */
+/**
+ * Script to generate the data below:
+ *
+ * \code{.py}
+ * # This example assumes we have a mesh object in edit-mode
+ *
+ * import bpy
+ * import bmesh
+ *
+ * obj = bpy.context.edit_object
+ * me = obj.data
+ * bm = bmesh.from_edit_mesh(me)
+ *
+ * def clean_float(num):
+ * if int(num) == num:
+ * return str(int(num))
+ * prec = 1
+ * while True:
+ * text = f"{num:.{prec}f}"
+ * if float(text) == num:
+ * return text
+ * prec += 1
+ *
+ * for f in bm.faces:
+ * if f.select:
+ * print(f"\t// data for face: {f.index}")
+ * print("\tconst float poly[][2] = {", end="")
+ * coords = [[clean_float(num) for num in l.vert.co[0:2]] for l in f.loops]
+ * print("\t ", end="")
+ * for i, (x, y) in enumerate(coords):
+ * if (i % 2) == 0:
+ * print("\n\t ", end="")
+ * print(f"{{{x}, {y}}}", end=",")
+ * print("\n\t};")
+ * \endcode
+ */
+
#define POLY_TRI_COUNT(len) ((len) - 2)
@@ -517,3 +576,17 @@ TEST(polyfill2d, IssueT41986_axis_align)
TEST_POLYFILL_TEMPLATE_STATIC(poly, false);
}
+
+/* Blender bug T52834 */
+TEST(polyfill2d, IssueT52834_axis_align_co_linear)
+{
+ const float poly[][2] = {
+ {40, 0}, {36, 0}, {36, 5}, {35, 5}, {35, 0}, {30, 0}, {30, 5}, {29, 5}, {29, 0}, {24, 0}, {24, 3},
+ {23, 4}, {23, 0}, {18, 0}, {18, 5}, {17, 5}, {17, 0}, {12, 0}, {12, 5}, {11, 5}, {11, 0}, {6, 0},
+ {6, 5}, {5, 5}, {5, 0}, {0, 0}, {0, 5}, {-1, 5}, {-1, 0}, {-6, 0}, {-9, -3}, {-6, -3}, {-6, -2},
+ {-1, -2}, {0, -2}, {5, -2}, {6, -2}, {11, -2}, {12, -2}, {17, -2}, {18, -2}, {23, -2}, {24, -2},
+ {29, -2}, {30, -2}, {35, -2}, {36, -2}, {40, -2},
+ };
+
+ TEST_POLYFILL_TEMPLATE_STATIC(poly, false);
+}
diff --git a/tests/gtests/blenlib/BLI_stack_test.cc b/tests/gtests/blenlib/BLI_stack_test.cc
index 4c0b95f4b6b..18188937355 100644
--- a/tests/gtests/blenlib/BLI_stack_test.cc
+++ b/tests/gtests/blenlib/BLI_stack_test.cc
@@ -24,7 +24,7 @@ TEST(stack, Empty)
BLI_Stack *stack;
stack = BLI_stack_new(sizeof(int), __func__);
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
EXPECT_EQ(BLI_stack_count(stack), 0);
BLI_stack_free(stack);
}
@@ -37,11 +37,11 @@ TEST(stack, One)
stack = BLI_stack_new(sizeof(in), __func__);
BLI_stack_push(stack, (void *)&in);
- EXPECT_EQ(BLI_stack_is_empty(stack), false);
+ EXPECT_FALSE(BLI_stack_is_empty(stack));
EXPECT_EQ(BLI_stack_count(stack), 1);
BLI_stack_pop(stack, (void *)&out);
- EXPECT_EQ(in, out);
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_EQ(out, in);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
EXPECT_EQ(BLI_stack_count(stack), 0);
BLI_stack_free(stack);
}
@@ -59,12 +59,12 @@ TEST(stack, Range)
}
for (in = tot - 1; in >= 0; in--) {
- EXPECT_EQ(BLI_stack_is_empty(stack), false);
+ EXPECT_FALSE(BLI_stack_is_empty(stack));
BLI_stack_pop(stack, (void *)&out);
- EXPECT_EQ(in, out);
+ EXPECT_EQ(out, in);
}
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
BLI_stack_free(stack);
}
@@ -86,12 +86,12 @@ TEST(stack, String)
}
for (i = tot - 1; i >= 0; i--) {
- EXPECT_EQ(BLI_stack_is_empty(stack), false);
+ EXPECT_FALSE(BLI_stack_is_empty(stack));
*((int *)in) = i;
BLI_stack_pop(stack, (void *)&out);
EXPECT_STREQ(in, out);
}
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
BLI_stack_free(stack);
}
@@ -115,7 +115,7 @@ TEST(stack, Peek)
EXPECT_EQ(*ret, in[i % ARRAY_SIZE(in)]);
}
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
BLI_stack_free(stack);
}
@@ -140,7 +140,7 @@ TEST(stack, Clear)
}
BLI_stack_clear(stack);
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
/* and again, this time check its valid */
for (in = 0; in < tot; in++) {
@@ -148,12 +148,12 @@ TEST(stack, Clear)
}
for (in = tot - 1; in >= 0; in--) {
- EXPECT_EQ(BLI_stack_is_empty(stack), false);
+ EXPECT_FALSE(BLI_stack_is_empty(stack));
BLI_stack_pop(stack, (void *)&out);
- EXPECT_EQ(in, out);
+ EXPECT_EQ(out, in);
}
- EXPECT_EQ(BLI_stack_is_empty(stack), true);
+ EXPECT_TRUE(BLI_stack_is_empty(stack));
/* without this, we wont test case when mixed free/used */
tot /= 2;
@@ -204,10 +204,10 @@ TEST(stack, Reuse)
while (!BLI_stack_is_empty(stack)) {
i--;
BLI_stack_pop(stack, (void *)&sizes_test[i]);
- EXPECT_EQ(sizes[i], sizes_test[i]);
+ EXPECT_EQ(sizes_test[i], sizes[i]);
EXPECT_GT(i, -1);
}
- EXPECT_EQ(i, 0);
+ EXPECT_EQ(0, i);
EXPECT_EQ(memcmp(sizes, sizes_test, sizeof(sizes) - sizeof(int)), 0);
diff --git a/tests/gtests/blenlib/BLI_string_test.cc b/tests/gtests/blenlib/BLI_string_test.cc
index 17a4b5e82b9..f6f7e17c8ca 100644
--- a/tests/gtests/blenlib/BLI_string_test.cc
+++ b/tests/gtests/blenlib/BLI_string_test.cc
@@ -44,7 +44,7 @@ TEST(string, StrPartition)
/* "mat.e-r_ial" -> "mat", '.', "e-r_ial", 3 */
pre_ln = BLI_str_partition(str, delim, &sep, &suf);
- EXPECT_EQ(3, pre_ln);
+ EXPECT_EQ(pre_ln, 3);
EXPECT_EQ(&str[3], sep);
EXPECT_STREQ("e-r_ial", suf);
}
@@ -55,7 +55,7 @@ TEST(string, StrPartition)
/* ".mate-rial--" -> "", '.', "mate-rial--", 0 */
pre_ln = BLI_str_partition(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
+ EXPECT_EQ(pre_ln, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ("mate-rial--", suf);
}
@@ -65,7 +65,7 @@ TEST(string, StrPartition)
/* ".__.--_" -> "", '.', "__.--_", 0 */
pre_ln = BLI_str_partition(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
+ EXPECT_EQ(pre_ln, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ("__.--_", suf);
}
@@ -75,9 +75,9 @@ TEST(string, StrPartition)
/* "" -> "", NULL, NULL, 0 */
pre_ln = BLI_str_partition(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 0);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
{
@@ -85,9 +85,9 @@ TEST(string, StrPartition)
/* "material" -> "material", NULL, NULL, 8 */
pre_ln = BLI_str_partition(str, delim, &sep, &suf);
- EXPECT_EQ(8, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 8);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
}
@@ -103,7 +103,7 @@ TEST(string, StrRPartition)
/* "mat.e-r_ial" -> "mat.e-r", '_', "ial", 7 */
pre_ln = BLI_str_rpartition(str, delim, &sep, &suf);
- EXPECT_EQ(7, pre_ln);
+ EXPECT_EQ(pre_ln, 7);
EXPECT_EQ(&str[7], sep);
EXPECT_STREQ("ial", suf);
}
@@ -114,7 +114,7 @@ TEST(string, StrRPartition)
/* ".mate-rial--" -> ".mate-rial-", '-', "", 11 */
pre_ln = BLI_str_rpartition(str, delim, &sep, &suf);
- EXPECT_EQ(11, pre_ln);
+ EXPECT_EQ(pre_ln, 11);
EXPECT_EQ(&str[11], sep);
EXPECT_STREQ("", suf);
}
@@ -124,7 +124,7 @@ TEST(string, StrRPartition)
/* ".__.--_" -> ".__.--", '_', "", 6 */
pre_ln = BLI_str_rpartition(str, delim, &sep, &suf);
- EXPECT_EQ(6, pre_ln);
+ EXPECT_EQ(pre_ln, 6);
EXPECT_EQ(&str[6], sep);
EXPECT_STREQ("", suf);
}
@@ -134,9 +134,9 @@ TEST(string, StrRPartition)
/* "" -> "", NULL, NULL, 0 */
pre_ln = BLI_str_rpartition(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 0);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
{
@@ -144,9 +144,9 @@ TEST(string, StrRPartition)
/* "material" -> "material", NULL, NULL, 8 */
pre_ln = BLI_str_rpartition(str, delim, &sep, &suf);
- EXPECT_EQ(8, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 8);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
}
@@ -164,7 +164,7 @@ TEST(string, StrPartitionEx)
/* "mat.e-r_ia.l" over "mat.e-r" -> "mat.e", '.', "r_ia.l", 3 */
pre_ln = BLI_str_partition_ex(str, str + 6, delim, &sep, &suf, true);
- EXPECT_EQ(5, pre_ln);
+ EXPECT_EQ(pre_ln, 5);
EXPECT_EQ(&str[5], sep);
EXPECT_STREQ("r_ia.l", suf);
}
@@ -175,9 +175,9 @@ TEST(string, StrPartitionEx)
/* "mate.rial" over "mate" -> "mate.rial", NULL, NULL, 4 */
pre_ln = BLI_str_partition_ex(str, str + 4, delim, &sep, &suf, true);
- EXPECT_EQ(4, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 4);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
}
@@ -193,7 +193,7 @@ TEST(string, StrPartitionUtf8)
/* "ma\xc3\xb1te-r\xe2\x98\xafial" -> "ma", '\xc3\xb1', "te-r\xe2\x98\xafial", 2 */
pre_ln = BLI_str_partition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(2, pre_ln);
+ EXPECT_EQ(pre_ln, 2);
EXPECT_EQ(&str[2], sep);
EXPECT_STREQ("te-r\xe2\x98\xafial", suf);
}
@@ -204,7 +204,7 @@ TEST(string, StrPartitionUtf8)
/* "\xe2\x98\xafmate-rial-\xc3\xb1" -> "", '\xe2\x98\xaf', "mate-rial-\xc3\xb1", 0 */
pre_ln = BLI_str_partition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
+ EXPECT_EQ(pre_ln, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ("mate-rial-\xc3\xb1", suf);
}
@@ -214,7 +214,7 @@ TEST(string, StrPartitionUtf8)
/* "\xe2\x98\xaf.\xc3\xb1_.--\xc3\xb1" -> "", '\xe2\x98\xaf', ".\xc3\xb1_.--\xc3\xb1", 0 */
pre_ln = BLI_str_partition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
+ EXPECT_EQ(pre_ln, 0);
EXPECT_EQ(&str[0], sep);
EXPECT_STREQ(".\xc3\xb1_.--\xc3\xb1", suf);
}
@@ -224,9 +224,9 @@ TEST(string, StrPartitionUtf8)
/* "" -> "", NULL, NULL, 0 */
pre_ln = BLI_str_partition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 0);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
{
@@ -234,9 +234,9 @@ TEST(string, StrPartitionUtf8)
/* "material" -> "material", NULL, NULL, 8 */
pre_ln = BLI_str_partition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(8, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 8);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
}
@@ -252,7 +252,7 @@ TEST(string, StrRPartitionUtf8)
/* "ma\xc3\xb1te-r\xe2\x98\xafial" -> "mat\xc3\xb1te-r", '\xe2\x98\xaf', "ial", 8 */
pre_ln = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(8, pre_ln);
+ EXPECT_EQ(pre_ln, 8);
EXPECT_EQ(&str[8], sep);
EXPECT_STREQ("ial", suf);
}
@@ -263,7 +263,7 @@ TEST(string, StrRPartitionUtf8)
/* "\xe2\x98\xafmate-rial-\xc3\xb1" -> "\xe2\x98\xafmate-rial-", '\xc3\xb1', "", 13 */
pre_ln = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(13, pre_ln);
+ EXPECT_EQ(pre_ln, 13);
EXPECT_EQ(&str[13], sep);
EXPECT_STREQ("", suf);
}
@@ -273,7 +273,7 @@ TEST(string, StrRPartitionUtf8)
/* "\xe2\x98\xaf.\xc3\xb1_.--\xc3\xb1" -> "\xe2\x98\xaf.\xc3\xb1_.--", '\xc3\xb1', "", 10 */
pre_ln = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(10, pre_ln);
+ EXPECT_EQ(pre_ln, 10);
EXPECT_EQ(&str[10], sep);
EXPECT_STREQ("", suf);
}
@@ -283,9 +283,9 @@ TEST(string, StrRPartitionUtf8)
/* "" -> "", NULL, NULL, 0 */
pre_ln = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(0, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 0);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
{
@@ -293,9 +293,9 @@ TEST(string, StrRPartitionUtf8)
/* "material" -> "material", NULL, NULL, 8 */
pre_ln = BLI_str_rpartition_utf8(str, delim, &sep, &suf);
- EXPECT_EQ(8, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 8);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
}
@@ -313,7 +313,7 @@ TEST(string, StrPartitionExUtf8)
/* "ma\xc3\xb1te-r\xe2\x98\xafial" over "ma\xc3\xb1te" -> "ma", '\xc3\xb1', "te-r\xe2\x98\xafial", 2 */
pre_ln = BLI_str_partition_ex_utf8(str, str + 6, delim, &sep, &suf, true);
- EXPECT_EQ(2, pre_ln);
+ EXPECT_EQ(pre_ln, 2);
EXPECT_EQ(&str[2], sep);
EXPECT_STREQ("te-r\xe2\x98\xafial", suf);
}
@@ -324,9 +324,9 @@ TEST(string, StrPartitionExUtf8)
/* "mate\xe2\x98\xafrial" over "mate" -> "mate\xe2\x98\xafrial", NULL, NULL, 4 */
pre_ln = BLI_str_partition_ex_utf8(str, str + 4, delim, &sep, &suf, true);
- EXPECT_EQ(4, pre_ln);
- EXPECT_EQ(NULL, sep);
- EXPECT_EQ(NULL, suf);
+ EXPECT_EQ(pre_ln, 4);
+ EXPECT_EQ(sep, (void*)NULL);
+ EXPECT_EQ(suf, (void*)NULL);
}
}
@@ -373,8 +373,8 @@ TEST(string, StrFormatIntGrouped)
const int word_cmp_size = ARRAY_SIZE(word_cmp); \
const int word_num = BLI_string_find_split_words( \
word_str_src, word_str_src_len, ' ', word_info, word_cmp_size_input); \
- EXPECT_EQ(word_num, word_cmp_size - 1); \
- EXPECT_EQ_ARRAY_ND(word_cmp, word_info, word_cmp_size, 2); \
+ EXPECT_EQ(word_cmp_size - 1, word_num); \
+ EXPECT_EQ_ARRAY_ND<const int[2]>(word_cmp, word_info, word_cmp_size, 2); \
} ((void)0)
#define STRING_FIND_SPLIT_WORDS(word_str_src, ...) \
@@ -449,20 +449,20 @@ TEST(string, StringStrncasestr)
const char *res;
res = BLI_strncasestr(str_test0, "", 0);
- EXPECT_EQ(str_test0, res);
+ EXPECT_EQ(res, str_test0);
res = BLI_strncasestr(str_test0, " ", 1);
- EXPECT_EQ(str_test0 + 6, res);
+ EXPECT_EQ(res, str_test0 + 6);
res = BLI_strncasestr(str_test0, "her", 3);
- EXPECT_EQ(str_test0 + 7, res);
+ EXPECT_EQ(res, str_test0 + 7);
res = BLI_strncasestr(str_test0, "ARCh", 4);
- EXPECT_EQ(str_test0 + 2, res);
+ EXPECT_EQ(res, str_test0 + 2);
res = BLI_strncasestr(str_test0, "earcq", 4);
- EXPECT_EQ(str_test0 + 1, res);
+ EXPECT_EQ(res, str_test0 + 1);
res = BLI_strncasestr(str_test0, "not there", 9);
- EXPECT_EQ(NULL, res);
+ EXPECT_EQ(res, (void*)NULL);
}
diff --git a/tests/gtests/blenlib/BLI_string_utf8_test.cc b/tests/gtests/blenlib/BLI_string_utf8_test.cc
new file mode 100644
index 00000000000..95d73b4242f
--- /dev/null
+++ b/tests/gtests/blenlib/BLI_string_utf8_test.cc
@@ -0,0 +1,304 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+extern "C" {
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+#include "BLI_string_utf8.h"
+}
+
+/* Note that 'common' utf-8 variants of string functions (like copy, etc.) are tested in BLI_string_test.cc
+ * However, tests below are specific utf-8 conformance ones, and since they eat quite their share of lines,
+ * they deserved their own file. */
+
+/* -------------------------------------------------------------------- */
+/* stubs */
+
+extern "C" {
+
+int mk_wcwidth(wchar_t ucs);
+int mk_wcswidth(const wchar_t *pwcs, size_t n);
+
+int mk_wcwidth(wchar_t ucs)
+{
+ return 0;
+}
+
+int mk_wcswidth(const wchar_t *pwcs, size_t n)
+{
+ return 0;
+}
+
+}
+
+/* -------------------------------------------------------------------- */
+/* tests */
+
+/* Each test is made of a 79 bytes (80 with NULL char) string to test, expected string result after
+ * stripping invalid utf8 bytes, and a single-byte string encoded with expected number of errors.
+ *
+ * Based on utf-8 decoder stress-test (https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt)
+ * by Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2015-08-28 - CC BY 4.0
+ */
+const char *utf8_invalid_tests[][3] = {
+// 1 Some correct UTF-8 text
+ {"You should see the Greek word 'kosme': \"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\" |",
+ "You should see the Greek word 'kosme': \"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\" |", "\x00"},
+
+// 2 Boundary condition test cases
+// Note that those will pass for us, those are not erronéous unicode code points
+// (asside from \x00, which is only valid as string terminator).
+// 2.1 First possible sequence of a certain length
+ {"2.1.1 1 byte (U-00000000): \"\x00\" |",
+ "2.1.1 1 byte (U-00000000): \"\" |", "\x01"},
+ {"2.1.2 2 bytes (U-00000080): \"\xc2\x80\" |",
+ "2.1.2 2 bytes (U-00000080): \"\xc2\x80\" |", "\x00"},
+ {"2.1.3 3 bytes (U-00000800): \"\xe0\xa0\x80\" |",
+ "2.1.3 3 bytes (U-00000800): \"\xe0\xa0\x80\" |", "\x00"},
+ {"2.1.4 4 bytes (U-00010000): \"\xf0\x90\x80\x80\" |",
+ "2.1.4 4 bytes (U-00010000): \"\xf0\x90\x80\x80\" |", "\x00"},
+ {"2.1.5 5 bytes (U-00200000): \"\xf8\x88\x80\x80\x80\" |",
+ "2.1.5 5 bytes (U-00200000): \"\xf8\x88\x80\x80\x80\" |", "\x00"},
+ {"2.1.6 6 bytes (U-04000000): \"\xfc\x84\x80\x80\x80\x80\" |",
+ "2.1.6 6 bytes (U-04000000): \"\xfc\x84\x80\x80\x80\x80\" |", "\x00"},
+// 2.2 Last possible sequence of a certain length
+ {"2.2.1 1 byte (U-0000007F): \"\x7f\" |",
+ "2.2.1 1 byte (U-0000007F): \"\x7f\" |", "\x00"},
+ {"2.2.2 2 bytes (U-000007FF): \"\xdf\xbf\" |",
+ "2.2.2 2 bytes (U-000007FF): \"\xdf\xbf\" |", "\x00"},
+ {"2.2.3 3 bytes (U-0000FFFF): \"\xef\xbf\xbf\" |",
+ "2.2.3 3 bytes (U-0000FFFF): \"\" |", "\x03"}, /* matches one of 5.3 sequences... */
+ {"2.2.4 4 bytes (U-001FFFFF): \"\xf7\xbf\xbf\xbf\" |",
+ "2.2.4 4 bytes (U-001FFFFF): \"\xf7\xbf\xbf\xbf\" |", "\x00"},
+ {"2.2.5 5 bytes (U-03FFFFFF): \"\xfb\xbf\xbf\xbf\xbf\" |",
+ "2.2.5 5 bytes (U-03FFFFFF): \"\xfb\xbf\xbf\xbf\xbf\" |", "\x00"},
+ {"2.2.6 6 bytes (U-7FFFFFFF): \"\xfd\xbf\xbf\xbf\xbf\xbf\" |",
+ "2.2.6 6 bytes (U-7FFFFFFF): \"\xfd\xbf\xbf\xbf\xbf\xbf\" |", "\x00"},
+// 2.3 Other boundary conditions
+ {"2.3.1 U-0000D7FF = ed 9f bf = \"\xed\x9f\xbf\" |",
+ "2.3.1 U-0000D7FF = ed 9f bf = \"\xed\x9f\xbf\" |", "\x00"},
+ {"2.3.2 U-0000E000 = ee 80 80 = \"\xee\x80\x80\" |",
+ "2.3.2 U-0000E000 = ee 80 80 = \"\xee\x80\x80\" |", "\x00"},
+ {"2.3.3 U-0000FFFD = ef bf bd = \"\xef\xbf\xbd\" |",
+ "2.3.3 U-0000FFFD = ef bf bd = \"\xef\xbf\xbd\" |", "\x00"},
+ {"2.3.4 U-0010FFFF = f4 8f bf bf = \"\xf4\x8f\xbf\xbf\" |",
+ "2.3.4 U-0010FFFF = f4 8f bf bf = \"\xf4\x8f\xbf\xbf\" |", "\x00"},
+ {"2.3.5 U-00110000 = f4 90 80 80 = \"\xf4\x90\x80\x80\" |",
+ "2.3.5 U-00110000 = f4 90 80 80 = \"\xf4\x90\x80\x80\" |", "\x00"},
+
+// 3 Malformed sequences
+// 3.1 Unexpected continuation bytes
+// Each unexpected continuation byte should be separately signalled as a malformed sequence of its own.
+ {"3.1.1 First continuation byte 0x80: \"\x80\" |",
+ "3.1.1 First continuation byte 0x80: \"\" |", "\x01"},
+ {"3.1.2 Last continuation byte 0xbf: \"\xbf\" |",
+ "3.1.2 Last continuation byte 0xbf: \"\" |", "\x01"},
+ {"3.1.3 2 continuation bytes: \"\x80\xbf\" |",
+ "3.1.3 2 continuation bytes: \"\" |", "\x02"},
+ {"3.1.4 3 continuation bytes: \"\x80\xbf\x80\" |",
+ "3.1.4 3 continuation bytes: \"\" |", "\x03"},
+ {"3.1.5 4 continuation bytes: \"\x80\xbf\x80\xbf\" |",
+ "3.1.5 4 continuation bytes: \"\" |", "\x04"},
+ {"3.1.6 5 continuation bytes: \"\x80\xbf\x80\xbf\x80\" |",
+ "3.1.6 5 continuation bytes: \"\" |", "\x05"},
+ {"3.1.7 6 continuation bytes: \"\x80\xbf\x80\xbf\x80\xbf\" |",
+ "3.1.7 6 continuation bytes: \"\" |", "\x06"},
+ {"3.1.8 7 continuation bytes: \"\x80\xbf\x80\xbf\x80\xbf\x80\" |",
+ "3.1.8 7 continuation bytes: \"\" |", "\x07"},
+// 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): |
+ {"3.1.9 \"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\" |",
+ "3.1.9 \"\" |", "\x40"},
+// 3.2 Lonely start characters
+// 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), each followed by a space character:
+ {"3.2.1 \"\xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf "
+ "\xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf \" |",
+ "3.2.1 \" \" |", "\x20"},
+// 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), each followed by a space character:
+ {"3.2.2 \"\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef \" |",
+ "3.2.2 \" \" |", "\x10"},
+// 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), each followed by a space character:
+ {"3.2.3 \"\xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \" |",
+ "3.2.3 \" \" |", "\x08"},
+// 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), each followed by a space character:
+ {"3.2.4 \"\xf8 \xf9 \xfa \xfb \" |",
+ "3.2.4 \" \" |", "\x04"},
+// 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), each followed by a space character:
+ {"3.2.4 \"\xfc \xfd \" |",
+ "3.2.4 \" \" |", "\x02"},
+// 3.3 Sequences with last continuation byte missing
+// All bytes of an incomplete sequence should be signalled as a single malformed sequence,
+// i.e., you should see only a single replacement character in each of the next 10 tests.
+// (Characters as in section 2)
+ {"3.3.1 2-byte sequence with last byte missing (U+0000): \"\xc0\" |",
+ "3.3.1 2-byte sequence with last byte missing (U+0000): \"\" |", "\x01"},
+ {"3.3.2 3-byte sequence with last byte missing (U+0000): \"\xe0\x80\" |",
+ "3.3.2 3-byte sequence with last byte missing (U+0000): \"\" |", "\x02"},
+ {"3.3.3 4-byte sequence with last byte missing (U+0000): \"\xf0\x80\x80\" |",
+ "3.3.3 4-byte sequence with last byte missing (U+0000): \"\" |", "\x03"},
+ {"3.3.4 5-byte sequence with last byte missing (U+0000): \"\xf8\x80\x80\x80\" |",
+ "3.3.4 5-byte sequence with last byte missing (U+0000): \"\" |", "\x04"},
+ {"3.3.5 6-byte sequence with last byte missing (U+0000): \"\xfc\x80\x80\x80\x80\" |",
+ "3.3.5 6-byte sequence with last byte missing (U+0000): \"\" |", "\x05"},
+ {"3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\xdf\" |",
+ "3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\" |", "\x01"},
+ {"3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\xef\xbf\" |",
+ "3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\" |", "\x02"},
+ {"3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\xf7\xbf\xbf\" |",
+ "3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\" |", "\x03"},
+ {"3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\xfb\xbf\xbf\xbf\" |",
+ "3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\" |", "\x04"},
+ {"3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\xfd\xbf\xbf\xbf\xbf\" |",
+ "3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\" |", "\x05"},
+// 3.4 Concatenation of incomplete sequences
+// All the 10 sequences of 3.3 concatenated, you should see 10 malformed sequences being signalled:
+ {"3.4 \"\xc0\xe0\x80\xf0\x80\x80\xf8\x80\x80\x80\xfc\x80\x80\x80\x80"
+ "\xdf\xef\xbf\xf7\xbf\xbf\xfb\xbf\xbf\xbf\xfd\xbf\xbf\xbf\xbf\""
+ " |",
+ "3.4 \"\" |", "\x1e"},
+// 3.5 Impossible bytes
+// The following two bytes cannot appear in a correct UTF-8 string
+ {"3.5.1 fe = \"\xfe\" |",
+ "3.5.1 fe = \"\" |", "\x01"},
+ {"3.5.2 ff = \"\xff\" |",
+ "3.5.2 ff = \"\" |", "\x01"},
+ {"3.5.3 fe fe ff ff = \"\xfe\xfe\xff\xff\" |",
+ "3.5.3 fe fe ff ff = \"\" |", "\x04"},
+
+// 4 Overlong sequences
+// The following sequences are not malformed according to the letter of the Unicode 2.0 standard.
+// However, they are longer then necessary and a correct UTF-8 encoder is not allowed to produce them.
+// A "safe UTF-8 decoder" should reject them just like malformed sequences for two reasons:
+// (1) It helps to debug applications if overlong sequences are not treated as valid representations
+// of characters, because this helps to spot problems more quickly. (2) Overlong sequences provide
+// alternative representations of characters, that could maliciously be used to bypass filters that check
+// only for ASCII characters. For instance, a 2-byte encoded line feed (LF) would not be caught by a
+// line counter that counts only 0x0a bytes, but it would still be processed as a line feed by an unsafe
+// UTF-8 decoder later in the pipeline. From a security point of view, ASCII compatibility of UTF-8
+// sequences means also, that ASCII characters are *only* allowed to be represented by ASCII bytes
+// in the range 0x00-0x7f. To ensure this aspect of ASCII compatibility, use only "safe UTF-8 decoders"
+// that reject overlong UTF-8 sequences for which a shorter encoding exists.
+//
+// 4.1 Examples of an overlong ASCII character
+// With a safe UTF-8 decoder, all of the following five overlong representations of the ASCII character
+// slash ("/") should be rejected like a malformed UTF-8 sequence, for instance by substituting it with
+// a replacement character. If you see a slash below, you do not have a safe UTF-8 decoder!
+ {"4.1.1 U+002F = c0 af = \"\xc0\xaf\" |",
+ "4.1.1 U+002F = c0 af = \"\" |", "\x02"},
+ {"4.1.2 U+002F = e0 80 af = \"\xe0\x80\xaf\" |",
+ "4.1.2 U+002F = e0 80 af = \"\" |", "\x03"},
+ {"4.1.3 U+002F = f0 80 80 af = \"\xf0\x80\x80\xaf\" |",
+ "4.1.3 U+002F = f0 80 80 af = \"\" |", "\x04"},
+ {"4.1.4 U+002F = f8 80 80 80 af = \"\xf8\x80\x80\x80\xaf\" |",
+ "4.1.4 U+002F = f8 80 80 80 af = \"\" |", "\x05"},
+ {"4.1.5 U+002F = fc 80 80 80 80 af = \"\xfc\x80\x80\x80\x80\xaf\" |",
+ "4.1.5 U+002F = fc 80 80 80 80 af = \"\" |", "\x06"},
+// 4.2 Maximum overlong sequences
+// Below you see the highest Unicode value that is still resulting in an overlong sequence if represented
+// with the given number of bytes. This is a boundary test for safe UTF-8 decoders. All five characters
+// should be rejected like malformed UTF-8 sequences.
+ {"4.2.1 U-0000007F = c1 bf = \"\xc1\xbf\" |",
+ "4.2.1 U-0000007F = c1 bf = \"\" |", "\x02"},
+ {"4.2.2 U-000007FF = e0 9f bf = \"\xe0\x9f\xbf\" |",
+ "4.2.2 U-000007FF = e0 9f bf = \"\" |", "\x03"},
+ {"4.2.3 U-0000FFFF = f0 8f bf bf = \"\xf0\x8f\xbf\xbf\" |",
+ "4.2.3 U-0000FFFF = f0 8f bf bf = \"\" |", "\x04"},
+ {"4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\xf8\x87\xbf\xbf\xbf\" |",
+ "4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\" |", "\x05"},
+ {"4.2.5 U+0000 = fc 83 bf bf bf bf = \"\xfc\x83\xbf\xbf\xbf\xbf\" |",
+ "4.2.5 U+0000 = fc 83 bf bf bf bf = \"\" |", "\x06"},
+// 4.3 Overlong representation of the NUL character
+// The following five sequences should also be rejected like malformed UTF-8 sequences and should not be
+// treated like the ASCII NUL character.
+ {"4.3.1 U+0000 = c0 80 = \"\xc0\x80\" |",
+ "4.3.1 U+0000 = c0 80 = \"\" |", "\x02"},
+ {"4.3.2 U+0000 = e0 80 80 = \"\xe0\x80\x80\" |",
+ "4.3.2 U+0000 = e0 80 80 = \"\" |", "\x03"},
+ {"4.3.3 U+0000 = f0 80 80 80 = \"\xf0\x80\x80\x80\" |",
+ "4.3.3 U+0000 = f0 80 80 80 = \"\" |", "\x04"},
+ {"4.3.4 U+0000 = f8 80 80 80 80 = \"\xf8\x80\x80\x80\x80\" |",
+ "4.3.4 U+0000 = f8 80 80 80 80 = \"\" |", "\x05"},
+ {"4.3.5 U+0000 = fc 80 80 80 80 80 = \"\xfc\x80\x80\x80\x80\x80\" |",
+ "4.3.5 U+0000 = fc 80 80 80 80 80 = \"\" |", "\x06"},
+
+// 5 Illegal code positions
+// The following UTF-8 sequences should be rejected like malformed sequences, because they never represent
+// valid ISO 10646 characters and a UTF-8 decoder that accepts them might introduce security problems
+// comparable to overlong UTF-8 sequences.
+// 5.1 Single UTF-16 surrogates
+ {"5.1.1 U+D800 = ed a0 80 = \"\xed\xa0\x80\" |",
+ "5.1.1 U+D800 = ed a0 80 = \"\" |", "\x03"},
+ {"5.1.2 U+DB7F = ed ad bf = \"\xed\xad\xbf\" |",
+ "5.1.2 U+DB7F = ed ad bf = \"\" |", "\x03"},
+ {"5.1.3 U+DB80 = ed ae 80 = \"\xed\xae\x80\" |",
+ "5.1.3 U+DB80 = ed ae 80 = \"\" |", "\x03"},
+ {"5.1.4 U+DBFF = ed af bf = \"\xed\xaf\xbf\" |",
+ "5.1.4 U+DBFF = ed af bf = \"\" |", "\x03"},
+ {"5.1.5 U+DC00 = ed b0 80 = \"\xed\xb0\x80\" |",
+ "5.1.5 U+DC00 = ed b0 80 = \"\" |", "\x03"},
+ {"5.1.6 U+DF80 = ed be 80 = \"\xed\xbe\x80\" |",
+ "5.1.6 U+DF80 = ed be 80 = \"\" |", "\x03"},
+ {"5.1.7 U+DFFF = ed bf bf = \"\xed\xbf\xbf\" |",
+ "5.1.7 U+DFFF = ed bf bf = \"\" |", "\x03"},
+// 5.2 Paired UTF-16 surrogates
+ {"5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\xed\xa0\x80\xed\xb0\x80\" |",
+ "5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\" |", "\x06"},
+ {"5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\xed\xa0\x80\xed\xbf\xbf\" |",
+ "5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\" |", "\x06"},
+ {"5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\xed\xad\xbf\xed\xb0\x80\" |",
+ "5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\" |", "\x06"},
+ {"5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\xed\xad\xbf\xed\xbf\xbf\" |",
+ "5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\" |", "\x06"},
+ {"5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\xed\xae\x80\xed\xb0\x80\" |",
+ "5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\" |", "\x06"},
+ {"5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\xed\xae\x80\xed\xbf\xbf\" |",
+ "5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\" |", "\x06"},
+ {"5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\xed\xaf\xbf\xed\xb0\x80\" |",
+ "5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\" |", "\x06"},
+ {"5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\xed\xaf\xbf\xed\xbf\xbf\" |",
+ "5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\" |", "\x06"},
+// 5.3 Noncharacter code positions
+// The following "noncharacters" are "reserved for internal use" by applications, and according to older versions
+// of the Unicode Standard "should never be interchanged". Unicode Corrigendum #9 dropped the latter restriction.
+// Nevertheless, their presence in incoming UTF-8 data can remain a potential security risk, depending
+// on what use is made of these codes subsequently. Examples of such internal use:
+// - Some file APIs with 16-bit characters may use the integer value -1 = U+FFFF to signal
+// an end-of-file (EOF) or error condition.
+// - In some UTF-16 receivers, code point U+FFFE might trigger a byte-swap operation
+// (to convert between UTF-16LE and UTF-16BE).
+// With such internal use of noncharacters, it may be desirable and safer to block those code points in
+// UTF-8 decoders, as they should never occur legitimately in incoming UTF-8 data, and could trigger
+// unsafe behaviour in subsequent processing.
+//
+// Particularly problematic noncharacters in 16-bit applications:
+ {"5.3.1 U+FFFE = ef bf be = \"\xef\xbf\xbe\" |",
+ "5.3.1 U+FFFE = ef bf be = \"\" |", "\x03"},
+ {"5.3.2 U+FFFF = ef bf bf = \"\xef\xbf\xbf\" |",
+ "5.3.2 U+FFFF = ef bf bf = \"\" |", "\x03"},
+ /* Fo now, we ignore those, they do not seem to be crucial anyway... */
+// 5.3.3 U+FDD0 .. U+FDEF
+// 5.3.4 U+nFFFE U+nFFFF (for n = 1..10)
+ {NULL, NULL, NULL}
+};
+
+/* BLI_utf8_invalid_strip (and indirectly, BLI_utf8_invalid_byte). */
+TEST(string, Utf8InvalidBytes)
+{
+ for (int i = 0; utf8_invalid_tests[i][0] != NULL; i++) {
+ const char *tst = utf8_invalid_tests[i][0];
+ const char *tst_stripped = utf8_invalid_tests[i][1];
+ const int num_errors = (int)utf8_invalid_tests[i][2][0];
+
+ char buff[80];
+ memcpy(buff, tst, sizeof(buff));
+
+ const int num_errors_found = BLI_utf8_invalid_strip(buff, sizeof(buff) - 1);
+
+ printf("[%02d] -> [%02d] \"%s\" -> \"%s\"\n", num_errors, num_errors_found, tst, buff);
+ EXPECT_EQ(num_errors_found, num_errors);
+ EXPECT_STREQ(buff, tst_stripped);
+ }
+}
diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt
index 12112e7a481..ffdb8d08d31 100644
--- a/tests/gtests/blenlib/CMakeLists.txt
+++ b/tests/gtests/blenlib/CMakeLists.txt
@@ -34,22 +34,27 @@ include_directories(${INC})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
+if(WIN32)
+ set(BLI_path_util_extra_libs "bf_blenlib;bf_intern_utfconv;extern_wcwidth;${ZLIB_LIBRARIES}")
+else()
+ set(BLI_path_util_extra_libs "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}")
+endif()
BLENDER_TEST(BLI_array_store "bf_blenlib")
BLENDER_TEST(BLI_array_utils "bf_blenlib")
-BLENDER_TEST(BLI_stack "bf_blenlib")
-BLENDER_TEST(BLI_math_color "bf_blenlib")
-BLENDER_TEST(BLI_math_geom "bf_blenlib;bf_intern_eigen")
+BLENDER_TEST(BLI_ghash "bf_blenlib")
+BLENDER_TEST(BLI_hash_mm2a "bf_blenlib")
+BLENDER_TEST(BLI_kdopbvh "bf_blenlib")
+BLENDER_TEST(BLI_listbase "bf_blenlib")
BLENDER_TEST(BLI_math_base "bf_blenlib")
+BLENDER_TEST(BLI_math_color "bf_blenlib")
+BLENDER_TEST(BLI_math_geom "bf_blenlib")
+BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}")
+BLENDER_TEST(BLI_polyfill2d "bf_blenlib")
+BLENDER_TEST(BLI_stack "bf_blenlib")
BLENDER_TEST(BLI_string "bf_blenlib")
-if(WIN32)
- BLENDER_TEST(BLI_path_util "bf_blenlib;bf_intern_utfconv;extern_wcwidth;${ZLIB_LIBRARIES}")
-else()
- BLENDER_TEST(BLI_path_util "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}")
-endif()
-BLENDER_TEST(BLI_polyfill2d "bf_blenlib;bf_intern_eigen")
-BLENDER_TEST(BLI_listbase "bf_blenlib")
-BLENDER_TEST(BLI_hash_mm2a "bf_blenlib")
-BLENDER_TEST(BLI_ghash "bf_blenlib")
+BLENDER_TEST(BLI_string_utf8 "bf_blenlib")
BLENDER_TEST_PERFORMANCE(BLI_ghash_performance "bf_blenlib")
+
+unset(BLI_path_util_extra_libs)
diff --git a/tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h b/tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h
new file mode 100644
index 00000000000..dad77a257d5
--- /dev/null
+++ b/tests/gtests/blenlib/stubs/bf_intern_eigen_stubs.h
@@ -0,0 +1,18 @@
+/* Apache License, Version 2.0 */
+
+extern "C" {
+
+bool EIG_self_adjoint_eigen_solve(const int size, const float *matrix, float *r_eigen_values, float *r_eigen_vectors)
+{
+ BLI_assert(0);
+ UNUSED_VARS(size, matrix, r_eigen_values, r_eigen_vectors);
+ return false;
+}
+
+void EIG_svd_square_matrix(const int size, const float *matrix, float *r_U, float *r_S, float *r_V)
+{
+ BLI_assert(0);
+ UNUSED_VARS(size, matrix, r_U, r_S, r_V);
+}
+
+}
diff --git a/tests/gtests/bmesh/bmesh_core_test.cc b/tests/gtests/bmesh/bmesh_core_test.cc
index f386abc0b2b..b2cb1a4e8a3 100644
--- a/tests/gtests/bmesh/bmesh_core_test.cc
+++ b/tests/gtests/bmesh/bmesh_core_test.cc
@@ -12,18 +12,18 @@ TEST(bmesh_core, BMVertCreate) {
BMeshCreateParams bm_params;
bm_params.use_toolflags = true;
bm = BM_mesh_create(&bm_mesh_allocsize_default, &bm_params);
- EXPECT_EQ(0, bm->totvert);
+ EXPECT_EQ(bm->totvert, 0);
/* make a custom layer so we can see if it is copied properly */
BM_data_layer_add(bm, &bm->vdata, CD_PROP_FLT);
bv1 = BM_vert_create(bm, co1, NULL, BM_CREATE_NOP);
ASSERT_TRUE(bv1 != NULL);
- EXPECT_EQ(1.0f, bv1->co[0]);
- EXPECT_EQ(2.0f, bv1->co[1]);
- EXPECT_EQ(0.0f, bv1->co[2]);
+ EXPECT_EQ(bv1->co[0], 1.0f);
+ EXPECT_EQ(bv1->co[1], 2.0f);
+ EXPECT_EQ(bv1->co[2], 0.0f);
EXPECT_TRUE(is_zero_v3(bv1->no));
- EXPECT_EQ((char)BM_VERT, bv1->head.htype);
- EXPECT_EQ(0, bv1->head.hflag);
- EXPECT_EQ(0, bv1->head.api_flag);
+ EXPECT_EQ(bv1->head.htype, (char)BM_VERT);
+ EXPECT_EQ(bv1->head.hflag, 0);
+ EXPECT_EQ(bv1->head.api_flag, 0);
bv2 = BM_vert_create(bm, NULL, NULL, BM_CREATE_NOP);
ASSERT_TRUE(bv2 != NULL);
EXPECT_TRUE(is_zero_v3(bv2->co));
@@ -33,7 +33,7 @@ TEST(bmesh_core, BMVertCreate) {
bv3 = BM_vert_create(bm, co1, bv2, BM_CREATE_NOP);
ASSERT_TRUE(bv3 != NULL);
EXPECT_FALSE(BM_elem_flag_test((BMElem *)bv3, BM_ELEM_SELECT));
- EXPECT_EQ(1.5f, BM_elem_float_data_get(&bm->vdata, bv3, CD_PROP_FLT));
- EXPECT_EQ(3, BM_mesh_elem_count(bm, BM_VERT));
+ EXPECT_EQ(BM_elem_float_data_get(&bm->vdata, bv3, CD_PROP_FLT), 1.5f);
+ EXPECT_EQ(BM_mesh_elem_count(bm, BM_VERT), 3);
BM_mesh_free(bm);
}
diff --git a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
index 345c3824b63..01ff38f0528 100644
--- a/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
+++ b/tests/gtests/guardedalloc/guardedalloc_alignment_test.cc
@@ -8,7 +8,7 @@ extern "C" {
#include "MEM_guardedalloc.h"
-#define CHECK_ALIGNMENT(ptr, align) EXPECT_EQ(0, (size_t)ptr % align)
+#define CHECK_ALIGNMENT(ptr, align) EXPECT_EQ((size_t)ptr % align, 0)
namespace {
diff --git a/tests/gtests/testing/CMakeLists.txt b/tests/gtests/testing/CMakeLists.txt
index 1eb60e7f3b5..95ac59e6dce 100644
--- a/tests/gtests/testing/CMakeLists.txt
+++ b/tests/gtests/testing/CMakeLists.txt
@@ -24,20 +24,11 @@
set(INC
.
..
- ../../../extern/gflags/src
+ ${GLOG_INCLUDE_DIRS}
+ ${GFLAGS_INCLUDE_DIRS}
../../../extern/gtest/include
)
-if(WIN32)
- list(APPEND INC
- ../../../extern/glog/src/windows
- )
-else()
- list(APPEND INC
- ../../../extern/glog/src
- )
-endif()
-
set(INC_SYS
)
@@ -47,7 +38,4 @@ set(SRC
testing.h
)
-add_definitions(${GFLAGS_DEFINES})
-add_definitions(${GLOG_DEFINES})
-
blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/tests/gtests/testing/testing.h b/tests/gtests/testing/testing.h
index 1594ed3926c..d5a7b076970 100644
--- a/tests/gtests/testing/testing.h
+++ b/tests/gtests/testing/testing.h
@@ -12,6 +12,29 @@
EXPECT_NEAR(a[2], b[2], eps); \
} (void) 0
+#define EXPECT_V4_NEAR(a, b, eps) \
+{ \
+ EXPECT_NEAR(a[0], b[0], eps); \
+ EXPECT_NEAR(a[1], b[1], eps); \
+ EXPECT_NEAR(a[2], b[2], eps); \
+ EXPECT_NEAR(a[3], b[3], eps); \
+ } (void) 0
+
+#define EXPECT_M3_NEAR(a, b, eps) \
+do { \
+ EXPECT_V3_NEAR(a[0], b[0], eps); \
+ EXPECT_V3_NEAR(a[1], b[1], eps); \
+ EXPECT_V3_NEAR(a[2], b[2], eps); \
+} while(false);
+
+#define EXPECT_M4_NEAR(a, b, eps) \
+do { \
+ EXPECT_V3_NEAR(a[0], b[0], eps); \
+ EXPECT_V3_NEAR(a[1], b[1], eps); \
+ EXPECT_V3_NEAR(a[2], b[2], eps); \
+ EXPECT_V4_NEAR(a[3], b[3], eps); \
+} while(false);
+
#define EXPECT_MATRIX_NEAR(a, b, tolerance) \
do { \
bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
diff --git a/tests/gtests/testing/testing_main.cc b/tests/gtests/testing/testing_main.cc
index b2dcc445aca..e8104a2a026 100644
--- a/tests/gtests/testing/testing_main.cc
+++ b/tests/gtests/testing/testing_main.cc
@@ -28,7 +28,7 @@
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
- gflags::ParseCommandLineFlags(&argc, &argv, true);
+ BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
return RUN_ALL_TESTS();
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index a29b612e0ef..7e42f36c6e4 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -18,7 +18,7 @@
#
# ***** END GPL LICENSE BLOCK *****
-# --env-system-scripts allows to run without the install target.
+# --env-system-scripts allows to run without the install target.
# Use '--write-blend=/tmp/test.blend' to view output
@@ -39,83 +39,132 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR})
# all calls to blender use this
if(APPLE)
if(${CMAKE_GENERATOR} MATCHES "Xcode")
- set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/Debug/blender.app/Contents/MacOS/blender)
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup)
else()
- set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender.app/Contents/MacOS/blender)
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
endif()
else()
- set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender)
+ set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
endif()
-# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
-set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE})
-set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts)
+# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no
+# set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE})
+# set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} ${TEST_BLENDER_EXE_PARAMS} )
# ------------------------------------------------------------------------------
# GENERAL PYTHON CORRECTNESS TESTS
-add_test(script_load_keymap ${TEST_BLENDER_EXE}
+add_test(
+ NAME script_load_keymap
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_keymap_completeness.py
)
-add_test(script_load_addons ${TEST_BLENDER_EXE}
+add_test(
+ NAME script_load_addons
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_load_addons.py
)
-add_test(script_load_modules ${TEST_BLENDER_EXE}
+add_test(
+ NAME script_load_modules
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_load_py_modules.py
)
# test running operators doesn't segfault under various conditions
if(USE_EXPERIMENTAL_TESTS)
- add_test(script_run_operators ${TEST_BLENDER_EXE}
+ add_test(
+ NAME script_run_operators
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_run_operators.py
)
endif()
# ------------------------------------------------------------------------------
# PY API TESTS
-add_test(script_pyapi_bpy_path ${TEST_BLENDER_EXE}
+add_test(
+ NAME script_pyapi_bpy_path
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_path.py
)
-add_test(script_pyapi_bpy_utils_units ${TEST_BLENDER_EXE}
+add_test(
+ NAME script_pyapi_bpy_utils_units
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_utils_units.py
)
-# test running mathutils testing script
-add_test(script_pyapi_mathutils ${TEST_BLENDER_EXE}
+add_test(
+ NAME script_pyapi_mathutils
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_mathutils.py
)
+add_test(
+ NAME script_pyapi_idprop
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
+ --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py
+)
+
+add_test(
+ NAME script_pyapi_idprop_datablock
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
+ --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop_datablock.py
+)
+
# ------------------------------------------------------------------------------
# MODELING TESTS
-add_test(bevel ${TEST_BLENDER_EXE}
+add_test(
+ NAME bevel
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/modeling/bevel_regression.blend
--python-text run_tests
)
+add_test(
+ NAME split_faces
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
+ ${TEST_SRC_DIR}/modeling/split_faces_test.blend
+ --python-text run_tests
+)
+
+# ------------------------------------------------------------------------------
+# MODIFIERS TESTS
+add_test(
+ NAME modifier_array
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
+ ${TEST_SRC_DIR}/modifier_stack/array_test.blend
+ --python-text run_tests
+)
+
# ------------------------------------------------------------------------------
# IO TESTS
# OBJ Import tests
# disabled until updated & working
if(FALSE)
-add_test(import_obj_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_obj_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/cube.obj'\)
--md5=39cce4bacac2d1b18fc470380279bc15 --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_obj_cube.blend
)
-add_test(import_obj_nurbs_cyclic ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_obj_nurbs_cyclic
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/nurbs_cyclic.obj'\)
--md5=ad3c307e5883224a0492378cd32691ab --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_obj_nurbs_cyclic.blend
)
-add_test(import_obj_makehuman ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_obj_makehuman
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/makehuman.obj'\)
--md5=c9f78b185e58358daa4ecaecfa75464e --md5_method=SCENE
@@ -124,7 +173,9 @@ add_test(import_obj_makehuman ${TEST_BLENDER_EXE}
endif()
# OBJ Export tests
-add_test(export_obj_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_obj_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_cube.obj',use_selection=False\)
@@ -133,7 +184,9 @@ add_test(export_obj_cube ${TEST_BLENDER_EXE}
--md5=e80660437ad9bfe082849641c361a233 --md5_method=FILE
)
-add_test(export_obj_nurbs ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_obj_nurbs
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_nurbs.obj',use_selection=False,use_nurbs=True\)
@@ -144,7 +197,9 @@ add_test(export_obj_nurbs ${TEST_BLENDER_EXE}
# disabled until updated & working
if(FALSE)
-add_test(export_obj_all_objects ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_obj_all_objects
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_all_objects.obj',use_selection=False,use_nurbs=True\)
@@ -157,21 +212,27 @@ endif()
# PLY Import tests
-add_test(import_ply_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_ply_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/cube_ascii.ply'\)
--md5=527134343c27fc0ea73115b85fbfd3ac --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_ply_cube.blend
)
-add_test(import_ply_bunny ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_ply_bunny
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/bunny2.ply'\)
--md5=6ea5b8533400a17accf928b8fd024eaa --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_ply_bunny.blend
)
-add_test(import_ply_small_holes ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_ply_small_holes
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/many_small_holes.ply'\)
--md5=c3093e26ecae5b6d59fbbcf2a0d0b39f --md5_method=SCENE
@@ -181,7 +242,9 @@ add_test(import_ply_small_holes ${TEST_BLENDER_EXE}
# PLY Export
# disabled until updated & working
if(FALSE)
-add_test(export_ply_cube_all_data ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_ply_cube_all_data
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/cube_all_data.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/export_ply_cube_all_data.ply'\)
@@ -189,7 +252,9 @@ add_test(export_ply_cube_all_data ${TEST_BLENDER_EXE}
--md5=6adc3748ceae8298496f99d0e7e76c15 --md5_method=FILE
)
-add_test(export_ply_suzanne_all_data ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_ply_suzanne_all_data
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/suzanne_all_data.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/export_ply_suzanne_all_data.ply'\)
@@ -198,7 +263,9 @@ add_test(export_ply_suzanne_all_data ${TEST_BLENDER_EXE}
)
endif()
-add_test(export_ply_vertices ${TEST_BLENDER_EXE} # lame, add a better one
+add_test(
+ NAME export_ply_vertices # lame, add a better one
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/vertices.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/export_ply_vertices.ply'\)
@@ -210,21 +277,27 @@ add_test(export_ply_vertices ${TEST_BLENDER_EXE} # lame, add a better one
# STL Import tests
# disabled until updated & working
if(FALSE)
-add_test(import_stl_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_stl_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/cube.stl'\)
--md5=8ceb5bb7e1cb5f4342fa1669988c66b4 --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_stl_cube.blend
)
-add_test(import_stl_conrod ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_stl_conrod
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/conrod.stl'\)
--md5=690a4b8eb9002dcd8631c5a575ea7348 --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_stl_conrod.blend
)
-add_test(import_stl_knot_max_simplified ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_stl_knot_max_simplified
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/knot_max_simplified.stl'\)
--md5=baf82803f45a84ec4ddbad9cef57dd3e --md5_method=SCENE
@@ -235,7 +308,9 @@ endif()
# STL Export
# disabled until updated & working
if(FALSE)
-add_test(export_stl_cube_all_data ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_stl_cube_all_data
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/cube_all_data.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.stl\(filepath='${TEST_OUT_DIR}/export_stl_cube_all_data.stl'\)
@@ -243,7 +318,9 @@ add_test(export_stl_cube_all_data ${TEST_BLENDER_EXE}
--md5=64cb97c0cabb015e1c3f76369835075a --md5_method=FILE
)
-add_test(export_stl_suzanne_all_data ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_stl_suzanne_all_data
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/suzanne_all_data.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.stl\(filepath='${TEST_OUT_DIR}/export_stl_suzanne_all_data.stl'\)
@@ -251,7 +328,9 @@ add_test(export_stl_suzanne_all_data ${TEST_BLENDER_EXE}
--md5=e9b23c97c139ad64961c635105bb9192 --md5_method=FILE
)
-add_test(export_stl_vertices ${TEST_BLENDER_EXE} # lame, add a better one
+add_test(
+ NAME export_stl_vertices # lame, add a better one
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/vertices.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_mesh.stl\(filepath='${TEST_OUT_DIR}/export_stl_vertices.stl'\)
@@ -264,21 +343,27 @@ endif()
# X3D Import
# disabled until updated & working
if(FALSE)
-add_test(import_x3d_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_x3d_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/color_cube.x3d'\)
--md5=3fae9be004199c145941cd3f9f80ad7b --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_x3d_cube.blend
)
-add_test(import_x3d_teapot ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_x3d_teapot
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/teapot.x3d'\)
--md5=8ee196c71947dce4199d55698501691e --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_x3d_teapot.blend
)
-add_test(import_x3d_suzanne_material ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_x3d_suzanne_material
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/suzanne_material.x3d'\)
--md5=3edea1353257d8b5a5f071942f417be6 --md5_method=SCENE
@@ -286,7 +371,9 @@ add_test(import_x3d_suzanne_material ${TEST_BLENDER_EXE}
)
# X3D Export
-add_test(export_x3d_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_x3d_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_cube.x3d',use_selection=False\)
@@ -294,7 +381,9 @@ add_test(export_x3d_cube ${TEST_BLENDER_EXE}
--md5=05312d278fe41da33560fdfb9bdb268f --md5_method=FILE
)
-add_test(export_x3d_nurbs ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_x3d_nurbs
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_nurbs.x3d',use_selection=False\)
@@ -302,7 +391,9 @@ add_test(export_x3d_nurbs ${TEST_BLENDER_EXE}
--md5=4286d4a2aa507ef78b22ddcbdcc88481 --md5_method=FILE
)
-add_test(export_x3d_all_objects ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_x3d_all_objects
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_all_objects.x3d',use_selection=False\)
@@ -316,21 +407,27 @@ endif()
# 3DS Import
# disabled until updated & working
if(FALSE)
-add_test(import_3ds_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_3ds_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/cube.3ds'\)
--md5=cb5a45c35a343c3f5beca2a918472951 --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_3ds_cube.blend
)
-add_test(import_3ds_hierarchy_lara ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_3ds_hierarchy_lara
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/hierarchy_lara.3ds'\)
--md5=766c873d9fdb5f190e43796cfbae63b6 --md5_method=SCENE
--write-blend=${TEST_OUT_DIR}/import_3ds_hierarchy_lara.blend
)
-add_test(import_3ds_hierarchy_greek_trireme ${TEST_BLENDER_EXE}
+add_test(
+ NAME import_3ds_hierarchy_greek_trireme
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/hierarchy_greek_trireme.3ds'\)
--md5=b62ee30101e8999cb91ef4f8a8760056 --md5_method=SCENE
@@ -341,7 +438,9 @@ endif()
# 3DS Export
# disabled until updated & working
if(FALSE)
-add_test(export_3ds_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_3ds_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_cube.3ds',use_selection=False\)
@@ -349,7 +448,9 @@ add_test(export_3ds_cube ${TEST_BLENDER_EXE}
--md5=a31f5071b6c6dc7445b9099cdc7f63b3 --md5_method=FILE
)
-add_test(export_3ds_nurbs ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_3ds_nurbs
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_nurbs.3ds',use_selection=False\)
@@ -357,7 +458,9 @@ add_test(export_3ds_nurbs ${TEST_BLENDER_EXE}
--md5=5bdd21be3c80d814fbc83cb25edb08c2 --md5_method=FILE
)
-add_test(export_3ds_all_objects ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_3ds_all_objects
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_all_objects.3ds',use_selection=False\)
@@ -372,7 +475,9 @@ endif()
# 'use_metadata=False' for reliable md5's
# disabled until updated & working
if(FALSE)
-add_test(export_fbx_cube ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_fbx_cube
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_cube.fbx',use_selection=False,use_metadata=False\)
@@ -380,7 +485,9 @@ add_test(export_fbx_cube ${TEST_BLENDER_EXE}
--md5=59a35577462f95f9a0b4e6035226ce9b --md5_method=FILE
)
-add_test(export_fbx_nurbs ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_fbx_nurbs
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_nurbs.fbx',use_selection=False,use_metadata=False\)
@@ -388,7 +495,9 @@ add_test(export_fbx_nurbs ${TEST_BLENDER_EXE}
--md5=d31875f18f613fa0c3b16e978f87f6f8 --md5_method=FILE
)
-add_test(export_fbx_all_objects ${TEST_BLENDER_EXE}
+add_test(
+ NAME export_fbx_all_objects
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend
--python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py --
--run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_all_objects.fbx',use_selection=False,use_metadata=False\)
@@ -399,25 +508,86 @@ endif()
if(WITH_CYCLES)
if(OPENIMAGEIO_IDIFF AND EXISTS "${TEST_SRC_DIR}/cycles/ctests/shader")
- add_test(cycles_reports_test
- ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
- -blender "${TEST_BLENDER_EXE_BARE}"
- -testdir "${TEST_SRC_DIR}/cycles/ctests/reports"
- -idiff "${OPENIMAGEIO_IDIFF}"
- )
- add_test(cycles_render_test
- ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
- -blender "${TEST_BLENDER_EXE_BARE}"
- -testdir "${TEST_SRC_DIR}/cycles/ctests/render"
- -idiff "${OPENIMAGEIO_IDIFF}"
- )
- add_test(cycles_shaders_test
- ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
- -blender "${TEST_BLENDER_EXE_BARE}"
- -testdir "${TEST_SRC_DIR}/cycles/ctests/shader"
- -idiff "${OPENIMAGEIO_IDIFF}"
- )
+ macro(add_cycles_render_test subject)
+ if(MSVC)
+ add_test(
+ NAME cycles_${subject}_test
+ COMMAND
+ "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}/python/bin/python$<$<CONFIG:Debug>:_d>"
+ ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
+ -blender "$<TARGET_FILE:blender>"
+ -testdir "${TEST_SRC_DIR}/cycles/ctests/${subject}"
+ -idiff "${OPENIMAGEIO_IDIFF}"
+ -outdir "${TEST_OUT_DIR}/cycles"
+ )
+ else()
+ add_test(
+ NAME cycles_${subject}_test
+ COMMAND ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
+ -blender "$<TARGET_FILE:blender>"
+ -testdir "${TEST_SRC_DIR}/cycles/ctests/${subject}"
+ -idiff "${OPENIMAGEIO_IDIFF}"
+ -outdir "${TEST_OUT_DIR}/cycles"
+ )
+ endif()
+ endmacro()
+ if(WITH_OPENGL_TESTS)
+ add_cycles_render_test(opengl)
+ endif()
+ add_cycles_render_test(bake)
+ add_cycles_render_test(denoise)
+ add_cycles_render_test(displacement)
+ add_cycles_render_test(image_data_types)
+ add_cycles_render_test(image_mapping)
+ add_cycles_render_test(image_texture_limit)
+ add_cycles_render_test(light)
+ add_cycles_render_test(mblur)
+ add_cycles_render_test(reports)
+ add_cycles_render_test(render)
+ add_cycles_render_test(shader)
+ add_cycles_render_test(shader_tangent)
+ add_cycles_render_test(shadow_catcher)
+ add_cycles_render_test(volume)
else()
MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist")
endif()
endif()
+
+if(WITH_ALEMBIC)
+ find_package_wrapper(Alembic)
+ if(NOT ALEMBIC_FOUND)
+ message(FATAL_ERROR "Alembic is enabled but cannot be found")
+ endif()
+ get_filename_component(real_include_dir ${ALEMBIC_INCLUDE_DIR} REALPATH)
+ get_filename_component(ALEMBIC_ROOT_DIR ${real_include_dir} DIRECTORY)
+
+ if(MSVC)
+ # FIXME, de-duplicate.
+ add_test(
+ NAME alembic_tests
+ COMMAND
+ "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}/python/bin/python$<$<CONFIG:Debug>:_d>"
+ ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py
+ --blender "$<TARGET_FILE:blender>"
+ --testdir "${TEST_SRC_DIR}/alembic"
+ --alembic-root "${ALEMBIC_ROOT_DIR}"
+ )
+ else()
+ add_test(
+ NAME alembic_tests
+ COMMAND ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py
+ --blender "$<TARGET_FILE:blender>"
+ --testdir "${TEST_SRC_DIR}/alembic"
+ --alembic-root "${ALEMBIC_ROOT_DIR}"
+ )
+ endif()
+
+ add_test(
+ NAME script_alembic_import
+ COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS}
+ --python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py
+ --
+ --testdir "${TEST_SRC_DIR}/alembic"
+ --with-legacy-depsgraph=${WITH_LEGACY_DEPSGRAPH}
+ )
+endif()
diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py
new file mode 100755
index 00000000000..96a68de9801
--- /dev/null
+++ b/tests/python/alembic_tests.py
@@ -0,0 +1,478 @@
+#!/usr/bin/env python3
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+import argparse
+import functools
+import shutil
+import pathlib
+import subprocess
+import sys
+import tempfile
+import unittest
+
+
+def with_tempdir(wrapped):
+ """Creates a temporary directory for the function, cleaning up after it returns normally.
+
+ When the wrapped function raises an exception, the contents of the temporary directory
+ remain available for manual inspection.
+
+ The wrapped function is called with an extra positional argument containing
+ the pathlib.Path() of the temporary directory.
+ """
+
+ @functools.wraps(wrapped)
+ def decorator(*args, **kwargs):
+ dirname = tempfile.mkdtemp(prefix='blender-alembic-test')
+ try:
+ retval = wrapped(*args, pathlib.Path(dirname), **kwargs)
+ except:
+ print('Exception in %s, not cleaning up temporary directory %s' % (wrapped, dirname))
+ raise
+ else:
+ shutil.rmtree(dirname)
+ return retval
+
+ return decorator
+
+
+class AbcPropError(Exception):
+ """Raised when AbstractAlembicTest.abcprop() finds an error."""
+
+
+class AbstractAlembicTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ import re
+
+ cls.blender = args.blender
+ cls.testdir = pathlib.Path(args.testdir)
+ cls.alembic_root = pathlib.Path(args.alembic_root)
+
+ # 'abcls' outputs ANSI colour codes, even when stdout is not a terminal.
+ # See https://github.com/alembic/alembic/issues/120
+ cls.ansi_remove_re = re.compile(rb'\x1b[^m]*m')
+
+ # 'abcls' array notation, like "name[16]"
+ cls.abcls_array = re.compile(r'^(?P<name>[^\[]+)(\[(?P<arraysize>\d+)\])?$')
+
+ def run_blender(self, filepath: str, python_script: str, timeout: int=300) -> str:
+ """Runs Blender by opening a blendfile and executing a script.
+
+ Returns Blender's stdout + stderr combined into one string.
+
+ :param filepath: taken relative to self.testdir.
+ :param timeout: in seconds
+ """
+
+ blendfile = self.testdir / filepath
+
+ command = (
+ self.blender,
+ '--background',
+ '-noaudio',
+ '--factory-startup',
+ '--enable-autoexec',
+ str(blendfile),
+ '-E', 'CYCLES',
+ '--python-exit-code', '47',
+ '--python-expr', python_script,
+ )
+
+ proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ timeout=timeout)
+ output = proc.stdout.decode('utf8')
+ if proc.returncode:
+ self.fail('Error %d running Blender:\n%s' % (proc.returncode, output))
+
+ return output
+
+ def abcprop(self, filepath: pathlib.Path, proppath: str) -> dict:
+ """Uses abcls to obtain compound property values from an Alembic object.
+
+ A dict of subproperties is returned, where the values are Python values.
+
+ The Python bindings for Alembic are old, and only compatible with Python 2.x,
+ so that's why we can't use them here, and have to rely on other tooling.
+ """
+ import collections
+
+ abcls = self.alembic_root / 'bin' / 'abcls'
+
+ command = (str(abcls), '-vl', '%s%s' % (filepath, proppath))
+ proc = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+ timeout=30)
+
+ coloured_output = proc.stdout
+ output = self.ansi_remove_re.sub(b'', coloured_output).decode('utf8')
+
+ # Because of the ANSI colour codes, we need to remove those first before
+ # decoding to text. This means that we cannot use the universal_newlines
+ # parameter to subprocess.run(), and have to do the conversion ourselves
+ output = output.replace('\r\n', '\n').replace('\r', '\n')
+
+ if proc.returncode:
+ raise AbcPropError('Error %d running abcls:\n%s' % (proc.returncode, output))
+
+ # Mapping from value type to callable that can convert a string to Python values.
+ converters = {
+ 'bool_t': int,
+ 'uint8_t': int,
+ 'int16_t': int,
+ 'int32_t': int,
+ 'uint64_t': int,
+ 'float64_t': float,
+ 'float32_t': float,
+ }
+
+ result = {}
+
+ # Ideally we'd get abcls to output JSON, see https://github.com/alembic/alembic/issues/121
+ lines = collections.deque(output.split('\n'))
+ while lines:
+ info = lines.popleft()
+ if not info:
+ continue
+ parts = info.split()
+ proptype = parts[0]
+
+ if proptype == 'CompoundProperty':
+ # To read those, call self.abcprop() on it.
+ continue
+ if len(parts) < 2:
+ raise ValueError('Error parsing result from abcprop: %s', info.strip())
+ valtype_and_arrsize, name_and_extent = parts[1:]
+
+ # Parse name and extent
+ m = self.abcls_array.match(name_and_extent)
+ if not m:
+ self.fail('Unparsable name/extent from abcls: %s' % name_and_extent)
+ name, extent = m.group('name'), m.group('arraysize')
+
+ if extent != '1':
+ self.fail('Unsupported extent %s for property %s/%s' % (extent, proppath, name))
+
+ # Parse type
+ m = self.abcls_array.match(valtype_and_arrsize)
+ if not m:
+ self.fail('Unparsable value type from abcls: %s' % valtype_and_arrsize)
+ valtype, scalarsize = m.group('name'), m.group('arraysize')
+
+ # Convert values
+ try:
+ conv = converters[valtype]
+ except KeyError:
+ self.fail('Unsupported type %s for property %s/%s' % (valtype, proppath, name))
+
+ def convert_single_line(linevalue):
+ try:
+ if scalarsize is None:
+ return conv(linevalue)
+ else:
+ return [conv(v.strip()) for v in linevalue.split(',')]
+ except ValueError as ex:
+ return str(ex)
+
+ if proptype == 'ScalarProperty':
+ value = lines.popleft()
+ result[name] = convert_single_line(value)
+ elif proptype == 'ArrayProperty':
+ arrayvalue = []
+ # Arrays consist of a variable number of items, and end in a blank line.
+ while True:
+ linevalue = lines.popleft()
+ if not linevalue:
+ break
+ arrayvalue.append(convert_single_line(linevalue))
+ result[name] = arrayvalue
+ else:
+ self.fail('Unsupported type %s for property %s/%s' % (proptype, proppath, name))
+
+ return result
+
+ def assertAlmostEqualFloatArray(self, actual, expect, places=6, delta=None):
+ """Asserts that the arrays of floats are almost equal."""
+
+ self.assertEqual(len(actual), len(expect),
+ 'Actual array has %d items, expected %d' % (len(actual), len(expect)))
+
+ for idx, (act, exp) in enumerate(zip(actual, expect)):
+ self.assertAlmostEqual(act, exp, places=places, delta=delta,
+ msg='%f != %f at index %d' % (act, exp, idx))
+
+
+class HierarchicalAndFlatExportTest(AbstractAlembicTest):
+ @with_tempdir
+ def test_hierarchical_export(self, tempdir: pathlib.Path):
+ abc = tempdir / 'cubes_hierarchical.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=True, visible_layers_only=True, flatten=False)" % abc.as_posix()
+ self.run_blender('cubes-hierarchy.blend', script)
+
+ # Now check the resulting Alembic file.
+ xform = self.abcprop(abc, '/Cube/Cube_002/Cube_012/.xform')
+ self.assertEqual(1, xform['.inherits'])
+ self.assertAlmostEqualFloatArray(
+ xform['.vals'],
+ [1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 3.07484, -2.92265, 0.0586434, 1.0]
+ )
+
+ @with_tempdir
+ def test_flat_export(self, tempdir: pathlib.Path):
+ abc = tempdir / 'cubes_flat.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=True, visible_layers_only=True, flatten=True)" % abc.as_posix()
+ self.run_blender('cubes-hierarchy.blend', script)
+
+ # Now check the resulting Alembic file.
+ xform = self.abcprop(abc, '/Cube_012/.xform')
+ self.assertEqual(0, xform['.inherits'])
+
+ self.assertAlmostEqualFloatArray(
+ xform['.vals'],
+ [0.343134, 0.485243, 0.804238, 0,
+ 0.0, 0.856222, -0.516608, 0,
+ -0.939287, 0.177266, 0.293799, 0,
+ 1, 3, 4, 1],
+ )
+
+
+class DupliGroupExportTest(AbstractAlembicTest):
+ @with_tempdir
+ def test_hierarchical_export(self, tempdir: pathlib.Path):
+ abc = tempdir / 'dupligroup_hierarchical.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=True, visible_layers_only=True, flatten=False)" % abc.as_posix()
+ self.run_blender('dupligroup-scene.blend', script)
+
+ # Now check the resulting Alembic file.
+ xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder/Suzanne/.xform')
+ self.assertEqual(1, xform['.inherits'])
+ self.assertAlmostEqualFloatArray(
+ xform['.vals'],
+ [1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 2.0, 0.0, 1.0]
+ )
+
+ @with_tempdir
+ def test_flat_export(self, tempdir: pathlib.Path):
+ abc = tempdir / 'dupligroup_hierarchical.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=True, visible_layers_only=True, flatten=True)" % abc.as_posix()
+ self.run_blender('dupligroup-scene.blend', script)
+
+ # Now check the resulting Alembic file.
+ xform = self.abcprop(abc, '/Suzanne/.xform')
+ self.assertEqual(0, xform['.inherits'])
+
+ self.assertAlmostEqualFloatArray(
+ xform['.vals'],
+ [1.5, 0.0, 0.0, 0.0,
+ 0.0, 1.5, 0.0, 0.0,
+ 0.0, 0.0, 1.5, 0.0,
+ 2.0, 3.0, 0.0, 1.0]
+ )
+
+
+class CurveExportTest(AbstractAlembicTest):
+ @with_tempdir
+ def test_export_single_curve(self, tempdir: pathlib.Path):
+ abc = tempdir / 'single-curve.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=True, visible_layers_only=True, flatten=False)" % abc.as_posix()
+ self.run_blender('single-curve.blend', script)
+
+ # Now check the resulting Alembic file.
+ abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom')
+ self.assertEqual(abcprop['.orders'], [4])
+
+ abcprop = self.abcprop(abc, '/NurbsCurve/NurbsCurveShape/.geom/.userProperties')
+ self.assertEqual(abcprop['blender:resolution'], 10)
+
+
+class HairParticlesExportTest(AbstractAlembicTest):
+ """Tests exporting with/without hair/particles.
+
+ Just a basic test to ensure that the enabling/disabling works, and that export
+ works at all. NOT testing the quality/contents of the exported file.
+ """
+
+ def _do_test(self, tempdir: pathlib.Path, export_hair: bool, export_particles: bool) -> pathlib.Path:
+ abc = tempdir / 'hair-particles.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=True, visible_layers_only=True, flatten=False, " \
+ "export_hair=%r, export_particles=%r, as_background_job=False)" \
+ % (abc.as_posix(), export_hair, export_particles)
+ self.run_blender('hair-particles.blend', script)
+ return abc
+
+ @with_tempdir
+ def test_with_both(self, tempdir: pathlib.Path):
+ abc = self._do_test(tempdir, True, True)
+
+ abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom')
+ self.assertIn('nVertices', abcprop)
+
+ abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom')
+ self.assertIn('.velocities', abcprop)
+
+ abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ self.assertIn('.faceIndices', abcprop)
+
+ @with_tempdir
+ def test_with_hair_only(self, tempdir: pathlib.Path):
+ abc = self._do_test(tempdir, True, False)
+
+ abcprop = self.abcprop(abc, '/Suzanne/Hair system/.geom')
+ self.assertIn('nVertices', abcprop)
+
+ self.assertRaises(AbcPropError, self.abcprop, abc,
+ '/Suzanne/Non-hair particle system/.geom')
+
+ abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ self.assertIn('.faceIndices', abcprop)
+
+ @with_tempdir
+ def test_with_particles_only(self, tempdir: pathlib.Path):
+ abc = self._do_test(tempdir, False, True)
+
+ self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom')
+
+ abcprop = self.abcprop(abc, '/Suzanne/Non-hair particle system/.geom')
+ self.assertIn('.velocities', abcprop)
+
+ abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ self.assertIn('.faceIndices', abcprop)
+
+ @with_tempdir
+ def test_with_neither(self, tempdir: pathlib.Path):
+ abc = self._do_test(tempdir, False, False)
+
+ self.assertRaises(AbcPropError, self.abcprop, abc, '/Suzanne/Hair system/.geom')
+ self.assertRaises(AbcPropError, self.abcprop, abc,
+ '/Suzanne/Non-hair particle system/.geom')
+
+ abcprop = self.abcprop(abc, '/Suzanne/SuzanneShape/.geom')
+ self.assertIn('.faceIndices', abcprop)
+
+
+class LongNamesExportTest(AbstractAlembicTest):
+ @with_tempdir
+ def test_export_long_names(self, tempdir: pathlib.Path):
+ abc = tempdir / 'long-names.abc'
+ script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, " \
+ "renderable_only=False, visible_layers_only=False, flatten=False)" % abc.as_posix()
+ self.run_blender('long-names.blend', script)
+
+ name_parts = [
+ 'foG9aeLahgoh5goacee1dah6Hethaghohjaich5pasizairuWigee1ahPeekiGh',
+ 'yoNgoisheedah2ua0eigh2AeCaiTee5bo0uphoo7Aixephah9racahvaingeeH4',
+ 'zuthohnoi1thooS3eezoo8seuph2Boo5aefacaethuvee1aequoonoox1sookie',
+ 'wugh4ciTh3dipiepeequait5uug7thiseek5ca7Eijei5ietaizokohhaecieto',
+ 'up9aeheenein9oteiX6fohP3thiez6Ahvah0oohah1ep2Eesho4Beboechaipoh',
+ 'coh4aehiacheTh0ue0eegho9oku1lohl4loht9ohPoongoow7dasiego6yimuis',
+ 'lohtho8eigahfeipohviepajaix4it2peeQu6Iefee1nevihaes4cee2soh4noy',
+ 'kaht9ahv0ieXaiyih7ohxe8bah7eeyicahjoa2ohbu7Choxua7oongah6sei4bu',
+ 'deif0iPaechohkee5nahx6oi2uJeeN7ze3seunohJibe4shai0mah5Iesh3Quai',
+ 'ChohDahshooNee0NeNohthah0eiDeese3Vu6ohShil1Iey9ja0uebi2quiShae6',
+ 'Dee1kai7eiph2ahh2nufah3zai3eexeengohQue1caj0eeW0xeghi3eshuadoot',
+ 'aeshiup3aengajoog0AhCoo5tiu3ieghaeGhie4Tu1ohh1thee8aepheingah1E',
+ 'ooRa6ahciolohshaifoopeo9ZeiGhae2aech4raisheiWah9AaNga0uas9ahquo',
+ 'thaepheip2aip6shief4EaXopei8ohPo0ighuiXah2ashowai9nohp4uach6Mei',
+ 'ohph4yaev3quieji3phophiem3OoNuisheepahng4waithae3Naichai7aw3noo',
+ 'aibeawaneBahmieyuph8ieng8iopheereeD2uu9Uyee5bei2phahXeir8eeJ8oo',
+ 'ooshahphei2hoh3uth5chaen7ohsai6uutiesucheichai8ungah9Gie1Aiphie',
+ 'eiwohchoo7ere2iebohn4Aapheichaelooriiyaoxaik7ooqua7aezahx0aeJei',
+ 'Vah0ohgohphiefohTheshieghichaichahch5moshoo0zai5eeva7eisi4yae8T',
+ 'EibeeN0fee0Gohnguz8iec6yeigh7shuNg4eingu3siph9joucahpeidoom4ree',
+ 'iejiu3shohheeZahHusheimeefaihoh5eecachu5eeZie9ceisugu9taidohT3U',
+ 'eex6dilakaix5Eetai7xiCh5Jaa8aiD4Ag3tuij1aijohv5fo0heevah8hohs3m',
+ 'ohqueeNgahraew6uraemohtoo5qua3oojiex6ohqu6Aideibaithaiphuriquie',
+ 'cei0eiN4Shiey7Aeluy3unohboo5choiphahc2mahbei5paephaiKeso1thoog1',
+ 'ieghif4ohKequ7ong0jah5ooBah0eiGh1caechahnahThae9Shoo0phopashoo4',
+ 'roh9er3thohwi5am8iequeequuSh3aic0voocai3ihi5nie2abahphupiegh7vu',
+ 'uv3Quei7wujoo5beingei2aish5op4VaiX0aebai7iwoaPee5pei8ko9IepaPig',
+ 'co7aegh5beitheesi9lu7jeeQu3johgeiphee9cheichi8aithuDehu2gaeNein',
+ 'thai3Tiewoo4nuir1ohy4aithiuZ7shae1luuwei5phibohriepe2paeci1Ach8',
+ 'phoi3ribah7ufuvoh8eigh1oB6deeBaiPohphaghiPieshahfah5EiCi3toogoo',
+ 'aiM8geil7ooreinee4Cheiwea4yeec8eeshi7Sei4Shoo3wu6ohkaNgooQu1mai',
+ 'agoo3faciewah9ZeesiXeereek7am0eigaeShie3Tisu8haReeNgoo0ci2Hae5u',
+ 'Aesatheewiedohshaephaenohbooshee8eu7EiJ8isal1laech2eiHo0noaV3ta',
+ 'liunguep3ooChoo4eir8ahSie8eenee0oo1TooXu8Cais8Aimo4eir6Phoo3xei',
+ 'toe9heepeobein3teequachemei0Cejoomef9ujie3ohwae9AiNgiephi3ep0de',
+ 'ua6xooY9uzaeB3of6sheiyaedohoiS5Eev0Aequ9ahm1zoa5Aegh3ooz9ChahDa',
+ 'eevasah6Bu9wi7EiwiequumahkaeCheegh6lui8xoh4eeY4ieneavah8phaibun',
+ 'AhNgei2sioZeeng6phaecheemeehiShie5eFeiTh6ooV8iiphabud0die4siep4',
+ 'kushe6Xieg6ahQuoo9aex3aipheefiec1esa7OhBuG0ueziep9phai5eegh1vie',
+ 'Jie5yu8aafuQuoh9shaep3moboh3Pooy7och8oC6obeik6jaew2aiLooweib3ch',
+ 'ohohjajaivaiRail3odaimei6aekohVaicheip2wu7phieg5Gohsaing2ahxaiy',
+ 'hahzaht6yaiYu9re9jah9loisiit4ahtoh2quoh9xohishioz4oo4phofu3ogha',
+ 'pu4oorea0uh2tahB8aiZoonge1aophaes6ogaiK9ailaigeej4zoVou8ielotee',
+ 'cae2thei3Luphuqu0zeeG8leeZuchahxaicai4ui4Eedohte9uW6gae8Geeh0ea',
+ 'air7tuy7ohw5sho2Tahpai8aep4so5ria7eaShus5weaqu0Naquei2xaeyoo2ae',
+ 'vohge4aeCh7ahwoo7Jaex6sohl0Koong4Iejisei8Coir0iemeiz9uru9Iebaep',
+ 'aepeidie8aiw6waish9gie4Woolae2thuj5phae4phexux7gishaeph4Deu7ooS',
+ 'vahc5ia0xohHooViT0uyuxookiaquu2ogueth0ahquoudeefohshai8aeThahba',
+ 'mun3oagah2eequaenohfoo8DaigeghoozaV2eiveeQuee7kah0quaa6tiesheet',
+ 'ooSet4IdieC4ugow3za0die4ohGoh1oopoh6luaPhaeng4Eechea1hae0eimie5',
+ 'iedeimadaefu2NeiPaey2jooloov5iehiegeakoo4ueso7aeK9ahqu2Thahkaes',
+ 'nahquah9Quuu2uuf0aJah7eishi2siegh8ue5eiJa2EeVu8ebohkepoh4dahNgo',
+ 'io1bie7chioPiej5ae2oohe2fee6ooP2thaeJohjohb9Se8tang3eipaifeimai',
+ 'oungoqu6dieneejiechez1xeD2Zi9iox2Ahchaiy9ithah3ohVoolu2euQuuawo',
+ 'thaew0veigei4neishohd8mecaixuqu7eeshiex1chaigohmoThoghoitoTa0Eo',
+ 'ahroob2phohvaiz0Ohteik2ohtakie6Iu1vitho8IyiyeeleeShae9defaiw9ki',
+ 'DohHoothohzeaxolai3Toh5eJie7ahlah9reF0ohn1chaipoogain2aibahw4no',
+ 'aif8lo5she4aich5cho2rie8ieJaujeem2Joongeedae4vie3tah1Leequaix1O',
+ 'Aang0Shaih6chahthie1ahZ7aewei9thiethee7iuThah3yoongi8ahngiobaa5',
+ 'iephoBuayoothah0Ru6aichai4aiw8deg1umongauvaixai3ohy6oowohlee8ei',
+ 'ohn5shigoameer0aejohgoh8oChohlaecho9jie6shu0ahg9Bohngau6paevei9',
+ 'edahghaishak0paigh1eecuich3aad7yeB0ieD6akeeliem2beifufaekee6eat',
+ 'hiechahgheloh2zo7Ieghaiph0phahhu8aeyuiKie1xeipheech9zai4aeme0ee',
+ 'Cube'
+ ]
+ name = '/' + '/'.join(name_parts)
+
+ # Now check the resulting Alembic file.
+ abcprop = self.abcprop(abc, '%s/.xform' % name)
+ self.assertEqual(abcprop['.vals'], [
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 3.0, 0.0, 1.0,
+ ])
+
+ abcprop = self.abcprop(abc, '%s/CubeShape/.geom' % name)
+ self.assertIn('.faceCounts', abcprop)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--blender', required=True)
+ parser.add_argument('--testdir', required=True)
+ parser.add_argument('--alembic-root', required=True)
+ args, remaining = parser.parse_known_args()
+
+ unittest.main(argv=sys.argv[0:1] + remaining)
diff --git a/tests/python/batch_import.py b/tests/python/batch_import.py
index 8fc679a7c15..a6e2469349b 100644
--- a/tests/python/batch_import.py
+++ b/tests/python/batch_import.py
@@ -48,29 +48,17 @@ import os
import sys
-def clear_scene():
- import bpy
- unique_obs = set()
- for scene in bpy.data.scenes:
- for obj in scene.objects[:]:
- scene.objects.unlink(obj)
- unique_obs.add(obj)
-
- # remove obdata, for now only worry about the startup scene
- for bpy_data_iter in (bpy.data.objects, bpy.data.meshes, bpy.data.lamps, bpy.data.cameras):
- for id_data in bpy_data_iter:
- bpy_data_iter.remove(id_data)
-
-
-def batch_import(operator="",
- path="",
- save_path="",
- match="",
- start=0,
- end=sys.maxsize,
- ):
+def batch_import(
+ operator="",
+ path="",
+ save_path="",
+ match="",
+ start=0,
+ end=sys.maxsize,
+):
import addon_utils
_reset_all = addon_utils.reset_all # XXX, hack
+ _disable_all = addon_utils.disable_all # XXX, hack
import fnmatch
@@ -84,10 +72,8 @@ def batch_import(operator="",
def file_generator(path):
for dirpath, dirnames, filenames in os.walk(path):
-
- # skip '.svn'
- if dirpath.startswith("."):
- continue
+ # skip '.git'
+ dirnames[:] = [d for d in dirnames if not d.startswith(".")]
for filename in filenames:
if pattern_match(filename):
@@ -116,11 +102,12 @@ def batch_import(operator="",
# hack so loading the new file doesn't undo our loaded addons
addon_utils.reset_all = lambda: None # XXX, hack
+ addon_utils.disable_all = lambda: None # XXX, hack
- bpy.ops.wm.read_factory_settings()
+ bpy.ops.wm.read_factory_settings(use_empty=True)
addon_utils.reset_all = _reset_all # XXX, hack
- clear_scene()
+ addon_utils.disable_all = _disable_all # XXX, hack
result = op(filepath=f)
diff --git a/tests/python/bl_alembic_import_test.py b/tests/python/bl_alembic_import_test.py
new file mode 100644
index 00000000000..c3a4af26e11
--- /dev/null
+++ b/tests/python/bl_alembic_import_test.py
@@ -0,0 +1,270 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+./blender.bin --background -noaudio --factory-startup --python tests/python/bl_alembic_import_test.py -- --testdir /path/to/lib/tests/alembic
+"""
+
+import pathlib
+import sys
+import unittest
+
+import bpy
+
+args = None
+
+
+class AbstractAlembicTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.testdir = args.testdir
+
+ def setUp(self):
+ self.assertTrue(self.testdir.exists(),
+ 'Test dir %s should exist' % self.testdir)
+
+ # Make sure we always start with a known-empty file.
+ bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend"))
+
+ def assertAlmostEqualFloatArray(self, actual, expect, places=6, delta=None):
+ """Asserts that the arrays of floats are almost equal."""
+
+ self.assertEqual(len(actual), len(expect),
+ 'Actual array has %d items, expected %d' % (len(actual), len(expect)))
+
+ for idx, (act, exp) in enumerate(zip(actual, expect)):
+ self.assertAlmostEqual(act, exp, places=places, delta=delta,
+ msg='%f != %f at index %d' % (act, exp, idx))
+
+
+class SimpleImportTest(AbstractAlembicTest):
+ def test_import_cube_hierarchy(self):
+ res = bpy.ops.wm.alembic_import(
+ filepath=str(self.testdir / "cubes-hierarchy.abc"),
+ as_background_job=False)
+ self.assertEqual({'FINISHED'}, res)
+
+ # The objects should be linked to scene_collection in Blender 2.8,
+ # and to scene in Blender 2.7x.
+ objects = bpy.context.scene.objects
+ self.assertEqual(13, len(objects))
+
+ # Test the hierarchy.
+ self.assertIsNone(objects['Cube'].parent)
+ self.assertEqual(objects['Cube'], objects['Cube_001'].parent)
+ self.assertEqual(objects['Cube'], objects['Cube_002'].parent)
+ self.assertEqual(objects['Cube'], objects['Cube_003'].parent)
+ self.assertEqual(objects['Cube_003'], objects['Cube_004'].parent)
+ self.assertEqual(objects['Cube_003'], objects['Cube_005'].parent)
+ self.assertEqual(objects['Cube_003'], objects['Cube_006'].parent)
+
+ def test_inherit_or_not(self):
+ res = bpy.ops.wm.alembic_import(
+ filepath=str(self.testdir / "T52022-inheritance.abc"),
+ as_background_job=False)
+ self.assertEqual({'FINISHED'}, res)
+
+ # The objects should be linked to scene_collection in Blender 2.8,
+ # and to scene in Blender 2.7x.
+ objects = bpy.context.scene.objects
+
+ # ABC parent is top-level object, which translates to nothing in Blender
+ self.assertIsNone(objects['locator1'].parent)
+
+ # ABC parent is locator1, but locator2 has "inherits Xforms" = false, which
+ # translates to "no parent" in Blender.
+ self.assertIsNone(objects['locator2'].parent)
+
+ # Shouldn't have inherited the ABC parent's transform.
+ x, y, z = objects['locator2'].matrix_world.to_translation()
+ self.assertAlmostEqual(0, x)
+ self.assertAlmostEqual(0, y)
+ self.assertAlmostEqual(2, z)
+
+ # ABC parent is inherited and translates to normal parent in Blender.
+ self.assertEqual(objects['locator2'], objects['locatorShape2'].parent)
+
+ # Should have inherited its ABC parent's transform.
+ x, y, z = objects['locatorShape2'].matrix_world.to_translation()
+ self.assertAlmostEqual(0, x)
+ self.assertAlmostEqual(0, y)
+ self.assertAlmostEqual(2, z)
+
+
+ def test_select_after_import(self):
+ # Add a sphere, so that there is something in the scene, selected, and active,
+ # before we do the Alembic import.
+ bpy.ops.mesh.primitive_uv_sphere_add()
+ sphere = bpy.context.active_object
+ self.assertEqual('Sphere', sphere.name)
+ self.assertEqual([sphere], bpy.context.selected_objects)
+
+ bpy.ops.wm.alembic_import(
+ filepath=str(self.testdir / "cubes-hierarchy.abc"),
+ as_background_job=False)
+
+ # The active object is probably the first one that was imported, but this
+ # behaviour is not defined. At least it should be one of the cubes, and
+ # not the sphere.
+ self.assertNotEqual(sphere, bpy.context.active_object)
+ self.assertTrue('Cube' in bpy.context.active_object.name)
+
+ # All cubes should be selected, but the sphere shouldn't be.
+ for ob in bpy.data.objects:
+ self.assertEqual('Cube' in ob.name, ob.select)
+
+ def test_change_path_constraint(self):
+ import math
+
+ fname = 'cube-rotating1.abc'
+ abc = self.testdir / fname
+ relpath = bpy.path.relpath(str(abc))
+
+ res = bpy.ops.wm.alembic_import(filepath=str(abc), as_background_job=False)
+ self.assertEqual({'FINISHED'}, res)
+ cube = bpy.context.active_object
+
+ # Check that the file loaded ok.
+ bpy.context.scene.frame_set(10)
+ x, y, z = cube.matrix_world.to_euler('XYZ')
+ self.assertAlmostEqual(x, 0)
+ self.assertAlmostEqual(y, 0)
+ self.assertAlmostEqual(z, math.pi / 2, places=5)
+
+ # Change path from absolute to relative. This should not break the animation.
+ bpy.context.scene.frame_set(1)
+ bpy.data.cache_files[fname].filepath = relpath
+ bpy.context.scene.frame_set(10)
+
+ x, y, z = cube.matrix_world.to_euler('XYZ')
+ self.assertAlmostEqual(x, 0)
+ self.assertAlmostEqual(y, 0)
+ self.assertAlmostEqual(z, math.pi / 2, places=5)
+
+ # Replace the Alembic file; this should apply new animation.
+ bpy.data.cache_files[fname].filepath = relpath.replace('1.abc', '2.abc')
+ bpy.context.scene.update()
+
+ if args.with_legacy_depsgraph:
+ bpy.context.scene.frame_set(10)
+
+ x, y, z = cube.matrix_world.to_euler('XYZ')
+ self.assertAlmostEqual(x, math.pi / 2, places=5)
+ self.assertAlmostEqual(y, 0)
+ self.assertAlmostEqual(z, 0)
+
+ def test_change_path_modifier(self):
+ import math
+
+ fname = 'animated-mesh.abc'
+ abc = self.testdir / fname
+ relpath = bpy.path.relpath(str(abc))
+
+ res = bpy.ops.wm.alembic_import(filepath=str(abc), as_background_job=False)
+ self.assertEqual({'FINISHED'}, res)
+ plane = bpy.context.active_object
+
+ # Check that the file loaded ok.
+ bpy.context.scene.frame_set(6)
+ mesh = plane.to_mesh(bpy.context.scene, True, 'RENDER')
+ self.assertAlmostEqual(-1, mesh.vertices[0].co.x)
+ self.assertAlmostEqual(-1, mesh.vertices[0].co.y)
+ self.assertAlmostEqual(0.5905638933181763, mesh.vertices[0].co.z)
+
+ # Change path from absolute to relative. This should not break the animation.
+ bpy.context.scene.frame_set(1)
+ bpy.data.cache_files[fname].filepath = relpath
+ bpy.context.scene.frame_set(6)
+
+ mesh = plane.to_mesh(bpy.context.scene, True, 'RENDER')
+ self.assertAlmostEqual(1, mesh.vertices[3].co.x)
+ self.assertAlmostEqual(1, mesh.vertices[3].co.y)
+ self.assertAlmostEqual(0.5905638933181763, mesh.vertices[3].co.z)
+
+ def test_import_long_names(self):
+ # This file contains very long names. The longest name is 4047 chars.
+ bpy.ops.wm.alembic_import(
+ filepath=str(self.testdir / "long-names.abc"),
+ as_background_job=False)
+
+ self.assertIn('Cube', bpy.data.objects)
+ self.assertEqual('CubeShape', bpy.data.objects['Cube'].data.name)
+
+
+class VertexColourImportTest(AbstractAlembicTest):
+ def test_import_from_houdini(self):
+ # Houdini saved "face-varying", and as RGB.
+ res = bpy.ops.wm.alembic_import(
+ filepath=str(self.testdir / "vertex-colours-houdini.abc"),
+ as_background_job=False)
+ self.assertEqual({'FINISHED'}, res)
+
+ ob = bpy.context.active_object
+ layer = ob.data.vertex_colors['Cf'] # MeshLoopColorLayer
+
+ # Test some known-good values.
+ self.assertAlmostEqualFloatArray(layer.data[0].color, (0, 0, 0))
+ self.assertAlmostEqualFloatArray(layer.data[98].color, (0.9019607, 0.4745098, 0.2666666))
+ self.assertAlmostEqualFloatArray(layer.data[99].color, (0.8941176, 0.4705882, 0.2627451))
+
+ def test_import_from_blender(self):
+ # Blender saved per-vertex, and as RGBA.
+ res = bpy.ops.wm.alembic_import(
+ filepath=str(self.testdir / "vertex-colours-blender.abc"),
+ as_background_job=False)
+ self.assertEqual({'FINISHED'}, res)
+
+ ob = bpy.context.active_object
+ layer = ob.data.vertex_colors['Cf'] # MeshLoopColorLayer
+
+ # Test some known-good values.
+ self.assertAlmostEqualFloatArray(layer.data[0].color, (1.0, 0.0156862, 0.3607843))
+ self.assertAlmostEqualFloatArray(layer.data[98].color, (0.0941176, 0.1215686, 0.9137254))
+ self.assertAlmostEqualFloatArray(layer.data[99].color, (0.1294117, 0.3529411, 0.7529411))
+
+
+def main():
+ global args
+ import argparse
+
+ if '--' in sys.argv:
+ argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
+ else:
+ argv = sys.argv
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--testdir', required=True, type=pathlib.Path)
+ parser.add_argument('--with-legacy-depsgraph', default=False,
+ type=lambda v: v in {'ON', 'YES', 'TRUE'})
+ args, remaining = parser.parse_known_args(argv)
+
+ unittest.main(argv=remaining)
+
+
+if __name__ == "__main__":
+ import traceback
+ # So a python error exits Blender itself too
+ try:
+ main()
+ except SystemExit:
+ raise
+ except:
+ traceback.print_exc()
+ sys.exit(1)
diff --git a/tests/python/bl_keymap_completeness.py b/tests/python/bl_keymap_completeness.py
index 00322907f69..652ed449a3c 100644
--- a/tests/python/bl_keymap_completeness.py
+++ b/tests/python/bl_keymap_completeness.py
@@ -80,5 +80,6 @@ def main():
import sys
sys.exit(1)
+
if __name__ == "__main__":
main()
diff --git a/tests/python/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py
index c13679d16f0..39e7bd33d44 100644
--- a/tests/python/bl_load_py_modules.py
+++ b/tests/python/bl_load_py_modules.py
@@ -36,6 +36,9 @@ BLACKLIST = {
"cycles",
"io_export_dxf", # TODO, check on why this fails
'io_import_dxf', # Because of cydxfentity.so dependency
+
+ # The unpacked wheel is only loaded when actually used, not directly on import:
+ os.path.join("io_blend_utils", "blender_bam-unpacked.whl"),
}
# Some modules need to add to the `sys.path`.
@@ -90,9 +93,8 @@ def addon_modules_sorted():
def source_list(path, filename_check=None):
from os.path import join
for dirpath, dirnames, filenames in os.walk(path):
- # skip '.svn'
- if dirpath.startswith("."):
- continue
+ # skip '.git'
+ dirnames[:] = [d for d in dirnames if not d.startswith(".")]
for filename in filenames:
filepath = join(dirpath, filename)
@@ -120,6 +122,8 @@ def load_addons():
def load_modules():
+ VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None
+
modules = []
module_paths = []
@@ -159,6 +163,14 @@ def load_modules():
del module_names
#
+ # test we tested all files except for presets and templates
+ ignore_paths = [
+ os.sep + "presets" + os.sep,
+ os.sep + "templates" + os.sep,
+ ] + ([(os.sep + f + os.sep) for f in BLACKLIST] +
+ [(os.sep + f + ".py") for f in BLACKLIST])
+
+ #
# now submodules
for m in modules:
filepath = m.__file__
@@ -175,15 +187,35 @@ def load_modules():
for f in MODULE_SYS_PATHS.get(mod_name_full, ())
])
- __import__(mod_name_full)
- mod_imp = sys.modules[mod_name_full]
-
- sys.path[:] = sys_path_back
-
- # check we load what we ask for.
- assert(os.path.samefile(mod_imp.__file__, submod_full))
-
- modules.append(mod_imp)
+ try:
+ __import__(mod_name_full)
+ mod_imp = sys.modules[mod_name_full]
+
+ sys.path[:] = sys_path_back
+
+ # check we load what we ask for.
+ assert(os.path.samefile(mod_imp.__file__, submod_full))
+
+ modules.append(mod_imp)
+ except Exception as e:
+ import traceback
+ # Module might fail to import, but we don't want whole test to fail here.
+ # Reasoning:
+ # - This module might be in ignored list (for example, preset or template),
+ # so failing here will cause false-positive test failure.
+ # - If this is module which should not be ignored, it is not added to list
+ # of successfully loaded modules, meaning the test will catch this
+ # import failure.
+ # - We want to catch all failures of this script instead of stopping on
+ # a first big failure.
+ do_print = True
+ if not VERBOSE:
+ for ignore in ignore_paths:
+ if ignore in submod_full:
+ do_print = False
+ break
+ if do_print:
+ traceback.print_exc()
#
# check which filepaths we didn't load
@@ -202,20 +234,11 @@ def load_modules():
for f in loaded_files:
source_files.remove(f)
- #
- # test we tested all files except for presets and templates
- ignore_paths = [
- os.sep + "presets" + os.sep,
- os.sep + "templates" + os.sep,
- ] + ([(os.sep + f + os.sep) for f in BLACKLIST] +
- [(os.sep + f + ".py") for f in BLACKLIST])
-
for f in source_files:
- ok = False
for ignore in ignore_paths:
if ignore in f:
- ok = True
- if not ok:
+ break
+ else:
raise Exception("Source file %r not loaded in test" % f)
print("loaded %d modules" % len(loaded_files))
diff --git a/tests/python/bl_mesh_modifiers.py b/tests/python/bl_mesh_modifiers.py
index 526a54a49a2..bff2c31984c 100644
--- a/tests/python/bl_mesh_modifiers.py
+++ b/tests/python/bl_mesh_modifiers.py
@@ -31,7 +31,6 @@
import math
USE_QUICK_RENDER = False
-IS_BMESH = hasattr(__import__("bpy").types, "LoopColors")
# -----------------------------------------------------------------------------
# utility functions
@@ -203,13 +202,8 @@ def defaults_object(obj):
mesh.show_normal_vertex = True
- # lame!
- if IS_BMESH:
- for poly in mesh.polygons:
- poly.use_smooth = True
- else:
- for face in mesh.faces:
- face.use_smooth = True
+ for poly in mesh.polygons:
+ poly.use_smooth = True
def defaults_modifier(mod):
@@ -220,16 +214,14 @@ def defaults_modifier(mod):
# -----------------------------------------------------------------------------
# models (utils)
+def mesh_bmesh_poly_elems(poly, elems):
+ vert_start = poly.loop_start
+ vert_total = poly.loop_total
+ return elems[vert_start:vert_start + vert_total]
-if IS_BMESH:
- def mesh_bmesh_poly_elems(poly, elems):
- vert_start = poly.loop_start
- vert_total = poly.loop_total
- return elems[vert_start:vert_start + vert_total]
-
- def mesh_bmesh_poly_vertices(poly):
- return [loop.vertex_index
- for loop in mesh_bmesh_poly_elems(poly, poly.id_data.loops)]
+def mesh_bmesh_poly_vertices(poly):
+ return [loop.vertex_index
+ for loop in mesh_bmesh_poly_elems(poly, poly.id_data.loops)]
def mesh_bounds(mesh):
@@ -258,21 +250,14 @@ def mesh_uv_add(obj):
uv_lay = obj.data.uv_textures.new()
- if IS_BMESH:
- # XXX, odd that we need to do this. until UV's and texface
- # are separated we will need to keep it
- uv_loops = obj.data.uv_layers[-1]
- uv_list = uv_loops.data[:]
- for poly in obj.data.polygons:
- poly_uvs = mesh_bmesh_poly_elems(poly, uv_list)
- for i, c in enumerate(poly_uvs):
- c.uv = uvs[i % 4]
- else:
- for uv in uv_lay.data:
- uv.uv1 = uvs[0]
- uv.uv2 = uvs[1]
- uv.uv3 = uvs[2]
- uv.uv4 = uvs[3]
+ # XXX, odd that we need to do this. until UV's and texface
+ # are separated we will need to keep it
+ uv_loops = obj.data.uv_layers[-1]
+ uv_list = uv_loops.data[:]
+ for poly in obj.data.polygons:
+ poly_uvs = mesh_bmesh_poly_elems(poly, uv_list)
+ for i, c in enumerate(poly_uvs):
+ c.uv = uvs[i % 4]
return uv_lay
@@ -296,21 +281,12 @@ def mesh_vcol_add(obj, mode=0):
mesh = obj.data
- if IS_BMESH:
- col_list = vcol_lay.data[:]
- for poly in mesh.polygons:
- face_verts = mesh_bmesh_poly_vertices(poly)
- poly_cols = mesh_bmesh_poly_elems(poly, col_list)
- for i, c in enumerate(poly_cols):
- c.color = colors_get(face_verts[i])
- else:
- for i, col in enumerate(vcol_lay.data):
- face_verts = mesh.faces[i].vertices
- col.color1 = colors_get(face_verts[0])
- col.color2 = colors_get(face_verts[1])
- col.color3 = colors_get(face_verts[2])
- if len(face_verts) == 4:
- col.color4 = colors_get(face_verts[3])
+ col_list = vcol_lay.data[:]
+ for poly in mesh.polygons:
+ face_verts = mesh_bmesh_poly_vertices(poly)
+ poly_cols = mesh_bmesh_poly_elems(poly, col_list)
+ for i, c in enumerate(poly_cols):
+ c.color = colors_get(face_verts[i])
return vcol_lay
@@ -470,10 +446,7 @@ def modifier_build_add(scene, obj):
defaults_modifier(mod)
# ensure we display some faces
- if IS_BMESH:
- totface = len(obj.data.polygons)
- else:
- totface = len(obj.data.faces)
+ totface = len(obj.data.polygons)
mod.frame_start = totface // 2
mod.frame_duration = totface
diff --git a/tests/python/bl_pyapi_bpy_utils_units.py b/tests/python/bl_pyapi_bpy_utils_units.py
index f40dab4b5eb..251419cb9ef 100644
--- a/tests/python/bl_pyapi_bpy_utils_units.py
+++ b/tests/python/bl_pyapi_bpy_utils_units.py
@@ -32,18 +32,17 @@ class UnitsTesting(unittest.TestCase):
OUTPUT_TESTS = (
# system, type, prec, sep, compat, value, output
##### LENGTH
+ # Note: precision handling is a bit complicated when using multi-units...
('IMPERIAL', 'LENGTH', 3, False, False, 0.3048, "1'"),
('IMPERIAL', 'LENGTH', 3, False, True, 0.3048, "1ft"),
- ('IMPERIAL', 'LENGTH', 3, True, False, 0.3048 * 2 + 0.0254 * 5.5, "2' 5.5\""),
- # Those next two fail, here again because precision ignores order magnitude :/
- #('IMPERIAL', 'LENGTH', 3, False, False, 1609.344 * 1e6, "1000000mi"), # == 1000000.004mi!!!
- #('IMPERIAL', 'LENGTH', 6, False, False, 1609.344 * 1e6, "1000000mi"), # == 1000000.003641mi!!!
- ('METRIC', 'LENGTH', 3, True, False, 1000 * 2 + 0.001 * 15, "2km 1.5cm"),
- ('METRIC', 'LENGTH', 3, True, False, 1234.56789, "1km 234.6m"),
- # Note: precision seems basically unused when using multi units!
- ('METRIC', 'LENGTH', 9, True, False, 1234.56789, "1km 234.6m"),
- ('METRIC', 'LENGTH', 9, False, False, 1234.56789, "1.23456789km"),
- ('METRIC', 'LENGTH', 9, True, False, 1000.000123456789, "1km 0.1mm"),
+ ('IMPERIAL', 'LENGTH', 4, True, False, 0.3048 * 2 + 0.0254 * 5.5, "2' 5.5\""),
+ ('IMPERIAL', 'LENGTH', 3, False, False, 1609.344 * 1e6, "1000000mi"),
+ ('IMPERIAL', 'LENGTH', 6, False, False, 1609.344 * 1e6, "1000000mi"),
+ ('METRIC', 'LENGTH', 3, True, False, 1000 * 2 + 0.001 * 15, "2km 2cm"),
+ ('METRIC', 'LENGTH', 5, True, False, 1234.56789, "1km 234.6m"),
+ ('METRIC', 'LENGTH', 6, True, False, 1234.56789, "1km 234.57m"),
+ ('METRIC', 'LENGTH', 9, False, False, 1234.56789, "1.234568km"),
+ ('METRIC', 'LENGTH', 9, True, False, 1000.000123456789, "1km 0.123mm"),
)
def test_units_inputs(self):
diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py
new file mode 100644
index 00000000000..7bf68c16cc7
--- /dev/null
+++ b/tests/python/bl_pyapi_idprop.py
@@ -0,0 +1,204 @@
+# Apache License, Version 2.0
+
+# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose
+import bpy
+import unittest
+import numpy as np
+from array import array
+
+
+class TestHelper:
+
+ @property
+ def id(self):
+ return self._id
+
+ def setUp(self):
+ self._id = bpy.context.scene
+ assert(len(self._id.keys()) == 0)
+
+ def tearDown(self):
+ for key in list(self._id.keys()):
+ del self._id[key]
+
+ def assertAlmostEqualSeq(self, list1, list2):
+ self.assertEqual(len(list1), len(list2))
+ for v1, v2 in zip(list1, list2):
+ self.assertAlmostEqual(v1, v2, places=5)
+
+
+class TestIdPropertyCreation(TestHelper, unittest.TestCase):
+
+ def test_name_empty(self):
+ self.id[""] = 4
+ self.assertEqual(self.id[""], 4)
+
+ def test_name_too_long(self):
+ with self.assertRaises(KeyError):
+ self.id["name" * 30] = 4
+
+ def test_int(self):
+ self.id["a"] = 2
+ self.assertEqual(self.id["a"], 2)
+ self.assertTrue(isinstance(self.id["a"], int))
+
+ with self.assertRaises(OverflowError):
+ self.id["a"] = 2 ** 31 # integer <= 2 ** 31-1
+
+ def test_double(self):
+ self.id["a"] = 2.5
+ self.assertEqual(self.id["a"], 2.5)
+ self.assertTrue(isinstance(self.id["a"], float))
+
+ def test_unicode(self):
+ self.id["a"] = "Hello World"
+ self.assertEqual(self.id["a"], "Hello World")
+ self.assertTrue(isinstance(self.id["a"], str))
+
+ def test_bytes(self):
+ self.id["a"] = b"Hello World"
+ self.assertEqual(self.id["a"], b"Hello World")
+ self.assertTrue(isinstance(self.id["a"], bytes))
+
+ def test_sequence_double_list(self):
+ mylist = [1.2, 3.4, 5.6]
+ self.id["a"] = mylist
+ self.assertEqual(self.id["a"].to_list(), mylist)
+ self.assertEqual(self.id["a"].typecode, "d")
+
+ def test_sequence_int_list(self):
+ mylist = [1, 2, 3]
+ self.id["a"] = mylist
+ self.assertEqual(self.id["a"].to_list(), mylist)
+ self.assertEqual(self.id["a"].typecode, "i")
+
+ def test_sequence_float_array(self):
+ mylist = [1.2, 3.4, 5.6]
+ self.id["a"] = array("f", mylist)
+ self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
+ self.assertEqual(self.id["a"].typecode, "f")
+
+ def test_sequence_double_array(self):
+ mylist = [1.2, 3.4, 5.6]
+ self.id["a"] = array("d", mylist)
+ self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
+ self.assertEqual(self.id["a"].typecode, "d")
+
+ def test_sequence_int_array(self):
+ mylist = [1, 2, 3]
+ self.id["a"] = array("i", mylist)
+ self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
+ self.assertEqual(self.id["a"].typecode, "i")
+
+ def test_sequence_other_array(self):
+ mylist = [1, 2, 3]
+ self.id["a"] = array("Q", mylist)
+ self.assertEqual(self.id["a"].to_list(), mylist)
+
+ def test_sequence_mixed_numerical_type(self):
+ self.id["a"] = [1, 2, 3.4, 5]
+ self.assertAlmostEqualSeq(self.id["a"].to_list(), [1.0, 2.0, 3.4, 5.0])
+ self.assertEqual(self.id["a"].typecode, "d")
+
+ def test_sequence_str_list(self):
+ # I'm a bit surprised that this works
+ mylist = ["abc", "qwe"]
+ self.id["a"] = mylist
+ self.assertEqual(self.id["a"], mylist)
+
+ def test_sequence_mixed_type(self):
+ with self.assertRaises(TypeError):
+ mylist = ["abc", 3, "qwe", 3.4]
+ self.id["a"] = mylist
+
+ def test_mapping_simple(self):
+ mydict = {"1": 10, "2": "20", "3": 30.5}
+ self.id["a"] = mydict
+ self.assertEqual(self.id["a"]["1"], mydict["1"])
+ self.assertEqual(self.id["a"]["2"], mydict["2"])
+ self.assertEqual(self.id["a"]["3"], mydict["3"])
+
+ def test_mapping_complex(self):
+ mydict = {
+ "1": [1, 2, 3],
+ "2": {"1": "abc", "2": array("i", [4, 5, 6])},
+ "3": {"1": {"1": 10}, "2": b"qwe"},
+ }
+ self.id["a"] = mydict
+ self.assertEqual(self.id["a"]["1"].to_list(), [1, 2, 3])
+ self.assertEqual(self.id["a"]["2"]["1"], "abc")
+ self.assertEqual(self.id["a"]["2"]["2"].to_list(), [4, 5, 6])
+ self.assertEqual(self.id["a"]["3"]["1"]["1"], 10)
+ self.assertEqual(self.id["a"]["3"]["2"], b"qwe")
+
+ with self.assertRaises(KeyError):
+ a = self.id["a"]["2"]["a"]
+
+ def test_invalid_type(self):
+ with self.assertRaises(TypeError):
+ self.id["a"] = self
+
+
+class TestBufferProtocol(TestHelper, unittest.TestCase):
+
+ def test_int(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ self.assertEqual(len(a), 5)
+ a[2] = 10
+ self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5])
+
+ def test_float(self):
+ self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ self.assertEqual(len(a), 4)
+ a[-1] = 10
+ self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0])
+
+ def test_double(self):
+ self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ a[1] = 10
+ self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0])
+
+ def test_full_update(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5, 6])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ a[:] = [10, 20, 30, 40, 50, 60]
+ self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60])
+
+ def test_partial_update(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8])
+ a = np.frombuffer(self.id["a"], self.id["a"].typecode)
+ a[1:5] = [10, 20, 30, 40]
+ self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8])
+
+ def test_copy(self):
+ self.id["a"] = array("i", [1, 2, 3, 4, 5])
+ self.id["b"] = self.id["a"]
+ self.assertEqual(self.id["a"].to_list(), self.id["b"].to_list())
+
+ def test_memview_attributes(self):
+ mylist = [1, 2, 3]
+ self.id["a"] = mylist
+
+ view1 = memoryview(self.id["a"])
+ view2 = memoryview(array("i", mylist))
+
+ self.assertEqualMemviews(view1, view2)
+
+ def assertEqualMemviews(self, view1, view2):
+ props_to_compare = (
+ "contiguous", "format", "itemsize", "nbytes", "ndim",
+ "readonly", "shape", "strides", "suboffsets"
+ )
+ for attr in props_to_compare:
+ self.assertEqual(getattr(view1, attr), getattr(view2, attr))
+
+ self.assertEqual(list(view1), list(view2))
+ self.assertEqual(view1.tobytes(), view2.tobytes())
+
+if __name__ == '__main__':
+ import sys
+ sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
+ unittest.main()
diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py
new file mode 100644
index 00000000000..4acfb83bd95
--- /dev/null
+++ b/tests/python/bl_pyapi_idprop_datablock.py
@@ -0,0 +1,338 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+import sys
+import os
+import tempfile
+import traceback
+import inspect
+from bpy.types import UIList
+
+arr_len = 100
+ob_cp_count = 100
+lib_path = os.path.join(tempfile.gettempdir(), "lib.blend")
+test_path = os.path.join(tempfile.gettempdir(), "test.blend")
+
+
+def print_fail_msg_and_exit(msg):
+ def __LINE__():
+ try:
+ raise Exception
+ except:
+ return sys.exc_info()[2].tb_frame.f_back.f_back.f_back.f_lineno
+
+ def __FILE__():
+ return inspect.currentframe().f_code.co_filename
+
+ print("'%s': %d >> %s" % (__FILE__(), __LINE__(), msg), file=sys.stderr)
+ sys.stderr.flush()
+ sys.stdout.flush()
+ os._exit(1)
+
+
+def abort_if_false(expr, msg=None):
+ if not expr:
+ if not msg:
+ msg = "test failed"
+ print_fail_msg_and_exit(msg)
+
+
+class TestClass(bpy.types.PropertyGroup):
+ test_prop = bpy.props.PointerProperty(type=bpy.types.Object)
+ name = bpy.props.StringProperty()
+
+
+def get_scene(lib_name, sce_name):
+ for s in bpy.data.scenes:
+ if s.name == sce_name:
+ if (s.library and s.library.name == lib_name) or \
+ (lib_name == None and s.library == None):
+ return s
+
+
+def check_crash(fnc, args=None):
+ try:
+ fnc(args) if args else fnc()
+ except:
+ return
+ print_fail_msg_and_exit("test failed")
+
+
+def init():
+ bpy.utils.register_class(TestClass)
+ bpy.types.Object.prop_array = bpy.props.CollectionProperty(
+ name="prop_array",
+ type=TestClass)
+ bpy.types.Object.prop = bpy.props.PointerProperty(type=bpy.types.Object)
+
+
+def make_lib():
+ bpy.ops.wm.read_factory_settings()
+
+ # datablock pointer to the Camera object
+ bpy.data.objects["Cube"].prop = bpy.data.objects['Camera']
+
+ # array of datablock pointers to the Lamp object
+ for i in range(0, arr_len):
+ a = bpy.data.objects["Cube"].prop_array.add()
+ a.test_prop = bpy.data.objects['Lamp']
+ a.name = a.test_prop.name
+
+ # make unique named copy of the cube
+ ob = bpy.data.objects["Cube"].copy()
+ bpy.context.scene.objects.link(ob)
+
+ bpy.data.objects["Cube.001"].name = "Unique_Cube"
+
+ # duplicating of Cube
+ for i in range(0, ob_cp_count):
+ ob = bpy.data.objects["Cube"].copy()
+ bpy.context.scene.objects.link(ob)
+
+ # nodes
+ bpy.data.scenes["Scene"].use_nodes = True
+ bpy.data.scenes["Scene"].node_tree.nodes['Render Layers']["prop"] =\
+ bpy.data.objects['Camera']
+
+ # rename scene and save
+ bpy.data.scenes["Scene"].name = "Scene_lib"
+ bpy.ops.wm.save_as_mainfile(filepath=lib_path)
+
+
+def check_lib():
+ # check pointer
+ abort_if_false(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera'])
+
+ # check array of pointers in duplicated object
+ for i in range(0, arr_len):
+ abort_if_false(bpy.data.objects["Cube.001"].prop_array[i].test_prop ==
+ bpy.data.objects['Lamp'])
+
+
+def check_lib_linking():
+ # open startup file
+ bpy.ops.wm.read_factory_settings()
+
+ # link scene to the startup file
+ with bpy.data.libraries.load(lib_path, link=True) as (data_from, data_to):
+ data_to.scenes = ["Scene_lib"]
+
+ o = bpy.data.scenes["Scene_lib"].objects['Unique_Cube']
+
+ abort_if_false(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Lamp'])
+ abort_if_false(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera'])
+ abort_if_false(o.prop.library == o.library)
+
+ bpy.ops.wm.save_as_mainfile(filepath=test_path)
+
+
+def check_linked_scene_copying():
+ # full copy of the scene with datablock props
+ bpy.ops.wm.open_mainfile(filepath=test_path)
+ bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
+ bpy.ops.scene.new(type='FULL_COPY')
+
+ # check save/open
+ bpy.ops.wm.save_as_mainfile(filepath=test_path)
+ bpy.ops.wm.open_mainfile(filepath=test_path)
+
+ intern_sce = get_scene(None, "Scene_lib")
+ extern_sce = get_scene("Lib", "Scene_lib")
+
+ # check node's props
+ # we made full copy from linked scene, so pointers must equal each other
+ abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and
+ intern_sce.node_tree.nodes['Render Layers']["prop"] ==
+ extern_sce.node_tree.nodes['Render Layers']["prop"])
+
+
+def check_scene_copying():
+ # full copy of the scene with datablock props
+ bpy.ops.wm.open_mainfile(filepath=lib_path)
+ bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
+ bpy.ops.scene.new(type='FULL_COPY')
+
+ path = test_path + "_"
+ # check save/open
+ bpy.ops.wm.save_as_mainfile(filepath=path)
+ bpy.ops.wm.open_mainfile(filepath=path)
+
+ first_sce = get_scene(None, "Scene_lib")
+ second_sce = get_scene(None, "Scene_lib.001")
+
+ # check node's props
+ # must point to own scene camera
+ abort_if_false(not (first_sce.node_tree.nodes['Render Layers']["prop"] ==
+ second_sce.node_tree.nodes['Render Layers']["prop"]))
+
+
+# count users
+def test_users_counting():
+ bpy.ops.wm.read_factory_settings()
+ lamp_us = bpy.data.objects["Lamp"].data.users
+ n = 1000
+ for i in range(0, n):
+ bpy.data.objects["Cube"]["a%s" % i] = bpy.data.objects["Lamp"].data
+ abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + n)
+
+ for i in range(0, int(n / 2)):
+ bpy.data.objects["Cube"]["a%s" % i] = 1
+ abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + int(n / 2))
+
+
+# linking
+def test_linking():
+ make_lib()
+ check_lib()
+ check_lib_linking()
+ check_linked_scene_copying()
+ check_scene_copying()
+
+
+# check restrictions for datablock pointers for some classes; GUI for manual testing
+def test_restrictions1():
+ class TEST_Op(bpy.types.Operator):
+ bl_idname = 'scene.test_op'
+ bl_label = 'Test'
+ bl_options = {"INTERNAL"}
+ str_prop = bpy.props.StringProperty(name="str_prop")
+
+ # disallow registration of datablock properties in operators
+ # will be checked in the draw method (test manually)
+ # also, see console:
+ # ValueError: bpy_struct "SCENE_OT_test_op" doesn't support datablock properties
+ id_prop = bpy.props.PointerProperty(type=bpy.types.Object)
+
+ def execute(self, context):
+ return {'FINISHED'}
+
+ # just panel for testing the poll callback with lots of objects
+ class TEST_PT_DatablockProp(bpy.types.Panel):
+ bl_label = "Datablock IDProp"
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "render"
+
+ def draw(self, context):
+ self.layout.prop_search(context.scene, "prop", bpy.data,
+ "objects")
+ self.layout.template_ID(context.scene, "prop1")
+ self.layout.prop_search(context.scene, "prop2", bpy.data, "node_groups")
+
+ op = self.layout.operator("scene.test_op")
+ op.str_prop = "test string"
+
+ def test_fnc(op):
+ op["ob"] = bpy.data.objects['Unique_Cube']
+ check_crash(test_fnc, op)
+ abort_if_false(not hasattr(op, "id_prop"))
+
+ bpy.utils.register_class(TEST_PT_DatablockProp)
+ bpy.utils.register_class(TEST_Op)
+
+ def poll(self, value):
+ return value.name in bpy.data.scenes["Scene_lib"].objects
+
+ def poll1(self, value):
+ return True
+
+ bpy.types.Scene.prop = bpy.props.PointerProperty(type=bpy.types.Object)
+ bpy.types.Scene.prop1 = bpy.props.PointerProperty(type=bpy.types.Object, poll=poll)
+ bpy.types.Scene.prop2 = bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll1)
+
+ # check poll effect on UI (poll returns false => red alert)
+ bpy.context.scene.prop = bpy.data.objects["Lamp.001"]
+ bpy.context.scene.prop1 = bpy.data.objects["Lamp.001"]
+
+ # check incorrect type assignment
+ def sub_test():
+ # NodeTree id_prop
+ bpy.context.scene.prop2 = bpy.data.objects["Lamp.001"]
+
+ check_crash(sub_test)
+
+ bpy.context.scene.prop2 = bpy.data.node_groups.new("Shader", "ShaderNodeTree")
+
+ print("Please, test GUI performance manually on the Render tab, '%s' panel" %
+ TEST_PT_DatablockProp.bl_label, file=sys.stderr)
+ sys.stderr.flush()
+
+
+# check some possible regressions
+def test_regressions():
+ bpy.types.Object.prop_str = bpy.props.StringProperty(name="str")
+ bpy.data.objects["Unique_Cube"].prop_str = "test"
+
+ bpy.types.Object.prop_gr = bpy.props.PointerProperty(
+ name="prop_gr",
+ type=TestClass,
+ description="test")
+
+ bpy.data.objects["Unique_Cube"].prop_gr = None
+
+
+# test restrictions for datablock pointers
+def test_restrictions2():
+ class TestClassCollection(bpy.types.PropertyGroup):
+ prop = bpy.props.CollectionProperty(
+ name="prop_array",
+ type=TestClass)
+ bpy.utils.register_class(TestClassCollection)
+
+ class TestPrefs(bpy.types.AddonPreferences):
+ bl_idname = "testprefs"
+ # expecting crash during registering
+ my_prop2 = bpy.props.PointerProperty(type=TestClass)
+
+ prop = bpy.props.PointerProperty(
+ name="prop",
+ type=TestClassCollection,
+ description="test")
+
+ bpy.types.Addon.a = bpy.props.PointerProperty(type=bpy.types.Object)
+
+ class TestUIList(UIList):
+ test = bpy.props.PointerProperty(type=bpy.types.Object)
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ layout.prop(item, "name", text="", emboss=False, icon_value=icon)
+
+ check_crash(bpy.utils.register_class, TestPrefs)
+ check_crash(bpy.utils.register_class, TestUIList)
+
+ bpy.utils.unregister_class(TestClassCollection)
+
+
+def main():
+ init()
+ test_users_counting()
+ test_linking()
+ test_restrictions1()
+ check_crash(test_regressions)
+ test_restrictions2()
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except:
+ import traceback
+
+ traceback.print_exc()
+ sys.stderr.flush()
+ os._exit(1)
diff --git a/tests/python/bl_pyapi_mathutils.py b/tests/python/bl_pyapi_mathutils.py
index 7761b6cb7b1..9ca0376192a 100644
--- a/tests/python/bl_pyapi_mathutils.py
+++ b/tests/python/bl_pyapi_mathutils.py
@@ -260,6 +260,11 @@ class KDTreeTesting(unittest.TestCase):
k.balance()
return k
+ def assertAlmostEqualVector(self, first, second, places=7, msg=None, delta=None):
+ self.assertAlmostEqual(first[0], second[0], places=places, msg=msg, delta=delta)
+ self.assertAlmostEqual(first[1], second[1], places=places, msg=msg, delta=delta)
+ self.assertAlmostEqual(first[2], second[2], places=places, msg=msg, delta=delta)
+
def test_kdtree_single(self):
co = (0,) * 3
index = 2
@@ -360,12 +365,12 @@ class KDTreeTesting(unittest.TestCase):
ret_regular = k_odd.find(co)
self.assertEqual(ret_regular[1] % 2, 1)
ret_filter = k_all.find(co, lambda i: (i % 2) == 1)
- self.assertEqual(ret_regular, ret_filter)
+ self.assertAlmostEqualVector(ret_regular, ret_filter)
ret_regular = k_evn.find(co)
self.assertEqual(ret_regular[1] % 2, 0)
ret_filter = k_all.find(co, lambda i: (i % 2) == 0)
- self.assertEqual(ret_regular, ret_filter)
+ self.assertAlmostEqualVector(ret_regular, ret_filter)
# filter out all values (search odd tree for even values and the reverse)
diff --git a/tests/python/bl_run_operators.py b/tests/python/bl_run_operators.py
index 7e92b424faa..7b6b97e5ad1 100644
--- a/tests/python/bl_run_operators.py
+++ b/tests/python/bl_run_operators.py
@@ -65,6 +65,7 @@ op_blacklist = (
"wm.blenderplayer_start",
"wm.recover_auto_save",
"wm.quit_blender",
+ "wm.window_close",
"wm.url_open",
"wm.doc_view",
"wm.doc_edit",
@@ -99,10 +100,8 @@ def blend_list(mainpath):
def file_list(path, filename_check=None):
for dirpath, dirnames, filenames in os.walk(path):
-
- # skip '.svn'
- if dirpath.startswith("."):
- continue
+ # skip '.git'
+ dirnames[:] = [d for d in dirnames if not d.startswith(".")]
for filename in filenames:
filepath = join(dirpath, filename)
@@ -308,16 +307,7 @@ def run_ops(operators, setup_func=None, reset=True):
# contexts
def ctx_clear_scene(): # copied from batch_import.py
- unique_obs = set()
- for scene in bpy.data.scenes:
- for obj in scene.objects[:]:
- scene.objects.unlink(obj)
- unique_obs.add(obj)
-
- # remove obdata, for now only worry about the startup scene
- for bpy_data_iter in (bpy.data.objects, bpy.data.meshes, bpy.data.lamps, bpy.data.cameras):
- for id_data in bpy_data_iter:
- bpy_data_iter.remove(id_data)
+ bpy.ops.wm.read_factory_settings(use_empty=True)
def ctx_editmode_mesh():
diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py
index 78b4b346f24..731996df8ef 100755
--- a/tests/python/cycles_render_tests.py
+++ b/tests/python/cycles_render_tests.py
@@ -2,27 +2,112 @@
# Apache License, Version 2.0
import argparse
+import glob
import os
+import pathlib
+import shlex
import shutil
import subprocess
import sys
+import time
import tempfile
+class COLORS_ANSI:
+ RED = '\033[00;31m'
+ GREEN = '\033[00;32m'
+ ENDC = '\033[0m'
+
+
+class COLORS_DUMMY:
+ RED = ''
+ GREEN = ''
+ ENDC = ''
+
+COLORS = COLORS_DUMMY
+
+
+def print_message(message, type=None, status=''):
+ if type == 'SUCCESS':
+ print(COLORS.GREEN, end="")
+ elif type == 'FAILURE':
+ print(COLORS.RED, end="")
+ status_text = ...
+ if status == 'RUN':
+ status_text = " RUN "
+ elif status == 'OK':
+ status_text = " OK "
+ elif status == 'PASSED':
+ status_text = " PASSED "
+ elif status == 'FAILED':
+ status_text = " FAILED "
+ else:
+ status_text = status
+ if status_text:
+ print("[{}]" . format(status_text), end="")
+ print(COLORS.ENDC, end="")
+ print(" {}" . format(message))
+ sys.stdout.flush()
+
+
def render_file(filepath):
- command = (
- BLENDER,
- "--background",
- "-noaudio",
- "--factory-startup",
- filepath,
- "-E", "CYCLES",
- # Run with OSL enabled
- # "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True",
- "-o", TEMP_FILE_MASK,
- "-F", "PNG",
- "-f", "1",
- )
+ dirname = os.path.dirname(filepath)
+ basedir = os.path.dirname(dirname)
+ subject = os.path.basename(dirname)
+
+ custom_args = os.getenv('CYCLESTEST_ARGS')
+ custom_args = shlex.split(custom_args) if custom_args else []
+
+ # OSL and GPU examples
+ # custom_args += ["--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True"]
+ # custom_args += ["--python-expr", "import bpy; bpy.context.scene.cycles.device = 'GPU'"]
+
+ if subject == 'opengl':
+ command = [
+ BLENDER,
+ "--window-geometry", "0", "0", "1", "1",
+ "-noaudio",
+ "--factory-startup",
+ "--enable-autoexec",
+ filepath,
+ "-E", "CYCLES"]
+ command += custom_args
+ command += [
+ "-o", TEMP_FILE_MASK,
+ "-F", "PNG",
+ '--python', os.path.join(basedir,
+ "util",
+ "render_opengl.py")]
+ elif subject == 'bake':
+ command = [
+ BLENDER,
+ "-b",
+ "-noaudio",
+ "--factory-startup",
+ "--enable-autoexec",
+ filepath,
+ "-E", "CYCLES"]
+ command += custom_args
+ command += [
+ "-o", TEMP_FILE_MASK,
+ "-F", "PNG",
+ '--python', os.path.join(basedir,
+ "util",
+ "render_bake.py")]
+ else:
+ command = [
+ BLENDER,
+ "--background",
+ "-noaudio",
+ "--factory-startup",
+ "--enable-autoexec",
+ filepath,
+ "-E", "CYCLES"]
+ command += custom_args
+ command += [
+ "-o", TEMP_FILE_MASK,
+ "-F", "PNG",
+ "-f", "1"]
try:
output = subprocess.check_output(command)
if VERBOSE:
@@ -50,52 +135,241 @@ def test_get_name(filepath):
filename = os.path.basename(filepath)
return os.path.splitext(filename)[0]
-
-def verify_output(filepath):
+def test_get_images(filepath):
testname = test_get_name(filepath)
dirpath = os.path.dirname(filepath)
- reference_dirpath = os.path.join(dirpath, "reference_renders")
- reference_image = os.path.join(reference_dirpath, testname + ".png")
- failed_image = os.path.join(reference_dirpath, testname + ".fail.png")
- if not os.path.exists(reference_image):
- return False
+
+ old_dirpath = os.path.join(dirpath, "reference_renders")
+ old_img = os.path.join(old_dirpath, testname + ".png")
+
+ ref_dirpath = os.path.join(OUTDIR, os.path.basename(dirpath), "ref")
+ ref_img = os.path.join(ref_dirpath, testname + ".png")
+ if not os.path.exists(ref_dirpath):
+ os.makedirs(ref_dirpath)
+ if os.path.exists(old_img):
+ shutil.copy(old_img, ref_img)
+
+ new_dirpath = os.path.join(OUTDIR, os.path.basename(dirpath))
+ if not os.path.exists(new_dirpath):
+ os.makedirs(new_dirpath)
+ new_img = os.path.join(new_dirpath, testname + ".png")
+
+ diff_dirpath = os.path.join(OUTDIR, os.path.basename(dirpath), "diff")
+ if not os.path.exists(diff_dirpath):
+ os.makedirs(diff_dirpath)
+ diff_img = os.path.join(diff_dirpath, testname + ".diff.png")
+
+ return old_img, ref_img, new_img, diff_img
+
+
+class Report:
+ def __init__(self, testname):
+ self.failed_tests = ""
+ self.passed_tests = ""
+ self.testname = testname
+
+ def output(self):
+ # write intermediate data for single test
+ outdir = os.path.join(OUTDIR, self.testname)
+ if not os.path.exists(outdir):
+ os.makedirs(outdir)
+
+ filepath = os.path.join(outdir, "failed.data")
+ pathlib.Path(filepath).write_text(self.failed_tests)
+
+ filepath = os.path.join(outdir, "passed.data")
+ pathlib.Path(filepath).write_text(self.passed_tests)
+
+ # gather intermediate data for all tests
+ failed_data = sorted(glob.glob(os.path.join(OUTDIR, "*/failed.data")))
+ passed_data = sorted(glob.glob(os.path.join(OUTDIR, "*/passed.data")))
+
+ failed_tests = ""
+ passed_tests = ""
+
+ for filename in failed_data:
+ filepath = os.path.join(OUTDIR, filename)
+ failed_tests += pathlib.Path(filepath).read_text()
+ for filename in passed_data:
+ filepath = os.path.join(OUTDIR, filename)
+ passed_tests += pathlib.Path(filepath).read_text()
+
+ # write html for all tests
+ self.html = """
+<html>
+<head>
+ <title>Cycles Test Report</title>
+ <style>
+ img {{ image-rendering: pixelated; width: 256px; background-color: #000; }}
+ img.render {{
+ background-color: #fff;
+ background-image:
+ -moz-linear-gradient(45deg, #eee 25%, transparent 25%),
+ -moz-linear-gradient(-45deg, #eee 25%, transparent 25%),
+ -moz-linear-gradient(45deg, transparent 75%, #eee 75%),
+ -moz-linear-gradient(-45deg, transparent 75%, #eee 75%);
+ background-image:
+ -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, #eee), color-stop(.25, transparent)),
+ -webkit-gradient(linear, 0 0, 100% 100%, color-stop(.25, #eee), color-stop(.25, transparent)),
+ -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.75, transparent), color-stop(.75, #eee)),
+ -webkit-gradient(linear, 0 0, 100% 100%, color-stop(.75, transparent), color-stop(.75, #eee));
+
+ -moz-background-size:50px 50px;
+ background-size:50px 50px;
+ -webkit-background-size:50px 51px; /* override value for shitty webkit */
+
+ background-position:0 0, 25px 0, 25px -25px, 0px 25px;
+ }}
+ table td:first-child {{ width: 256px; }}
+ </style>
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
+</head>
+<body>
+ <div class="container">
+ <br/>
+ <h1>Cycles Test Report</h1>
+ <br/>
+ <table class="table table-striped">
+ <thead class="thead-default">
+ <tr><th>Name</th><th>New</th><th>Reference</th><th>Diff</th>
+ </thead>
+ {}{}
+ </table>
+ <br/>
+ </div>
+</body>
+</html>
+ """ . format(failed_tests, passed_tests)
+
+ filepath = os.path.join(OUTDIR, "report.html")
+ pathlib.Path(filepath).write_text(self.html)
+
+ print_message("Report saved to: " + pathlib.Path(filepath).as_uri())
+
+ def relative_url(self, filepath):
+ relpath = os.path.relpath(filepath, OUTDIR)
+ return pathlib.Path(relpath).as_posix()
+
+ def add_test(self, filepath, error):
+ name = test_get_name(filepath)
+ name = name.replace('_', ' ')
+
+ old_img, ref_img, new_img, diff_img = test_get_images(filepath)
+
+ status = error if error else ""
+ style = """ style="background-color: #f99;" """ if error else ""
+
+ new_url = self.relative_url(new_img)
+ ref_url = self.relative_url(ref_img)
+ diff_url = self.relative_url(diff_img)
+
+ test_html = """
+ <tr{}>
+ <td><b>{}</b><br/>{}<br/>{}</td>
+ <td><img src="{}" onmouseover="this.src='{}';" onmouseout="this.src='{}';" class="render"></td>
+ <td><img src="{}" onmouseover="this.src='{}';" onmouseout="this.src='{}';" class="render"></td>
+ <td><img src="{}"></td>
+ </tr>""" . format(style, name, self.testname, status,
+ new_url, ref_url, new_url,
+ ref_url, new_url, ref_url,
+ diff_url)
+
+ if error:
+ self.failed_tests += test_html
+ else:
+ self.passed_tests += test_html
+
+
+def verify_output(report, filepath):
+ old_img, ref_img, new_img, diff_img = test_get_images(filepath)
+
+ # copy new image
+ if os.path.exists(new_img):
+ os.remove(new_img)
+ if os.path.exists(TEMP_FILE):
+ shutil.copy(TEMP_FILE, new_img)
+
+ update = os.getenv('CYCLESTEST_UPDATE')
+
+ if os.path.exists(ref_img):
+ # diff test with threshold
+ command = (
+ IDIFF,
+ "-fail", "0.016",
+ "-failpercent", "1",
+ ref_img,
+ TEMP_FILE,
+ )
+ try:
+ subprocess.check_output(command)
+ failed = False
+ except subprocess.CalledProcessError as e:
+ if VERBOSE:
+ print_message(e.output.decode("utf-8"))
+ failed = e.returncode != 1
+ else:
+ if not update:
+ return False
+
+ failed = True
+
+ if failed and update:
+ # update reference
+ shutil.copy(new_img, ref_img)
+ shutil.copy(new_img, old_img)
+ failed = False
+
+ # generate diff image
command = (
IDIFF,
- "-fail", "0.015",
- "-failpercent", "1",
- reference_image,
- TEMP_FILE,
+ "-o", diff_img,
+ "-abs", "-scale", "16",
+ ref_img,
+ TEMP_FILE
)
+
try:
subprocess.check_output(command)
- failed = False
except subprocess.CalledProcessError as e:
if VERBOSE:
- print(e.output.decode("utf-8"))
- failed = e.returncode != 1
- if failed:
- shutil.copy(TEMP_FILE, failed_image)
- elif os.path.exists(failed_image):
- os.remove(failed_image)
+ print_message(e.output.decode("utf-8"))
+
return not failed
-def run_test(filepath):
+def run_test(report, filepath):
testname = test_get_name(filepath)
spacer = "." * (32 - len(testname))
- print(testname, spacer, end="")
- sys.stdout.flush()
+ print_message(testname, 'SUCCESS', 'RUN')
+ time_start = time.time()
error = render_file(filepath)
+ status = "FAIL"
if not error:
- if verify_output(filepath):
- print("PASS")
- else:
+ if not verify_output(report, filepath):
error = "VERIFY"
- if error:
- print("FAIL", error)
+ time_end = time.time()
+ elapsed_ms = int((time_end - time_start) * 1000)
+ if not error:
+ print_message("{} ({} ms)" . format(testname, elapsed_ms),
+ 'SUCCESS', 'OK')
+ else:
+ if error == "NO_CYCLES":
+ print_message("Can't perform tests because Cycles failed to load!")
+ return error
+ elif error == "NO_START":
+ print_message('Can not perform tests because blender fails to start.',
+ 'Make sure INSTALL target was run.')
+ return error
+ elif error == 'VERIFY':
+ print_message("Rendered result is different from reference image")
+ else:
+ print_message("Unknown error %r" % error)
+ print_message("{} ({} ms)" . format(testname, elapsed_ms),
+ 'FAILURE', 'FAILED')
return error
+
def blend_list(path):
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
@@ -103,40 +377,54 @@ def blend_list(path):
filepath = os.path.join(dirpath, filename)
yield filepath
-
def run_all_tests(dirpath):
+ passed_tests = []
failed_tests = []
all_files = list(blend_list(dirpath))
all_files.sort()
+ report = Report(os.path.basename(dirpath))
+ print_message("Running {} tests from 1 test case." .
+ format(len(all_files)),
+ 'SUCCESS', "==========")
+ time_start = time.time()
for filepath in all_files:
- error = run_test(filepath)
+ error = run_test(report, filepath)
+ testname = test_get_name(filepath)
if error:
if error == "NO_CYCLES":
- print("Can't perform tests because Cycles failed to load!")
return False
elif error == "NO_START":
- print('Can not perform tests because blender fails to start.',
- 'Make sure INSTALL target was run.')
return False
- elif error == 'VERIFY':
- pass
- else:
- print("Unknown error %r" % error)
- testname = test_get_name(filepath)
failed_tests.append(testname)
+ else:
+ passed_tests.append(testname)
+ report.add_test(filepath, error)
+ time_end = time.time()
+ elapsed_ms = int((time_end - time_start) * 1000)
+ print_message("")
+ print_message("{} tests from 1 test case ran. ({} ms total)" .
+ format(len(all_files), elapsed_ms),
+ 'SUCCESS', "==========")
+ print_message("{} tests." .
+ format(len(passed_tests)),
+ 'SUCCESS', 'PASSED')
if failed_tests:
+ print_message("{} tests, listed below:" .
+ format(len(failed_tests)),
+ 'FAILURE', 'FAILED')
failed_tests.sort()
- print("\n\nFAILED tests:")
for test in failed_tests:
- print(" ", test)
- return False
- return True
+ print_message("{}" . format(test), 'FAILURE', "FAILED")
+
+ report.output()
+ return not bool(failed_tests)
def create_argparse():
parser = argparse.ArgumentParser()
parser.add_argument("-blender", nargs="+")
parser.add_argument("-testdir", nargs=1)
+ parser.add_argument("-outdir", nargs=1)
parser.add_argument("-idiff", nargs=1)
return parser
@@ -145,13 +433,21 @@ def main():
parser = create_argparse()
args = parser.parse_args()
- global BLENDER, ROOT, IDIFF
+ global COLORS
+ global BLENDER, TESTDIR, IDIFF, OUTDIR
global TEMP_FILE, TEMP_FILE_MASK, TEST_SCRIPT
global VERBOSE
+ if os.environ.get("CYCLESTEST_COLOR") is not None:
+ COLORS = COLORS_ANSI
+
BLENDER = args.blender[0]
- ROOT = args.testdir[0]
+ TESTDIR = args.testdir[0]
IDIFF = args.idiff[0]
+ OUTDIR = args.outdir[0]
+
+ if not os.path.exists(OUTDIR):
+ os.makedirs(OUTDIR)
TEMP = tempfile.mkdtemp()
TEMP_FILE_MASK = os.path.join(TEMP, "test")
@@ -161,7 +457,7 @@ def main():
VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None
- ok = run_all_tests(ROOT)
+ ok = run_all_tests(TESTDIR)
# Cleanup temp files and folders
if os.path.exists(TEMP_FILE):
diff --git a/tests/python/pep8.py b/tests/python/pep8.py
index 0e6250f534b..dde4250f6aa 100644
--- a/tests/python/pep8.py
+++ b/tests/python/pep8.py
@@ -178,5 +178,6 @@ def main():
"--max-line-length=1000"
" '%s'" % f)
+
if __name__ == "__main__":
main()
diff --git a/tests/python/rna_info_dump.py b/tests/python/rna_info_dump.py
index c26d94a1246..da228e52652 100644
--- a/tests/python/rna_info_dump.py
+++ b/tests/python/rna_info_dump.py
@@ -127,5 +127,6 @@ def api_dump(use_properties=True, use_functions=True):
print("END")
+
if __name__ == "__main__":
api_dump()