Download src/runtime/tests/runtime_api_test.c from Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5: direct link, hf CLI and curl.
- Browser
- Download file 5.91 kB
-
https://huggingface.co/Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5/resolve/main/src/runtime/tests/runtime_api_test.c
- Command line
-
hf download hf://Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5/src/runtime/tests/runtime_api_test.c
-
curl -L -o runtime_api_test.c https://huggingface.co/Arm/Arm-Runtime-for-YOLO26-on-Raspberry-Pi-5/resolve/main/src/runtime/tests/runtime_api_test.c
5.91 kB
| enum { | |
| // Little-endian model.bin header and first-directory-entry fields. | |
| kResolutionOffset = 12, | |
| kClassCountOffset = 16, | |
| kSlotCountOffset = 20, | |
| kFirstSlotSizeOffset = 72, | |
| }; | |
| static const uint32_t kUnrepresentableClassCount = UINT32_C(2147483648); | |
| /* | |
| * The Python API validates tensor sizes and allocates disjoint input and output | |
| * buffers before calling the native runtime. Its end-to-end tests therefore | |
| * cannot reach the C API's own rejection paths. This test calls that public C | |
| * boundary directly and verifies invalid buffers are rejected without starting | |
| * an inference or changing the observable execution count. | |
| */ | |
| static void fail(const char* message) { | |
| fprintf(stderr, "%s\n", message); | |
| exit(EXIT_FAILURE); | |
| } | |
| static unsigned char* read_model(const char* path, size_t* size) { | |
| FILE* file = fopen(path, "rb"); | |
| if (file == NULL || fseek(file, 0, SEEK_END) != 0) fail("could not open test model"); | |
| const long end = ftell(file); | |
| if (end <= 0 || fseek(file, 0, SEEK_SET) != 0) fail("could not size test model"); | |
| *size = (size_t)end; | |
| unsigned char* data = malloc(*size); | |
| if (data == NULL || fread(data, 1, *size, file) != *size) fail("could not read test model"); | |
| if (fclose(file) != 0) fail("could not close test model"); | |
| return data; | |
| } | |
| static void write_u32(unsigned char* data, size_t offset, uint32_t value) { | |
| memcpy(data + offset, &value, sizeof(value)); | |
| } | |
| static void require_model_rejected(unsigned char* data, size_t size, const char* message) { | |
| if (y26_native_create(data, size) != NULL) fail(message); | |
| } | |
| int main(void) { | |
| const char* model_path = getenv("YOLO26N_RUNTIME_API_TEST_MODEL"); | |
| if (model_path == NULL || model_path[0] == '\0') { | |
| printf("YOLO26N_RUNTIME_API_TEST_MODEL is not set\n"); | |
| return 77; | |
| } | |
| // Setup: create the runtime from caller-owned bytes through the public C API. | |
| size_t model_size = 0; | |
| unsigned char* model_data = read_model(model_path, &model_size); | |
| if (y26_native_create(NULL, model_size) != NULL || y26_native_create(model_data, 0) != NULL) { | |
| fail("invalid model span was not rejected"); | |
| } | |
| // These fields control graph shapes and weight lookup. Each unsupported | |
| // value must fail at the public creation boundary, before inference is | |
| // possible. Restore the valid bytes after every independent case. | |
| unsigned char original[4]; | |
| memcpy(original, model_data + kResolutionOffset, sizeof(original)); | |
| write_u32(model_data, kResolutionOffset, 321); | |
| require_model_rejected(model_data, model_size, "unsupported resolution was not rejected"); | |
| memcpy(model_data + kResolutionOffset, original, sizeof(original)); | |
| memcpy(original, model_data + kClassCountOffset, sizeof(original)); | |
| write_u32(model_data, kClassCountOffset, 0); | |
| require_model_rejected(model_data, model_size, "zero class count was not rejected"); | |
| write_u32(model_data, kClassCountOffset, kUnrepresentableClassCount); | |
| require_model_rejected(model_data, model_size, | |
| "unrepresentable model dimensions were not rejected"); | |
| memcpy(model_data + kClassCountOffset, original, sizeof(original)); | |
| memcpy(original, model_data + kSlotCountOffset, sizeof(original)); | |
| uint32_t slot_count = 0; | |
| memcpy(&slot_count, original, sizeof(slot_count)); | |
| if (slot_count <= 1) fail("test model has an unexpectedly small slot directory"); | |
| write_u32(model_data, kSlotCountOffset, slot_count - 1); | |
| require_model_rejected(model_data, model_size, "incomplete weight directory was not rejected"); | |
| memcpy(model_data + kSlotCountOffset, original, sizeof(original)); | |
| // Changing a slot extent without changing its payload range leaves a | |
| // structurally readable file, but it no longer matches the fixed graph. | |
| unsigned char original_size[8]; | |
| memcpy(original_size, model_data + kFirstSlotSizeOffset, sizeof(original_size)); | |
| uint64_t first_slot_size = 0; | |
| memcpy(&first_slot_size, original_size, sizeof(first_slot_size)); | |
| if (first_slot_size <= 1) fail("test model has an unexpectedly small first weight"); | |
| --first_slot_size; | |
| memcpy(model_data + kFirstSlotSizeOffset, &first_slot_size, sizeof(first_slot_size)); | |
| require_model_rejected(model_data, model_size, "incorrect weight extent was not rejected"); | |
| memcpy(model_data + kFirstSlotSizeOffset, original_size, sizeof(original_size)); | |
| y26_native_runtime* runtime = y26_native_create(model_data, model_size); | |
| if (runtime == NULL) fail("could not create runtime from model"); | |
| free(model_data); | |
| const size_t required_input_bytes = y26_native_input_bytes(runtime); | |
| const size_t required_output_bytes = y26_native_output_bytes(runtime); | |
| const size_t shared_bytes = | |
| required_input_bytes > required_output_bytes ? required_input_bytes : required_output_bytes; | |
| float* input = malloc(required_input_bytes); | |
| float* output = malloc(required_output_bytes); | |
| float* shared = malloc(shared_bytes); | |
| if (input == NULL || output == NULL || shared == NULL) fail("could not allocate test buffers"); | |
| // Exercise the two invalid-buffer cases without performing inference. | |
| const y26_native_status wrong_size = y26_native_execute( | |
| runtime, input, required_input_bytes - sizeof(float), output, required_output_bytes); | |
| const y26_native_status overlap = | |
| y26_native_execute(runtime, shared, required_input_bytes, shared, required_output_bytes); | |
| // Verify both calls were rejected before changing observable runtime state. | |
| if (wrong_size != Y26_NATIVE_INVALID_ARGUMENT) fail("incorrect input size was not rejected"); | |
| if (overlap != Y26_NATIVE_INVALID_ARGUMENT) fail("overlapping buffers were not rejected"); | |
| if (y26_native_calls(runtime) != 0) fail("rejected calls changed the execution count"); | |
| free(shared); | |
| free(output); | |
| free(input); | |
| y26_native_destroy(runtime); | |
| return EXIT_SUCCESS; | |
| } | |