Files
ncnn/tests/test_net.cpp

1499 lines
51 KiB
C++

// Copyright 2026 Tencent
// SPDX-License-Identifier: BSD-3-Clause
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include "datareader.h"
#include "layer_type.h"
#include "modelbin.h"
#include "net.h"
#include "paramdict.h"
#include "testutil.h"
#include <errno.h>
#if defined _WIN32
#include <process.h>
#endif
struct LayerState
{
LayerState()
: created(0), destroyed(0), pipeline_created(0), pipeline_destroyed(0), pipeline_destroyed_before_param(0), load_param_ret(0), cpu_load_param_ret(0), one_blob_only(false), support_vulkan(false), fail_create(false)
{
}
int created;
int destroyed;
int pipeline_created;
int pipeline_destroyed;
int pipeline_destroyed_before_param;
int load_param_ret;
int cpu_load_param_ret;
bool one_blob_only;
bool support_vulkan;
bool fail_create;
};
class TestLayer : public ncnn::Layer
{
public:
TestLayer(LayerState* _state, bool _support_vulkan, int _load_param_ret)
: state(_state), load_param_ret(_load_param_ret), param_loaded(false)
{
one_blob_only = state->one_blob_only;
support_vulkan = _support_vulkan;
}
virtual int load_param(const ncnn::ParamDict&)
{
support_vulkan = false;
param_loaded = load_param_ret == 0;
return load_param_ret;
}
virtual int create_pipeline(const ncnn::Option&)
{
state->pipeline_created++;
return 0;
}
virtual int destroy_pipeline(const ncnn::Option&)
{
state->pipeline_destroyed++;
if (!param_loaded)
state->pipeline_destroyed_before_param++;
return 0;
}
private:
LayerState* state;
int load_param_ret;
bool param_loaded;
};
static ncnn::Layer* create_test_layer(void* userdata)
{
LayerState* state = (LayerState*)userdata;
if (state->fail_create)
return 0;
state->created++;
return new TestLayer(state, state->support_vulkan, state->load_param_ret);
}
static void destroy_test_layer(ncnn::Layer* layer, void* userdata)
{
LayerState* state = (LayerState*)userdata;
state->destroyed++;
delete layer;
}
static ncnn::Layer* create_null_layer(void*)
{
return 0;
}
static ncnn::Layer* create_layer_once(void* userdata)
{
LayerState* state = (LayerState*)userdata;
ncnn::Layer* layer = create_test_layer(userdata);
state->fail_create = true;
return layer;
}
static ncnn::Layer* create_layer_with_cpu_fallback(void* userdata)
{
LayerState* state = (LayerState*)userdata;
ncnn::Layer* layer = create_test_layer(userdata);
// configure the next instance for cpu fallback
state->support_vulkan = false;
state->load_param_ret = state->cpu_load_param_ret;
return layer;
}
static bool empty_net(const ncnn::Net& net)
{
return net.layers().empty() && net.blobs().empty() && net.input_indexes().empty() && net.output_indexes().empty()
&& net.input_names().empty() && net.output_names().empty();
}
// memory readers without a length cannot detect truncated binary input
class BoundedNetReader : public ncnn::DataReader
{
public:
BoundedNetReader(const unsigned char* p, size_t size)
: data(p), remaining(size)
{
}
virtual size_t read(void* buf, size_t size) const
{
if (size > remaining) size = remaining;
if (size != 0)
{
memcpy(buf, data, size);
data += size;
remaining -= size;
}
return size;
}
private:
mutable const unsigned char* data;
mutable size_t remaining;
};
static int load_binary(ncnn::Net& net, const unsigned char* data, size_t size)
{
BoundedNetReader dr(data, size);
return net.load_param_bin(dr);
}
static void append_int(std::vector<unsigned char>& data, int value)
{
const unsigned int v = (unsigned int)value;
for (int i = 0; i < 4; i++)
data.push_back((unsigned char)(v >> (i * 8)));
}
static std::vector<unsigned char> binary_header(int layers, int blobs)
{
std::vector<unsigned char> data;
append_int(data, 7767517);
append_int(data, layers);
append_int(data, blobs);
return data;
}
static void append_layer_header(std::vector<unsigned char>& data, int type, int bottoms, int tops)
{
append_int(data, type);
append_int(data, bottoms);
append_int(data, tops);
}
static int check_text(const char* text, int expected_ret)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
net.register_custom_layer("Input", create_test_layer, destroy_test_layer, &state);
int ret = net.load_param_mem(text);
if (ret != expected_ret || (ret != 0 && !empty_net(net)))
{
fprintf(stderr, "test_net text failed ret=%d expected=%d\n%s\n", ret, expected_ret, text);
return -1;
}
net.clear();
if (!empty_net(net) || state.created != state.destroyed || state.pipeline_destroyed_before_param != 0
|| state.pipeline_destroyed != (expected_ret == 0 ? state.created : 0))
{
fprintf(stderr, "test_net text cleanup failed created=%d destroyed=%d pipelines=%d pipelines_before_param=%d\n%s\n", state.created, state.destroyed, state.pipeline_destroyed, state.pipeline_destroyed_before_param, text);
return -1;
}
return 0;
}
static int check_binary(const char* name, const std::vector<unsigned char>& data, size_t size, int expected_ret)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
net.register_custom_layer("Input", create_test_layer, destroy_test_layer, &state);
int ret = load_binary(net, &data[0], size);
if (ret != expected_ret || (ret != 0 && !empty_net(net)))
{
fprintf(stderr, "test_net binary %s failed size=%zu ret=%d expected=%d\n", name, size, ret, expected_ret);
return -1;
}
net.clear();
if (!empty_net(net) || state.created != state.destroyed || state.pipeline_destroyed_before_param != 0
|| state.pipeline_destroyed != (expected_ret == 0 ? state.created : 0))
{
fprintf(stderr, "test_net binary %s cleanup failed created=%d destroyed=%d pipelines=%d pipelines_before_param=%d\n", name, state.created, state.destroyed, state.pipeline_destroyed, state.pipeline_destroyed_before_param);
return -1;
}
return 0;
}
static int test_binary_truncated()
{
const int custom = ncnn::LayerType::CustomBit;
for (int builtin = 0; builtin < 2; builtin++)
{
const int type = builtin ? ncnn::LayerType::Input : custom;
const char* layer_name = builtin ? "overwritten layer" : "custom layer";
std::vector<unsigned char> valid = binary_header(2, 2);
append_layer_header(valid, type, 0, 1);
append_int(valid, 0);
append_int(valid, -233);
append_int(valid, type);
append_int(valid, 1);
append_int(valid, 1);
append_int(valid, 0);
append_int(valid, 1);
append_int(valid, -233);
for (size_t n = 0; n < valid.size(); n++)
{
if (check_binary(layer_name, valid, n, -1))
return -1;
}
if (check_binary(layer_name, valid, valid.size(), 0))
return -1;
}
return 0;
}
static int test_binary_dependencies()
{
const int custom = ncnn::LayerType::CustomBit;
struct Case
{
const char* name;
int layers;
int blobs;
int values[24];
int count;
int expected_ret;
};
const Case cases[] = {
{"forward reference", 2, 2, {custom, 1, 1, 1, 0, -233, custom, 0, 1, 1, -233}, 11, 0},
{"shared dependency", 4, 4, {custom, 2, 1, 1, 2, 0, -233, custom, 1, 1, 3, 1, -233, custom, 1, 1, 3, 2, -233, custom, 0, 1, 3, -233}, 24, 0},
{"repeated dependency", 2, 2, {custom, 2, 1, 1, 1, 0, -233, custom, 0, 1, 1, -233}, 12, 0},
{"external input", 1, 2, {custom, 1, 1, 0, 1, -233}, 6, 0}
};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
{
const Case& c = cases[i];
std::vector<unsigned char> data;
append_int(data, 7767517);
append_int(data, c.layers);
append_int(data, c.blobs);
for (int j = 0; j < c.count; j++)
append_int(data, c.values[j]);
if (check_binary(c.name, data, data.size(), c.expected_ret))
return -1;
}
return 0;
}
static int test_binary_deep_dependencies(int cyclic)
{
// only test that cycle detection handles deep graphs without recursive traversal
const int layer_count = 16384;
std::vector<unsigned char> data;
append_int(data, 7767517);
append_int(data, layer_count);
append_int(data, layer_count + 1);
for (int i = 0; i < layer_count; i++)
{
append_int(data, ncnn::LayerType::CustomBit);
append_int(data, 1);
append_int(data, 1);
append_int(data, cyclic && i == layer_count - 1 ? 0 : i + 1);
append_int(data, i);
append_int(data, -233);
}
if (check_binary(cyclic ? "deep cycle" : "deep chain", data, data.size(), cyclic ? -1 : 0))
return -1;
return 0;
}
static int test_layer_load_param_error()
{
const char* layer_types[] = {"Test", "Input"};
const int type_indexes[] = {ncnn::LayerType::CustomBit, ncnn::LayerType::Input};
for (int i = 0; i < 2; i++)
{
for (int binary = 0; binary < 2; binary++)
{
LayerState state;
state.load_param_ret = -1;
ncnn::Net net;
net.register_custom_layer(layer_types[i], create_test_layer, destroy_test_layer, &state);
char text[128];
snprintf(text, sizeof(text), "7767517\n1 1\n%s t 0 1 out\n", layer_types[i]);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, type_indexes[i], 0, 1);
append_int(data, 0);
append_int(data, -233);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != -1 || !empty_net(net) || state.created != 1 || state.destroyed != 1 || state.pipeline_destroyed != 0)
{
fprintf(stderr, "test_net load_param error cleanup failed type=%s binary=%d ret=%d created=%d destroyed=%d pipelines=%d\n", layer_types[i], binary, ret, state.created, state.destroyed, state.pipeline_destroyed);
return -1;
}
}
}
return 0;
}
static int check_shape_hints(const ncnn::Net& net, const int shapes[][5])
{
const ncnn::Layer* layer = net.layers()[0];
for (size_t i = 0; i < layer->tops.size(); i++)
{
const ncnn::Mat& shape = net.blobs()[layer->tops[i]].shape;
const int* expected = shapes[i];
if (shape.dims != expected[0] || shape.w != expected[1] || shape.h != expected[2]
|| shape.d != expected[3] || shape.c != expected[4])
{
fprintf(stderr, "test_net shape hint failed top=%zu shape=%d,%d,%d,%d,%d expected=%d,%d,%d,%d,%d\n",
i, shape.dims, shape.w, shape.h, shape.d, shape.c,
expected[0], expected[1], expected[2], expected[3], expected[4]);
return -1;
}
}
return 0;
}
static int test_shape_hints()
{
struct Case
{
const char* text;
int count;
int values[10];
int tops;
// dims, w, h, d, c for each top
int shapes[2][5];
};
const Case cases[] = {
{"4,1,7,0,0", 4, {1, 7, 0, 0}, 1, {{1, 7, 1, 1, 1}}},
{"4,2,7,6,0", 4, {2, 7, 6, 0}, 1, {{2, 7, 6, 1, 1}}},
{"4,3,7,6,5", 4, {3, 7, 6, 5}, 1, {{3, 7, 6, 1, 5}}},
{"5,3,7,6,1,5", 5, {3, 7, 6, 1, 5}, 1, {{3, 7, 6, 1, 5}}},
{"5,4,7,6,5,4", 5, {4, 7, 6, 5, 4}, 1, {{4, 7, 6, 5, 4}}},
{"8,1,7,0,0,2,6,5,0", 8, {1, 7, 0, 0, 2, 6, 5, 0}, 2, {{1, 7, 1, 1, 1}, {2, 6, 5, 1, 1}}},
{"4,0,0,0,0", 4, {0, 0, 0, 0}, 1, {{0}}},
{"4,1,0,0,0", 4, {1, 0, 0, 0}, 1, {{1, 0, 1, 1, 1}}},
{"1,4", 1, {4}, 0, {{0}}}
};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
{
const Case& c = cases[i];
char text[512];
const char* top_names[] = {"", " out", " out out2"};
snprintf(text, sizeof(text), "7767517\n1 2\nTest t 0 %d%s -23330=%s\n", c.tops, top_names[c.tops], c.text);
std::vector<unsigned char> data = binary_header(1, 2);
append_layer_header(data, ncnn::LayerType::CustomBit, 0, c.tops);
for (int j = 0; j < c.tops; j++) append_int(data, j);
append_int(data, -23330);
append_int(data, c.count);
for (int j = 0; j < c.count; j++) append_int(data, c.values[j]);
append_int(data, -233);
for (int binary = 0; binary < 2; binary++)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != 0)
{
fprintf(stderr, "test_net shape hint load failed binary=%d hints=%s ret=%d\n", binary, c.text, ret);
return -1;
}
if (check_shape_hints(net, c.shapes) != 0)
{
fprintf(stderr, "test_net shape hint failed binary=%d hints=%s\n", binary, c.text);
return -1;
}
net.clear();
if (!empty_net(net) || state.created != state.destroyed || state.pipeline_destroyed != state.created || state.pipeline_destroyed_before_param != 0)
{
fprintf(stderr, "test_net shape hint cleanup failed binary=%d hints=%s\n", binary, c.text);
return -1;
}
}
}
if (check_text("7767517\n1 1\nTest t 0 1 out 30=3,7,6,5\n", 0))
return -1;
return 0;
}
static int test_reload()
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Input", create_test_layer, destroy_test_layer, &state);
const char* text = "7767517\n1 1\nInput t 0 1 out\n";
int ret = net.load_param_mem(text);
if (ret != 0 || net.input_names().size() != 1 || net.output_names().size() != 1)
{
fprintf(stderr, "test_net initial load failed ret=%d inputs=%zu outputs=%zu\n", ret, net.input_names().size(), net.output_names().size());
return -1;
}
ret = net.load_param_mem(text);
if (ret != 0 || state.created != 2 || state.destroyed != 1)
{
fprintf(stderr, "test_net reload failed ret=%d created=%d destroyed=%d\n", ret, state.created, state.destroyed);
return -1;
}
// truncate the bottom name to exercise failed reload cleanup
ret = net.load_param_mem("7767517\n1 1\nInput t 1 0");
if (ret != -1 || !empty_net(net) || state.created != state.destroyed)
{
fprintf(stderr, "test_net failed reload cleanup failed ret=%d created=%d destroyed=%d\n", ret, state.created, state.destroyed);
return -1;
}
ret = net.load_param_mem(text);
if (ret != 0)
{
fprintf(stderr, "test_net reload after error failed ret=%d\n", ret);
return -1;
}
net.clear();
if (!empty_net(net) || state.created != state.destroyed)
{
fprintf(stderr, "test_net clear after reload failed created=%d destroyed=%d\n", state.created, state.destroyed);
return -1;
}
return 0;
}
static int test_name_length()
{
char name[257];
memset(name, 'a', 256);
name[255] = 0;
char text[1024];
snprintf(text, sizeof(text), "7767517\n1 1\nTest %s 0 1 %s\n", name, name);
if (check_text(text, 0))
return -1;
return 0;
}
static int test_integer_length(int length)
{
char token[33];
memset(token, '0', length);
token[length - 1] = '1';
token[length] = 0;
char text[256];
snprintf(text, sizeof(text), "7767517\n%s 1\nTest t 0 1 out\n", token);
if (check_text(text, length <= 31 ? 0 : -1))
return -1;
snprintf(text, sizeof(text), "7767517\n1 1\nTest t 0 %s out\n", token);
if (check_text(text, length <= 31 ? 0 : -1))
return -1;
return 0;
}
static int test_repeated_blob_references()
{
// duplicate references are legal even when the count exceeds blob_count
if (check_text("7767517\n1 1\nTest t 2 0 in in\n", 0))
return -1;
if (check_text("7767517\n1 2\nTest t 3 0 a a b\n", 0)
|| check_text("7767517\n1 2\nTest t 3 0 a b a\n", 0))
return -1;
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::CustomBit, 2, 0);
append_int(data, 0);
append_int(data, 0);
append_int(data, -233);
if (check_binary("repeated blob references", data, data.size(), 0))
return -1;
return 0;
}
static int test_builtin_without_override()
{
for (int binary = 0; binary < 2; binary++)
{
ncnn::Net net;
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::Input, 0, 1);
append_int(data, 0);
append_int(data, -233);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem("7767517\n1 1\nInput t 0 1 out\n");
if (ret != 0)
{
fprintf(stderr, "test_net builtin without override failed binary=%d ret=%d\n", binary, ret);
return -1;
}
}
return 0;
}
static int test_null_creators()
{
for (int binary = 0; binary < 2; binary++)
{
for (int builtin = 0; builtin < 2; builtin++)
{
for (int null_function = 0; null_function < 2; null_function++)
{
LayerState state;
ncnn::Net net;
const int typeindex = builtin ? ncnn::LayerType::Input : ncnn::LayerType::CustomBit;
ncnn::layer_creator_func creator = null_function ? 0 : create_null_layer;
if (binary)
net.register_custom_layer(typeindex, creator, destroy_test_layer, &state);
else
net.register_custom_layer(builtin ? "Input" : "Test", creator, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, typeindex, 0, 1);
append_int(data, 0);
append_int(data, -233);
const char* text = builtin ? "7767517\n1 1\nInput t 0 1 out\n" : "7767517\n1 1\nTest t 0 1 out\n";
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != -1 || !empty_net(net) || state.created != 0 || state.destroyed != 0)
{
fprintf(stderr, "test_net null creator failed binary=%d builtin=%d null_function=%d ret=%d created=%d destroyed=%d\n", binary, builtin, null_function, ret, state.created, state.destroyed);
return -1;
}
}
}
}
return 0;
}
static int test_shape_layout()
{
const char* text = "7767517\n2 2\nInput in 0 1 in -23330=5,4,7,6,5,4\nTest t 1 1 in out -23330=4,3,3,2,1\n";
std::vector<unsigned char> data = binary_header(2, 2);
append_layer_header(data, ncnn::LayerType::Input, 0, 1);
const int values[] = {0, -23330, 5, 4, 7, 6, 5, 4, -233, ncnn::LayerType::CustomBit, 1, 1, 0, 1, -23330, 4, 3, 3, 2, 1, -233};
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) append_int(data, values[i]);
for (int binary = 0; binary < 2; binary++)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != 0)
{
fprintf(stderr, "test_net shape layout load failed binary=%d ret=%d\n", binary, ret);
return -1;
}
const ncnn::Mat& in = net.layers()[1]->bottom_shapes[0];
const ncnn::Mat& out = net.layers()[1]->top_shapes[0];
if (in.dims != 4 || in.w != 7 || in.h != 6 || in.d != 5 || in.c != 4
|| out.dims != 3 || out.w != 3 || out.h != 2 || out.d != 1 || out.c != 1)
{
fprintf(stderr, "test_net shape layout binary=%d failed\n", binary);
return -1;
}
}
return 0;
}
#if NCNN_BF16
class StoragePackingLayer : public ncnn::Layer
{
public:
StoragePackingLayer()
{
one_blob_only = true;
support_packing = true;
}
virtual int forward(const ncnn::Mat& bottom_blob, ncnn::Mat& top_blob, const ncnn::Option&) const
{
top_blob = bottom_blob;
return 0;
}
};
static ncnn::Layer* create_storage_packing_layer(void*)
{
return new StoragePackingLayer;
}
static int test_storage_packing(int storage_type)
{
if (storage_type == 2)
{
bool support_fp16 = false;
#if NCNN_ARM82
support_fp16 = support_fp16 || ncnn::cpu_support_arm_asimdhp();
#endif
#if NCNN_VFPV4
support_fp16 = support_fp16 || ncnn::cpu_support_arm_vfpv4();
#endif
#if NCNN_ZFH
support_fp16 = support_fp16 || ncnn::cpu_support_riscv_zvfh() || (!ncnn::cpu_support_riscv_v() && ncnn::cpu_support_riscv_zfh());
#endif
if (!support_fp16)
return 0;
}
ncnn::Net net;
net.opt.num_threads = 1;
net.opt.use_packing_layout = true;
net.opt.use_fp16_storage = storage_type == 2;
net.opt.use_bf16_storage = storage_type == 4;
#if NCNN_RVV || NCNN_XTHEADVECTOR
if (!ncnn::cpu_support_riscv_v() && !ncnn::cpu_support_riscv_xtheadvector())
net.opt.use_packing_layout = false;
#endif
net.register_custom_layer("StoragePacking", create_storage_packing_layer);
const char* param = "7767517\n2 2\nInput in 0 1 in\nStoragePacking fp32 1 1 in out\n";
BoundedNetReader dr(0, 0);
if (net.load_param_mem(param) != 0 || net.load_model(dr) != 0)
{
fprintf(stderr, "test_net storage packing load failed storage_type=%d\n", storage_type);
return -1;
}
ncnn::Mat in(5, 3, 128);
for (int q = 0; q < in.c; q++)
{
float* p = in.channel(q);
for (int i = 0; i < in.w * in.h; i++)
p[i] = (i - 7) * 0.25f + (q % 8);
}
ncnn::Mat input;
if (storage_type == 2)
ncnn::cast_float32_to_float16(in, input, net.opt);
else
ncnn::cast_float32_to_bfloat16(in, input, net.opt);
ncnn::Extractor ref = net.create_extractor();
ncnn::Mat expected;
if (ref.input("in", in) != 0 || ref.extract("out", expected, 1) != 0)
{
fprintf(stderr, "test_net storage packing reference failed storage_type=%d\n", storage_type);
return -1;
}
ncnn::Extractor ex = net.create_extractor();
ncnn::Mat out;
int ret = ex.input("in", input);
if (ret == 0)
ret = ex.extract("out", out, 1);
if (ret != 0 || out.elembits() != 32 || out.elempack != expected.elempack || CompareMat(in, out, 0.f) != 0)
{
fprintf(stderr, "test_net storage packing failed storage_type=%d ret=%d elembits=%d elempack=%d expected=%d\n", storage_type, ret, out.elembits(), out.elempack, expected.elempack);
return -1;
}
return 0;
}
#endif // NCNN_BF16
static int test_recreate_layer()
{
for (int binary = 0; binary < 2; binary++)
{
for (int builtin = 0; builtin < 2; builtin++)
{
for (int fail = 0; fail < 2; fail++)
{
LayerState state;
state.support_vulkan = true;
state.cpu_load_param_ret = fail ? -1 : 0;
ncnn::Net net;
net.register_custom_layer(builtin ? "Input" : "Test", create_layer_with_cpu_fallback, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, builtin ? ncnn::LayerType::Input : ncnn::LayerType::CustomBit, 0, 1);
append_int(data, 0);
append_int(data, -233);
const char* text = builtin ? "7767517\n1 1\nInput t 0 1 out\n" : "7767517\n1 1\nTest t 0 1 out\n";
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != (fail ? -1 : 0) || state.created != 2 || state.destroyed != (fail ? 2 : 1) || state.pipeline_destroyed != 0 || (fail && !empty_net(net)))
{
fprintf(stderr, "test_net recreate binary=%d builtin=%d fail=%d ret=%d created=%d destroyed=%d pipelines=%d\n", binary, builtin, fail, ret, state.created, state.destroyed, state.pipeline_destroyed);
return -1;
}
net.clear();
if (state.destroyed != 2 || state.pipeline_destroyed != (fail ? 0 : 1) || state.pipeline_destroyed_before_param != 0)
{
fprintf(stderr, "test_net recreate cleanup failed binary=%d builtin=%d fail=%d destroyed=%d pipelines=%d pipelines_before_param=%d\n", binary, builtin, fail, state.destroyed, state.pipeline_destroyed, state.pipeline_destroyed_before_param);
return -1;
}
}
}
}
return 0;
}
static int test_recreate_layer_null_creator()
{
for (int binary = 0; binary < 2; binary++)
{
for (int builtin = 0; builtin < 2; builtin++)
{
LayerState state;
state.support_vulkan = true;
ncnn::Net net;
const int typeindex = builtin ? ncnn::LayerType::Input : ncnn::LayerType::CustomBit;
if (binary)
net.register_custom_layer(typeindex, create_layer_once, destroy_test_layer, &state);
else
net.register_custom_layer(builtin ? "Input" : "Test", create_layer_once, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, typeindex, 0, 1);
append_int(data, 0);
append_int(data, -233);
const char* text = builtin ? "7767517\n1 1\nInput t 0 1 out\n" : "7767517\n1 1\nTest t 0 1 out\n";
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != -1 || !empty_net(net) || state.created != 1 || state.destroyed != 1 || state.pipeline_destroyed != 0)
{
fprintf(stderr, "test_net null cpu creator failed binary=%d builtin=%d ret=%d created=%d destroyed=%d pipelines=%d\n", binary, builtin, ret, state.created, state.destroyed, state.pipeline_destroyed);
return -1;
}
}
}
return 0;
}
static int test_external_input()
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
if (net.load_param_mem("7767517\n1 1\nTest t 1 0 in\n") != 0)
{
fprintf(stderr, "test_net external input load failed\n");
return -1;
}
ncnn::Extractor ex = net.create_extractor();
ncnn::Mat out;
int ret = ex.extract(0, out);
if (ret != -1)
{
fprintf(stderr, "test_net missing input ret=%d\n", ret);
return -1;
}
ncnn::Mat in(1);
in[0] = 42.f;
int input_ret = ex.input(0, in);
ret = ex.extract(0, out);
if (input_ret || ret || out.empty() || out[0] != 42.f)
{
fprintf(stderr, "test_net external input input_ret=%d ret=%d dims=%d value=%f\n", input_ret, ret, out.dims, out.empty() ? 0.f : out[0]);
return -1;
}
return 0;
}
static int test_binary_layer_types()
{
LayerState state;
ncnn::Net net;
net.register_custom_layer(ncnn::LayerType::CustomBit | 512, create_test_layer, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, 512, 0, 1);
append_int(data, 0);
append_int(data, -233);
int ret = load_binary(net, &data[0], data.size());
if (ret != -1 || state.created != 0 || !empty_net(net))
{
fprintf(stderr, "test_net untagged custom layer failed ret=%d created=%d\n", ret, state.created);
return -1;
}
data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::CustomBit | 512, 0, 1);
append_int(data, 0);
append_int(data, -233);
ret = load_binary(net, &data[0], data.size());
if (ret != 0 || state.created != 1)
{
fprintf(stderr, "test_net tagged custom layer failed ret=%d created=%d\n", ret, state.created);
return -1;
}
net.clear();
if (state.destroyed != 1 || !empty_net(net))
{
fprintf(stderr, "test_net tagged custom layer cleanup failed destroyed=%d\n", state.destroyed);
return -1;
}
return 0;
}
static int test_magic_mismatch()
{
for (int binary = 0; binary < 2; binary++)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Input", create_test_layer, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::Input, 0, 1);
append_int(data, 0);
append_int(data, -233);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem("7767517\n1 1\nInput t 0 1 out\n");
if (ret != 0 || net.input_indexes().size() != 1 || net.output_indexes().size() != 1)
{
fprintf(stderr, "test_net magic mismatch setup failed binary=%d ret=%d\n", binary, ret);
return -1;
}
data[0] = 0;
ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem("0\n1 1\nInput t 0 1 out\n");
if (ret != -1 || !empty_net(net) || state.created != 1 || state.destroyed != 1 || state.pipeline_destroyed != 1 || state.pipeline_destroyed_before_param != 0)
{
fprintf(stderr, "test_net magic mismatch failed binary=%d ret=%d created=%d destroyed=%d pipelines=%d\n", binary, ret, state.created, state.destroyed, state.pipeline_destroyed);
return -1;
}
}
return 0;
}
static int test_feature_mask()
{
const int masks[] = {0, 1, 2, 17, 128, 255};
for (size_t i = 0; i < sizeof(masks) / sizeof(masks[0]); i++)
{
for (int binary = 0; binary < 2; binary++)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
char text[128];
snprintf(text, sizeof(text), "7767517\n1 1\nTest t 0 1 out 31=%d\n", masks[i]);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::CustomBit, 0, 1);
append_int(data, 0);
append_int(data, 31);
append_int(data, masks[i]);
append_int(data, -233);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text);
if (ret != 0 || net.layers().size() != 1 || net.layers()[0]->featmask != masks[i])
{
fprintf(stderr, "test_net feature mask failed binary=%d mask=%d ret=%d\n", binary, masks[i], ret);
return -1;
}
}
}
return 0;
}
static int test_pipeline_lifecycle()
{
for (int binary = 0; binary < 2; binary++)
{
LayerState state;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::CustomBit, 0, 1);
append_int(data, 0);
append_int(data, -233);
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem("7767517\n1 1\nTest t 0 1 out\n");
if (ret != 0 || state.pipeline_created != 0 || state.pipeline_destroyed != 0)
{
fprintf(stderr, "test_net pipeline load_param failed binary=%d ret=%d\n", binary, ret);
return -1;
}
BoundedNetReader dr(&data[0], 0);
ret = net.load_model(dr);
if (ret != 0 || state.pipeline_created != 1 || state.pipeline_destroyed != 0)
{
fprintf(stderr, "test_net pipeline load_model failed binary=%d ret=%d created=%d destroyed=%d\n", binary, ret, state.pipeline_created, state.pipeline_destroyed);
return -1;
}
net.clear();
if (!empty_net(net) || state.destroyed != 1 || state.pipeline_destroyed != 1 || state.pipeline_destroyed_before_param != 0)
{
fprintf(stderr, "test_net pipeline clear failed binary=%d destroyed=%d pipelines=%d pipelines_before_param=%d\n", binary, state.destroyed, state.pipeline_destroyed, state.pipeline_destroyed_before_param);
return -1;
}
}
return 0;
}
static int test_bounded_text()
{
const unsigned char text[] = "7767517\n1 1\nInput input 0 1 out\n";
const unsigned char* mem = text;
ncnn::DataReaderFromMemory dr(mem, sizeof(text) - 1);
ncnn::Net net;
if (net.load_param(dr) != 0)
{
fprintf(stderr, "test_net bounded text failed\n");
return -1;
}
return 0;
}
#if defined _WIN32 || __ANDROID__ || defined __OHOS__ || defined __linux__ || __APPLE__
class TestModelLayer : public ncnn::Layer
{
public:
virtual int load_model(const ncnn::ModelBin& mb)
{
weights = mb.load(3, 1);
return weights.empty() ? -100 : 0;
}
virtual int forward(const std::vector<ncnn::Mat>&, std::vector<ncnn::Mat>& top_blobs, const ncnn::Option& opt) const
{
top_blobs[0] = weights.clone(opt.blob_allocator);
return top_blobs[0].empty() ? -100 : 0;
}
private:
ncnn::Mat weights;
};
static ncnn::Layer* create_model_layer(void*)
{
return new TestModelLayer;
}
static int check_model_file(const char* path, size_t size, bool mapped)
{
const unsigned char model_data[] = {
0x00, 0x00, 0x80, 0x3f,
0x00, 0x00, 0x20, 0xc0,
0x00, 0x00, 0x00, 0x3f
};
const float weights[] = {1.f, -2.5f, 0.5f};
FILE* fp = fopen(path, "wb");
if (!fp)
{
fprintf(stderr, "test_net model fopen failed\n");
return -1;
}
const size_t nwrite = fwrite(model_data, 1, size, fp);
const int close_ret = fclose(fp);
if (nwrite != size || close_ret != 0)
{
fprintf(stderr, "test_net model fwrite failed\n");
return -1;
}
// mapped loading falls back to stdio when file mapping is unavailable
if (mapped && size != 0)
{
ncnn::MappedFile file;
if (file.open(path) != 0)
{
fprintf(stderr, "test_net model mapping unavailable size=%zu errno=%d, testing stdio fallback\n", size, errno);
}
}
ncnn::Net net;
net.opt.use_mapped_model_loading = mapped;
net.register_custom_layer("TestModel", create_model_layer);
if (net.load_param_mem("7767517\n1 1\nTestModel weights 0 1 out\n") != 0)
{
fprintf(stderr, "test_net model load_param failed\n");
return -1;
}
// a bounded short read consumes the entire file, so the load status must be checked
const int ret = net.load_model(path);
if ((ret != 0) != (size < sizeof(weights)))
{
fprintf(stderr, "test_net model load failed mapped=%d size=%zu ret=%d\n", mapped, size, ret);
return -1;
}
#if _WIN32
wchar_t wide_path[128];
size_t i = 0;
for (; path[i]; i++)
wide_path[i] = (unsigned char)path[i];
wide_path[i] = 0;
ncnn::Net net2;
net2.opt.use_mapped_model_loading = mapped;
net2.register_custom_layer("TestModel", create_model_layer);
if (net2.load_param_mem("7767517\n1 1\nTestModel weights 0 1 out\n") != 0
|| (net2.load_model(wide_path) != 0) != (size < sizeof(weights)))
{
fprintf(stderr, "test_net wide model load failed mapped=%d size=%zu\n", mapped, size);
return -1;
}
#endif
if (ret == 0)
{
ncnn::Mat output;
ncnn::Extractor ex = net.create_extractor();
if (ex.extract("out", output) != 0 || output.w != 3 || memcmp(output.data, weights, sizeof(weights)))
{
fprintf(stderr, "test_net model output failed mapped=%d\n", mapped);
return -1;
}
}
return 0;
}
static int test_model_file()
{
char path[128];
#if _WIN32
snprintf(path, sizeof(path), "test_net_model.%u.bin", (unsigned int)_getpid());
#else
snprintf(path, sizeof(path), "test_net_model.%u.bin", (unsigned int)getpid());
#endif
for (int mapped = 0; mapped < 2; mapped++)
{
for (size_t size = 0; size <= 3 * sizeof(float); size++)
{
const int ret = check_model_file(path, size, mapped != 0);
remove(path);
if (ret != 0)
return ret;
}
}
return 0;
}
#endif // defined _WIN32 || __ANDROID__ || defined __OHOS__ || defined __linux__ || __APPLE__
#if NCNN_VULKAN
class OptionalInputLayer : public ncnn::Layer
{
public:
OptionalInputLayer(bool _support_vulkan)
{
one_blob_only = false;
support_vulkan = _support_vulkan;
}
virtual int forward(const std::vector<ncnn::Mat>& bottom_blobs, std::vector<ncnn::Mat>& top_blobs, const ncnn::Option&) const
{
top_blobs[0] = bottom_blobs.size() == 2 && !bottom_blobs[1].empty() ? bottom_blobs[1] : bottom_blobs[0];
return 0;
}
virtual int forward(const std::vector<ncnn::VkMat>& bottom_blobs, std::vector<ncnn::VkMat>& top_blobs, ncnn::VkCompute& cmd, const ncnn::Option& opt) const
{
const ncnn::VkMat& a = bottom_blobs.size() == 2 && !bottom_blobs[1].empty() ? bottom_blobs[1] : bottom_blobs[0];
cmd.record_clone(a, top_blobs[0], opt);
return top_blobs[0].empty() ? -100 : 0;
}
};
static ncnn::Layer* create_optional_input_layer(void*)
{
return new OptionalInputLayer(false);
}
static ncnn::Layer* create_optional_input_layer_vulkan(void*)
{
return new OptionalInputLayer(true);
}
static int test_vulkan_optional_input(int vulkan, int lightmode, int empty_input)
{
ncnn::Net net;
net.opt.use_vulkan_compute = true;
net.opt.lightmode = lightmode;
net.opt.num_threads = 1;
net.register_custom_layer("OptionalInput", create_optional_input_layer);
net.register_custom_layer("OptionalInputVulkan", create_optional_input_layer_vulkan);
const char* param_cpu = "7767517\n"
"4 4\n"
"Input in 0 1 in\n"
"Input optional 0 1 optional\n"
"OptionalInputVulkan pre 1 1 in data\n"
"OptionalInput target 2 1 data optional out\n";
const char* param_vulkan = "7767517\n"
"4 4\n"
"Input in 0 1 in\n"
"Input optional 0 1 optional\n"
"OptionalInput pre 1 1 in data\n"
"OptionalInputVulkan target 2 1 data optional out\n";
BoundedNetReader dr(0, 0);
if (net.load_param_mem(vulkan ? param_vulkan : param_cpu) != 0 || net.load_model(dr) != 0)
{
fprintf(stderr, "test_net vulkan optional input load failed vulkan=%d lightmode=%d\n", vulkan, lightmode);
return -1;
}
ncnn::Mat in(4);
ncnn::Mat optional(4);
for (int i = 0; i < 4; i++)
{
in[i] = i + 1.f;
optional[i] = i + 11.f;
}
ncnn::Extractor ex = net.create_extractor();
ex.input("in", in);
if (!empty_input)
ex.input("optional", optional);
ncnn::Mat out;
int ret = ex.extract("out", out);
const ncnn::Mat& expected = empty_input ? in : optional;
if (ret != 0 || CompareMat(expected, out, 0.f) != 0)
{
fprintf(stderr, "test_net vulkan optional input failed vulkan=%d lightmode=%d empty_input=%d ret=%d\n", vulkan, lightmode, empty_input, ret);
return -1;
}
return 0;
}
static int test_vulkan_optional_input()
{
if (ncnn::get_gpu_count() == 0)
return 0;
return test_vulkan_optional_input(0, 0, 1)
|| test_vulkan_optional_input(0, 0, 0)
|| test_vulkan_optional_input(0, 1, 1)
|| test_vulkan_optional_input(0, 1, 0)
|| test_vulkan_optional_input(1, 0, 1)
|| test_vulkan_optional_input(1, 0, 0)
|| test_vulkan_optional_input(1, 1, 1)
|| test_vulkan_optional_input(1, 1, 0);
}
#endif // NCNN_VULKAN
#if NCNN_VALIDATION
static int test_text_errors()
{
const char* cases[] = {
"7767517\n1 1\nTest t 3 0 a a b\n",
"", "7767517", "7767517\n0 1\n", "7767517\n1 -1\n",
"7767517\n2147483647 1\n", "7767517\n1 2147483647\n",
"7767517\n1000001 1\n", "7767517\n1 1000001\n",
"7767517\n1 1\nTest t 1000001 0\n", "7767517\n1 1\nTest t 0 1000001\n",
"7767517\n4294967297 1\nTest t 0 1 out\n",
"7767517\n1 1\nTest t -1 1 out\n", "7767517\n1 1\nTest t 0 -1\n",
"7767517\n1 1\nTest t 2147483647 1\n", "7767517\n1 1\nTest t 0 2147483647\n",
"7767517\n1 1\nTest t 0 4294967297 out\n",
"7767517\n1x 1\nTest t 0 1 out\n",
"7767517\n1 1\nTest t 0 1x out\n",
"7767517\n1 1\nTest t 1 1 in out\n",
"7767517\n1 1\nTest t 2 0 in in2\n",
"7767517\n1 1\nTest t 0 2 out out2\n",
"7767517\n1 1\nTest t 1 0", "7767517\n1 1\nTest t 0 1",
"7767517\n2 1\nTest t 0 1 out\n",
"7767517\n1 1\nTest t 0 1 out 0=1,,2\n",
"7767517\n1 1\nTest t 0 1 out 31=1.0\n",
"7767517\n1 1\nTest t 0 1 out 31=1,2\n",
// missing bottom name checks builtin override cleanup after a truncated connection
"7767517\n1 1\nInput t 1 0",
"7767517\n1 1\nInput t 0 1 out 0=1,,2\n"
};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
{
if (check_text(cases[i], -1))
return -1;
}
return 0;
}
static int test_one_blob_only()
{
const int counts[][2] = {{0, 1}, {1, 0}, {1, 2}, {2, 1}};
for (int binary = 0; binary < 2; binary++)
{
for (size_t i = 0; i < sizeof(counts) / sizeof(counts[0]); i++)
{
const int bottom_count = counts[i][0];
const int top_count = counts[i][1];
LayerState state;
state.one_blob_only = true;
ncnn::Net net;
net.register_custom_layer("Test", create_test_layer, destroy_test_layer, &state);
std::vector<unsigned char> data = binary_header(1, bottom_count + top_count);
append_layer_header(data, ncnn::LayerType::CustomBit, bottom_count, top_count);
for (int j = 0; j < bottom_count + top_count; j++) append_int(data, j);
append_int(data, -233);
const char* text[] = {
"7767517\n1 1\nTest t 0 1 out\n",
"7767517\n1 1\nTest t 1 0 in\n",
"7767517\n1 3\nTest t 1 2 in out out2\n",
"7767517\n1 3\nTest t 2 1 in in2 out\n"
};
int ret = binary ? load_binary(net, &data[0], data.size()) : net.load_param_mem(text[i]);
if (ret != -1 || !empty_net(net) || state.created != 1 || state.destroyed != 1 || state.pipeline_destroyed != 0)
{
fprintf(stderr, "test_net one_blob_only failed binary=%d bottoms=%d tops=%d ret=%d created=%d destroyed=%d pipelines=%d\n", binary, bottom_count, top_count, ret, state.created, state.destroyed, state.pipeline_destroyed);
return -1;
}
}
}
return 0;
}
static int test_shape_hint_errors()
{
struct Case
{
const char* text;
int count;
int values[10];
int tops;
};
const Case cases[] = {
{"1,4", 1, {4}, 1},
{"3,3,7,6", 3, {3, 7, 6}, 1},
{"4,4,7,6,5", 4, {4, 7, 6, 5}, 1},
{"5,3,7,6,1,5", 5, {3, 7, 6, 1, 5}, 2},
{"4,5,7,6,5", 4, {5, 7, 6, 5}, 1},
{"4,-1,7,6,5", 4, {-1, 7, 6, 5}, 1},
{"4,3,-1,6,5", 4, {3, -1, 6, 5}, 1},
{"5,4,2147483647,2147483647,2147483647,1", 5, {4, INT_MAX, INT_MAX, INT_MAX, 1}, 1},
{"4,3,2147483647,2147483647,2147483647", 4, {3, INT_MAX, INT_MAX, INT_MAX}, 1}
};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
{
const Case& c = cases[i];
char text[512];
const char* top_names[] = {"", " out", " out out2"};
snprintf(text, sizeof(text), "7767517\n1 2\nTest t 0 %d%s -23330=%s\n", c.tops, top_names[c.tops], c.text);
if (check_text(text, -1))
return -1;
std::vector<unsigned char> data = binary_header(1, 2);
append_layer_header(data, ncnn::LayerType::CustomBit, 0, c.tops);
for (int j = 0; j < c.tops; j++) append_int(data, j);
append_int(data, -23330);
append_int(data, c.count);
for (int j = 0; j < c.count; j++) append_int(data, c.values[j]);
append_int(data, -233);
if (check_binary(c.text, data, data.size(), -1))
return -1;
}
if (check_text("7767517\n1 1\nTest t 0 1 out 30=4\n", -1)
|| check_text("7767517\n1 1\nTest t 0 1 out 30=3.0,7.0,6.0,5.0\n", -1))
return -1;
return 0;
}
static int test_custom_index_limit()
{
ncnn::Net net;
const int indexes[] = {-1, 1000000 | ncnn::LayerType::CustomBit, INT_MAX};
for (size_t i = 0; i < sizeof(indexes) / sizeof(indexes[0]); i++)
{
int ret = net.register_custom_layer(indexes[i], create_null_layer);
if (ret != -1)
{
fprintf(stderr, "test_net custom layer index limit failed index=%d ret=%d\n", indexes[i], ret);
return -1;
}
}
return 0;
}
static int test_binary_param_types()
{
const int ids[] = {30, -23331};
for (int i = 0; i < 2; i++)
{
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, ncnn::LayerType::CustomBit, 0, 1);
append_int(data, 0);
append_int(data, ids[i]);
append_int(data, 1);
if (i == 1) append_int(data, 1);
append_int(data, -233);
if (check_binary("parameter type", data, data.size(), -1))
{
fprintf(stderr, "test_net binary parameter type failed id=%d\n", ids[i]);
return -1;
}
}
return 0;
}
static int test_binary_errors()
{
const int custom = ncnn::LayerType::CustomBit;
for (int builtin = 0; builtin < 2; builtin++)
{
const int type = builtin ? ncnn::LayerType::Input : custom;
const char* layer_name = builtin ? "overwritten layer" : "custom layer";
const int bad[] = {-1, INT_MIN, 2, INT_MAX};
for (size_t i = 0; i < sizeof(bad) / sizeof(bad[0]); i++)
{
for (int bottom = 0; bottom < 2; bottom++)
{
std::vector<unsigned char> data = binary_header(1, 2);
append_layer_header(data, type, bottom, 1 - bottom);
append_int(data, bad[i]);
append_int(data, -233);
if (check_binary(layer_name, data, data.size(), -1))
{
fprintf(stderr, "test_net blob index failed bottom=%d index=%d\n", bottom, bad[i]);
return -1;
}
}
}
}
const int bad_counts[] = {-1, INT_MIN, 1000001, INT_MAX};
for (size_t i = 0; i < sizeof(bad_counts) / sizeof(bad_counts[0]); i++)
{
for (int field = 0; field < 4; field++)
{
int counts[] = {1, 1, 0, 1};
counts[field] = bad_counts[i];
std::vector<unsigned char> data = binary_header(counts[0], counts[1]);
append_layer_header(data, custom, counts[2], counts[3]);
if (check_binary("header count", data, data.size(), -1))
{
fprintf(stderr, "test_net header count failed field=%d value=%d\n", field, bad_counts[i]);
return -1;
}
}
}
std::vector<unsigned char> data = binary_header(1, 1);
append_layer_header(data, -1, 0, 1);
append_int(data, 0);
append_int(data, -233);
if (check_binary("negative layer type", data, data.size(), -1))
return -1;
return 0;
}
static int test_binary_cycles()
{
const int custom = ncnn::LayerType::CustomBit;
struct Case
{
const char* name;
int layers;
int blobs;
int values[24];
int count;
int expected_ret;
};
const Case cases[] = {
{"self cycle", 1, 1, {custom, 1, 1, 0, 0, -233}, 6, -1},
{"two layer cycle", 2, 2, {custom, 1, 1, 1, 0, -233, custom, 1, 1, 0, 1, -233}, 12, -1},
{"disconnected cycle", 3, 3, {custom, 0, 1, 0, -233, custom, 1, 1, 2, 1, -233, custom, 1, 1, 1, 2, -233}, 17, -1},
{"reassigned producer cycle", 3, 2, {custom, 0, 1, 0, -233, custom, 1, 1, 0, 1, -233, custom, 1, 1, 1, 0, -233}, 17, -1},
};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++)
{
const Case& c = cases[i];
std::vector<unsigned char> data;
append_int(data, 7767517);
append_int(data, c.layers);
append_int(data, c.blobs);
for (int j = 0; j < c.count; j++)
append_int(data, c.values[j]);
if (check_binary(c.name, data, data.size(), c.expected_ret))
return -1;
}
return 0;
}
static int test_name_length_invalid()
{
char name[257];
memset(name, 'a', 256);
name[256] = 0;
char text[1024];
snprintf(text, sizeof(text), "7767517\n1 1\nTest %s 0 1 out\n", name);
if (check_text(text, -1))
return -1;
snprintf(text, sizeof(text), "7767517\n1 1\nTest t 0 1 %s\n", name);
if (check_text(text, -1))
return -1;
return 0;
}
#endif // NCNN_VALIDATION
int main()
{
return 0
|| test_binary_truncated()
|| test_binary_dependencies()
|| test_binary_deep_dependencies(0)
|| test_shape_hints()
|| test_layer_load_param_error()
|| test_reload()
|| test_name_length()
|| test_integer_length(30)
|| test_integer_length(31)
|| test_repeated_blob_references()
|| test_builtin_without_override()
|| test_null_creators()
|| test_shape_layout()
#if NCNN_BF16
|| test_storage_packing(4)
|| test_storage_packing(2)
#endif
|| test_recreate_layer()
|| test_recreate_layer_null_creator()
|| test_external_input()
|| test_binary_layer_types()
|| test_magic_mismatch()
|| test_feature_mask()
|| test_pipeline_lifecycle()
|| test_bounded_text()
#if defined _WIN32 || __ANDROID__ || defined __OHOS__ || defined __linux__ || __APPLE__
|| test_model_file()
#endif
#if NCNN_VULKAN
|| test_vulkan_optional_input()
#endif // NCNN_VULKAN
#if NCNN_VALIDATION
|| test_text_errors()
|| test_binary_errors()
|| test_binary_cycles()
|| test_binary_deep_dependencies(1)
|| test_name_length_invalid()
|| test_integer_length(32)
|| test_shape_hint_errors()
|| test_one_blob_only()
|| test_custom_index_limit()
|| test_binary_param_types()
#endif // NCNN_VALIDATION
;
}