/********************************************************************* testimgt.c Imgtool testing code *********************************************************************/ #include "testimgt.h" #include "../imgtool/imgtool.h" #include "../imgtool/modules.h" #define VERBOSE_FILECHAIN 0 struct expected_dirent { const char *filename; int size; }; static const char *tempfile_name(void) { static char buffer[256]; osd_get_temp_filename(buffer, ARRAY_LENGTH(buffer), NULL); return buffer; } static void report_imgtoolerr(imgtoolerr_t err) { const char *msg; msg = imgtool_error(err); report_message(MSG_FAILURE, msg); } struct imgtooltest_state { imgtool_image *image; imgtool_partition *partition; UINT64 recorded_freespace; int failed; }; static void node_createimage(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; xml_data_node *child_node; xml_attribute_node *attr_node; option_resolution *opts = NULL; const imgtool_module *module; const char *driver; const char *param_name; const char *param_value; attr_node = xml_get_attribute(node, "driver"); if (!attr_node) { error_missingattribute("driver"); return; } driver = attr_node->value; /* does image creation support options? */ module = imgtool_find_module(attr_node->value); if (module && module->createimage_optguide && module->createimage_optspec) opts = option_resolution_create(module->createimage_optguide, module->createimage_optspec); report_message(MSG_INFO, "Creating image (module '%s')", driver); for (child_node = xml_get_sibling(node->child, "param"); child_node; child_node = xml_get_sibling(child_node->next, "param")) { if (!opts) { report_message(MSG_FAILURE, "Cannot specify creation options with this module"); return; } attr_node = xml_get_attribute(child_node, "name"); if (!attr_node) { error_missingattribute("name"); return; } param_name = attr_node->value; attr_node = xml_get_attribute(child_node, "value"); if (!attr_node) { error_missingattribute("value"); return; } param_value = attr_node->value; option_resolution_add_param(opts, param_name, param_value); } err = imgtool_image_create_byname(driver, tempfile_name(), opts, &state->image); if (opts) { option_resolution_close(opts); opts = NULL; } if (err) { state->failed = 1; report_imgtoolerr(err); return; } err = imgtool_partition_open(state->image, 0, &state->partition); if (err) { state->failed = 1; report_imgtoolerr(err); return; } } static void get_file_params(xml_data_node *node, const char **filename, const char **fork, filter_getinfoproc *filter) { xml_attribute_node *attr_node; *filename = NULL; *fork = NULL; *filter = NULL; attr_node = xml_get_attribute(node, "name"); if (!attr_node) { error_missingattribute("name"); return; } *filename = attr_node->value; attr_node = xml_get_attribute(node, "fork"); if (attr_node) *fork = attr_node->value; attr_node = xml_get_attribute(node, "filter"); if (attr_node) { *filter = filter_lookup(attr_node->value); if (!*filter) report_message(MSG_FAILURE, "Cannot find filter '%s'", attr_node->value); } } static void node_putfile(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; const char *filename; const char *fork; filter_getinfoproc filter; imgtool_stream *stream; mess_pile pile; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } get_file_params(node, &filename, &fork, &filter); if (!filename) return; pile_init(&pile); messtest_get_data(node, &pile); stream = stream_open_mem(NULL, 0); if (!stream) { state->failed = 1; error_outofmemory(); return; } stream_write(stream, pile_getptr(&pile), pile_size(&pile)); stream_seek(stream, 0, SEEK_SET); pile_delete(&pile); err = imgtool_partition_write_file(state->partition, filename, fork, stream, NULL, filter); if (err) { state->failed = 1; report_imgtoolerr(err); return; } if (VERBOSE_FILECHAIN) { char buf[1024]; err = imgtool_partition_get_chain_string(state->partition, filename, buf, sizeof(buf) / sizeof(buf[0])); if (err == IMGTOOLERR_SUCCESS) report_message(MSG_INFO, "Filechain '%s': %s", filename, buf); } } static void node_checkfile(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; const char *filename; const char *fork; filter_getinfoproc filter; imgtool_stream *stream; UINT64 stream_sz; const void *stream_ptr; mess_pile pile; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } get_file_params(node, &filename, &fork, &filter); if (!filename) return; stream = stream_open_mem(NULL, 0); if (!stream) { state->failed = 1; error_outofmemory(); return; } err = imgtool_partition_read_file(state->partition, filename, fork, stream, filter); if (err) { state->failed = 1; report_imgtoolerr(err); return; } pile_init(&pile); messtest_get_data(node, &pile); stream_ptr = stream_getptr(stream); stream_sz = stream_size(stream); if ((pile_size(&pile) != stream_sz) || (memcmp(stream_ptr, pile_getptr(&pile), pile_size(&pile)))) { report_message(MSG_FAILURE, "Failed file verification"); return; } pile_delete(&pile); } static void append_to_list(char *buf, size_t buflen, const char *entry) { size_t pos; pos = strlen(buf); snprintf(buf + pos, buflen - pos - 1, (pos > 0) ? ", %s" : "%s", entry); } static void node_checkdirectory(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err = IMGTOOLERR_SUCCESS; imgtool_directory *imageenum; imgtool_dirent ent; char expected_listing[1024]; char actual_listing[1024]; int i, actual_count; int mismatch; xml_attribute_node *attr_node; xml_data_node *child_node; const char *filename; struct expected_dirent *entry; struct expected_dirent entries[256]; int entry_count; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } attr_node = xml_get_attribute(node, "path"); filename = attr_node ? attr_node->value : ""; memset(&entries, 0, sizeof(entries)); entry_count = 0; for (child_node = xml_get_sibling(node->child, "entry"); child_node; child_node = xml_get_sibling(child_node->next, "entry")) { if (entry_count >= sizeof(entries) / sizeof(entries[0])) { report_message(MSG_FAILURE, "Too many directory entries"); return; } entry = &entries[entry_count++]; attr_node = xml_get_attribute(child_node, "name"); entry->filename = attr_node ? attr_node->value : NULL; attr_node = xml_get_attribute(child_node, "size"); entry->size = attr_node ? atoi(attr_node->value) : -1; } /* build expected listing string */ expected_listing[0] = '\0'; for (i = 0; i < entry_count; i++) { append_to_list(expected_listing, sizeof(expected_listing) / sizeof(expected_listing[0]), entries[i].filename); } /* now enumerate though listing */ actual_count = 0; actual_listing[0] = '\0'; mismatch = FALSE; memset(&ent, 0, sizeof(ent)); err = imgtool_directory_open(state->partition, filename, &imageenum); if (err) goto done; i = 0; do { err = imgtool_directory_get_next(imageenum, &ent); if (err) goto done; if (!ent.eof) { append_to_list(actual_listing, sizeof(actual_listing) / sizeof(actual_listing[0]), ent.filename); if (i < entry_count && (strcmp(ent.filename, entries[i].filename))) mismatch = TRUE; i++; } } while(!ent.eof); if (i != entry_count) mismatch = TRUE; if (mismatch) { state->failed = 1; report_message(MSG_FAILURE, "File listing mismatch: {%s} expected {%s}", actual_listing, expected_listing); goto done; } done: if (imageenum) imgtool_directory_close(imageenum); if (err) { state->failed = 1; report_imgtoolerr(err); } } static void node_deletefile(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; xml_attribute_node *attr_node; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } attr_node = xml_get_attribute(node, "name"); if (!attr_node) { state->failed = 1; error_missingattribute("name"); return; } err = imgtool_partition_delete_file(state->partition, attr_node->value); if (err) { state->failed = 1; report_imgtoolerr(err); return; } } static void node_createdirectory(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; xml_attribute_node *attr_node; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } attr_node = xml_get_attribute(node, "path"); if (!attr_node) { state->failed = 1; error_missingattribute("path"); return; } err = imgtool_partition_create_directory(state->partition, attr_node->value); if (err) { state->failed = 1; report_imgtoolerr(err); return; } } static void node_deletedirectory(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; xml_attribute_node *attr_node; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } attr_node = xml_get_attribute(node, "path"); if (!attr_node) { state->failed = 1; error_missingattribute("path"); return; } err = imgtool_partition_delete_directory(state->partition, attr_node->value); if (err) { state->failed = 1; report_imgtoolerr(err); return; } } static void node_recordfreespace(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } err = imgtool_partition_get_free_space(state->partition, &state->recorded_freespace); if (err) { state->failed = 1; report_imgtoolerr(err); return; } } static void node_checkfreespace(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; UINT64 current_freespace; INT64 leaked_space; const char *verb; if (!state->partition) { state->failed = 1; report_message(MSG_FAILURE, "Partition not loaded"); return; } err = imgtool_partition_get_free_space(state->partition, ¤t_freespace); if (err) { state->failed = 1; report_imgtoolerr(err); return; } if (state->recorded_freespace != current_freespace) { leaked_space = state->recorded_freespace - current_freespace; if (leaked_space > 0) { verb = "Leaked"; } else { leaked_space = -leaked_space; verb = "Reverse leaked"; } state->failed = 1; report_message(MSG_FAILURE, "%s %u bytes of space", verb, (unsigned int) leaked_space); } } static UINT32 identify_attribute(struct imgtooltest_state *state, xml_data_node *node, imgtool_attribute *value) { xml_attribute_node *attr_node; UINT32 attribute; attr_node = xml_get_attribute(node, "name"); if (!attr_node) { state->failed = 1; error_missingattribute("name"); return 0; } if (!strcmp(attr_node->value, "mac_file_type")) attribute = IMGTOOLATTR_INT_MAC_TYPE; else if (!strcmp(attr_node->value, "mac_file_creator")) attribute = IMGTOOLATTR_INT_MAC_CREATOR; else { error_missingattribute("name"); return 0; } attr_node = xml_get_attribute(node, "value"); if (!attr_node) { state->failed = 1; error_missingattribute("value"); return 0; } switch(attribute) { case IMGTOOLATTR_INT_MAC_TYPE: case IMGTOOLATTR_INT_MAC_CREATOR: if (strlen(attr_node->value) != 4) { state->failed = 1; error_missingattribute("value"); return 0; } value->i = pick_integer_be(attr_node->value, 0, 4); break; } return attribute; } static void node_setattr(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; xml_attribute_node *attr_node; UINT32 attribute; imgtool_attribute value; attr_node = xml_get_attribute(node, "path"); if (!attr_node) { state->failed = 1; error_missingattribute("path"); return; } attribute = identify_attribute(state, node, &value); if (!attribute) return; err = imgtool_partition_put_file_attribute(state->partition, attr_node->value, attribute, value); if (err) { state->failed = 1; report_imgtoolerr(err); return; } } static void node_checkattr(struct imgtooltest_state *state, xml_data_node *node) { imgtoolerr_t err; xml_attribute_node *attr_node; UINT32 attribute; imgtool_attribute value; imgtool_attribute expected_value; memset(&value, 0, sizeof(value)); memset(&expected_value, 0, sizeof(expected_value)); attr_node = xml_get_attribute(node, "path"); if (!attr_node) { state->failed = 1; error_missingattribute("path"); return; } attribute = identify_attribute(state, node, &expected_value); if (!attribute) return; err = imgtool_partition_get_file_attribute(state->partition, attr_node->value, attribute, &value); if (err) { state->failed = 1; report_imgtoolerr(err); return; } if (memcmp(&value, &expected_value, sizeof(value))) { state->failed = 1; report_message(MSG_FAILURE, "Comparison failed"); return; } } static void messtest_warn(const char *message) { report_message(MSG_FAILURE, "%s", message); } void node_testimgtool(xml_data_node *node) { xml_data_node *child_node; xml_attribute_node *attr_node; struct imgtooltest_state state; imgtool_init(FALSE, messtest_warn); attr_node = xml_get_attribute(node, "name"); report_testcase_begin(attr_node ? attr_node->value : NULL); memset(&state, 0, sizeof(state)); for (child_node = node->child; child_node; child_node = child_node->next) { if (!strcmp(child_node->name, "createimage")) node_createimage(&state, child_node); else if (!strcmp(child_node->name, "checkfile")) node_checkfile(&state, child_node); else if (!strcmp(child_node->name, "checkdirectory")) node_checkdirectory(&state, child_node); else if (!strcmp(child_node->name, "putfile")) node_putfile(&state, child_node); else if (!strcmp(child_node->name, "deletefile")) node_deletefile(&state, child_node); else if (!strcmp(child_node->name, "createdirectory")) node_createdirectory(&state, child_node); else if (!strcmp(child_node->name, "deletedirectory")) node_deletedirectory(&state, child_node); else if (!strcmp(child_node->name, "recordfreespace")) node_recordfreespace(&state, child_node); else if (!strcmp(child_node->name, "checkfreespace")) node_checkfreespace(&state, child_node); else if (!strcmp(child_node->name, "setattr")) node_setattr(&state, child_node); else if (!strcmp(child_node->name, "checkattr")) node_checkattr(&state, child_node); } report_testcase_ran(state.failed); /* close out any existing partition */ if (state.partition) { imgtool_partition_close(state.partition); state.partition = NULL; } /* close out any existing image */ if (state.image) { imgtool_image_close(state.image); state.image = NULL; } imgtool_exit(); }