an efficient binary archive format
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add c tests

zach 5b42490d dbd32f24

+1722 -43
+1
.gitignore
··· 8 8 *.dylib 9 9 *.so 10 10 *.a 11 + test/runtest
+7 -3
Makefile
··· 1 1 prefix?=/usr/local 2 2 3 - lib: 3 + build: 4 4 cargo build --release 5 5 cp target/release/libbindle_file.a . 6 6 cp target/release/libbindle_file.so libbindle.so || cp target/release/libbindle_file.dylib libbindle.dylib 7 7 8 8 install: 9 9 cp include/bindle.h "$(prefix)/include/bindle.h" 10 - cp libbindle.* "$(prefix)/lib" 10 + cp target/release/libbindle_file.a ."$(prefix)/lib/libbindle.a" 11 + -cp target/release/libbindle_file.so ."$(prefix)/lib/libbindle.so" 12 + -cp target/release/libbindle_file.dylib ."$(prefix)/lib/libbindle.dylib" 11 13 12 14 uninstall: 13 - rm -f "$(prefix)/include/bindle.h" "$(prefix)/lib/libbindle.*" 15 + rm -f "$(prefix)/include/bindle.h" "$(prefix)/lib/libbindle_file.*" 14 16 17 + .PHONY: test 15 18 test: 16 19 cargo test 20 + cd test && $(MAKE) test
+128 -20
include/bindle.h
··· 93 93 typedef struct BindleWriter BindleWriter; 94 94 95 95 /** 96 - * Open a bindle file from disk, the path paramter should be NUL terminated 96 + * Creates a new archive, overwriting any existing file. 97 + * 98 + * # Parameters 99 + * * `path` - NUL-terminated path to the archive file 100 + * 101 + * # Returns 102 + * A pointer to the Bindle handle, or NULL on error. Must be freed with `bindle_close()`. 103 + */ 104 + struct Bindle *bindle_create(const char *path); 105 + 106 + /** 107 + * Opens an existing archive or creates a new one. 108 + * 109 + * # Parameters 110 + * * `path` - NUL-terminated path to the archive file 111 + * 112 + * # Returns 113 + * A pointer to the Bindle handle, or NULL on error. Must be freed with `bindle_close()`. 97 114 */ 98 115 struct Bindle *bindle_open(const char *path); 99 116 100 117 /** 101 - * Adds a new entry, the name should be NUL terminated, will the data can contain NUL characters since the length 102 - * is provided 118 + * Opens an existing archive. Returns NULL if the file doesn't exist. 119 + * 120 + * # Parameters 121 + * * `path` - NUL-terminated path to the archive file 122 + * 123 + * # Returns 124 + * A pointer to the Bindle handle, or NULL on error. Must be freed with `bindle_close()`. 125 + */ 126 + struct Bindle *bindle_load(const char *path); 127 + 128 + /** 129 + * Adds data to the archive with the given name. 130 + * 131 + * # Parameters 132 + * * `ctx` - Bindle handle from `bindle_open()` 133 + * * `name` - NUL-terminated entry name 134 + * * `data` - Data bytes (may contain NUL bytes) 135 + * * `data_len` - Length of data in bytes 136 + * * `compress` - Compression mode (BindleCompressNone, BindleCompressZstd, or BindleCompressAuto) 137 + * 138 + * # Returns 139 + * True on success. Call `bindle_save()` to commit changes. 103 140 */ 104 141 bool bindle_add(struct Bindle *ctx, 105 142 const char *name, ··· 108 145 BindleCompress compress); 109 146 110 147 /** 111 - * Adds a new entry, the name should be NUL terminated, will the data can contain NUL characters since the length 112 - * is provided 148 + * Adds a file from the filesystem to the archive. 149 + * 150 + * # Parameters 151 + * * `ctx` - Bindle handle from `bindle_open()` 152 + * * `name` - NUL-terminated entry name 153 + * * `path` - NUL-terminated path to file on disk 154 + * * `compress` - Compression mode 155 + * 156 + * # Returns 157 + * True on success. Call `bindle_save()` to commit changes. 113 158 */ 114 159 bool bindle_add_file(struct Bindle *ctx, 115 160 const char *name, ··· 117 162 BindleCompress compress); 118 163 119 164 /** 120 - * Save any changed to disk 165 + * Commits all pending changes to disk. 166 + * 167 + * Writes the index and footer. Must be called after add/remove operations. 121 168 */ 122 169 bool bindle_save(struct Bindle *ctx); 123 170 124 171 /** 125 - * Close an open bindle file 172 + * Closes the archive and frees the handle. 173 + * 174 + * After calling this, the ctx pointer is no longer valid. 126 175 */ 127 176 void bindle_close(struct Bindle *ctx); 128 177 129 178 /** 130 - * Read a value from a bindle file in memory, returns a pointer that should be freed with 131 - * `bindle_free_buffer` 179 + * Reads an entry from the archive, decompressing if needed. 180 + * 181 + * # Parameters 182 + * * `ctx_ptr` - Bindle handle 183 + * * `name` - NUL-terminated entry name 184 + * * `out_len` - Output parameter for data length 185 + * 186 + * # Returns 187 + * Pointer to data buffer, or NULL if not found or CRC32 check fails. 188 + * Must be freed with `bindle_free_buffer()`. 132 189 */ 133 190 uint8_t *bindle_read(struct Bindle *ctx_ptr, const char *name, size_t *out_len); 134 191 135 192 /** 136 - * Used to free the results from `bindle_read` 193 + * Frees a buffer returned by `bindle_read()`. 137 194 */ 138 195 void bindle_free_buffer(uint8_t *ptr); 139 196 140 197 /** 141 - * Directly read an uncompressed entry from disk, returns NULL if the entry is compressed or doesn't exist 198 + * Reads an uncompressed entry without allocating. 199 + * 200 + * Returns a pointer directly into the memory-mapped archive. Only works for uncompressed entries. 201 + * 202 + * # Parameters 203 + * * `ctx` - Bindle handle 204 + * * `name` - NUL-terminated entry name 205 + * * `out_len` - Output parameter for data length 206 + * 207 + * # Returns 208 + * Pointer into the mmap, or NULL if entry is compressed or doesn't exist. 209 + * The pointer is valid as long as the Bindle handle is open. Do NOT free this pointer. 142 210 */ 143 211 const uint8_t *bindle_read_uncompressed_direct(struct Bindle *ctx, 144 212 const char *name, 145 213 size_t *out_len); 146 214 147 215 /** 148 - * Get the number of entries in a bindle file 216 + * Returns the number of entries in the archive. 149 217 */ 150 218 size_t bindle_length(const struct Bindle *ctx); 151 219 152 220 /** 153 221 * Returns the name of the entry at the given index. 154 - * The string is owned by the Bindle; the caller must NOT free it. 222 + * 223 + * Use with `bindle_length()` to iterate over all entries. The pointer is valid as long as the Bindle handle is open. 224 + * Do NOT free the returned pointer. 155 225 */ 156 - const char *bindle_entry_name(const struct Bindle *ctx, size_t index, size_t *len); 226 + const char *bindle_entry_name(const struct Bindle *ctx, 227 + size_t index, 228 + size_t *len); 157 229 158 230 /** 159 - * Compact and rewrite bindle file 231 + * Reclaims space by removing shadowed data. 232 + * 233 + * Rebuilds the archive with only live entries. 160 234 */ 161 235 bool bindle_vacuum(struct Bindle *ctx); 162 236 237 + /** 238 + * Extracts all entries to a destination directory. 239 + */ 163 240 bool bindle_unpack(struct Bindle *ctx, const char *dest_path); 164 241 242 + /** 243 + * Recursively adds all files from a directory to the archive. 244 + * 245 + * Call `bindle_save()` to commit changes. 246 + */ 165 247 bool bindle_pack(struct Bindle *ctx, const char *src_path, BindleCompress compress); 166 248 249 + /** 250 + * Returns true if an entry with the given name exists. 251 + */ 167 252 bool bindle_exists(const struct Bindle *ctx, const char *name); 168 253 169 254 /** 170 - * Remove an entry from the index. 171 - * The data remains in the file until bindle_vacuum is called. 172 - * Returns true if the entry existed and was removed, false otherwise. 255 + * Removes an entry from the index. 256 + * 257 + * Returns true if the entry existed. Data remains in the file until `bindle_vacuum()` is called. 258 + * Call `bindle_save()` to commit changes. 173 259 */ 174 260 bool bindle_remove(struct Bindle *ctx, const char *name); 175 261 176 262 /** 177 - * Create a new Writer, while the stream is active (until bindle_stream_finish is called), the 178 - * Bindle struct should not be accessed. 263 + * Creates a streaming writer for adding an entry. 264 + * 265 + * The writer must be closed with `bindle_writer_close()`, then call `bindle_save()` to commit. 266 + * Do not access the Bindle handle while the writer is active. 179 267 */ 180 268 struct BindleWriter *bindle_writer_new(struct Bindle *ctx, 181 269 const char *name, 182 270 BindleCompress compress); 183 271 272 + /** 273 + * Writes data to the writer. 274 + */ 184 275 bool bindle_writer_write(struct BindleWriter *stream, const uint8_t *data, size_t len); 185 276 277 + /** 278 + * Closes the writer and finalizes the entry. 279 + */ 186 280 bool bindle_writer_close(struct BindleWriter *stream); 187 281 282 + /** 283 + * Creates a streaming reader for an entry. 284 + * 285 + * Automatically decompresses if needed. Must be freed with `bindle_reader_close()`. 286 + * Call `bindle_reader_verify_crc32()` after reading to verify integrity. 287 + */ 188 288 struct BindleReader *bindle_reader_new(const struct Bindle *ctx, const char *name); 189 289 290 + /** 291 + * Reads data from the reader into the provided buffer. 292 + * 293 + * Returns the number of bytes read, or -1 on error. Returns 0 on EOF. 294 + */ 190 295 ptrdiff_t bindle_reader_read(struct BindleReader *reader, uint8_t *buffer, size_t buffer_len); 191 296 192 297 /** ··· 196 301 */ 197 302 bool bindle_reader_verify_crc32(const struct BindleReader *reader); 198 303 304 + /** 305 + * Closes the reader and frees the handle. 306 + */ 199 307 void bindle_reader_close(struct BindleReader *reader); 200 308 201 309 #endif /* BINDLE_H */
+139 -20
src/ffi.rs
··· 7 7 8 8 use crate::{Bindle, Compress, Reader, Writer}; 9 9 10 - /// Open a bindle file from disk, the path paramter should be NUL terminated 10 + /// Creates a new archive, overwriting any existing file. 11 + /// 12 + /// # Parameters 13 + /// * `path` - NUL-terminated path to the archive file 14 + /// 15 + /// # Returns 16 + /// A pointer to the Bindle handle, or NULL on error. Must be freed with `bindle_close()`. 17 + #[unsafe(no_mangle)] 18 + pub unsafe extern "C" fn bindle_create(path: *const c_char) -> *mut Bindle { 19 + if path.is_null() { 20 + return std::ptr::null_mut(); 21 + } 22 + 23 + let path_str = unsafe { 24 + match CStr::from_ptr(path).to_str() { 25 + Ok(s) => s, 26 + Err(_) => return std::ptr::null_mut(), 27 + } 28 + }; 29 + 30 + match Bindle::create(path_str) { 31 + Ok(b) => Box::into_raw(Box::new(b)), 32 + Err(_) => std::ptr::null_mut(), 33 + } 34 + } 35 + 36 + /// Opens an existing archive or creates a new one. 37 + /// 38 + /// # Parameters 39 + /// * `path` - NUL-terminated path to the archive file 40 + /// 41 + /// # Returns 42 + /// A pointer to the Bindle handle, or NULL on error. Must be freed with `bindle_close()`. 11 43 #[unsafe(no_mangle)] 12 44 pub unsafe extern "C" fn bindle_open(path: *const c_char) -> *mut Bindle { 13 45 if path.is_null() { 14 46 return std::ptr::null_mut(); 15 47 } 16 48 17 - // Explicit unsafe block for raw pointer dereference 18 49 let path_str = unsafe { 19 50 match CStr::from_ptr(path).to_str() { 20 51 Ok(s) => s, ··· 28 59 } 29 60 } 30 61 31 - /// Adds a new entry, the name should be NUL terminated, will the data can contain NUL characters since the length 32 - /// is provided 62 + /// Opens an existing archive. Returns NULL if the file doesn't exist. 63 + /// 64 + /// # Parameters 65 + /// * `path` - NUL-terminated path to the archive file 66 + /// 67 + /// # Returns 68 + /// A pointer to the Bindle handle, or NULL on error. Must be freed with `bindle_close()`. 69 + #[unsafe(no_mangle)] 70 + pub unsafe extern "C" fn bindle_load(path: *const c_char) -> *mut Bindle { 71 + if path.is_null() { 72 + return std::ptr::null_mut(); 73 + } 74 + 75 + let path_str = unsafe { 76 + match CStr::from_ptr(path).to_str() { 77 + Ok(s) => s, 78 + Err(_) => return std::ptr::null_mut(), 79 + } 80 + }; 81 + 82 + match Bindle::load(path_str) { 83 + Ok(b) => Box::into_raw(Box::new(b)), 84 + Err(_) => std::ptr::null_mut(), 85 + } 86 + } 87 + 88 + /// Adds data to the archive with the given name. 89 + /// 90 + /// # Parameters 91 + /// * `ctx` - Bindle handle from `bindle_open()` 92 + /// * `name` - NUL-terminated entry name 93 + /// * `data` - Data bytes (may contain NUL bytes) 94 + /// * `data_len` - Length of data in bytes 95 + /// * `compress` - Compression mode (BindleCompressNone, BindleCompressZstd, or BindleCompressAuto) 96 + /// 97 + /// # Returns 98 + /// True on success. Call `bindle_save()` to commit changes. 33 99 #[unsafe(no_mangle)] 34 100 pub unsafe extern "C" fn bindle_add( 35 101 ctx: *mut Bindle, ··· 55 121 } 56 122 } 57 123 58 - /// Adds a new entry, the name should be NUL terminated, will the data can contain NUL characters since the length 59 - /// is provided 124 + /// Adds a file from the filesystem to the archive. 125 + /// 126 + /// # Parameters 127 + /// * `ctx` - Bindle handle from `bindle_open()` 128 + /// * `name` - NUL-terminated entry name 129 + /// * `path` - NUL-terminated path to file on disk 130 + /// * `compress` - Compression mode 131 + /// 132 + /// # Returns 133 + /// True on success. Call `bindle_save()` to commit changes. 60 134 #[unsafe(no_mangle)] 61 135 pub unsafe extern "C" fn bindle_add_file( 62 136 ctx: *mut Bindle, ··· 85 159 } 86 160 } 87 161 88 - /// Save any changed to disk 162 + /// Commits all pending changes to disk. 163 + /// 164 + /// Writes the index and footer. Must be called after add/remove operations. 89 165 #[unsafe(no_mangle)] 90 166 pub unsafe extern "C" fn bindle_save(ctx: *mut Bindle) -> bool { 91 167 if ctx.is_null() { ··· 97 173 } 98 174 } 99 175 100 - /// Close an open bindle file 176 + /// Closes the archive and frees the handle. 177 + /// 178 + /// After calling this, the ctx pointer is no longer valid. 101 179 #[unsafe(no_mangle)] 102 180 pub unsafe extern "C" fn bindle_close(ctx: *mut Bindle) { 103 181 if ctx.is_null() { ··· 106 184 unsafe { drop(Box::from_raw(ctx)) } 107 185 } 108 186 109 - /// Read a value from a bindle file in memory, returns a pointer that should be freed with 110 - /// `bindle_free_buffer` 187 + /// Reads an entry from the archive, decompressing if needed. 188 + /// 189 + /// # Parameters 190 + /// * `ctx_ptr` - Bindle handle 191 + /// * `name` - NUL-terminated entry name 192 + /// * `out_len` - Output parameter for data length 193 + /// 194 + /// # Returns 195 + /// Pointer to data buffer, or NULL if not found or CRC32 check fails. 196 + /// Must be freed with `bindle_free_buffer()`. 111 197 #[unsafe(no_mangle)] 112 198 pub unsafe extern "C" fn bindle_read( 113 199 ctx_ptr: *mut Bindle, ··· 167 253 } 168 254 } 169 255 170 - /// Used to free the results from `bindle_read` 256 + /// Frees a buffer returned by `bindle_read()`. 171 257 #[unsafe(no_mangle)] 172 258 pub unsafe extern "C" fn bindle_free_buffer(ptr: *mut u8) { 173 259 unsafe { ··· 192 278 } 193 279 } 194 280 195 - /// Directly read an uncompressed entry from disk, returns NULL if the entry is compressed or doesn't exist 281 + /// Reads an uncompressed entry without allocating. 282 + /// 283 + /// Returns a pointer directly into the memory-mapped archive. Only works for uncompressed entries. 284 + /// 285 + /// # Parameters 286 + /// * `ctx` - Bindle handle 287 + /// * `name` - NUL-terminated entry name 288 + /// * `out_len` - Output parameter for data length 289 + /// 290 + /// # Returns 291 + /// Pointer into the mmap, or NULL if entry is compressed or doesn't exist. 292 + /// The pointer is valid as long as the Bindle handle is open. Do NOT free this pointer. 196 293 #[unsafe(no_mangle)] 197 294 pub unsafe extern "C" fn bindle_read_uncompressed_direct( 198 295 ctx: *mut Bindle, ··· 221 318 } 222 319 } 223 320 224 - /// Get the number of entries in a bindle file 321 + /// Returns the number of entries in the archive. 225 322 #[unsafe(no_mangle)] 226 323 pub unsafe extern "C" fn bindle_length(ctx: *const Bindle) -> usize { 227 324 if ctx.is_null() { ··· 231 328 } 232 329 233 330 /// Returns the name of the entry at the given index. 234 - /// The string is owned by the Bindle; the caller must NOT free it. 331 + /// 332 + /// Use with `bindle_length()` to iterate over all entries. The pointer is valid as long as the Bindle handle is open. 333 + /// Do NOT free the returned pointer. 235 334 #[unsafe(no_mangle)] 236 335 pub unsafe extern "C" fn bindle_entry_name( 237 336 ctx: *const Bindle, ··· 254 353 } 255 354 } 256 355 257 - /// Compact and rewrite bindle file 356 + /// Reclaims space by removing shadowed data. 357 + /// 358 + /// Rebuilds the archive with only live entries. 258 359 #[unsafe(no_mangle)] 259 360 pub unsafe extern "C" fn bindle_vacuum(ctx: *mut Bindle) -> bool { 260 361 if ctx.is_null() { ··· 264 365 b.vacuum().is_ok() 265 366 } 266 367 368 + /// Extracts all entries to a destination directory. 267 369 #[unsafe(no_mangle)] 268 370 pub unsafe extern "C" fn bindle_unpack(ctx: *mut Bindle, dest_path: *const c_char) -> bool { 269 371 if ctx.is_null() || dest_path.is_null() { ··· 274 376 b.unpack(path.as_ref()).is_ok() 275 377 } 276 378 379 + /// Recursively adds all files from a directory to the archive. 380 + /// 381 + /// Call `bindle_save()` to commit changes. 277 382 #[unsafe(no_mangle)] 278 383 pub unsafe extern "C" fn bindle_pack( 279 384 ctx: *mut Bindle, ··· 288 393 b.pack(path.as_ref(), compress).is_ok() 289 394 } 290 395 396 + /// Returns true if an entry with the given name exists. 291 397 #[unsafe(no_mangle)] 292 398 pub unsafe extern "C" fn bindle_exists(ctx: *const Bindle, name: *const c_char) -> bool { 293 399 if ctx.is_null() || name.is_null() { ··· 305 411 b.exists(name_str) 306 412 } 307 413 308 - /// Remove an entry from the index. 309 - /// The data remains in the file until bindle_vacuum is called. 310 - /// Returns true if the entry existed and was removed, false otherwise. 414 + /// Removes an entry from the index. 415 + /// 416 + /// Returns true if the entry existed. Data remains in the file until `bindle_vacuum()` is called. 417 + /// Call `bindle_save()` to commit changes. 311 418 #[unsafe(no_mangle)] 312 419 pub unsafe extern "C" fn bindle_remove(ctx: *mut Bindle, name: *const c_char) -> bool { 313 420 if ctx.is_null() || name.is_null() { ··· 325 432 b.remove(name_str) 326 433 } 327 434 328 - /// Create a new Writer, while the stream is active (until bindle_stream_finish is called), the 329 - /// Bindle struct should not be accessed. 435 + /// Creates a streaming writer for adding an entry. 436 + /// 437 + /// The writer must be closed with `bindle_writer_close()`, then call `bindle_save()` to commit. 438 + /// Do not access the Bindle handle while the writer is active. 330 439 #[unsafe(no_mangle)] 331 440 pub unsafe extern "C" fn bindle_writer_new<'a>( 332 441 ctx: *mut Bindle, ··· 344 453 } 345 454 } 346 455 456 + /// Writes data to the writer. 347 457 #[unsafe(no_mangle)] 348 458 pub unsafe extern "C" fn bindle_writer_write( 349 459 stream: *mut Writer, ··· 357 467 } 358 468 } 359 469 470 + /// Closes the writer and finalizes the entry. 360 471 #[unsafe(no_mangle)] 361 472 pub unsafe extern "C" fn bindle_writer_close(stream: *mut Writer) -> bool { 362 473 let s = unsafe { Box::from_raw(stream) }; 363 474 s.close().is_ok() 364 475 } 365 476 477 + /// Creates a streaming reader for an entry. 478 + /// 479 + /// Automatically decompresses if needed. Must be freed with `bindle_reader_close()`. 480 + /// Call `bindle_reader_verify_crc32()` after reading to verify integrity. 366 481 #[unsafe(no_mangle)] 367 482 pub unsafe extern "C" fn bindle_reader_new<'a>( 368 483 ctx: *const Bindle, ··· 381 496 } 382 497 } 383 498 499 + /// Reads data from the reader into the provided buffer. 500 + /// 501 + /// Returns the number of bytes read, or -1 on error. Returns 0 on EOF. 384 502 #[unsafe(no_mangle)] 385 503 pub unsafe extern "C" fn bindle_reader_read( 386 504 reader: *mut Reader, ··· 413 531 r.verify_crc32().is_ok() 414 532 } 415 533 534 + /// Closes the reader and frees the handle. 416 535 #[unsafe(no_mangle)] 417 536 pub unsafe extern "C" fn bindle_reader_close(reader: *mut Reader) { 418 537 if !reader.is_null() {
+30
test/Makefile
··· 1 + # Makefile for C API tests 2 + 3 + CARGO_TARGET_DIR ?= ../target 4 + RUST_LIB = $(CARGO_TARGET_DIR)/debug/libbindle_file.a 5 + TEST_BINARY = runtest 6 + 7 + # Detect OS 8 + UNAME_S := $(shell uname -s) 9 + 10 + ifeq ($(UNAME_S),Darwin) 11 + LDFLAGS = -framework Security -lSystem -lresolv -lc -lm 12 + else 13 + LDFLAGS = -lpthread -ldl -lm 14 + endif 15 + 16 + all: test 17 + 18 + $(RUST_LIB): 19 + cd .. && cargo build 20 + 21 + $(TEST_BINARY): test.c $(RUST_LIB) 22 + $(CC) -o $(TEST_BINARY) test.c $(RUST_LIB) $(LDFLAGS) 23 + 24 + test: $(TEST_BINARY) 25 + ./$(TEST_BINARY) 26 + 27 + clean: 28 + rm -f $(TEST_BINARY) *.bndl 29 + 30 + .PHONY: all test clean
+1266
test/greatest.h
··· 1 + /* 2 + * Copyright (c) 2011-2021 Scott Vokes <vokes.s@gmail.com> 3 + * 4 + * Permission to use, copy, modify, and/or distribute this software for any 5 + * purpose with or without fee is hereby granted, provided that the above 6 + * copyright notice and this permission notice appear in all copies. 7 + * 8 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 + */ 16 + 17 + #ifndef GREATEST_H 18 + #define GREATEST_H 19 + 20 + #if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) 21 + extern "C" { 22 + #endif 23 + 24 + /* 1.5.0 */ 25 + #define GREATEST_VERSION_MAJOR 1 26 + #define GREATEST_VERSION_MINOR 5 27 + #define GREATEST_VERSION_PATCH 0 28 + 29 + /* A unit testing system for C, contained in 1 file. 30 + * It doesn't use dynamic allocation or depend on anything 31 + * beyond ANSI C89. 32 + * 33 + * An up-to-date version can be found at: 34 + * https://github.com/silentbicycle/greatest/ 35 + */ 36 + 37 + 38 + /********************************************************************* 39 + * Minimal test runner template 40 + *********************************************************************/ 41 + #if 0 42 + 43 + #include "greatest.h" 44 + 45 + TEST foo_should_foo(void) { 46 + PASS(); 47 + } 48 + 49 + static void setup_cb(void *data) { 50 + printf("setup callback for each test case\n"); 51 + } 52 + 53 + static void teardown_cb(void *data) { 54 + printf("teardown callback for each test case\n"); 55 + } 56 + 57 + SUITE(suite) { 58 + /* Optional setup/teardown callbacks which will be run before/after 59 + * every test case. If using a test suite, they will be cleared when 60 + * the suite finishes. */ 61 + SET_SETUP(setup_cb, voidp_to_callback_data); 62 + SET_TEARDOWN(teardown_cb, voidp_to_callback_data); 63 + 64 + RUN_TEST(foo_should_foo); 65 + } 66 + 67 + /* Add definitions that need to be in the test runner's main file. */ 68 + GREATEST_MAIN_DEFS(); 69 + 70 + /* Set up, run suite(s) of tests, report pass/fail/skip stats. */ 71 + int run_tests(void) { 72 + GREATEST_INIT(); /* init. greatest internals */ 73 + /* List of suites to run (if any). */ 74 + RUN_SUITE(suite); 75 + 76 + /* Tests can also be run directly, without using test suites. */ 77 + RUN_TEST(foo_should_foo); 78 + 79 + GREATEST_PRINT_REPORT(); /* display results */ 80 + return greatest_all_passed(); 81 + } 82 + 83 + /* main(), for a standalone command-line test runner. 84 + * This replaces run_tests above, and adds command line option 85 + * handling and exiting with a pass/fail status. */ 86 + int main(int argc, char **argv) { 87 + GREATEST_MAIN_BEGIN(); /* init & parse command-line args */ 88 + RUN_SUITE(suite); 89 + GREATEST_MAIN_END(); /* display results */ 90 + } 91 + 92 + #endif 93 + /*********************************************************************/ 94 + 95 + 96 + #include <stdlib.h> 97 + #include <stdio.h> 98 + #include <string.h> 99 + #include <ctype.h> 100 + 101 + /*********** 102 + * Options * 103 + ***********/ 104 + 105 + /* Default column width for non-verbose output. */ 106 + #ifndef GREATEST_DEFAULT_WIDTH 107 + #define GREATEST_DEFAULT_WIDTH 72 108 + #endif 109 + 110 + /* FILE *, for test logging. */ 111 + #ifndef GREATEST_STDOUT 112 + #define GREATEST_STDOUT stdout 113 + #endif 114 + 115 + /* Remove GREATEST_ prefix from most commonly used symbols? */ 116 + #ifndef GREATEST_USE_ABBREVS 117 + #define GREATEST_USE_ABBREVS 1 118 + #endif 119 + 120 + /* Set to 0 to disable all use of setjmp/longjmp. */ 121 + #ifndef GREATEST_USE_LONGJMP 122 + #define GREATEST_USE_LONGJMP 0 123 + #endif 124 + 125 + /* Make it possible to replace fprintf with another 126 + * function with the same interface. */ 127 + #ifndef GREATEST_FPRINTF 128 + #define GREATEST_FPRINTF fprintf 129 + #endif 130 + 131 + #if GREATEST_USE_LONGJMP 132 + #include <setjmp.h> 133 + #endif 134 + 135 + /* Set to 0 to disable all use of time.h / clock(). */ 136 + #ifndef GREATEST_USE_TIME 137 + #define GREATEST_USE_TIME 1 138 + #endif 139 + 140 + #if GREATEST_USE_TIME 141 + #include <time.h> 142 + #endif 143 + 144 + /* Floating point type, for ASSERT_IN_RANGE. */ 145 + #ifndef GREATEST_FLOAT 146 + #define GREATEST_FLOAT double 147 + #define GREATEST_FLOAT_FMT "%g" 148 + #endif 149 + 150 + /* Size of buffer for test name + optional '_' separator and suffix */ 151 + #ifndef GREATEST_TESTNAME_BUF_SIZE 152 + #define GREATEST_TESTNAME_BUF_SIZE 128 153 + #endif 154 + 155 + 156 + /********* 157 + * Types * 158 + *********/ 159 + 160 + /* Info for the current running suite. */ 161 + typedef struct greatest_suite_info { 162 + unsigned int tests_run; 163 + unsigned int passed; 164 + unsigned int failed; 165 + unsigned int skipped; 166 + 167 + #if GREATEST_USE_TIME 168 + /* timers, pre/post running suite and individual tests */ 169 + clock_t pre_suite; 170 + clock_t post_suite; 171 + clock_t pre_test; 172 + clock_t post_test; 173 + #endif 174 + } greatest_suite_info; 175 + 176 + /* Type for a suite function. */ 177 + typedef void greatest_suite_cb(void); 178 + 179 + /* Types for setup/teardown callbacks. If non-NULL, these will be run 180 + * and passed the pointer to their additional data. */ 181 + typedef void greatest_setup_cb(void *udata); 182 + typedef void greatest_teardown_cb(void *udata); 183 + 184 + /* Type for an equality comparison between two pointers of the same type. 185 + * Should return non-0 if equal, otherwise 0. 186 + * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 187 + typedef int greatest_equal_cb(const void *expd, const void *got, void *udata); 188 + 189 + /* Type for a callback that prints a value pointed to by T. 190 + * Return value has the same meaning as printf's. 191 + * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 192 + typedef int greatest_printf_cb(const void *t, void *udata); 193 + 194 + /* Callbacks for an arbitrary type; needed for type-specific 195 + * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/ 196 + typedef struct greatest_type_info { 197 + greatest_equal_cb *equal; 198 + greatest_printf_cb *print; 199 + } greatest_type_info; 200 + 201 + typedef struct greatest_memory_cmp_env { 202 + const unsigned char *exp; 203 + const unsigned char *got; 204 + size_t size; 205 + } greatest_memory_cmp_env; 206 + 207 + /* Callbacks for string and raw memory types. */ 208 + extern greatest_type_info greatest_type_info_string; 209 + extern greatest_type_info greatest_type_info_memory; 210 + 211 + typedef enum { 212 + GREATEST_FLAG_FIRST_FAIL = 0x01, 213 + GREATEST_FLAG_LIST_ONLY = 0x02, 214 + GREATEST_FLAG_ABORT_ON_FAIL = 0x04 215 + } greatest_flag_t; 216 + 217 + /* Internal state for a PRNG, used to shuffle test order. */ 218 + struct greatest_prng { 219 + unsigned char random_order; /* use random ordering? */ 220 + unsigned char initialized; /* is random ordering initialized? */ 221 + unsigned char pad_0[6]; 222 + unsigned long state; /* PRNG state */ 223 + unsigned long count; /* how many tests, this pass */ 224 + unsigned long count_ceil; /* total number of tests */ 225 + unsigned long count_run; /* total tests run */ 226 + unsigned long a; /* LCG multiplier */ 227 + unsigned long c; /* LCG increment */ 228 + unsigned long m; /* LCG modulus, based on count_ceil */ 229 + }; 230 + 231 + /* Struct containing all test runner state. */ 232 + typedef struct greatest_run_info { 233 + unsigned char flags; 234 + unsigned char verbosity; 235 + unsigned char running_test; /* guard for nested RUN_TEST calls */ 236 + unsigned char exact_name_match; 237 + 238 + unsigned int tests_run; /* total test count */ 239 + 240 + /* currently running test suite */ 241 + greatest_suite_info suite; 242 + 243 + /* overall pass/fail/skip counts */ 244 + unsigned int passed; 245 + unsigned int failed; 246 + unsigned int skipped; 247 + unsigned int assertions; 248 + 249 + /* info to print about the most recent failure */ 250 + unsigned int fail_line; 251 + unsigned int pad_1; 252 + const char *fail_file; 253 + const char *msg; 254 + 255 + /* current setup/teardown hooks and userdata */ 256 + greatest_setup_cb *setup; 257 + void *setup_udata; 258 + greatest_teardown_cb *teardown; 259 + void *teardown_udata; 260 + 261 + /* formatting info for ".....s...F"-style output */ 262 + unsigned int col; 263 + unsigned int width; 264 + 265 + /* only run a specific suite or test */ 266 + const char *suite_filter; 267 + const char *test_filter; 268 + const char *test_exclude; 269 + const char *name_suffix; /* print suffix with test name */ 270 + char name_buf[GREATEST_TESTNAME_BUF_SIZE]; 271 + 272 + struct greatest_prng prng[2]; /* 0: suites, 1: tests */ 273 + 274 + #if GREATEST_USE_TIME 275 + /* overall timers */ 276 + clock_t begin; 277 + clock_t end; 278 + #endif 279 + 280 + #if GREATEST_USE_LONGJMP 281 + int pad_jmp_buf; 282 + unsigned char pad_2[4]; 283 + jmp_buf jump_dest; 284 + #endif 285 + } greatest_run_info; 286 + 287 + struct greatest_report_t { 288 + /* overall pass/fail/skip counts */ 289 + unsigned int passed; 290 + unsigned int failed; 291 + unsigned int skipped; 292 + unsigned int assertions; 293 + }; 294 + 295 + /* Global var for the current testing context. 296 + * Initialized by GREATEST_MAIN_DEFS(). */ 297 + extern greatest_run_info greatest_info; 298 + 299 + /* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */ 300 + typedef const char *greatest_enum_str_fun(int value); 301 + 302 + 303 + /********************** 304 + * Exported functions * 305 + **********************/ 306 + 307 + /* These are used internally by greatest macros. */ 308 + int greatest_test_pre(const char *name); 309 + void greatest_test_post(int res); 310 + int greatest_do_assert_equal_t(const void *expd, const void *got, 311 + greatest_type_info *type_info, void *udata); 312 + void greatest_prng_init_first_pass(int id); 313 + int greatest_prng_init_second_pass(int id, unsigned long seed); 314 + void greatest_prng_step(int id); 315 + 316 + /* These are part of the public greatest API. */ 317 + void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); 318 + void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); 319 + void GREATEST_INIT(void); 320 + void GREATEST_PRINT_REPORT(void); 321 + int greatest_all_passed(void); 322 + void greatest_set_suite_filter(const char *filter); 323 + void greatest_set_test_filter(const char *filter); 324 + void greatest_set_test_exclude(const char *filter); 325 + void greatest_set_exact_name_match(void); 326 + void greatest_stop_at_first_fail(void); 327 + void greatest_abort_on_fail(void); 328 + void greatest_list_only(void); 329 + void greatest_get_report(struct greatest_report_t *report); 330 + unsigned int greatest_get_verbosity(void); 331 + void greatest_set_verbosity(unsigned int verbosity); 332 + void greatest_set_flag(greatest_flag_t flag); 333 + void greatest_set_test_suffix(const char *suffix); 334 + 335 + 336 + /******************** 337 + * Language Support * 338 + ********************/ 339 + 340 + /* If __VA_ARGS__ (C99) is supported, allow parametric testing 341 + * without needing to manually manage the argument struct. */ 342 + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19901L) || \ 343 + (defined(_MSC_VER) && _MSC_VER >= 1800) 344 + #define GREATEST_VA_ARGS 345 + #endif 346 + 347 + 348 + /********** 349 + * Macros * 350 + **********/ 351 + 352 + /* Define a suite. (The duplication is intentional -- it eliminates 353 + * a warning from -Wmissing-declarations.) */ 354 + #define GREATEST_SUITE(NAME) void NAME(void); void NAME(void) 355 + 356 + /* Declare a suite, provided by another compilation unit. */ 357 + #define GREATEST_SUITE_EXTERN(NAME) void NAME(void) 358 + 359 + /* Start defining a test function. 360 + * The arguments are not included, to allow parametric testing. */ 361 + #define GREATEST_TEST static enum greatest_test_res 362 + 363 + /* PASS/FAIL/SKIP result from a test. Used internally. */ 364 + typedef enum greatest_test_res { 365 + GREATEST_TEST_RES_PASS = 0, 366 + GREATEST_TEST_RES_FAIL = -1, 367 + GREATEST_TEST_RES_SKIP = 1 368 + } greatest_test_res; 369 + 370 + /* Run a suite. */ 371 + #define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) 372 + 373 + /* Run a test in the current suite. */ 374 + #define GREATEST_RUN_TEST(TEST) \ 375 + do { \ 376 + if (greatest_test_pre(#TEST) == 1) { \ 377 + enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 378 + if (res == GREATEST_TEST_RES_PASS) { \ 379 + res = TEST(); \ 380 + } \ 381 + greatest_test_post(res); \ 382 + } \ 383 + } while (0) 384 + 385 + /* Ignore a test, don't warn about it being unused. */ 386 + #define GREATEST_IGNORE_TEST(TEST) (void)TEST 387 + 388 + /* Run a test in the current suite with one void * argument, 389 + * which can be a pointer to a struct with multiple arguments. */ 390 + #define GREATEST_RUN_TEST1(TEST, ENV) \ 391 + do { \ 392 + if (greatest_test_pre(#TEST) == 1) { \ 393 + enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 394 + if (res == GREATEST_TEST_RES_PASS) { \ 395 + res = TEST(ENV); \ 396 + } \ 397 + greatest_test_post(res); \ 398 + } \ 399 + } while (0) 400 + 401 + #ifdef GREATEST_VA_ARGS 402 + #define GREATEST_RUN_TESTp(TEST, ...) \ 403 + do { \ 404 + if (greatest_test_pre(#TEST) == 1) { \ 405 + enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 406 + if (res == GREATEST_TEST_RES_PASS) { \ 407 + res = TEST(__VA_ARGS__); \ 408 + } \ 409 + greatest_test_post(res); \ 410 + } \ 411 + } while (0) 412 + #endif 413 + 414 + 415 + /* Check if the test runner is in verbose mode. */ 416 + #define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0) 417 + #define GREATEST_LIST_ONLY() \ 418 + (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) 419 + #define GREATEST_FIRST_FAIL() \ 420 + (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) 421 + #define GREATEST_ABORT_ON_FAIL() \ 422 + (greatest_info.flags & GREATEST_FLAG_ABORT_ON_FAIL) 423 + #define GREATEST_FAILURE_ABORT() \ 424 + (GREATEST_FIRST_FAIL() && \ 425 + (greatest_info.suite.failed > 0 || greatest_info.failed > 0)) 426 + 427 + /* Message-less forms of tests defined below. */ 428 + #define GREATEST_PASS() GREATEST_PASSm(NULL) 429 + #define GREATEST_FAIL() GREATEST_FAILm(NULL) 430 + #define GREATEST_SKIP() GREATEST_SKIPm(NULL) 431 + #define GREATEST_ASSERT(COND) \ 432 + GREATEST_ASSERTm(#COND, COND) 433 + #define GREATEST_ASSERT_OR_LONGJMP(COND) \ 434 + GREATEST_ASSERT_OR_LONGJMPm(#COND, COND) 435 + #define GREATEST_ASSERT_FALSE(COND) \ 436 + GREATEST_ASSERT_FALSEm(#COND, COND) 437 + #define GREATEST_ASSERT_EQ(EXP, GOT) \ 438 + GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) 439 + #define GREATEST_ASSERT_NEQ(EXP, GOT) \ 440 + GREATEST_ASSERT_NEQm(#EXP " == " #GOT, EXP, GOT) 441 + #define GREATEST_ASSERT_GT(EXP, GOT) \ 442 + GREATEST_ASSERT_GTm(#EXP " <= " #GOT, EXP, GOT) 443 + #define GREATEST_ASSERT_GTE(EXP, GOT) \ 444 + GREATEST_ASSERT_GTEm(#EXP " < " #GOT, EXP, GOT) 445 + #define GREATEST_ASSERT_LT(EXP, GOT) \ 446 + GREATEST_ASSERT_LTm(#EXP " >= " #GOT, EXP, GOT) 447 + #define GREATEST_ASSERT_LTE(EXP, GOT) \ 448 + GREATEST_ASSERT_LTEm(#EXP " > " #GOT, EXP, GOT) 449 + #define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \ 450 + GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT) 451 + #define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \ 452 + GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL) 453 + #define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \ 454 + GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA) 455 + #define GREATEST_ASSERT_STR_EQ(EXP, GOT) \ 456 + GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) 457 + #define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \ 458 + GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) 459 + #define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \ 460 + GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) 461 + #define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \ 462 + GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR) 463 + 464 + /* The following forms take an additional message argument first, 465 + * to be displayed by the test runner. */ 466 + 467 + /* Fail if a condition is not true, with message. */ 468 + #define GREATEST_ASSERTm(MSG, COND) \ 469 + do { \ 470 + greatest_info.assertions++; \ 471 + if (!(COND)) { GREATEST_FAILm(MSG); } \ 472 + } while (0) 473 + 474 + /* Fail if a condition is not true, longjmping out of test. */ 475 + #define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \ 476 + do { \ 477 + greatest_info.assertions++; \ 478 + if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \ 479 + } while (0) 480 + 481 + /* Fail if a condition is not false, with message. */ 482 + #define GREATEST_ASSERT_FALSEm(MSG, COND) \ 483 + do { \ 484 + greatest_info.assertions++; \ 485 + if ((COND)) { GREATEST_FAILm(MSG); } \ 486 + } while (0) 487 + 488 + /* Internal macro for relational assertions */ 489 + #define GREATEST__REL(REL, MSG, EXP, GOT) \ 490 + do { \ 491 + greatest_info.assertions++; \ 492 + if (!((EXP) REL (GOT))) { GREATEST_FAILm(MSG); } \ 493 + } while (0) 494 + 495 + /* Fail if EXP is not ==, !=, >, <, >=, or <= to GOT. */ 496 + #define GREATEST_ASSERT_EQm(MSG,E,G) GREATEST__REL(==, MSG,E,G) 497 + #define GREATEST_ASSERT_NEQm(MSG,E,G) GREATEST__REL(!=, MSG,E,G) 498 + #define GREATEST_ASSERT_GTm(MSG,E,G) GREATEST__REL(>, MSG,E,G) 499 + #define GREATEST_ASSERT_GTEm(MSG,E,G) GREATEST__REL(>=, MSG,E,G) 500 + #define GREATEST_ASSERT_LTm(MSG,E,G) GREATEST__REL(<, MSG,E,G) 501 + #define GREATEST_ASSERT_LTEm(MSG,E,G) GREATEST__REL(<=, MSG,E,G) 502 + 503 + /* Fail if EXP != GOT (equality comparison by ==). 504 + * Warning: FMT, EXP, and GOT will be evaluated more 505 + * than once on failure. */ 506 + #define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \ 507 + do { \ 508 + greatest_info.assertions++; \ 509 + if ((EXP) != (GOT)) { \ 510 + GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ 511 + GREATEST_FPRINTF(GREATEST_STDOUT, FMT, EXP); \ 512 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ 513 + GREATEST_FPRINTF(GREATEST_STDOUT, FMT, GOT); \ 514 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 515 + GREATEST_FAILm(MSG); \ 516 + } \ 517 + } while (0) 518 + 519 + /* Fail if EXP is not equal to GOT, printing enum IDs. */ 520 + #define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \ 521 + do { \ 522 + int greatest_EXP = (int)(EXP); \ 523 + int greatest_GOT = (int)(GOT); \ 524 + greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \ 525 + if (greatest_EXP != greatest_GOT) { \ 526 + GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: %s", \ 527 + greatest_ENUM_STR(greatest_EXP)); \ 528 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: %s\n", \ 529 + greatest_ENUM_STR(greatest_GOT)); \ 530 + GREATEST_FAILm(MSG); \ 531 + } \ 532 + } while (0) \ 533 + 534 + /* Fail if GOT not in range of EXP +|- TOL. */ 535 + #define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \ 536 + do { \ 537 + GREATEST_FLOAT greatest_EXP = (EXP); \ 538 + GREATEST_FLOAT greatest_GOT = (GOT); \ 539 + GREATEST_FLOAT greatest_TOL = (TOL); \ 540 + greatest_info.assertions++; \ 541 + if ((greatest_EXP > greatest_GOT && \ 542 + greatest_EXP - greatest_GOT > greatest_TOL) || \ 543 + (greatest_EXP < greatest_GOT && \ 544 + greatest_GOT - greatest_EXP > greatest_TOL)) { \ 545 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 546 + "\nExpected: " GREATEST_FLOAT_FMT \ 547 + " +/- " GREATEST_FLOAT_FMT \ 548 + "\n Got: " GREATEST_FLOAT_FMT \ 549 + "\n", \ 550 + greatest_EXP, greatest_TOL, greatest_GOT); \ 551 + GREATEST_FAILm(MSG); \ 552 + } \ 553 + } while (0) 554 + 555 + /* Fail if EXP is not equal to GOT, according to strcmp. */ 556 + #define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ 557 + do { \ 558 + GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 559 + &greatest_type_info_string, NULL); \ 560 + } while (0) \ 561 + 562 + /* Fail if EXP is not equal to GOT, according to strncmp. */ 563 + #define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \ 564 + do { \ 565 + size_t size = SIZE; \ 566 + GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 567 + &greatest_type_info_string, &size); \ 568 + } while (0) \ 569 + 570 + /* Fail if EXP is not equal to GOT, according to memcmp. */ 571 + #define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \ 572 + do { \ 573 + greatest_memory_cmp_env env; \ 574 + env.exp = (const unsigned char *)EXP; \ 575 + env.got = (const unsigned char *)GOT; \ 576 + env.size = SIZE; \ 577 + GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \ 578 + &greatest_type_info_memory, &env); \ 579 + } while (0) \ 580 + 581 + /* Fail if EXP is not equal to GOT, according to a comparison 582 + * callback in TYPE_INFO. If they are not equal, optionally use a 583 + * print callback in TYPE_INFO to print them. */ 584 + #define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \ 585 + do { \ 586 + greatest_type_info *type_info = (TYPE_INFO); \ 587 + greatest_info.assertions++; \ 588 + if (!greatest_do_assert_equal_t(EXP, GOT, \ 589 + type_info, UDATA)) { \ 590 + if (type_info == NULL || type_info->equal == NULL) { \ 591 + GREATEST_FAILm("type_info->equal callback missing!"); \ 592 + } else { \ 593 + GREATEST_FAILm(MSG); \ 594 + } \ 595 + } \ 596 + } while (0) \ 597 + 598 + /* Pass. */ 599 + #define GREATEST_PASSm(MSG) \ 600 + do { \ 601 + greatest_info.msg = MSG; \ 602 + return GREATEST_TEST_RES_PASS; \ 603 + } while (0) 604 + 605 + /* Fail. */ 606 + #define GREATEST_FAILm(MSG) \ 607 + do { \ 608 + greatest_info.fail_file = __FILE__; \ 609 + greatest_info.fail_line = __LINE__; \ 610 + greatest_info.msg = MSG; \ 611 + if (GREATEST_ABORT_ON_FAIL()) { abort(); } \ 612 + return GREATEST_TEST_RES_FAIL; \ 613 + } while (0) 614 + 615 + /* Optional GREATEST_FAILm variant that longjmps. */ 616 + #if GREATEST_USE_LONGJMP 617 + #define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL) 618 + #define GREATEST_FAIL_WITH_LONGJMPm(MSG) \ 619 + do { \ 620 + greatest_info.fail_file = __FILE__; \ 621 + greatest_info.fail_line = __LINE__; \ 622 + greatest_info.msg = MSG; \ 623 + longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \ 624 + } while (0) 625 + #endif 626 + 627 + /* Skip the current test. */ 628 + #define GREATEST_SKIPm(MSG) \ 629 + do { \ 630 + greatest_info.msg = MSG; \ 631 + return GREATEST_TEST_RES_SKIP; \ 632 + } while (0) 633 + 634 + /* Check the result of a subfunction using ASSERT, etc. */ 635 + #define GREATEST_CHECK_CALL(RES) \ 636 + do { \ 637 + enum greatest_test_res greatest_RES = RES; \ 638 + if (greatest_RES != GREATEST_TEST_RES_PASS) { \ 639 + return greatest_RES; \ 640 + } \ 641 + } while (0) \ 642 + 643 + #if GREATEST_USE_TIME 644 + #define GREATEST_SET_TIME(NAME) \ 645 + NAME = clock(); \ 646 + if (NAME == (clock_t) -1) { \ 647 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 648 + "clock error: %s\n", #NAME); \ 649 + exit(EXIT_FAILURE); \ 650 + } 651 + 652 + #define GREATEST_CLOCK_DIFF(C1, C2) \ 653 + GREATEST_FPRINTF(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ 654 + (long unsigned int) (C2) - (long unsigned int)(C1), \ 655 + (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) 656 + #else 657 + #define GREATEST_SET_TIME(UNUSED) 658 + #define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2) 659 + #endif 660 + 661 + #if GREATEST_USE_LONGJMP 662 + #define GREATEST_SAVE_CONTEXT() \ 663 + /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call * \ 664 + * so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \ 665 + ((enum greatest_test_res)(setjmp(greatest_info.jump_dest))) 666 + #else 667 + #define GREATEST_SAVE_CONTEXT() \ 668 + /*a no-op, since setjmp/longjmp aren't being used */ \ 669 + GREATEST_TEST_RES_PASS 670 + #endif 671 + 672 + /* Run every suite / test function run within BODY in pseudo-random 673 + * order, seeded by SEED. (The top 3 bits of the seed are ignored.) 674 + * 675 + * This should be called like: 676 + * GREATEST_SHUFFLE_TESTS(seed, { 677 + * GREATEST_RUN_TEST(some_test); 678 + * GREATEST_RUN_TEST(some_other_test); 679 + * GREATEST_RUN_TEST(yet_another_test); 680 + * }); 681 + * 682 + * Note that the body of the second argument will be evaluated 683 + * multiple times. */ 684 + #define GREATEST_SHUFFLE_SUITES(SD, BODY) GREATEST_SHUFFLE(0, SD, BODY) 685 + #define GREATEST_SHUFFLE_TESTS(SD, BODY) GREATEST_SHUFFLE(1, SD, BODY) 686 + #define GREATEST_SHUFFLE(ID, SD, BODY) \ 687 + do { \ 688 + struct greatest_prng *prng = &greatest_info.prng[ID]; \ 689 + greatest_prng_init_first_pass(ID); \ 690 + do { \ 691 + prng->count = 0; \ 692 + if (prng->initialized) { greatest_prng_step(ID); } \ 693 + BODY; \ 694 + if (!prng->initialized) { \ 695 + if (!greatest_prng_init_second_pass(ID, SD)) { break; } \ 696 + } else if (prng->count_run == prng->count_ceil) { \ 697 + break; \ 698 + } \ 699 + } while (!GREATEST_FAILURE_ABORT()); \ 700 + prng->count_run = prng->random_order = prng->initialized = 0; \ 701 + } while(0) 702 + 703 + /* Include several function definitions in the main test file. */ 704 + #define GREATEST_MAIN_DEFS() \ 705 + \ 706 + /* Is FILTER a subset of NAME? */ \ 707 + static int greatest_name_match(const char *name, const char *filter, \ 708 + int res_if_none) { \ 709 + size_t offset = 0; \ 710 + size_t filter_len = filter ? strlen(filter) : 0; \ 711 + if (filter_len == 0) { return res_if_none; } /* no filter */ \ 712 + if (greatest_info.exact_name_match && strlen(name) != filter_len) { \ 713 + return 0; /* ignore substring matches */ \ 714 + } \ 715 + while (name[offset] != '\0') { \ 716 + if (name[offset] == filter[0]) { \ 717 + if (0 == strncmp(&name[offset], filter, filter_len)) { \ 718 + return 1; \ 719 + } \ 720 + } \ 721 + offset++; \ 722 + } \ 723 + \ 724 + return 0; \ 725 + } \ 726 + \ 727 + static void greatest_buffer_test_name(const char *name) { \ 728 + struct greatest_run_info *g = &greatest_info; \ 729 + size_t len = strlen(name), size = sizeof(g->name_buf); \ 730 + memset(g->name_buf, 0x00, size); \ 731 + (void)strncat(g->name_buf, name, size - 1); \ 732 + if (g->name_suffix && (len + 1 < size)) { \ 733 + g->name_buf[len] = '_'; \ 734 + strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \ 735 + } \ 736 + } \ 737 + \ 738 + /* Before running a test, check the name filtering and \ 739 + * test shuffling state, if applicable, and then call setup hooks. */ \ 740 + int greatest_test_pre(const char *name) { \ 741 + struct greatest_run_info *g = &greatest_info; \ 742 + int match; \ 743 + greatest_buffer_test_name(name); \ 744 + match = greatest_name_match(g->name_buf, g->test_filter, 1) && \ 745 + !greatest_name_match(g->name_buf, g->test_exclude, 0); \ 746 + if (GREATEST_LIST_ONLY()) { /* just listing test names */ \ 747 + if (match) { \ 748 + GREATEST_FPRINTF(GREATEST_STDOUT, " %s\n", g->name_buf); \ 749 + } \ 750 + goto clear; \ 751 + } \ 752 + if (match && (!GREATEST_FIRST_FAIL() || g->suite.failed == 0)) { \ 753 + struct greatest_prng *p = &g->prng[1]; \ 754 + if (p->random_order) { \ 755 + p->count++; \ 756 + if (!p->initialized || ((p->count - 1) != p->state)) { \ 757 + goto clear; /* don't run this test yet */ \ 758 + } \ 759 + } \ 760 + if (g->running_test) { \ 761 + fprintf(stderr, "Error: Test run inside another test.\n"); \ 762 + return 0; \ 763 + } \ 764 + GREATEST_SET_TIME(g->suite.pre_test); \ 765 + if (g->setup) { g->setup(g->setup_udata); } \ 766 + p->count_run++; \ 767 + g->running_test = 1; \ 768 + return 1; /* test should be run */ \ 769 + } else { \ 770 + goto clear; /* skipped */ \ 771 + } \ 772 + clear: \ 773 + g->name_suffix = NULL; \ 774 + return 0; \ 775 + } \ 776 + \ 777 + static void greatest_do_pass(void) { \ 778 + struct greatest_run_info *g = &greatest_info; \ 779 + if (GREATEST_IS_VERBOSE()) { \ 780 + GREATEST_FPRINTF(GREATEST_STDOUT, "PASS %s: %s", \ 781 + g->name_buf, g->msg ? g->msg : ""); \ 782 + } else { \ 783 + GREATEST_FPRINTF(GREATEST_STDOUT, "."); \ 784 + } \ 785 + g->suite.passed++; \ 786 + } \ 787 + \ 788 + static void greatest_do_fail(void) { \ 789 + struct greatest_run_info *g = &greatest_info; \ 790 + if (GREATEST_IS_VERBOSE()) { \ 791 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 792 + "FAIL %s: %s (%s:%u)", g->name_buf, \ 793 + g->msg ? g->msg : "", g->fail_file, g->fail_line); \ 794 + } else { \ 795 + GREATEST_FPRINTF(GREATEST_STDOUT, "F"); \ 796 + g->col++; /* add linebreak if in line of '.'s */ \ 797 + if (g->col != 0) { \ 798 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 799 + g->col = 0; \ 800 + } \ 801 + GREATEST_FPRINTF(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ 802 + g->name_buf, g->msg ? g->msg : "", \ 803 + g->fail_file, g->fail_line); \ 804 + } \ 805 + g->suite.failed++; \ 806 + } \ 807 + \ 808 + static void greatest_do_skip(void) { \ 809 + struct greatest_run_info *g = &greatest_info; \ 810 + if (GREATEST_IS_VERBOSE()) { \ 811 + GREATEST_FPRINTF(GREATEST_STDOUT, "SKIP %s: %s", \ 812 + g->name_buf, g->msg ? g->msg : ""); \ 813 + } else { \ 814 + GREATEST_FPRINTF(GREATEST_STDOUT, "s"); \ 815 + } \ 816 + g->suite.skipped++; \ 817 + } \ 818 + \ 819 + void greatest_test_post(int res) { \ 820 + GREATEST_SET_TIME(greatest_info.suite.post_test); \ 821 + if (greatest_info.teardown) { \ 822 + void *udata = greatest_info.teardown_udata; \ 823 + greatest_info.teardown(udata); \ 824 + } \ 825 + \ 826 + greatest_info.running_test = 0; \ 827 + if (res <= GREATEST_TEST_RES_FAIL) { \ 828 + greatest_do_fail(); \ 829 + } else if (res >= GREATEST_TEST_RES_SKIP) { \ 830 + greatest_do_skip(); \ 831 + } else if (res == GREATEST_TEST_RES_PASS) { \ 832 + greatest_do_pass(); \ 833 + } \ 834 + greatest_info.name_suffix = NULL; \ 835 + greatest_info.suite.tests_run++; \ 836 + greatest_info.col++; \ 837 + if (GREATEST_IS_VERBOSE()) { \ 838 + GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ 839 + greatest_info.suite.post_test); \ 840 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 841 + } else if (greatest_info.col % greatest_info.width == 0) { \ 842 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 843 + greatest_info.col = 0; \ 844 + } \ 845 + fflush(GREATEST_STDOUT); \ 846 + } \ 847 + \ 848 + static void report_suite(void) { \ 849 + if (greatest_info.suite.tests_run > 0) { \ 850 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 851 + "\n%u test%s - %u passed, %u failed, %u skipped", \ 852 + greatest_info.suite.tests_run, \ 853 + greatest_info.suite.tests_run == 1 ? "" : "s", \ 854 + greatest_info.suite.passed, \ 855 + greatest_info.suite.failed, \ 856 + greatest_info.suite.skipped); \ 857 + GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ 858 + greatest_info.suite.post_suite); \ 859 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 860 + } \ 861 + } \ 862 + \ 863 + static void update_counts_and_reset_suite(void) { \ 864 + greatest_info.setup = NULL; \ 865 + greatest_info.setup_udata = NULL; \ 866 + greatest_info.teardown = NULL; \ 867 + greatest_info.teardown_udata = NULL; \ 868 + greatest_info.passed += greatest_info.suite.passed; \ 869 + greatest_info.failed += greatest_info.suite.failed; \ 870 + greatest_info.skipped += greatest_info.suite.skipped; \ 871 + greatest_info.tests_run += greatest_info.suite.tests_run; \ 872 + memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \ 873 + greatest_info.col = 0; \ 874 + } \ 875 + \ 876 + static int greatest_suite_pre(const char *suite_name) { \ 877 + struct greatest_prng *p = &greatest_info.prng[0]; \ 878 + if (!greatest_name_match(suite_name, greatest_info.suite_filter, 1) \ 879 + || (GREATEST_FAILURE_ABORT())) { return 0; } \ 880 + if (p->random_order) { \ 881 + p->count++; \ 882 + if (!p->initialized || ((p->count - 1) != p->state)) { \ 883 + return 0; /* don't run this suite yet */ \ 884 + } \ 885 + } \ 886 + p->count_run++; \ 887 + update_counts_and_reset_suite(); \ 888 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ 889 + GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ 890 + return 1; \ 891 + } \ 892 + \ 893 + static void greatest_suite_post(void) { \ 894 + GREATEST_SET_TIME(greatest_info.suite.post_suite); \ 895 + report_suite(); \ 896 + } \ 897 + \ 898 + static void greatest_run_suite(greatest_suite_cb *suite_cb, \ 899 + const char *suite_name) { \ 900 + if (greatest_suite_pre(suite_name)) { \ 901 + suite_cb(); \ 902 + greatest_suite_post(); \ 903 + } \ 904 + } \ 905 + \ 906 + int greatest_do_assert_equal_t(const void *expd, const void *got, \ 907 + greatest_type_info *type_info, void *udata) { \ 908 + int eq = 0; \ 909 + if (type_info == NULL || type_info->equal == NULL) { return 0; } \ 910 + eq = type_info->equal(expd, got, udata); \ 911 + if (!eq) { \ 912 + if (type_info->print != NULL) { \ 913 + GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ 914 + (void)type_info->print(expd, udata); \ 915 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ 916 + (void)type_info->print(got, udata); \ 917 + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ 918 + } \ 919 + } \ 920 + return eq; \ 921 + } \ 922 + \ 923 + static void greatest_usage(const char *name) { \ 924 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 925 + "Usage: %s [-hlfavex] [-s SUITE] [-t TEST] [-x EXCLUDE]\n" \ 926 + " -h, --help print this Help\n" \ 927 + " -l List suites and tests, then exit (dry run)\n" \ 928 + " -f Stop runner after first failure\n" \ 929 + " -a Abort on first failure (implies -f)\n" \ 930 + " -v Verbose output\n" \ 931 + " -s SUITE only run suites containing substring SUITE\n" \ 932 + " -t TEST only run tests containing substring TEST\n" \ 933 + " -e only run exact name match for -s or -t\n" \ 934 + " -x EXCLUDE exclude tests containing substring EXCLUDE\n", \ 935 + name); \ 936 + } \ 937 + \ 938 + static void greatest_parse_options(int argc, char **argv) { \ 939 + int i = 0; \ 940 + for (i = 1; i < argc; i++) { \ 941 + if (argv[i][0] == '-') { \ 942 + char f = argv[i][1]; \ 943 + if ((f == 's' || f == 't' || f == 'x') && argc <= i + 1) { \ 944 + greatest_usage(argv[0]); exit(EXIT_FAILURE); \ 945 + } \ 946 + switch (f) { \ 947 + case 's': /* suite name filter */ \ 948 + greatest_set_suite_filter(argv[i + 1]); i++; break; \ 949 + case 't': /* test name filter */ \ 950 + greatest_set_test_filter(argv[i + 1]); i++; break; \ 951 + case 'x': /* test name exclusion */ \ 952 + greatest_set_test_exclude(argv[i + 1]); i++; break; \ 953 + case 'e': /* exact name match */ \ 954 + greatest_set_exact_name_match(); break; \ 955 + case 'f': /* first fail flag */ \ 956 + greatest_stop_at_first_fail(); break; \ 957 + case 'a': /* abort() on fail flag */ \ 958 + greatest_abort_on_fail(); break; \ 959 + case 'l': /* list only (dry run) */ \ 960 + greatest_list_only(); break; \ 961 + case 'v': /* first fail flag */ \ 962 + greatest_info.verbosity++; break; \ 963 + case 'h': /* help */ \ 964 + greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ 965 + default: \ 966 + case '-': \ 967 + if (0 == strncmp("--help", argv[i], 6)) { \ 968 + greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ 969 + } else if (0 == strcmp("--", argv[i])) { \ 970 + return; /* ignore following arguments */ \ 971 + } \ 972 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 973 + "Unknown argument '%s'\n", argv[i]); \ 974 + greatest_usage(argv[0]); \ 975 + exit(EXIT_FAILURE); \ 976 + } \ 977 + } \ 978 + } \ 979 + } \ 980 + \ 981 + int greatest_all_passed(void) { return (greatest_info.failed == 0); } \ 982 + \ 983 + void greatest_set_test_filter(const char *filter) { \ 984 + greatest_info.test_filter = filter; \ 985 + } \ 986 + \ 987 + void greatest_set_test_exclude(const char *filter) { \ 988 + greatest_info.test_exclude = filter; \ 989 + } \ 990 + \ 991 + void greatest_set_suite_filter(const char *filter) { \ 992 + greatest_info.suite_filter = filter; \ 993 + } \ 994 + \ 995 + void greatest_set_exact_name_match(void) { \ 996 + greatest_info.exact_name_match = 1; \ 997 + } \ 998 + \ 999 + void greatest_stop_at_first_fail(void) { \ 1000 + greatest_set_flag(GREATEST_FLAG_FIRST_FAIL); \ 1001 + } \ 1002 + \ 1003 + void greatest_abort_on_fail(void) { \ 1004 + greatest_set_flag(GREATEST_FLAG_ABORT_ON_FAIL); \ 1005 + } \ 1006 + \ 1007 + void greatest_list_only(void) { \ 1008 + greatest_set_flag(GREATEST_FLAG_LIST_ONLY); \ 1009 + } \ 1010 + \ 1011 + void greatest_get_report(struct greatest_report_t *report) { \ 1012 + if (report) { \ 1013 + report->passed = greatest_info.passed; \ 1014 + report->failed = greatest_info.failed; \ 1015 + report->skipped = greatest_info.skipped; \ 1016 + report->assertions = greatest_info.assertions; \ 1017 + } \ 1018 + } \ 1019 + \ 1020 + unsigned int greatest_get_verbosity(void) { \ 1021 + return greatest_info.verbosity; \ 1022 + } \ 1023 + \ 1024 + void greatest_set_verbosity(unsigned int verbosity) { \ 1025 + greatest_info.verbosity = (unsigned char)verbosity; \ 1026 + } \ 1027 + \ 1028 + void greatest_set_flag(greatest_flag_t flag) { \ 1029 + greatest_info.flags = (unsigned char)(greatest_info.flags | flag); \ 1030 + } \ 1031 + \ 1032 + void greatest_set_test_suffix(const char *suffix) { \ 1033 + greatest_info.name_suffix = suffix; \ 1034 + } \ 1035 + \ 1036 + void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ 1037 + greatest_info.setup = cb; \ 1038 + greatest_info.setup_udata = udata; \ 1039 + } \ 1040 + \ 1041 + void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata) { \ 1042 + greatest_info.teardown = cb; \ 1043 + greatest_info.teardown_udata = udata; \ 1044 + } \ 1045 + \ 1046 + static int greatest_string_equal_cb(const void *expd, const void *got, \ 1047 + void *udata) { \ 1048 + size_t *size = (size_t *)udata; \ 1049 + return (size != NULL \ 1050 + ? (0 == strncmp((const char *)expd, (const char *)got, *size)) \ 1051 + : (0 == strcmp((const char *)expd, (const char *)got))); \ 1052 + } \ 1053 + \ 1054 + static int greatest_string_printf_cb(const void *t, void *udata) { \ 1055 + (void)udata; /* note: does not check \0 termination. */ \ 1056 + return GREATEST_FPRINTF(GREATEST_STDOUT, "%s", (const char *)t); \ 1057 + } \ 1058 + \ 1059 + greatest_type_info greatest_type_info_string = { \ 1060 + greatest_string_equal_cb, greatest_string_printf_cb, \ 1061 + }; \ 1062 + \ 1063 + static int greatest_memory_equal_cb(const void *expd, const void *got, \ 1064 + void *udata) { \ 1065 + greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ 1066 + return (0 == memcmp(expd, got, env->size)); \ 1067 + } \ 1068 + \ 1069 + /* Hexdump raw memory, with differences highlighted */ \ 1070 + static int greatest_memory_printf_cb(const void *t, void *udata) { \ 1071 + greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ 1072 + const unsigned char *buf = (const unsigned char *)t; \ 1073 + unsigned char diff_mark = ' '; \ 1074 + FILE *out = GREATEST_STDOUT; \ 1075 + size_t i, line_i, line_len = 0; \ 1076 + int len = 0; /* format hexdump with differences highlighted */ \ 1077 + for (i = 0; i < env->size; i+= line_len) { \ 1078 + diff_mark = ' '; \ 1079 + line_len = env->size - i; \ 1080 + if (line_len > 16) { line_len = 16; } \ 1081 + for (line_i = i; line_i < i + line_len; line_i++) { \ 1082 + if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \ 1083 + } \ 1084 + len += GREATEST_FPRINTF(out, "\n%04x %c ", \ 1085 + (unsigned int)i, diff_mark); \ 1086 + for (line_i = i; line_i < i + line_len; line_i++) { \ 1087 + int m = env->exp[line_i] == env->got[line_i]; /* match? */ \ 1088 + len += GREATEST_FPRINTF(out, "%02x%c", \ 1089 + buf[line_i], m ? ' ' : '<'); \ 1090 + } \ 1091 + for (line_i = 0; line_i < 16 - line_len; line_i++) { \ 1092 + len += GREATEST_FPRINTF(out, " "); \ 1093 + } \ 1094 + GREATEST_FPRINTF(out, " "); \ 1095 + for (line_i = i; line_i < i + line_len; line_i++) { \ 1096 + unsigned char c = buf[line_i]; \ 1097 + len += GREATEST_FPRINTF(out, "%c", isprint(c) ? c : '.'); \ 1098 + } \ 1099 + } \ 1100 + len += GREATEST_FPRINTF(out, "\n"); \ 1101 + return len; \ 1102 + } \ 1103 + \ 1104 + void greatest_prng_init_first_pass(int id) { \ 1105 + greatest_info.prng[id].random_order = 1; \ 1106 + greatest_info.prng[id].count_run = 0; \ 1107 + } \ 1108 + \ 1109 + int greatest_prng_init_second_pass(int id, unsigned long seed) { \ 1110 + struct greatest_prng *p = &greatest_info.prng[id]; \ 1111 + if (p->count == 0) { return 0; } \ 1112 + p->count_ceil = p->count; \ 1113 + for (p->m = 1; p->m < p->count; p->m <<= 1) {} \ 1114 + p->state = seed & 0x1fffffff; /* only use lower 29 bits */ \ 1115 + p->a = 4LU * p->state; /* to avoid overflow when */ \ 1116 + p->a = (p->a ? p->a : 4) | 1; /* multiplied by 4 */ \ 1117 + p->c = 2147483647; /* and so p->c ((2 ** 31) - 1) is */ \ 1118 + p->initialized = 1; /* always relatively prime to p->a. */ \ 1119 + fprintf(stderr, "init_second_pass: a %lu, c %lu, state %lu\n", \ 1120 + p->a, p->c, p->state); \ 1121 + return 1; \ 1122 + } \ 1123 + \ 1124 + /* Step the pseudorandom number generator until its state reaches \ 1125 + * another test ID between 0 and the test count. \ 1126 + * This use a linear congruential pseudorandom number generator, \ 1127 + * with the power-of-two ceiling of the test count as the modulus, the \ 1128 + * masked seed as the multiplier, and a prime as the increment. For \ 1129 + * each generated value < the test count, run the corresponding test. \ 1130 + * This will visit all IDs 0 <= X < mod once before repeating, \ 1131 + * with a starting position chosen based on the initial seed. \ 1132 + * For details, see: Knuth, The Art of Computer Programming \ 1133 + * Volume. 2, section 3.2.1. */ \ 1134 + void greatest_prng_step(int id) { \ 1135 + struct greatest_prng *p = &greatest_info.prng[id]; \ 1136 + do { \ 1137 + p->state = ((p->a * p->state) + p->c) & (p->m - 1); \ 1138 + } while (p->state >= p->count_ceil); \ 1139 + } \ 1140 + \ 1141 + void GREATEST_INIT(void) { \ 1142 + /* Suppress unused function warning if features aren't used */ \ 1143 + (void)greatest_run_suite; \ 1144 + (void)greatest_parse_options; \ 1145 + (void)greatest_prng_step; \ 1146 + (void)greatest_prng_init_first_pass; \ 1147 + (void)greatest_prng_init_second_pass; \ 1148 + (void)greatest_set_test_suffix; \ 1149 + \ 1150 + memset(&greatest_info, 0, sizeof(greatest_info)); \ 1151 + greatest_info.width = GREATEST_DEFAULT_WIDTH; \ 1152 + GREATEST_SET_TIME(greatest_info.begin); \ 1153 + } \ 1154 + \ 1155 + /* Report passes, failures, skipped tests, the number of \ 1156 + * assertions, and the overall run time. */ \ 1157 + void GREATEST_PRINT_REPORT(void) { \ 1158 + if (!GREATEST_LIST_ONLY()) { \ 1159 + update_counts_and_reset_suite(); \ 1160 + GREATEST_SET_TIME(greatest_info.end); \ 1161 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 1162 + "\nTotal: %u test%s", \ 1163 + greatest_info.tests_run, \ 1164 + greatest_info.tests_run == 1 ? "" : "s"); \ 1165 + GREATEST_CLOCK_DIFF(greatest_info.begin, \ 1166 + greatest_info.end); \ 1167 + GREATEST_FPRINTF(GREATEST_STDOUT, ", %u assertion%s\n", \ 1168 + greatest_info.assertions, \ 1169 + greatest_info.assertions == 1 ? "" : "s"); \ 1170 + GREATEST_FPRINTF(GREATEST_STDOUT, \ 1171 + "Pass: %u, fail: %u, skip: %u.\n", \ 1172 + greatest_info.passed, \ 1173 + greatest_info.failed, greatest_info.skipped); \ 1174 + } \ 1175 + } \ 1176 + \ 1177 + greatest_type_info greatest_type_info_memory = { \ 1178 + greatest_memory_equal_cb, greatest_memory_printf_cb, \ 1179 + }; \ 1180 + \ 1181 + greatest_run_info greatest_info 1182 + 1183 + /* Handle command-line arguments, etc. */ 1184 + #define GREATEST_MAIN_BEGIN() \ 1185 + do { \ 1186 + GREATEST_INIT(); \ 1187 + greatest_parse_options(argc, argv); \ 1188 + } while (0) 1189 + 1190 + /* Report results, exit with exit status based on results. */ 1191 + #define GREATEST_MAIN_END() \ 1192 + do { \ 1193 + GREATEST_PRINT_REPORT(); \ 1194 + return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \ 1195 + } while (0) 1196 + 1197 + /* Make abbreviations without the GREATEST_ prefix for the 1198 + * most commonly used symbols. */ 1199 + #if GREATEST_USE_ABBREVS 1200 + #define TEST GREATEST_TEST 1201 + #define SUITE GREATEST_SUITE 1202 + #define SUITE_EXTERN GREATEST_SUITE_EXTERN 1203 + #define RUN_TEST GREATEST_RUN_TEST 1204 + #define RUN_TEST1 GREATEST_RUN_TEST1 1205 + #define RUN_SUITE GREATEST_RUN_SUITE 1206 + #define IGNORE_TEST GREATEST_IGNORE_TEST 1207 + #define ASSERT GREATEST_ASSERT 1208 + #define ASSERTm GREATEST_ASSERTm 1209 + #define ASSERT_FALSE GREATEST_ASSERT_FALSE 1210 + #define ASSERT_EQ GREATEST_ASSERT_EQ 1211 + #define ASSERT_NEQ GREATEST_ASSERT_NEQ 1212 + #define ASSERT_GT GREATEST_ASSERT_GT 1213 + #define ASSERT_GTE GREATEST_ASSERT_GTE 1214 + #define ASSERT_LT GREATEST_ASSERT_LT 1215 + #define ASSERT_LTE GREATEST_ASSERT_LTE 1216 + #define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT 1217 + #define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE 1218 + #define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T 1219 + #define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ 1220 + #define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ 1221 + #define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ 1222 + #define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ 1223 + #define ASSERT_FALSEm GREATEST_ASSERT_FALSEm 1224 + #define ASSERT_EQm GREATEST_ASSERT_EQm 1225 + #define ASSERT_NEQm GREATEST_ASSERT_NEQm 1226 + #define ASSERT_GTm GREATEST_ASSERT_GTm 1227 + #define ASSERT_GTEm GREATEST_ASSERT_GTEm 1228 + #define ASSERT_LTm GREATEST_ASSERT_LTm 1229 + #define ASSERT_LTEm GREATEST_ASSERT_LTEm 1230 + #define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm 1231 + #define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm 1232 + #define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm 1233 + #define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm 1234 + #define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm 1235 + #define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm 1236 + #define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm 1237 + #define PASS GREATEST_PASS 1238 + #define FAIL GREATEST_FAIL 1239 + #define SKIP GREATEST_SKIP 1240 + #define PASSm GREATEST_PASSm 1241 + #define FAILm GREATEST_FAILm 1242 + #define SKIPm GREATEST_SKIPm 1243 + #define SET_SETUP GREATEST_SET_SETUP_CB 1244 + #define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB 1245 + #define CHECK_CALL GREATEST_CHECK_CALL 1246 + #define SHUFFLE_TESTS GREATEST_SHUFFLE_TESTS 1247 + #define SHUFFLE_SUITES GREATEST_SHUFFLE_SUITES 1248 + 1249 + #ifdef GREATEST_VA_ARGS 1250 + #define RUN_TESTp GREATEST_RUN_TESTp 1251 + #endif 1252 + 1253 + #if GREATEST_USE_LONGJMP 1254 + #define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP 1255 + #define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm 1256 + #define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP 1257 + #define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm 1258 + #endif 1259 + 1260 + #endif /* USE_ABBREVS */ 1261 + 1262 + #if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) 1263 + } 1264 + #endif 1265 + 1266 + #endif
+151
test/test.c
··· 1 + #include "greatest.h" 2 + #include <string.h> 3 + 4 + // Forward declarations for the C API 5 + typedef struct Bindle Bindle; 6 + typedef struct Writer Writer; 7 + typedef struct Reader Reader; 8 + 9 + typedef enum { 10 + BindleCompressNone = 0, 11 + BindleCompressZstd = 1, 12 + BindleCompressAuto = 2, 13 + } Compress; 14 + 15 + Bindle* bindle_create(const char* path); 16 + Bindle* bindle_open(const char* path); 17 + Bindle* bindle_load(const char* path); 18 + int bindle_add(Bindle* ctx, const char* name, const unsigned char* data, size_t data_len, Compress compress); 19 + int bindle_save(Bindle* ctx); 20 + void bindle_close(Bindle* ctx); 21 + unsigned char* bindle_read(Bindle* ctx, const char* name, size_t* out_len); 22 + void bindle_free_buffer(unsigned char* ptr); 23 + int bindle_exists(const Bindle* ctx, const char* name); 24 + size_t bindle_length(const Bindle* ctx); 25 + int bindle_remove(Bindle* ctx, const char* name); 26 + int bindle_vacuum(Bindle* ctx); 27 + Writer* bindle_writer_new(Bindle* ctx, const char* name, Compress compress); 28 + int bindle_writer_write(Writer* writer, const unsigned char* data, size_t len); 29 + int bindle_writer_close(Writer* writer); 30 + Reader* bindle_reader_new(const Bindle* ctx, const char* name); 31 + long bindle_reader_read(Reader* reader, unsigned char* buffer, size_t buffer_len); 32 + int bindle_reader_verify_crc32(const Reader* reader); 33 + void bindle_reader_close(Reader* reader); 34 + 35 + TEST test_basic(void) { 36 + const char* path = "test_c.bndl"; 37 + const char* name = "test.txt"; 38 + const char* data = "Hello from C!"; 39 + 40 + // Create and add data 41 + Bindle* archive = bindle_create(path); 42 + ASSERT(archive != NULL); 43 + 44 + int success = bindle_add(archive, name, (unsigned char*)data, strlen(data), BindleCompressNone); 45 + ASSERT(success); 46 + 47 + success = bindle_save(archive); 48 + ASSERT(success); 49 + 50 + // Read back 51 + size_t len = 0; 52 + unsigned char* read_data = bindle_read(archive, name, &len); 53 + ASSERT(read_data != NULL); 54 + ASSERT_EQ(len, strlen(data)); 55 + ASSERT_MEM_EQ(read_data, data, len); 56 + 57 + bindle_free_buffer(read_data); 58 + 59 + // Check exists 60 + ASSERT(bindle_exists(archive, name)); 61 + ASSERT_EQ(bindle_length(archive), 1); 62 + 63 + bindle_close(archive); 64 + 65 + PASS(); 66 + } 67 + 68 + TEST test_writer_reader(void) { 69 + const char* path = "test_c_stream.bndl"; 70 + const char* name = "streamed.txt"; 71 + const char* data = "Streaming from C!"; 72 + 73 + // Write 74 + Bindle* archive = bindle_create(path); 75 + ASSERT(archive != NULL); 76 + 77 + Writer* writer = bindle_writer_new(archive, name, BindleCompressNone); 78 + ASSERT(writer != NULL); 79 + 80 + int success = bindle_writer_write(writer, (unsigned char*)data, strlen(data)); 81 + ASSERT(success); 82 + 83 + success = bindle_writer_close(writer); 84 + ASSERT(success); 85 + 86 + bindle_save(archive); 87 + bindle_close(archive); 88 + 89 + // Read 90 + archive = bindle_open(path); 91 + ASSERT(archive != NULL); 92 + 93 + Reader* reader = bindle_reader_new(archive, name); 94 + ASSERT(reader != NULL); 95 + 96 + unsigned char buffer[256]; 97 + long bytes_read = bindle_reader_read(reader, buffer, sizeof(buffer)); 98 + ASSERT_EQ(bytes_read, (long)strlen(data)); 99 + ASSERT_MEM_EQ(buffer, data, bytes_read); 100 + 101 + ASSERT(bindle_reader_verify_crc32(reader)); 102 + 103 + bindle_reader_close(reader); 104 + bindle_close(archive); 105 + 106 + PASS(); 107 + } 108 + 109 + TEST test_remove_vacuum(void) { 110 + const char* path = "test_c_vacuum.bndl"; 111 + 112 + Bindle* archive = bindle_create(path); 113 + ASSERT(archive != NULL); 114 + 115 + // Add two entries 116 + bindle_add(archive, "file1.txt", (unsigned char*)"Data 1", 6, BindleCompressNone); 117 + bindle_add(archive, "file2.txt", (unsigned char*)"Data 2", 6, BindleCompressNone); 118 + bindle_save(archive); 119 + 120 + ASSERT_EQ(bindle_length(archive), 2); 121 + 122 + // Remove one 123 + ASSERT(bindle_remove(archive, "file1.txt")); 124 + bindle_save(archive); 125 + 126 + ASSERT_EQ(bindle_length(archive), 1); 127 + ASSERT_FALSE(bindle_exists(archive, "file1.txt")); 128 + ASSERT(bindle_exists(archive, "file2.txt")); 129 + 130 + // Vacuum 131 + ASSERT(bindle_vacuum(archive)); 132 + ASSERT_EQ(bindle_length(archive), 1); 133 + 134 + bindle_close(archive); 135 + 136 + PASS(); 137 + } 138 + 139 + SUITE(c_api_suite) { 140 + RUN_TEST(test_basic); 141 + RUN_TEST(test_writer_reader); 142 + RUN_TEST(test_remove_vacuum); 143 + } 144 + 145 + GREATEST_MAIN_DEFS(); 146 + 147 + int main(int argc, char** argv) { 148 + GREATEST_MAIN_BEGIN(); 149 + RUN_SUITE(c_api_suite); 150 + GREATEST_MAIN_END(); 151 + }