mirror of
https://github.com/linux-msm/qdl.git
synced 2026-10-07 06:29:17 -07:00
USB warnings, Sahara ramdump failures, fatal CLI and archive errors, and packet hex dumps bypass the ux helpers, so they never get a --debug timestamp and leave gaps in the protocol timeline. Route them through shared logging via new warn and die helpers that keep the program-name prefix, errno text, and exit status. Output without --debug is unchanged and hex dumps stay visible in both modes. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
267 lines
7.8 KiB
C
267 lines
7.8 KiB
C
// SPDX-License-Identifier: BSD-3-Clause
|
|
/*
|
|
* Unit tests for the pure helpers in src/util.c: the storage address
|
|
* parser, the storage-type encode/decode pair, and the XML attribute
|
|
* accessors.
|
|
*
|
|
* util.c also contains load_sahara_image(), which references the
|
|
* qdl_file_* and ux_err symbols; the tests below never call it, but the
|
|
* whole translation unit is linked in, so trivial stubs are provided to
|
|
* satisfy the linker.
|
|
*/
|
|
#include <setjmp.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <cmocka.h>
|
|
#include <libxml/tree.h>
|
|
|
|
#include "qdl.h"
|
|
#include "file.h"
|
|
|
|
/* --- stubs for symbols referenced by unrelated util.c code --- */
|
|
void ux_err(const char *fmt, ...) { (void)fmt; }
|
|
void ux_info(const char *fmt, ...) { (void)fmt; }
|
|
int qdl_file_open(struct qdl_zip *z, const char *f, struct qdl_file *file)
|
|
{ (void)z; (void)f; (void)file; return -1; }
|
|
void *qdl_file_load(struct qdl_file *file, size_t *len) { (void)file; (void)len; return NULL; }
|
|
void qdl_file_close(struct qdl_file *file) { (void)file; }
|
|
|
|
/* --- parse_storage_address --- */
|
|
|
|
static void test_parse_addr_partition_only(void **state)
|
|
{
|
|
int part = 99;
|
|
unsigned int start = 99, num = 99;
|
|
char *gpt = (char *)0x1;
|
|
(void)state;
|
|
|
|
assert_int_equal(parse_storage_address("3", &part, &start, &num, &gpt), 0);
|
|
assert_int_equal(part, 3);
|
|
assert_int_equal(start, 0);
|
|
assert_int_equal(num, 0);
|
|
assert_null(gpt);
|
|
}
|
|
|
|
static void test_parse_addr_partition_sector(void **state)
|
|
{
|
|
int part = 0;
|
|
unsigned int start = 0, num = 99;
|
|
char *gpt = (char *)0x1;
|
|
(void)state;
|
|
|
|
assert_int_equal(parse_storage_address("2/100", &part, &start, &num, &gpt), 0);
|
|
assert_int_equal(part, 2);
|
|
assert_int_equal(start, 100);
|
|
assert_int_equal(num, 0);
|
|
assert_null(gpt);
|
|
}
|
|
|
|
static void test_parse_addr_sector_length(void **state)
|
|
{
|
|
int part = 0;
|
|
unsigned int start = 0, num = 0;
|
|
char *gpt = (char *)0x1;
|
|
(void)state;
|
|
|
|
assert_int_equal(parse_storage_address("1/50+10", &part, &start, &num, &gpt), 0);
|
|
assert_int_equal(part, 1);
|
|
assert_int_equal(start, 50);
|
|
assert_int_equal(num, 10);
|
|
assert_null(gpt);
|
|
}
|
|
|
|
static void test_parse_addr_gpt_name(void **state)
|
|
{
|
|
int part = 0;
|
|
unsigned int start = 0, num = 0;
|
|
char *gpt = NULL;
|
|
(void)state;
|
|
|
|
/* Bare name: match across all physical partitions. */
|
|
assert_int_equal(parse_storage_address("system", &part, &start, &num, &gpt), 0);
|
|
assert_int_equal(part, -1);
|
|
assert_non_null(gpt);
|
|
assert_string_equal(gpt, "system");
|
|
free(gpt);
|
|
|
|
/* N/name: match within a physical partition. */
|
|
gpt = NULL;
|
|
assert_int_equal(parse_storage_address("2/boot", &part, &start, &num, &gpt), 0);
|
|
assert_int_equal(part, 2);
|
|
assert_non_null(gpt);
|
|
assert_string_equal(gpt, "boot");
|
|
free(gpt);
|
|
}
|
|
|
|
static void test_parse_addr_errors(void **state)
|
|
{
|
|
int part;
|
|
unsigned int start, num;
|
|
char *gpt;
|
|
(void)state;
|
|
|
|
/* Zero length is rejected. */
|
|
assert_int_equal(parse_storage_address("1/50+0", &part, &start, &num, &gpt), -1);
|
|
/* Missing length digits after '+'. */
|
|
assert_int_equal(parse_storage_address("1/50+", &part, &start, &num, &gpt), -1);
|
|
/* Non-numeric length. */
|
|
assert_int_equal(parse_storage_address("1/50+abc", &part, &start, &num, &gpt), -1);
|
|
/* Trailing separator that is neither '+' nor end. */
|
|
assert_int_equal(parse_storage_address("1/2/3", &part, &start, &num, &gpt), -1);
|
|
}
|
|
|
|
/* --- storage type encode/decode --- */
|
|
|
|
static void test_storage_type_roundtrip(void **state)
|
|
{
|
|
const enum qdl_storage_type types[] = {
|
|
QDL_STORAGE_EMMC, QDL_STORAGE_NAND, QDL_STORAGE_UFS,
|
|
QDL_STORAGE_NVME, QDL_STORAGE_SPINOR,
|
|
};
|
|
size_t i;
|
|
(void)state;
|
|
|
|
assert_int_equal(decode_storage_type("emmc"), QDL_STORAGE_EMMC);
|
|
assert_int_equal(decode_storage_type("ufs"), QDL_STORAGE_UFS);
|
|
assert_int_equal(decode_storage_type("nvme"), QDL_STORAGE_NVME);
|
|
assert_int_equal(decode_storage_type("bogus"), QDL_STORAGE_UNKNOWN);
|
|
assert_int_equal(decode_storage_type(NULL), QDL_STORAGE_UNKNOWN);
|
|
|
|
assert_string_equal(encode_storage_type(QDL_STORAGE_UFS), "ufs");
|
|
assert_null(encode_storage_type((enum qdl_storage_type)999));
|
|
|
|
for (i = 0; i < ARRAY_SIZE(types); i++)
|
|
assert_int_equal(decode_storage_type(encode_storage_type(types[i])), types[i]);
|
|
}
|
|
|
|
/* --- attr_as_* --- */
|
|
|
|
static void test_attr_accessors(void **state)
|
|
{
|
|
xmlDoc *doc;
|
|
xmlNode *node;
|
|
int errors = 0;
|
|
const char *s;
|
|
(void)state;
|
|
|
|
doc = xmlNewDoc((const xmlChar *)"1.0");
|
|
node = xmlNewNode(NULL, (const xmlChar *)"n");
|
|
xmlNewProp(node, (const xmlChar *)"num", (const xmlChar *)"42");
|
|
xmlNewProp(node, (const xmlChar *)"str", (const xmlChar *)"hello");
|
|
xmlNewProp(node, (const xmlChar *)"empty", (const xmlChar *)"");
|
|
xmlNewProp(node, (const xmlChar *)"flag", (const xmlChar *)"true");
|
|
xmlNewProp(node, (const xmlChar *)"flag0", (const xmlChar *)"false");
|
|
|
|
/* Present attributes do not raise errors. */
|
|
assert_int_equal(attr_as_unsigned(node, "num", &errors), 42);
|
|
assert_int_equal(errors, 0);
|
|
|
|
s = attr_as_string(node, "str", &errors);
|
|
assert_non_null(s);
|
|
assert_string_equal(s, "hello");
|
|
assert_int_equal(errors, 0);
|
|
free((void *)s);
|
|
|
|
assert_true(attr_as_bool(node, "flag", &errors));
|
|
assert_false(attr_as_bool(node, "flag0", &errors));
|
|
assert_int_equal(errors, 0);
|
|
|
|
/*
|
|
* A present-but-empty string attribute returns NULL without flagging
|
|
* an error - callers must treat NULL as "not provided". This is the
|
|
* behaviour several load paths depend on.
|
|
*/
|
|
assert_null(attr_as_string(node, "empty", &errors));
|
|
assert_int_equal(errors, 0);
|
|
|
|
/* A missing attribute raises an error via the accessor. */
|
|
assert_null(attr_as_string(node, "missing", &errors));
|
|
assert_int_equal(errors, 1);
|
|
errors = 0;
|
|
assert_int_equal(attr_as_unsigned(node, "missing", &errors), 0);
|
|
assert_int_equal(errors, 1);
|
|
|
|
/* A missing bool is simply false and raises no error. */
|
|
errors = 0;
|
|
assert_false(attr_as_bool(node, "missing", &errors));
|
|
assert_int_equal(errors, 0);
|
|
|
|
xmlFreeNode(node);
|
|
xmlFreeDoc(doc);
|
|
}
|
|
|
|
/* --- decode_backend --- */
|
|
|
|
static void test_decode_backend(void **state)
|
|
{
|
|
enum QDL_DEVICE_TYPE t = QDL_DEVICE_SIM;
|
|
(void)state;
|
|
|
|
assert_int_equal(decode_backend("auto", &t), 0);
|
|
assert_int_equal(t, QDL_DEVICE_AUTO);
|
|
|
|
/* A NULL name defaults to auto. */
|
|
t = QDL_DEVICE_SIM;
|
|
assert_int_equal(decode_backend(NULL, &t), 0);
|
|
assert_int_equal(t, QDL_DEVICE_AUTO);
|
|
|
|
assert_int_equal(decode_backend("usb", &t), 0);
|
|
assert_int_equal(t, QDL_DEVICE_USB);
|
|
|
|
assert_int_equal(decode_backend("qud", &t), 0);
|
|
assert_int_equal(t, QDL_DEVICE_QUD);
|
|
|
|
assert_int_equal(decode_backend("bogus", &t), -1);
|
|
}
|
|
|
|
/* --- qdl_split_specifier --- */
|
|
|
|
static void test_split_specifier(void **state)
|
|
{
|
|
char *spec = (char *)0x1;
|
|
char *file;
|
|
(void)state;
|
|
|
|
/* Plain filename: no specifier. */
|
|
file = qdl_split_specifier("contents.xml", &spec);
|
|
assert_non_null(file);
|
|
assert_string_equal(file, "contents.xml");
|
|
assert_null(spec);
|
|
free(file);
|
|
|
|
/* file::selector splits into the two parts. */
|
|
file = qdl_split_specifier("contents.xml::ufs,safe", &spec);
|
|
assert_non_null(file);
|
|
assert_string_equal(file, "contents.xml");
|
|
assert_non_null(spec);
|
|
assert_string_equal(spec, "ufs,safe");
|
|
free(file);
|
|
|
|
/* Malformed: empty, double "::", empty filename, empty selector. */
|
|
assert_null(qdl_split_specifier("", &spec));
|
|
assert_null(qdl_split_specifier("a::b::c", &spec));
|
|
assert_null(qdl_split_specifier("::ufs", &spec));
|
|
assert_null(qdl_split_specifier("file::", &spec));
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test(test_parse_addr_partition_only),
|
|
cmocka_unit_test(test_parse_addr_partition_sector),
|
|
cmocka_unit_test(test_parse_addr_sector_length),
|
|
cmocka_unit_test(test_parse_addr_gpt_name),
|
|
cmocka_unit_test(test_parse_addr_errors),
|
|
cmocka_unit_test(test_storage_type_roundtrip),
|
|
cmocka_unit_test(test_attr_accessors),
|
|
cmocka_unit_test(test_decode_backend),
|
|
cmocka_unit_test(test_split_specifier),
|
|
};
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
}
|