star-stl

Load STereo Lithography (StL) file format
git clone git://git.meso-star.fr/star-stl.git
Log | Files | Refs | README | LICENSE

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:
MMakefile | 8++++++--
Dsrc/test_sstl_load.c | 401-------------------------------------------------------------------------------
Asrc/test_sstl_load_ascii.c | 472+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sstl_load_binary.c | 404+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +}