[80430] branches/gsoc11-rev-upgrade/base/src/libmachista1.0

cal at macports.org cal at macports.org
Tue Jul 12 15:01:13 PDT 2011


Revision: 80430
          http://trac.macports.org/changeset/80430
Author:   cal at macports.org
Date:     2011-07-12 15:01:12 -0700 (Tue, 12 Jul 2011)
Log Message:
-----------
rev-upgrade: Unit test for libmachista and fixes for two regressions in libmachista

Modified Paths:
--------------
    branches/gsoc11-rev-upgrade/base/src/libmachista1.0/Makefile
    branches/gsoc11-rev-upgrade/base/src/libmachista1.0/libmachista.c

Added Paths:
-----------
    branches/gsoc11-rev-upgrade/base/src/libmachista1.0/tests/
    branches/gsoc11-rev-upgrade/base/src/libmachista1.0/tests/libmachista-test.c

Modified: branches/gsoc11-rev-upgrade/base/src/libmachista1.0/Makefile
===================================================================
--- branches/gsoc11-rev-upgrade/base/src/libmachista1.0/Makefile	2011-07-12 20:33:18 UTC (rev 80429)
+++ branches/gsoc11-rev-upgrade/base/src/libmachista1.0/Makefile	2011-07-12 22:01:12 UTC (rev 80430)
@@ -13,6 +13,8 @@
 
 clean::
 	rm -f ${OBJS} ${SHLIB_NAME}
+	rm -f ./tests/libmachista-test
+	rm -rf ./tests/libmachista-test.dSYM
 
 distclean:: clean
 
@@ -20,3 +22,9 @@
 	${INSTALL} -d -o ${DSTUSR} -g ${DSTGRP} -m ${DSTMODE} ${INSTALLDIR}
 	${INSTALL} -o ${DSTUSR} -g ${DSTGRP} -m 444 ${SHLIB_NAME} ${INSTALLDIR}
 
+test:: tests/libmachista-test
+	./tests/libmachista-test
+
+tests/libmachista-test: tests/libmachista-test.c libmachista.h libmachista$(SHLIB_SUFFIX)
+	$(CC) $(CFLAGS) -D_POSIX_SOURCE -o $@ -I. -L. -lmachista $<
+

Modified: branches/gsoc11-rev-upgrade/base/src/libmachista1.0/libmachista.c
===================================================================
--- branches/gsoc11-rev-upgrade/base/src/libmachista1.0/libmachista.c	2011-07-12 20:33:18 UTC (rev 80429)
+++ branches/gsoc11-rev-upgrade/base/src/libmachista1.0/libmachista.c	2011-07-12 22:01:12 UTC (rev 80430)
@@ -79,7 +79,7 @@
 /* return a human readable formatted version number. the result must be free()'d. */
 char *macho_format_dylib_version (uint32_t version) {
     char *result;
-    asprintf(&result, "%"PRIu32".%"PRIu32".%"PRIu32, (version >> 16) & 0xFF, (version >> 8) & 0xFF, version & 0xFF);
+    asprintf(&result, "%"PRIu32".%"PRIu32".%"PRIu32, (version >> 16) & 0xFFFF, (version >> 8) & 0xFF, version & 0xFF);
     return result;
 }
 
@@ -229,7 +229,7 @@
                 return MACHO_EMEM;
 
             /* 32-bit Mach-O */
-            mat->mat_arch = header->cputype;
+            mat->mat_arch = swap32(header->cputype);
             break;
 
 
@@ -250,7 +250,7 @@
             header = (struct mach_header *) header64;
 
             /* 64-bit Macho-O */
-            mat->mat_arch = header->cputype;
+            mat->mat_arch = swap32(header->cputype);
             break;
 
         case FAT_CIGAM:
@@ -295,7 +295,7 @@
     }
 
     /* Copy the architecture */
-    mat->mat_arch = header->cputype;
+    mat->mat_arch = swap32(header->cputype);
 
     /* Parse the Mach-O load commands */
     const struct load_command *cmd = macho_offset(input, header, header_size, sizeof(struct load_command));

Added: branches/gsoc11-rev-upgrade/base/src/libmachista1.0/tests/libmachista-test.c
===================================================================
--- branches/gsoc11-rev-upgrade/base/src/libmachista1.0/tests/libmachista-test.c	                        (rev 0)
+++ branches/gsoc11-rev-upgrade/base/src/libmachista1.0/tests/libmachista-test.c	2011-07-12 22:01:12 UTC (rev 80430)
@@ -0,0 +1,348 @@
+#include <libmachista.h>
+#include <limits.h>
+#include <mach-o/arch.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define LIBSYSTEM_PATH "/usr/lib/libSystem.B.dylib"
+#define OTOOL_PATH "/usr/bin/otool"
+
+// check helper
+static bool check(bool condition, char *msg) {
+	if (!condition)
+		printf("Assertion failed: %s\n", msg);
+	return condition;
+}
+
+// forking helper
+static bool fork_test(void (*fp)(void), char *msg) {
+	pid_t p = fork();
+
+	switch (p) {
+		case -1:
+			perror("\tfork");
+			return false;
+		case 0:
+			fp();
+			exit(EXIT_SUCCESS);
+			break;
+		default: {
+			int status;
+			if (p != waitpid(p, &status, 0)) {
+				perror("\twaitpid");
+				return false;
+			}
+			if (WIFSIGNALED(status)) {
+				printf("\tProcess was terminated by signal %d: %s\n", WTERMSIG(status), msg);
+				return false;
+			}
+			if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+				printf("\tProcess was terminated with non-zero return value %d: %s\n", WEXITSTATUS(status), msg);
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+#define nullterminate(x) do { \
+	x[sizeof(x) - 1] = '\0'; \
+} while (false);
+
+// otool call helper
+static bool compare_to_otool_output(char *path, const macho_t *ref) {
+	FILE *tmpf = tmpfile();
+	int tmpfd;
+	if (tmpf == NULL) {
+		perror("\ttmpfile");
+		return false;
+	}
+
+	tmpfd = fileno(tmpf);
+
+	pid_t p = fork();
+	switch (p) {
+		case -1:
+			perror("\tfork");
+			goto error_out;
+		case 0:
+			if (-1 == dup2(tmpfd, STDOUT_FILENO)) {
+				perror("\tdup2");
+				exit(EXIT_FAILURE);
+			}
+			execl(OTOOL_PATH, OTOOL_PATH, "-L", "-arch", "all", path, NULL);
+			perror("\texecl");
+			exit(EXIT_FAILURE);
+		default: {
+			int status;
+			if (p != waitpid(p, &status, 0)) {
+				perror("\twaitpid");
+				goto error_out;
+			}
+			if (WIFSIGNALED(status)) {
+				fprintf(stderr, "\totool was signaled by signal %d\n", WTERMSIG(status));
+				goto error_out;
+			}
+			if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+				fprintf(stderr, "\totool terminated with non-zero return value %d\n", WEXITSTATUS(status));
+				goto error_out;
+			}
+		}
+	}
+
+	rewind(tmpf);
+
+	size_t libmachista_archs = 0;
+	for (macho_arch_t *mat = ref->mt_archs; mat; mat = mat->next) {
+		libmachista_archs++;
+	}
+	size_t found_archs = 0;
+
+	while (!feof(tmpf)) {
+		char architecture_name[32];
+		char install_name[_POSIX_PATH_MAX];
+		char compatibility_version[256];
+		char current_version[256];
+
+		// discard header line, read architecture
+		if (1 != fscanf(tmpf, "%*s (architecture %31[^)]):", architecture_name)) {
+			fprintf(stderr, "\tError getting arch header from otool output\n");
+			goto error_out;
+		}
+		nullterminate(architecture_name);
+		printf("\tarch: %s\n", architecture_name);
+
+		// search for the found architecture in libmachista output
+		const NXArchInfo *archInfo = NXGetArchInfoFromName(architecture_name);	
+		if (!archInfo) {
+			fprintf(stderr, "\tUnknown arch string in otool output: `%s'\n", architecture_name);
+			goto error_out;
+		}
+		macho_arch_t *mat;
+		for (mat = ref->mt_archs; mat; mat = mat->next) {
+			if (archInfo->cputype == mat->mat_arch) {
+				found_archs++;
+				break;
+			}
+		}
+		if (mat == NULL) {
+			printf("\tArchitecture `%s' found by otool not in libmachista output\n", architecture_name);
+			goto error_out;
+		}
+
+		// get install name
+		if (3 != fscanf(tmpf, "%*[\n]%*[\t]%255s (compatibility version %255[^,], current version %255[^)])", install_name, compatibility_version, current_version)) {
+			fprintf(stderr, "\tError getting install name from otool output\n");
+			goto error_out;
+		}
+		nullterminate(install_name);
+		nullterminate(compatibility_version);
+		nullterminate(current_version);
+
+		// format compatibility version for easier comparison
+		char *ref_current_version = macho_format_dylib_version(mat->mat_version);
+		char *ref_compatibility_version = macho_format_dylib_version(mat->mat_comp_version);
+
+		// compare compatibility versions
+		if (strcmp(current_version, ref_current_version) != 0) {
+			printf("\tCurrent version mismatch. Expected `%s', was `%s'\n", current_version, ref_current_version);
+			exit(EXIT_FAILURE);
+		}
+		if (strcmp(compatibility_version, ref_compatibility_version) != 0) {
+			printf("\tCurrent version mismatch. Expected `%s', was `%s'\n", compatibility_version, ref_compatibility_version);
+			exit(EXIT_FAILURE);
+		}
+
+		free(ref_current_version);
+		free(ref_compatibility_version);
+
+		// loop through loadcommands
+		size_t libmachista_libs = 0;
+		for (macho_loadcmd_t *mlt = mat->mat_loadcmds; mlt; mlt = mlt->next) {
+			libmachista_libs++;
+		}
+		size_t found_libs = 0;
+
+		do {
+			char lib_path[_POSIX_PATH_MAX];
+			char lib_comp_version[256];
+			char lib_curr_version[256];
+
+			// read loadcommand output line from otool
+			if (3 != fscanf(tmpf, "%*[\n]%*[\t]%255s (compatibility version %255[^,], current version %255[^)]))", lib_path, lib_comp_version, lib_curr_version)) {
+				// error out silently, probably been the last line
+				break;
+			}
+
+			nullterminate(lib_path);
+			nullterminate(lib_comp_version);
+			nullterminate(lib_curr_version);
+
+			printf("\t\t%s, %s, %s\n", lib_path, lib_comp_version, lib_curr_version);
+
+			// try to find the library in this architecture's list of loadcommands
+			macho_loadcmd_t *mlt;
+			for (mlt = mat->mat_loadcmds; mlt; mlt = mlt->next) {
+				if (strcmp(mlt->mlt_install_name, lib_path) == 0) {
+					// found library
+					found_libs++;
+
+					// check versions
+					char *ref_lib_curr_version = macho_format_dylib_version(mlt->mlt_version);
+					char *ref_lib_comp_version = macho_format_dylib_version(mlt->mlt_comp_version);
+
+					if (strcmp(lib_comp_version, ref_lib_comp_version) != 0) {
+						printf("\tLibrary compatibility version mismatch. Expected `%s', was `%s'\n", lib_comp_version, ref_lib_comp_version);
+						exit(EXIT_FAILURE);
+					}
+					if (strcmp(lib_curr_version, ref_lib_curr_version) != 0) {
+						printf("\tLibrary current version mismatch. Expected `%s', was `%s'\n", lib_curr_version, ref_lib_curr_version);
+						exit(EXIT_FAILURE);
+					}
+					break;
+				}
+			}
+			if (mlt == NULL) {
+				printf("\tLoadcommand for file `%s' found by otool not present in libmachista output\n", lib_path);
+				exit(EXIT_FAILURE);
+			}
+		} while (true);
+
+		// make sure we found the exact same number of loadcommands
+		if (libmachista_libs != found_libs) {
+			printf("\totool didn't return all libs found by libmachista. Was: %zu, expected %zu\n", found_libs, libmachista_libs);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	// make sure we found the exact same number of architectures
+	if (libmachista_archs != found_archs) {
+		printf("\totool didn't return all architectures found by libmachista. Was: %zu, expected %zu\n", found_archs, libmachista_archs);
+	}
+
+	// if we arrive here, everything went fine
+	return true;
+
+error_out:
+	fclose(tmpf);
+	return false;
+}
+
+/**
+ * Test creating and destroying a handle
+ */
+static void forked_test_handle(void) {
+	macho_handle_t *handle = macho_create_handle();
+	bool result = check(handle != NULL, "Error creating handle (epxected non-NULL, but was NULL)");
+	macho_destroy_handle(handle);
+	exit(!result);
+}
+static bool test_handle(void) {
+	puts("Testing creating and destroying handles");
+	if (fork_test(forked_test_handle, "Error creating or destroying handle")) {
+		puts("\tOK");
+		return true;
+	}
+	puts("\tError");
+	return false;
+}
+
+
+/**
+ * Test destroying a NULL handle
+ */
+static void forked_test_destroy_null(void) {
+	macho_destroy_handle(NULL);
+	exit(EXIT_SUCCESS);
+}
+static bool test_destroy_null(void) {
+	puts("Testing destroying NULL handle");
+	if (fork_test(forked_test_destroy_null, "Error destroying NULL handle")) {
+		puts("\tOK");
+		return true;
+	}
+	puts("\tError");
+	return false;
+}
+
+/**
+ * Test reading libSystem.B.dylib
+ */
+static void forked_test_libsystem(void) {
+	macho_handle_t *handle = macho_create_handle();
+	const macho_t *result;
+	int ret = 0;
+
+	// parse file
+	if ((ret = macho_parse_file(handle, LIBSYSTEM_PATH, &result)) != MACHO_SUCCESS) {
+		printf("\tError parsing `%s': %s\n", LIBSYSTEM_PATH, macho_strerror(ret));
+	}
+
+	// get otool reference output
+	bool success = compare_to_otool_output(LIBSYSTEM_PATH, result);
+
+	macho_destroy_handle(handle);
+	
+	exit(!success);
+}
+static bool test_libsystem(void) {
+	puts("Testing parsing libSystem.B.dylib");
+	if (fork_test(forked_test_libsystem, "Error parsing libSystem.B.dylib")) {
+		puts("\tOK");
+		return true;
+	}
+	puts("\tError");
+	return false;
+}
+
+/**
+ * Test macho_format_dylib_version
+ */
+static bool test_format_dylib_version(void) {
+	puts("Testing macho_format_dylib_version");
+
+	char *version_string;
+
+	// testing range
+	version_string = macho_format_dylib_version(0xffffffff);
+	if (strcmp(version_string, "65535.255.255") != 0) {
+		printf("\tmacho_format_dylib_version(0xffffffff) should be 65535.255.255, but is `%s'\n", version_string);
+		goto error_out;
+	}
+	free(version_string);
+
+	version_string = macho_format_dylib_version(0x80008080);
+	if (strcmp(version_string, "32768.128.128") != 0) {
+		printf("\tmacho_format_dylib_version(0x80008080) should be 32768.128.128, but is `%s'\n", version_string);
+		goto error_out;
+	}
+	free(version_string);
+
+	version_string = macho_format_dylib_version(0x1);
+	if (strcmp(version_string, "0.0.1") != 0) {
+		printf("\tmacho_format_dylib_version(0x1) should be 0.0.1, but is `%s'\n", version_string);
+		goto error_out;
+	}
+
+	puts("\tOK");
+	return true;
+error_out:
+	puts("\tError");
+	free(version_string);
+	return false;
+}
+
+int main() {
+	bool result = true;
+	result &= test_destroy_null();
+	result &= test_handle();
+	result &= test_format_dylib_version();
+	result &= test_libsystem();
+	exit(!result);
+}
+


Property changes on: branches/gsoc11-rev-upgrade/base/src/libmachista1.0/tests/libmachista-test.c
___________________________________________________________________
Added: svn:keywords
   + Id
Added: svn:eol-style
   + native
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macports-changes/attachments/20110712/e30f232b/attachment.html>


More information about the macports-changes mailing list