commit 70c0ba0d536609b99f383eb3287e1d485a536c57
parent c0400957c65a6e707dd7dd5670f7c2a0a33386ea
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 10 Apr 2025 17:05:52 +0200
Add load tests
Test the loading procedure for the binary format and check error
handling when the generic loading procedure is invoked on unseekable
files (in this case a pipe).
Diffstat:
4 files changed, 882 insertions(+), 403 deletions(-)
diff --git a/Makefile b/Makefile
@@ -112,7 +112,8 @@ lint:
################################################################################
TEST_SRC =\
src/test_sstl.c\
- src/test_sstl_load.c
+ src/test_sstl_load_ascii.c\
+ src/test_sstl_load_binary.c
TEST_OBJ = $(TEST_SRC:.c=.o)
TEST_DEP = $(TEST_SRC:.c=.d)
@@ -141,5 +142,8 @@ $(TEST_DEP): config.mk sstl-local.pc
$(TEST_OBJ): config.mk sstl-local.pc
$(CC) -std=c89 $(CFLAGS_EXE) $(RSYS_CFLAGS) $(SSTL_CFLAGS) -c $(@:.o=.c) -o $@
-test_sstl test_sstl_load: config.mk sstl-local.pc $(LIBNAME)
+test_sstl \
+test_sstl_load_ascii \
+test_sstl_load_binary \
+: config.mk sstl-local.pc $(LIBNAME)
$(CC) -std=c89 $(CFLAGS_EXE) -o $@ src/$@.o $(LDFLAGS_EXE) $(SSTL_LIBS) $(RSYS_LIBS) -lm
diff --git a/src/test_sstl_load.c b/src/test_sstl_load.c
@@ -1,401 +0,0 @@
-/* Copyright (C) 2015, 2016, 2019, 2021, 2023, 2025 |Méso|Star> (contact@meso-star.com)
- *
- * 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 3 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include "sstl.h"
-
-#include <rsys/float3.h>
-#include <rsys/mem_allocator.h>
-
-#include <string.h>
-
-/*******************************************************************************
- * Helper functions
- ******************************************************************************/
-static void
-check_api(struct sstl* sstl)
-{
- const char* filename = "test.stl";
- FILE* fp = NULL;
- struct sstl_desc desc = SSTL_DESC_NULL;
-
- CHK((fp = fopen(filename, "w+")) != NULL);
- rewind(fp);
-
- CHK(sstl_load(sstl, NULL) == RES_BAD_ARG);
- CHK(sstl_load(NULL, filename) == RES_BAD_ARG);
- CHK(sstl_load(sstl, "none.stl") == RES_IO_ERR);
- CHK(sstl_load(sstl, filename) == RES_OK); /* Empty file should be OK */
-
- CHK(sstl_get_desc(sstl, NULL) == RES_BAD_ARG);
- CHK(sstl_get_desc(NULL, &desc) == RES_BAD_ARG);
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
-
- CHK(desc.solid_name == NULL);
- CHK(desc.vertices_count == 0);
- CHK(desc.triangles_count == 0);
-
-
- CHK(sstl_load_stream(NULL, fp, "stream") == RES_BAD_ARG);
- CHK(sstl_load_stream(sstl, NULL, "stream") == RES_BAD_ARG);
- CHK(sstl_load_stream(sstl, fp, NULL) == RES_BAD_ARG);
- CHK(sstl_load_stream(sstl, fp, "stream") == RES_OK);
-
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
-
- CHK(desc.solid_name == NULL);
- CHK(desc.vertices_count == 0);
- CHK(desc.triangles_count == 0);
-
- CHK(fclose(fp) == 0);
-}
-
-static void
-check_no_triangle(struct sstl* sstl)
-{
- static const char* stl =
- "solid my_solid\n"
- "endsolid my_solid\n";
- const char* filename = "empty.stl";
- FILE* fp = NULL;
- struct sstl_desc desc = SSTL_DESC_NULL;
-
- CHK(sstl);
-
- CHK((fp = fopen(filename, "w+")) != NULL);
- CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
- rewind(fp);
-
- CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
- CHK(!strcmp(desc.solid_name, "my_solid"));
- CHK(desc.vertices_count == 0);
- CHK(desc.triangles_count == 0);
-
- CHK(fclose(fp) == 0);
-}
-
-static void
-check_1_triangle(struct sstl* sstl)
-{
- static const char* stl =
- "solid\n"
- " facet normal 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid";
- const char* filename = "1_triangle.stl";
- FILE* fp = NULL;
- float v[3] = {0,0,0};
- struct sstl_desc desc = SSTL_DESC_NULL;
-
- CHK(sstl);
-
- CHK((fp = fopen(filename, "w")) != NULL);
- CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
- CHK(fclose(fp) == 0);
-
- CHK(sstl_load(sstl, filename) == RES_OK);
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
-
- CHK(desc.solid_name == NULL);
- CHK(desc.vertices_count == 3);
- CHK(desc.triangles_count == 1);
- CHK(desc.indices[0] == 0);
- CHK(desc.indices[1] == 1);
- CHK(desc.indices[2] == 2);
- CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
- CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
-}
-
-static void
-check_1_triangle_with_noise(struct sstl* sstl)
-{
- static const char* stl =
- "solid My Solid\n"
- "\n"
- " facet normal 0.0 -1.0 0.0\n"
- " outer loop hophophophophop\n"
- " vertex\t 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0 \taaa\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop \n"
- " endfacet \t\t\t noise\n"
- "endsolid pouet\n";
- const char* filename = "1_triangle_with_noise.stl";
- FILE* fp = NULL;
- float v[3] = {0,0,0};
- struct sstl_desc desc = SSTL_DESC_NULL;
-
- CHK(sstl);
-
- CHK((fp = fopen(filename, "w")) != NULL);
- CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
- CHK(fclose(fp) == 0);
-
- CHK(sstl_load(sstl, filename) == RES_OK);
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
-
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
- CHK(strcmp(desc.solid_name, "My Solid") == 0);
- CHK(desc.vertices_count == 3);
- CHK(desc.triangles_count == 1);
- CHK(desc.indices[0] == 0);
- CHK(desc.indices[1] == 1);
- CHK(desc.indices[2] == 2);
- CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
- CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
-}
-
-static void
-check_1_triangle_no_normal(struct sstl* sstl)
-{
- static const char* stl =
- "solid\n"
- " facet normal 0.0 0.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid";
- const char* filename = "1_triangle_no_normal.stl";
- FILE* fp = NULL;
- float v[3] = {0,0,0};
- struct sstl_desc desc = SSTL_DESC_NULL;
-
- CHK((fp = fopen(filename, "w+")) != NULL);
- CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
- rewind(fp);
-
- CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
-
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
- CHK(desc.solid_name == NULL);
- CHK(desc.vertices_count == 3);
- CHK(desc.triangles_count == 1);
- CHK(desc.indices[0] == 0);
- CHK(desc.indices[1] == 1);
- CHK(desc.indices[2] == 2);
- CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
-
- /* Normal is automatically calculated */
- CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
-
- CHK(fclose(fp) == 0);
-}
-
-static void
-check_invalid_file(struct sstl* sstl)
-{
- static const char* bad[] = {
- /* "endsolid" is missing */
- "solid\n"
- " facet normal 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n",
-
- /* "solid" is missing */
- " facet normal 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid\n",
-
- /* "normal" is missing */
- "solid\n"
- " facet 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid\n",
-
- /* "facet" is missing */
- "solid\n"
- " normal 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid\n",
-
- /* invalid vertex coordinate */
- "solid\n"
- " facet normal 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 0.0 0.0 a.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid\n",
-
- /* Not enough vertices */
- "solid\n"
- " facet normal 0.0 -1.0 0.0\n"
- " outer loop\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endloop\n"
- " endfacet\n"
- "endsolid\n",
-
- /* "outer loop" is missing */
- "solid\n"
- " facet normal 0.0 -1.0 0.0\n"
- " vertex 0.0 0.0 0.0\n"
- " vertex 1.0 0.0 0.0\n"
- " vertex 0.0 0.0 1.0\n"
- " endfacet\n"
- "endsolid\n"
- };
- const size_t nbads = sizeof(bad)/sizeof(const char*);
- FILE* fp = NULL;
- size_t i;
-
- CHK(sstl != NULL);
-
- FOR_EACH(i, 0, nbads) {
- CHK((fp = tmpfile()) != NULL);
- CHK(fwrite(bad[i], sizeof(char), strlen(bad[i]), fp) == strlen(bad[i]));
- rewind(fp);
- CHK(sstl_load_stream(sstl, fp, "invalid StL") == RES_BAD_ARG);
- CHK(fclose(fp) == 0);
- }
-}
-
-static void
-check_tetrahedron(struct sstl* sstl)
-{
- static const char* tetrahedron[] = {
- "solid cube corner\n",
- " facet normal 0.0 -1.0 0.0\n",
- " outer loop\n",
- " vertex 0.0 0.0 0.0\n",
- " vertex 1.0 0.0 0.0\n",
- " vertex 0.0 0.0 1.0\n",
- " endloop\n",
- " endfacet\n",
- " facet normal 0.0 0.0 -1.0\n",
- " outer loop\n",
- " vertex 0.0 0.0 0.0\n",
- " vertex 0.0 1.0 0.0\n",
- " vertex 1.0 0.0 0.0\n",
- " endloop\n",
- " endfacet\n",
- " facet normal -1.0 0.0 0.0\n",
- " outer loop\n",
- " vertex 0.0 0.0 0.0\n",
- " vertex 0.0 0.0 1.0\n",
- " vertex 0.0 1.0 0.0\n",
- " endloop\n",
- " endfacet\n",
- " facet normal 0.577 0.577 0.577\n",
- " outer loop\n",
- " vertex 1.0 0.0 0.0\n",
- " vertex 0.0 1.0 0.0\n",
- " vertex 0.0 0.0 1.0\n",
- " endloop\n",
- " endfacet\n",
- "endsolid\n"
- };
- FILE* fp = NULL;
- const size_t nlines = sizeof(tetrahedron)/sizeof(const char*);
- struct sstl_desc desc = SSTL_DESC_NULL;
- float v[3];
- size_t i;
-
- CHK(sstl != NULL);
-
- CHK((fp = tmpfile()) != NULL);
- FOR_EACH(i, 0, nlines) {
- const size_t n = strlen(tetrahedron[i]);
- CHK(fwrite(tetrahedron[i], sizeof(char), n, fp) == n);
- }
- rewind(fp);
-
- CHK(sstl_load_stream(sstl, fp, "Tetrahedron") == RES_OK);
- CHK(fclose(fp) == 0);
-
- CHK(sstl_get_desc(sstl, &desc) == RES_OK);
- CHK(strcmp(desc.solid_name, "cube corner") == 0);
- CHK(desc.vertices_count == 4);
- CHK(desc.triangles_count == 4);
-
- CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
- CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
-
- CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1);
- CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1);
- CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1);
- f3_normalize(v, f3(v, 1.f, 1.f, 1.f));
- CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1);
-}
-
-/*******************************************************************************
- * The test
- ******************************************************************************/
-int
-main(int argc, char** argv)
-{
- struct sstl* sstl = NULL;
- (void)argc, (void)argv;
-
- CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK);
-
- check_api(sstl);
- check_no_triangle(sstl);
- check_1_triangle(sstl);
- check_1_triangle_with_noise(sstl);
- check_1_triangle_no_normal(sstl);
- check_invalid_file(sstl);
- check_tetrahedron(sstl);
-
- CHK(sstl_ref_put(sstl) == RES_OK);
- CHK(mem_allocated_size() == 0);
- return 0;
-}
diff --git a/src/test_sstl_load_ascii.c b/src/test_sstl_load_ascii.c
@@ -0,0 +1,472 @@
+/* Copyright (C) 2015, 2016, 2019, 2021, 2023, 2025 |Méso|Star> (contact@meso-star.com)
+ *
+ * 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 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define _POSIX_C_SOURCE 200112L /* fork */
+
+#include "sstl.h"
+
+#include <rsys/float3.h>
+#include <rsys/mem_allocator.h>
+
+#include <string.h>
+#include <unistd.h> /* fork, pipe */
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+check_api(struct sstl* sstl)
+{
+ const char* filename = "test.stl";
+ FILE* fp = NULL;
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK((fp = fopen(filename, "w+")) != NULL);
+ rewind(fp);
+
+ #define CHECK_EMPTY_FILE { \
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK); \
+ CHK(desc.read_type == SSTL_ASCII); \
+ CHK(desc.solid_name == NULL); \
+ CHK(desc.vertices_count == 0); \
+ CHK(desc.triangles_count == 0); \
+ } (void)0
+
+ CHK(sstl_load(sstl, NULL) == RES_BAD_ARG);
+ CHK(sstl_load(NULL, filename) == RES_BAD_ARG);
+ CHK(sstl_load(sstl, "none.stl") == RES_IO_ERR);
+ CHK(sstl_load(sstl, filename) == RES_OK); /* Empty file should be OK */
+
+ CHK(sstl_get_desc(sstl, NULL) == RES_BAD_ARG);
+ CHK(sstl_get_desc(NULL, &desc) == RES_BAD_ARG);
+ CHECK_EMPTY_FILE;
+
+ CHK(sstl_load_ascii(sstl, NULL) == RES_BAD_ARG);
+ CHK(sstl_load_ascii(NULL, filename) == RES_BAD_ARG);
+ CHK(sstl_load_ascii(sstl, "none.stl") == RES_IO_ERR);
+ CHK(sstl_load_ascii(sstl, filename) == RES_OK); /* Empty file should be OK */
+ CHECK_EMPTY_FILE;
+
+ CHK(sstl_load_stream(NULL, fp, filename) == RES_BAD_ARG);
+ CHK(sstl_load_stream(sstl, NULL, filename) == RES_BAD_ARG);
+ CHK(sstl_load_stream(sstl, fp, NULL) == RES_BAD_ARG);
+ CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
+ CHECK_EMPTY_FILE;
+
+ CHK(sstl_load_stream_ascii(NULL, fp, filename) == RES_BAD_ARG);
+ CHK(sstl_load_stream_ascii(sstl, NULL, filename) == RES_BAD_ARG);
+ CHK(sstl_load_stream_ascii(sstl, fp, NULL) == RES_BAD_ARG);
+ CHK(sstl_load_stream_ascii(sstl, fp, filename) == RES_OK);
+ CHECK_EMPTY_FILE;
+
+ #undef CHECK_EMPTY_FILE
+
+ CHK(fclose(fp) == 0);
+}
+
+static void
+check_no_triangle(struct sstl* sstl)
+{
+ static const char* stl =
+ "solid my_solid\n"
+ "endsolid my_solid\n";
+ const char* filename = "empty.stl";
+ FILE* fp = NULL;
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK(sstl);
+
+ CHK((fp = fopen(filename, "w+")) != NULL);
+ CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
+ rewind(fp);
+
+ CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_ASCII);
+ CHK(!strcmp(desc.solid_name, "my_solid"));
+ CHK(desc.vertices_count == 0);
+ CHK(desc.triangles_count == 0);
+
+ CHK(fclose(fp) == 0);
+}
+
+static void
+check_1_triangle(struct sstl* sstl)
+{
+ static const char* stl =
+ "solid\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid";
+ const char* filename = "1_triangle.stl";
+ FILE* fp = NULL;
+ float v[3] = {0,0,0};
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK(sstl);
+
+ CHK((fp = fopen(filename, "w")) != NULL);
+ CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_load_ascii(sstl, filename) == RES_OK);
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+
+ CHK(desc.read_type == SSTL_ASCII);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+}
+
+static void
+check_1_triangle_with_noise(struct sstl* sstl)
+{
+ static const char* stl =
+ "solid My Solid\n"
+ "\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop hophophophophop\n"
+ " vertex\t 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0 \taaa\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop \n"
+ " endfacet \t\t\t noise\n"
+ "endsolid pouet\n";
+ const char* filename = "1_triangle_with_noise.stl";
+ FILE* fp = NULL;
+ float v[3] = {0,0,0};
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK(sstl);
+
+ CHK((fp = fopen(filename, "w")) != NULL);
+ CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_load(sstl, filename) == RES_OK);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_ASCII);
+ CHK(strcmp(desc.solid_name, "My Solid") == 0);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+}
+
+static void
+check_1_triangle_no_normal(struct sstl* sstl)
+{
+ static const char* stl =
+ "solid\n"
+ " facet normal 0.0 0.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid";
+ const char* filename = "1_triangle_no_normal.stl";
+ FILE* fp = NULL;
+ float v[3] = {0,0,0};
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK((fp = fopen(filename, "w+")) != NULL);
+ CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl));
+ rewind(fp);
+
+ CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_ASCII);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+
+ /* Normal is automatically calculated */
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+
+ CHK(fclose(fp) == 0);
+}
+
+static void
+check_invalid_file(struct sstl* sstl)
+{
+ static const char* bad[] = {
+ /* "endsolid" is missing */
+ "solid\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n",
+
+ /* "solid" is missing */
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid\n",
+
+ /* "normal" is missing */
+ "solid\n"
+ " facet 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid\n",
+
+ /* "facet" is missing */
+ "solid\n"
+ " normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid\n",
+
+ /* invalid vertex coordinate */
+ "solid\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 0.0 0.0 a.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid\n",
+
+ /* Not enough vertices */
+ "solid\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid\n",
+
+ /* "outer loop" is missing */
+ "solid\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " vertex 0.0 0.0 0.0\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " endfacet\n"
+ "endsolid\n"
+ };
+ const size_t nbads = sizeof(bad)/sizeof(const char*);
+ FILE* fp = NULL;
+ size_t i;
+
+ CHK(sstl != NULL);
+
+ FOR_EACH(i, 0, nbads) {
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(bad[i], sizeof(char), strlen(bad[i]), fp) == strlen(bad[i]));
+ rewind(fp);
+ CHK(sstl_load_stream(sstl, fp, "invalid StL") == RES_BAD_ARG);
+ CHK(fclose(fp) == 0);
+ }
+}
+
+static void
+check_tetrahedron(struct sstl* sstl)
+{
+ static const char* tetrahedron[] = {
+ "solid cube corner\n",
+ " facet normal 0.0 -1.0 0.0\n",
+ " outer loop\n",
+ " vertex 0.0 0.0 0.0\n",
+ " vertex 1.0 0.0 0.0\n",
+ " vertex 0.0 0.0 1.0\n",
+ " endloop\n",
+ " endfacet\n",
+ " facet normal 0.0 0.0 -1.0\n",
+ " outer loop\n",
+ " vertex 0.0 0.0 0.0\n",
+ " vertex 0.0 1.0 0.0\n",
+ " vertex 1.0 0.0 0.0\n",
+ " endloop\n",
+ " endfacet\n",
+ " facet normal -1.0 0.0 0.0\n",
+ " outer loop\n",
+ " vertex 0.0 0.0 0.0\n",
+ " vertex 0.0 0.0 1.0\n",
+ " vertex 0.0 1.0 0.0\n",
+ " endloop\n",
+ " endfacet\n",
+ " facet normal 0.577 0.577 0.577\n",
+ " outer loop\n",
+ " vertex 1.0 0.0 0.0\n",
+ " vertex 0.0 1.0 0.0\n",
+ " vertex 0.0 0.0 1.0\n",
+ " endloop\n",
+ " endfacet\n",
+ "endsolid\n"
+ };
+ FILE* fp = NULL;
+ const size_t nlines = sizeof(tetrahedron)/sizeof(const char*);
+ struct sstl_desc desc = SSTL_DESC_NULL;
+ float v[3];
+ size_t i;
+
+ CHK(sstl != NULL);
+
+ CHK((fp = tmpfile()) != NULL);
+ FOR_EACH(i, 0, nlines) {
+ const size_t n = strlen(tetrahedron[i]);
+ CHK(fwrite(tetrahedron[i], sizeof(char), n, fp) == n);
+ }
+ rewind(fp);
+
+ CHK(sstl_load_stream_ascii(sstl, fp, "Tetrahedron") == RES_OK);
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(strcmp(desc.solid_name, "cube corner") == 0);
+ CHK(desc.read_type == SSTL_ASCII);
+ CHK(desc.vertices_count == 4);
+ CHK(desc.triangles_count == 4);
+
+ CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+
+ CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1);
+ CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1);
+ f3_normalize(v, f3(v, 1.f, 1.f, 1.f));
+ CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1);
+}
+
+static void
+check_no_seekable_file(struct sstl* sstl)
+{
+ const char* stl =
+ "solid Triangle\n"
+ " facet normal 0.0 -1.0 0.0\n"
+ " outer loop\n"
+ " vertex 1.0 0.0 0.0\n"
+ " vertex 0.0 0.0 1.0\n"
+ " vertex 0.0 0.0 0.0\n"
+ " endloop\n"
+ " endfacet\n"
+ "endsolid";
+ int fd[2] = {0,0};
+ pid_t pid = 0;
+
+ CHK(pipe(fd) == 0);
+ CHK((pid = fork()) != -1);
+
+ if(pid == 0) { /* Child process */
+ CHK(close(fd[0]) == 0); /* Close the unused input stream */
+ CHK(write(fd[1], stl, strlen(stl)) == (int)strlen(stl));
+ CHK(close(fd[1]) == 0);
+ exit(0);
+
+ } else { /* Parent process */
+ struct sstl_desc desc = SSTL_DESC_NULL;
+ float v[3];
+ FILE* fp = NULL;
+ CHK(close(fd[1]) == 0); /* Clse the unused output stream */
+
+ CHK(fp = fdopen(fd[0], "r"));
+ CHK(sstl_load_stream(sstl, fp, "Piped StL") == RES_BAD_ARG);
+ CHK(sstl_load_stream_ascii(sstl, fp, "Piped StL") == RES_OK);
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_ASCII);
+ CHK(strcmp(desc.solid_name, "Triangle") == 0);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+ }
+}
+
+/*******************************************************************************
+ * The test
+ ******************************************************************************/
+int
+main(int argc, char** argv)
+{
+ struct sstl* sstl = NULL;
+ (void)argc, (void)argv;
+
+ CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK);
+
+ check_api(sstl);
+ check_no_triangle(sstl);
+ check_1_triangle(sstl);
+ check_1_triangle_with_noise(sstl);
+ check_1_triangle_no_normal(sstl);
+ check_invalid_file(sstl);
+ check_tetrahedron(sstl);
+ check_no_seekable_file(sstl);
+
+ CHK(sstl_ref_put(sstl) == RES_OK);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
diff --git a/src/test_sstl_load_binary.c b/src/test_sstl_load_binary.c
@@ -0,0 +1,404 @@
+/* Copyright (C) 2015, 2016, 2019, 2021, 2023, 2025 |Méso|Star> (contact@meso-star.com)
+ *
+ * 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 3 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#define _POSIX_C_SOURCE 200112L /* fork */
+
+#include "sstl.h"
+
+#include <rsys/float3.h>
+#include <rsys/mem_allocator.h>
+
+#include <unistd.h> /* fork, pipe */
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+check_api(struct sstl* sstl)
+{
+ const char header[80] = {0};
+ const uint32_t ntris = 0;
+ const char* filename = "test.stl";
+ FILE* fp = NULL;
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK((fp = fopen(filename, "w+")) != NULL);
+ rewind(fp);
+
+ CHK(sstl_load_binary(sstl, NULL) == RES_BAD_ARG);
+ CHK(sstl_load_binary(NULL, filename) == RES_BAD_ARG);
+ CHK(sstl_load_binary(sstl, "none.stl") == RES_IO_ERR);
+ /* A binary cannot be empty */
+ CHK(sstl_load_binary(sstl, filename) == RES_BAD_ARG);
+
+ CHK(sstl_load_stream_binary(NULL, fp, filename) == RES_BAD_ARG);
+ CHK(sstl_load_stream_binary(sstl, NULL, filename) == RES_BAD_ARG);
+ CHK(sstl_load_stream_binary(sstl, fp, NULL) == RES_BAD_ARG);
+ /* A binary cannot be empty */
+ CHK(sstl_load_stream_binary(sstl, fp, filename) == RES_BAD_ARG);
+
+ /* Write the minimum data required by a binary StL */
+ CHK(fwrite(header, sizeof(header), 1, fp) == 1);
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ rewind(fp);
+
+ CHK(sstl_load_binary(sstl, filename) == RES_OK);
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 0);
+ CHK(desc.triangles_count == 0);
+
+ CHK(sstl_load_stream_binary(sstl, fp, filename) == RES_OK);
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 0);
+ CHK(desc.triangles_count == 0);
+
+ CHK(fclose(fp) == 0);
+}
+
+static void
+check_1_triangle(struct sstl* sstl)
+{
+ char header[80] = {0};
+ const uint32_t ntris = 1;
+ const float normal[3] = {0.f, -1.f, 0.f};
+ const float verts[9] = {
+ 0.f, 0.f, 0.f,
+ 1.f, 0.f, 0.f,
+ 0.f, 0.f, 1.f
+ };
+ const uint16_t nattrs = 0;
+ const char* filename = "1_triangle.stl";
+ FILE* fp = NULL;
+ float v[3] = {0,0,0};
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK(sstl);
+
+ CHK((fp = fopen(filename, "w")) != NULL);
+ CHK(fwrite(header, sizeof(header), 1, fp) == 1);
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ CHK(fwrite(normal, sizeof(normal), 1, fp) == 1);
+ CHK(fwrite(verts, sizeof(verts), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_load(sstl, filename) == RES_OK);
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+}
+
+static void
+check_1_triangle_no_normal(struct sstl* sstl)
+{
+ char header[80] = {0};
+ const uint32_t ntris = 1;
+ const float normal[3] = {0.f, 0.f, 0.f};
+ const float verts[9] = {
+ 0.f, 0.f, 0.f,
+ -1.f, 0.f, 0.f,
+ 0.f, 0.f,-1.f
+ };
+ const uint16_t nattrs = 0;
+ const char* filename = "1_triangle_no_normal.stl";
+ FILE* fp = NULL;
+ float v[3] = {0,0,0};
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK((fp = fopen(filename, "w+")) != NULL);
+ CHK(fwrite(header, sizeof(header), 1, fp) == 1);
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ CHK(fwrite(normal, sizeof(normal), 1, fp) == 1);
+ CHK(fwrite(verts, sizeof(verts), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+ rewind(fp);
+
+ CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v,-1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f,-1.f)) == 1);
+
+ /* Normal is automatically calculated */
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+
+ CHK(fclose(fp) == 0);
+}
+
+static void
+check_invalid_file(struct sstl* sstl)
+{
+ const char header[80];
+ const uint32_t ntris = 1;
+ const float N[3] = {0,0,0};
+ const float v0[3] = {0,0,0};
+ const float v1[3] = {1,0,0};
+ const float v2[3] = {0,0,1};
+ const uint16_t nattrs = 0;
+ FILE* fp = NULL;
+ float v[3] = {0,0,0};
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ /* First, check that the file should be OK if all the data has been correctly
+ * written, to make sure that the tests really check what we expect, i.e. a
+ * bad formatting and not a faulty data set */
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ CHK(fwrite(N, sizeof(N), 1, fp) == 1);
+ CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
+ CHK(fwrite(v1, sizeof(v1), 1, fp) == 1);
+ CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+ rewind(fp);
+ CHK(sstl_load_stream(sstl, fp, "Valid StL") == RES_OK);
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+
+ /* Header is too small */
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(&header, sizeof(char), sizeof(header)-1, fp) == sizeof(header)-1);
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ rewind(fp);
+ CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
+ CHK(fclose(fp) == 0);
+
+ /* Triangle is missing */
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ rewind(fp);
+ CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
+ CHK(fclose(fp) == 0);
+
+ /* Triangle normal is missing */
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
+ CHK(fwrite(v1, sizeof(v1), 1, fp) == 1);
+ CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+ rewind(fp);
+ CHK(sstl_load_stream_binary(sstl, fp, "Invalid StL") == RES_BAD_ARG);
+ CHK(fclose(fp) == 0);
+
+ /* One vertex of the triangle is wrongly written */
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ CHK(fwrite(N, sizeof(N), 1, fp) == 1);
+ CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
+ CHK(fwrite(v1, sizeof(v1)-1/*One byte is missing*/, 1, fp) == 1);
+ CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+ rewind(fp);
+ CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
+ CHK(fclose(fp) == 0);
+
+ /* The #attribs is missing */
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+ CHK(fwrite(N, sizeof(N), 1, fp) == 1);
+ CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
+ CHK(fwrite(v1, sizeof(v1), 1, fp) == 1);
+ CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
+ rewind(fp);
+ CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
+ CHK(fclose(fp) == 0);
+}
+
+static void
+check_tetrahedron(struct sstl* sstl)
+{
+ const char header[80] = {0};
+ float v[3] = {0,0,0};
+ const uint32_t ntris = 4;
+ const uint16_t nattrs = 0;
+ FILE* fp = NULL;
+ struct sstl_desc desc = SSTL_DESC_NULL;
+
+ CHK(sstl != NULL);
+
+ CHK((fp = tmpfile()) != NULL);
+ CHK(fwrite(header, sizeof(header), 1, fp) == 1);
+ CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
+
+ CHK(fwrite(f3(v, 0.f,-1.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 1.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 0.f, 1.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+
+ CHK(fwrite(f3(v, 0.f, 0.f,-1.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 1.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 1.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+
+ CHK(fwrite(f3(v,-1.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 0.f, 1.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 1.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+
+ CHK(fwrite(f3(v, 0.577f, 0.577f, 0.577f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 1.f, 0.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 1.f, 0.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(f3(v, 0.f, 0.f, 1.f), sizeof(v), 1, fp) == 1);
+ CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
+
+ rewind(fp);
+
+ CHK(sstl_load_stream(sstl, fp, "Tetrahedron") == RES_OK);
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 4);
+ CHK(desc.triangles_count == 4);
+
+ CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.f, 1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+
+ CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1);
+ CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1);
+ CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1);
+ f3_normalize(v, f3(v, 1.f, 1.f, 1.f));
+ CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1);
+}
+
+static void
+check_no_seekable_file(struct sstl* sstl)
+{
+ float v[3] = {0,0,0};
+ int fd[2] = {0,0};
+ pid_t pid = 0;
+
+ CHK(pipe(fd) == 0);
+ CHK((pid = fork()) != -1);
+
+ if(pid == 0) { /* Child process */
+ const char header[80] = {0};
+ const uint32_t ntris = 1;
+ const uint16_t nattrs = 0;
+
+ CHK(close(fd[0]) == 0); /* Close the unused input stream */
+
+ /* Write the binary StL */
+ CHK(write(fd[1], header, sizeof(header)) == sizeof(header));
+ CHK(write(fd[1], &ntris, sizeof(ntris)) == sizeof(ntris));
+ CHK(write(fd[1], f3(v, 0.f,-1.f, 0.f), sizeof(v)) == sizeof(v));
+ CHK(write(fd[1], f3(v, 1.f, 0.f, 0.f), sizeof(v)) == sizeof(v));
+ CHK(write(fd[1], f3(v, 0.f, 0.f, 1.f), sizeof(v)) == sizeof(v));
+ CHK(write(fd[1], f3(v, 0.f, 0.f, 0.f), sizeof(v)) == sizeof(v));
+ CHK(write(fd[1], &nattrs, sizeof(nattrs)) == sizeof(nattrs));
+
+ CHK(close(fd[1]) == 0);
+ exit(0);
+
+ } else { /* Parent process */
+ struct sstl_desc desc = SSTL_DESC_NULL;
+ FILE* fp = NULL;
+ CHK(close(fd[1]) == 0); /* Clse the unused output stream */
+
+ CHK(fp = fdopen(fd[0], "r"));
+ CHK(sstl_load_stream(sstl, fp, "Piped StL") == RES_BAD_ARG);
+ CHK(sstl_load_stream_binary(sstl, fp, "Piped StL") == RES_OK);
+ CHK(fclose(fp) == 0);
+
+ CHK(sstl_get_desc(sstl, &desc) == RES_OK);
+ CHK(desc.read_type == SSTL_BINARY);
+ CHK(desc.solid_name == NULL);
+ CHK(desc.vertices_count == 3);
+ CHK(desc.triangles_count == 1);
+ CHK(desc.indices[0] == 0);
+ CHK(desc.indices[1] == 1);
+ CHK(desc.indices[2] == 2);
+ CHK(f3_eq(desc.vertices + 0*3, f3(v, 1.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.vertices + 1*3, f3(v, 0.f, 0.f, 1.f)) == 1);
+ CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 0.f)) == 1);
+ CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
+ }
+}
+
+
+/*******************************************************************************
+ * The test
+ ******************************************************************************/
+int
+main(int argc, char** argv)
+{
+ struct sstl* sstl = NULL;
+ (void)argc, (void)argv;
+
+ CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK);
+
+ check_api(sstl);
+ check_1_triangle(sstl);
+ check_1_triangle_no_normal(sstl);
+ check_invalid_file(sstl);
+ check_tetrahedron(sstl);
+ check_no_seekable_file(sstl);
+
+ CHK(sstl_ref_put(sstl) == RES_OK);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}