[Libdispatch-changes] [13] trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Sep 10 16:26:20 PDT 2009


Revision: 13
          http://trac.macosforge.org/projects/libdispatch/changeset/13
Author:   kvv at apple.com
Date:     2009-09-08 17:25:04 -0700 (Tue, 08 Sep 2009)
Log Message:
-----------
added basic test suite

Added Paths:
-----------
    trunk/testing/
    trunk/testing/Makefile
    trunk/testing/dispatch_after.c
    trunk/testing/dispatch_api.c
    trunk/testing/dispatch_apply.c
    trunk/testing/dispatch_c99.c
    trunk/testing/dispatch_cascade.c
    trunk/testing/dispatch_cffd.c
    trunk/testing/dispatch_debug.c
    trunk/testing/dispatch_drift.c
    trunk/testing/dispatch_group.c
    trunk/testing/dispatch_pingpong.c
    trunk/testing/dispatch_plusplus.cpp
    trunk/testing/dispatch_priority.c
    trunk/testing/dispatch_proc.c
    trunk/testing/dispatch_read.c
    trunk/testing/dispatch_readsync.c
    trunk/testing/dispatch_sema.c
    trunk/testing/dispatch_starfish.c
    trunk/testing/dispatch_test.c
    trunk/testing/dispatch_test.h
    trunk/testing/dispatch_timer_bit31.c
    trunk/testing/dispatch_timer_bit63.c
    trunk/testing/dispatch_timer_set_time.c
    trunk/testing/fd_stress.c
    trunk/testing/harness.c
    trunk/testing/leaks-wrapper
    trunk/testing/nsoperation.m
    trunk/testing/queue_finalizer.c
    trunk/testing/summarize.c

Added: trunk/testing/Makefile
===================================================================
--- trunk/testing/Makefile	                        (rev 0)
+++ trunk/testing/Makefile	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,108 @@
+# No workie: dispatch_sema
+TESTS=	dispatch_apply \
+	dispatch_api \
+	dispatch_c99 \
+	dispatch_cffd \
+	dispatch_debug \
+	queue_finalizer \
+	dispatch_group \
+	dispatch_pingpong \
+	dispatch_plusplus \
+	dispatch_priority \
+	dispatch_priority2 \
+	dispatch_proc \
+	dispatch_read \
+	dispatch_after \
+	dispatch_sema \
+	dispatch_timer_bit31 \
+	dispatch_timer_bit63 \
+	dispatch_starfish \
+	dispatch_cascade \
+	dispatch_drift \
+	dispatch_readsync \
+	nsoperation
+
+all: harness summarize bench $(TESTS)
+	@lipo -remove x86_64 -output dispatch_timer_bit31 dispatch_timer_bit31 2>/dev/null || true
+
+logs: $(addsuffix .testlog, $(TESTS))
+debuglogs: $(addsuffix .debuglog, $(TESTS))
+
+testbots:
+	$(MAKE) test
+
+test: clean-logs
+	$(MAKE) _test
+
+_test: all logs debuglogs
+	@cat *.testlog *.debuglog
+	@cat *.testlog *.debuglog | ./summarize
+
+# Override ARCHS and SDKROOT to cross-build test suite
+
+SRCS = dispatch_test.c
+OBJS = $(SRCS:%.c=%.o)
+ARCHS=i386 x86_64 ppc
+CFLAGS = -Werror -Wall -Wextra -Wshadow -mdynamic-no-pic -Os -g $(patsubst %, -arch %,$(ARCHS)) -DDISPATCH_NO_LEGACY
+CPPFLAGS = $(CFLAGS)
+LDFLAGS = $(patsubst %, -arch %,$(ARCHS))
+LDLIBS = -lstdc++
+
+ifneq ($(SDKROOT),)
+CFLAGS += -isysroot $(SDKROOT)
+LDFLAGS += -isysroot $(SDKROOT)
+CC = xcrun -sdk $(SDKROOT) gcc
+endif
+
+harness: harness.o $(OBJS)
+summarize: summarize.o
+
+dispatch_apply: dispatch_apply.o $(OBJS)
+dispatch_api: dispatch_api.o $(OBJS)
+dispatch_c99: dispatch_c99.o $(OBJS)
+dispatch_cffd: dispatch_cffd.o $(OBJS)
+	$(CC) $(LDFLAGS) -framework CoreFoundation -o $@ $^
+dispatch_debug: dispatch_debug.o $(OBJS)
+dispatch_group: dispatch_group.o $(OBJS)
+dispatch_pingpong: dispatch_pingpong.o $(OBJS)
+dispatch_plusplus: dispatch_plusplus.o $(OBJS)
+dispatch_priority: dispatch_priority.o $(OBJS)
+dispatch_priority2: dispatch_priority2.o $(OBJS)
+dispatch_proc: dispatch_proc.o $(OBJS)
+queue_finalizer: queue_finalizer.o $(OBJS)
+dispatch_read:  dispatch_read.o $(OBJS)
+dispatch_read2: dispatch_read2.o $(OBJS)
+dispatch_after: dispatch_after.o $(OBJS)
+dispatch_timer: dispatch_timer.o $(OBJS)
+dispatch_suspend_timer: dispatch_suspend_timer.o $(OBJS)
+dispatch_sema: dispatch_sema.o $(OBJS)
+dispatch_timer_bit31: dispatch_timer_bit31.o $(OBJS)
+dispatch_timer_bit63: dispatch_timer_bit63.o $(OBJS)
+dispatch_drift: dispatch_drift.o $(OBJS)
+dispatch_starfish: dispatch_starfish.o $(OBJS)
+dispatch_cascade: dispatch_cascade.o $(OBJS)
+dispatch_readsync: dispatch_readsync.o $(OBJS)
+ENVIRON_nsoperation = NOLEAKS=1
+nsoperation: nsoperation.o $(OBJS)
+	$(CC) $(LDFLAGS) -framework Foundation -o $@ $^
+
+dispatch_api.o: dispatch_api.c
+	$(CC) -c $(CFLAGS) -include $(SDKROOT)/usr/include/dispatch/dispatch.h -pendantic -o $@ $^
+
+dispatch_c99.o: dispatch_c99.c
+	$(CC) -c $(CFLAGS) -std=c99 -pedantic -o $@ $^
+
+dispatch_priority2.o: dispatch_priority.c
+	$(CC) -c $(CFLAGS) -DUSE_SET_TARGET_QUEUE=1 -o $@ $^
+
+$(addsuffix .testlog, $(TESTS)): harness $(TESTS)
+	$(ENVIRON_$(basename $@)) ./harness ./$(basename $@) > $@
+
+$(addsuffix .debuglog, $(TESTS)): harness $(TESTS)
+	$(ENVIRON_$(basename $@)) DYLD_IMAGE_SUFFIX=_debug ./harness ./$(basename $@) > $@
+
+clean-logs:
+	rm -f *.testlog *.debuglog *.leakslog
+
+clean: clean-logs
+	rm -f *.o *.dSYM bench harness summarize $(TESTS)

Added: trunk/testing/dispatch_after.c
===================================================================
--- trunk/testing/dispatch_after.c	                        (rev 0)
+++ trunk/testing/dispatch_after.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+#include <Block.h>
+
+void done(void *arg __unused) {
+    sleep(1);
+    test_stop();
+}
+
+int
+main(void)
+{
+    __block dispatch_time_t time_a_min, time_a, time_a_max;
+    __block dispatch_time_t time_b_min, time_b, time_b_max;
+    __block dispatch_time_t time_c_min, time_c, time_c_max;
+
+
+	test_start("Dispatch After");
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+            time_a_min = dispatch_time(0,  5.5*NSEC_PER_SEC);
+            time_a     = dispatch_time(0,   6*NSEC_PER_SEC);
+            time_a_max = dispatch_time(0,  6.5*NSEC_PER_SEC);
+            dispatch_after(time_a, dispatch_get_current_queue(), ^{
+                    dispatch_time_t now_a = dispatch_time(0, 0);
+                    test_long_less_than("can't finish faster than 5.5s", 0, now_a - time_a_min);
+                    test_long_less_than("must finish faster than  6.5s", 0, time_a_max - now_a);
+                    
+                    time_b_min = dispatch_time(0,  1.5*NSEC_PER_SEC);
+                    time_b     = dispatch_time(0,    2*NSEC_PER_SEC);
+                    time_b_max = dispatch_time(0,  2.5*NSEC_PER_SEC);
+                    dispatch_after(time_b, dispatch_get_current_queue(), ^{
+                            dispatch_time_t now_b = dispatch_time(0, 0);
+                            test_long_less_than("can't finish faster than 1.5s", 0, now_b - time_b_min);
+                            test_long_less_than("must finish faster than  2.5s", 0, time_b_max - now_b);
+                            
+                            time_c_min = dispatch_time(0,  0*NSEC_PER_SEC);
+                            time_c     = dispatch_time(0,  0*NSEC_PER_SEC);
+                            time_c_max = dispatch_time(0,  .5*NSEC_PER_SEC);
+                            dispatch_after(time_c, dispatch_get_current_queue(), ^{
+                                    dispatch_time_t now_c = dispatch_time(0, 0);
+                                    test_long_less_than("can't finish faster than 0s", 0, now_c - time_c_min);
+                                    test_long_less_than("must finish faster than .5s", 0, time_c_max - now_c);
+
+                                    dispatch_async_f(dispatch_get_current_queue(), NULL, done);
+                                });
+                        });
+                });
+        });
+    
+    dispatch_main();
+    return 0;
+}

Added: trunk/testing/dispatch_api.c
===================================================================
--- trunk/testing/dispatch_api.c	                        (rev 0)
+++ trunk/testing/dispatch_api.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+void
+work(void *context __attribute__((unused)))
+{
+	test_stop();
+	exit(0);
+}
+
+int main(void) {
+	test_start("Dispatch (Public) API");
+	dispatch_queue_t q = dispatch_get_main_queue();
+	test_ptr_notnull("dispatch_get_main_queue", q);
+
+	dispatch_async_f(dispatch_get_main_queue(), NULL, work);
+	dispatch_main();
+	return 0;
+}

Added: trunk/testing/dispatch_apply.c
===================================================================
--- trunk/testing/dispatch_apply.c	                        (rev 0)
+++ trunk/testing/dispatch_apply.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+int
+main(void)
+{
+	test_start("Dispatch Apply");
+
+	volatile __block int32_t count = 0;
+	const int32_t final = 32;
+
+	dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
+	test_ptr_notnull("dispatch_get_concurrent_queue", queue);
+
+	dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) {
+		OSAtomicIncrement32(&count);
+	});
+
+	test_long("count", count, final);
+	test_stop();
+
+	return 0;
+}

Added: trunk/testing/dispatch_c99.c
===================================================================
--- trunk/testing/dispatch_c99.c	                        (rev 0)
+++ trunk/testing/dispatch_c99.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+void
+work(void *context __attribute__((unused)))
+{
+	test_stop();
+	exit(0);
+}
+
+int main(void) {
+	test_start("Dispatch C99");
+	dispatch_queue_t q = dispatch_get_main_queue();
+	test_ptr_notnull("dispatch_get_main_queue", q);
+
+	dispatch_async_f(dispatch_get_main_queue(), NULL, work);
+	dispatch_main();
+	return 0;
+}

Added: trunk/testing/dispatch_cascade.c
===================================================================
--- trunk/testing/dispatch_cascade.c	                        (rev 0)
+++ trunk/testing/dispatch_cascade.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <dispatch/dispatch.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+int done = 0;
+
+#define QUEUES 80
+dispatch_queue_t queues[QUEUES];
+
+
+#define BLOCKS 10000
+union {
+	size_t index;
+	char padding[64];
+} indices[BLOCKS];
+
+size_t iterations = QUEUES * BLOCKS * 0.25;
+
+void
+histogram(void) {
+	size_t counts[QUEUES] = {};
+	size_t maxcount = 0;
+	
+	size_t q;
+	for (q = 0; q < QUEUES; ++q) {
+		size_t i;
+		for (i = 0; i < BLOCKS; ++i) {
+			if (indices[i].index == q) {
+				++counts[q];
+			}
+		}
+	}
+	
+	for (q = 0; q < QUEUES; ++q) {
+		if (counts[q] > maxcount) {
+			maxcount = counts[q];
+		}
+	}
+	
+	printf("maxcount = %ld\n", maxcount);
+	
+	size_t x,y;
+	for (y = 20; y > 0; --y) {
+		for (x = 0; x < QUEUES; ++x) {
+			double fraction = (double)counts[x] / (double)maxcount;
+			double value = fraction * (double)20;
+			printf("%s", (value > y) ? "*" : " ");
+		}
+		printf("\n");
+	}
+}
+
+void
+cascade(void* context) {
+	size_t idx, *idxptr = context;
+
+	if (done) return;
+	
+	idx = *idxptr + 1;
+
+	if (idx < QUEUES) {
+		*idxptr = idx;
+		dispatch_async_f(queues[idx], context, cascade);
+	}
+
+	if (__sync_sub_and_fetch(&iterations, 1) == 0) {
+		done = 1;
+		histogram();
+		test_stop();
+		exit(0);
+	}
+}
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) {
+	int i;
+
+	test_start("Dispatch Cascade");
+	
+	for (i = 0; i < QUEUES; ++i) {
+		queues[i] = dispatch_queue_create(NULL, NULL);
+	}
+
+	for (i = 0; i < BLOCKS; ++i) {
+		cascade(&indices[i].index);
+	}
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/dispatch_cffd.c
===================================================================
--- trunk/testing/dispatch_cffd.c	                        (rev 0)
+++ trunk/testing/dispatch_cffd.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include <CoreServices/CoreServices.h>
+
+#include "dispatch_test.h"
+
+int debug = 0;
+
+#define DEBUG(...) do { \
+		if (debug) fprintf(stderr, __VA_ARGS__); \
+	} while(0);
+
+#define assert_errno(str, expr) do { \
+	if (!(expr)) { \
+		fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \
+		exit(1); \
+	} } while(0);
+
+int
+init_kqueue(void)
+{
+	int kq;
+	int res;
+	struct kevent ke;
+	static struct timespec t0;
+
+	kq = kqueue();
+	assert_errno("kqueue", kq >= 0);
+	
+	EV_SET(&ke, 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 1, 0);
+	
+	res = kevent(kq, &ke, 1, NULL, 0, &t0);
+	assert_errno("kevent", res == 0);
+
+	return kq;
+}
+
+int
+read_kevent(int kq)
+{
+	int res;
+	struct kevent ke;
+	//static struct timespec t0;
+
+	res = kevent(kq, NULL, 0, &ke, 1, NULL);
+	assert_errno("kevent", res >= 0);
+
+	fprintf(stdout, "kevent.data = %ld\n", ke.data);
+
+	return (res < 0);
+}
+
+
+static void
+cffd_callback(CFFileDescriptorRef cffd,
+	CFOptionFlags callBackTypes __attribute__((unused)),
+	void *info __attribute__((unused)))
+{
+	int kq;
+
+	kq = CFFileDescriptorGetNativeDescriptor(cffd);
+	if (read_kevent(kq) == 0) {
+		// ...
+	}
+ 
+	CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
+}
+
+void
+timer()
+{
+	dispatch_source_t ds;
+	ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+	assert(ds);
+	dispatch_source_set_timer(ds, dispatch_time(0, 1*NSEC_PER_SEC), NSEC_PER_SEC, 0);
+	dispatch_source_set_event_handler(ds, ^{
+		printf("ping\n");
+	});
+	dispatch_resume(ds);
+}
+
+void
+hangup()
+{
+	dispatch_source_t ds;
+	ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, dispatch_get_main_queue());
+	assert(ds);
+	dispatch_source_set_event_handler(ds, ^{
+		printf("hangup\n");
+	});
+	dispatch_resume(ds);
+}
+
+int
+main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+	int kq;
+	CFFileDescriptorRef cffd;
+	CFRunLoopSourceRef  rls;
+	CFFileDescriptorContext ctx;
+
+	test_start("CFFileDescriptor");
+
+	signal(SIGHUP, SIG_IGN);
+
+	kq = init_kqueue();
+
+	memset(&ctx, 0, sizeof(CFFileDescriptorContext));
+	cffd = CFFileDescriptorCreate(NULL, kq, 1, cffd_callback, &ctx);
+	assert(cffd);
+    
+	rls = CFFileDescriptorCreateRunLoopSource(NULL, cffd, 0);
+	assert(rls);
+	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+	CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
+
+//	timer();
+//	hangup();
+	
+	CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10.0, false);
+
+	test_stop();
+
+	return 0;
+}
+

Added: trunk/testing/dispatch_debug.c
===================================================================
--- trunk/testing/dispatch_debug.c	                        (rev 0)
+++ trunk/testing/dispatch_debug.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int
+main(void)
+{
+	test_start("Dispatch Debug");
+
+	dispatch_queue_t main_q = dispatch_get_main_queue();
+	dispatch_debug(main_q, "dispatch_queue_t");
+	
+	dispatch_queue_t default_q = dispatch_get_global_queue(0, 0);
+	dispatch_debug(default_q, "dispatch_queue_t");
+	
+	dispatch_source_t s = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
+	dispatch_debug(s, "dispatch_source_t");
+
+	dispatch_group_t g = dispatch_group_create();
+	dispatch_debug(g, "dispatch_group_t");
+
+	return 0;
+}

Added: trunk/testing/dispatch_drift.c
===================================================================
--- trunk/testing/dispatch_drift.c	                        (rev 0)
+++ trunk/testing/dispatch_drift.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <math.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "dispatch_test.h"
+
+//
+// Verify that dispatch timers do not drift, that is to say, increasingly
+// stray from their intended interval.
+//
+// The jitter of the event handler is defined to be the amount of time
+// difference between the actual and expected timing of the event handler
+// invocation. The drift is defined to be the amount that the jitter changes
+// over time. 
+//
+// Important: be sure to use the same clock when comparing actual and expected
+// values. Skew between different clocks is to be expected.
+//
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
+{
+	__block uint32_t count = 0;
+	__block double last_jitter = 0;
+	
+	// interval is 1/10th of a second
+	uint64_t interval = NSEC_PER_SEC / 10;
+	double interval_d = (double)interval / (double)NSEC_PER_SEC;
+	
+	// for 25 seconds
+	unsigned int target = 25 / interval_d;
+
+	__block double first = 0;
+	__block double jittersum = 0;
+	__block double driftsum = 0;
+	
+	test_start("Timer drift test");
+
+	dispatch_source_t timer;
+	timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+	test_ptr_notnull("DISPATCH_SOURCE_TYPE_TIMER", timer);
+	
+	dispatch_source_set_event_handler(timer, ^{
+		struct timeval now_tv;
+		gettimeofday(&now_tv, NULL);
+		double now = now_tv.tv_sec + ((double)now_tv.tv_usec / (double)USEC_PER_SEC);
+
+		if (first == 0) {
+			first = now;
+		}
+
+		double goal = first + interval_d * count;
+		double jitter = goal - now;
+		double drift = jitter - last_jitter;
+
+		count += dispatch_source_get_data(timer);
+		jittersum += jitter;
+		driftsum += drift;
+		
+		printf("%4d: jitter %f, drift %f\n", count, jitter, drift);
+		
+		if (count >= target) {
+			test_double_less_than("average jitter", fabs(jittersum) / (double)count, 0.0001);
+			test_double_less_than("average drift", fabs(driftsum) / (double)count, 0.0001);
+			test_stop();
+		}
+		last_jitter = jitter;
+	});
+	
+	struct timeval now_tv;
+	struct timespec now_ts;
+	
+	gettimeofday(&now_tv, NULL);
+	now_ts.tv_sec = now_tv.tv_sec;
+	now_ts.tv_nsec = now_tv.tv_usec * NSEC_PER_USEC;
+
+	dispatch_source_set_timer(timer, dispatch_walltime(&now_ts, interval), interval, 0);
+
+	dispatch_resume(timer);
+
+	dispatch_main();
+	return 0;
+}
+

Added: trunk/testing/dispatch_group.c
===================================================================
--- trunk/testing/dispatch_group.c	                        (rev 0)
+++ trunk/testing/dispatch_group.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC 1000000000
+#endif
+
+dispatch_group_t
+create_group(size_t count, int delay)
+{
+	size_t i;
+
+	dispatch_group_t group = dispatch_group_create();
+
+	for (i = 0; i < count; ++i) {
+		dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
+		assert(queue);
+
+		dispatch_group_async(group, queue, ^{
+			if (delay) {
+				fprintf(stderr, "sleeping...\n");
+				sleep(delay);
+				fprintf(stderr, "done.\n");
+			}
+		});
+
+		dispatch_release(queue);
+        }
+	return group;
+}
+
+int
+main(void)
+{
+	long res;
+
+	test_start("Dispatch Group");
+
+	dispatch_group_t group;
+
+	group = create_group(100, 0);
+	test_ptr_notnull("dispatch_group_async", group);
+
+	dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+	
+	// should be OK to re-use a group
+	dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{});
+	dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+
+	dispatch_release(group);
+	group = NULL;
+	
+	group = create_group(3, 7);
+	test_ptr_notnull("dispatch_group_async", group);
+
+	res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC));
+	test_long("dispatch_group_wait", !res, 0);
+
+	// retry after timeout (this time succeed)
+	res = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC));
+	test_long("dispatch_group_wait", res, 0);
+
+	dispatch_release(group);
+	group = NULL;
+
+	group = create_group(100, 0);
+	test_ptr_notnull("dispatch_group_async", group);
+
+	dispatch_group_notify(group, dispatch_get_main_queue(), ^{
+		dispatch_queue_t m = dispatch_get_main_queue();
+		dispatch_queue_t c = dispatch_get_current_queue();
+		test_ptr("Notification Received", m, c);
+		test_stop();
+	});
+	
+	dispatch_release(group);
+	group = NULL;
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/dispatch_pingpong.c
===================================================================
--- trunk/testing/dispatch_pingpong.c	                        (rev 0)
+++ trunk/testing/dispatch_pingpong.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+
+#include "dispatch_test.h"
+
+uint32_t count = 0;
+const uint32_t final = 1000000; // 10M
+
+void pingpongloop(dispatch_group_t group, dispatch_queue_t ping, dispatch_queue_t pong, size_t counter) {
+	//printf("[%p] %s: %lu\n", (void*)(uintptr_t)pthread_self(), dispatch_queue_get_label(dispatch_get_current_queue()), counter);
+	if (counter < final) {
+		dispatch_group_async(group, pong, ^{ pingpongloop(group, pong, ping, counter+1); });
+	} else {
+		count = counter;
+	}
+}
+
+int main(void) {
+	
+	test_start("Dispatch Ping Pong");
+
+	dispatch_queue_t ping = dispatch_queue_create("ping", NULL);
+	test_ptr_notnull("dispatch_queue_create(ping)", ping);
+	dispatch_queue_t pong = dispatch_queue_create("pong", NULL);
+	test_ptr_notnull("dispatch_queue_create(pong)", pong);
+	
+	dispatch_group_t group = dispatch_group_create();
+	test_ptr_notnull("dispatch_group_create", group);
+	
+	pingpongloop(group, ping, pong, 0);
+	dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+
+	test_long("count", count, final);
+	test_stop();
+
+	return 0;
+}

Added: trunk/testing/dispatch_plusplus.cpp
===================================================================
--- trunk/testing/dispatch_plusplus.cpp	                        (rev 0)
+++ trunk/testing/dispatch_plusplus.cpp	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <stdlib.h>
+
+#include "dispatch_test.h"
+
+int main(void) {
+	test_start("Dispatch C++");
+	dispatch_queue_t q = dispatch_get_main_queue();
+	test_ptr_notnull("dispatch_get_main_queue", q);
+
+	dispatch_async(dispatch_get_main_queue(), ^{
+		test_stop();
+		exit(0);
+	});
+	dispatch_main();
+	return 0;
+}

Added: trunk/testing/dispatch_priority.c
===================================================================
--- trunk/testing/dispatch_priority.c	                        (rev 0)
+++ trunk/testing/dispatch_priority.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <dispatch/dispatch.h>
+#include <dispatch/queue_private.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <TargetConditionals.h>
+
+#include "dispatch_test.h"
+
+int done = 0;
+
+#define BLOCKS 128
+#define PRIORITIES 3
+
+#if TARGET_OS_EMBEDDED
+#define LOOP_COUNT 2000000
+#else
+#define LOOP_COUNT 100000000
+#endif
+
+char *labels[PRIORITIES] = { "LOW", "DEFAULT", "HIGH" };
+int priorities[PRIORITIES] = { DISPATCH_QUEUE_PRIORITY_LOW, DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_HIGH };
+
+union {
+	size_t count;
+	char padding[64];
+} counts[PRIORITIES];
+
+#define ITERATIONS (size_t)(PRIORITIES * BLOCKS * 0.50)
+size_t iterations = ITERATIONS;
+
+void
+histogram(void) {
+	size_t maxcount = BLOCKS;
+	size_t sc[PRIORITIES];
+	
+	size_t total = 0;
+	
+	size_t x,y;
+	for (y = 0; y < PRIORITIES; ++y) {
+		sc[y] = counts[y].count;
+	}
+
+	for (y = 0; y < PRIORITIES; ++y) {
+		printf("%s: %ld\n", labels[y], sc[y]);
+		total += sc[y];
+		
+		double fraction = (double)sc[y] / (double)maxcount;
+		double value = fraction * (double)80;
+		for (x = 0; x < 80; ++x) {
+			printf("%s", (value > x) ? "*" : " ");
+		}
+		printf("\n");
+	}
+	
+	test_long("blocks completed", total, ITERATIONS);
+	test_long_less_than("high priority precedence", (long)sc[0], (long)sc[2]);
+}
+
+void
+cpubusy(void* context)
+{
+	size_t *count = context;
+	size_t iterdone;
+
+	size_t idx;
+	for (idx = 0; idx < LOOP_COUNT; ++idx) {
+		if (done) break;
+	}
+	
+	if ((iterdone = __sync_sub_and_fetch(&iterations, 1)) == 0) {
+		__sync_add_and_fetch(&done, 1);
+		__sync_add_and_fetch(count, 1);
+		histogram();
+		test_stop();
+		exit(0);
+	} else if (iterdone > 0) {
+		__sync_add_and_fetch(count, 1);
+	}
+}
+
+void
+submit_work(dispatch_queue_t queue, void* context)
+{
+	int i;
+
+	for (i = 0; i < BLOCKS; ++i) {
+		dispatch_async_f(queue, context, cpubusy);
+	}
+
+#if USE_SET_TARGET_QUEUE
+	dispatch_release(queue);
+#endif
+}
+
+int
+main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
+{
+	dispatch_queue_t q[PRIORITIES];
+	int i;
+
+#if USE_SET_TARGET_QUEUE
+	test_start("Dispatch Priority (Set Target Queue)");
+	for(i = 0; i < PRIORITIES; i++) {
+		q[i] = dispatch_queue_create(labels[i], NULL);
+		test_ptr_notnull("q[i]", q[i]);
+		assert(q[i]);
+		dispatch_set_target_queue(q[i], dispatch_get_global_queue(priorities[i], 0));
+		dispatch_queue_set_width(q[i], DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS);
+	}
+#else
+	test_start("Dispatch Priority");
+	for(i = 0; i < PRIORITIES; i++) {
+		q[i] = dispatch_get_global_queue(priorities[i], 0);
+	}
+#endif
+	
+	for(i = 0; i < PRIORITIES; i++) {
+		submit_work(q[i], &counts[i].count);
+	}
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/dispatch_proc.c
===================================================================
--- trunk/testing/dispatch_proc.c	                        (rev 0)
+++ trunk/testing/dispatch_proc.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <spawn.h>
+#include <signal.h>
+#include <libkern/OSAtomic.h>
+
+#include "dispatch_test.h"
+
+#define PID_CNT 5
+
+static long event_cnt;
+
+int
+main(void)
+{
+	dispatch_source_t proc;
+	int res;
+	pid_t pid;
+
+	test_start("Dispatch Proc");
+	
+	// Creates a process and register multiple observers.  Send a signal,
+	// exit the process, etc., and verify all observers were notified.
+	
+	//
+	// Simple child process that sleeps 2 seconds.
+	//
+	
+	posix_spawnattr_t attr;
+	res = posix_spawnattr_init(&attr);
+	assert(res == 0);
+	res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
+	assert(res == 0);
+
+	char* args[] = {
+		"/bin/sleep", "2", NULL
+	};
+	
+	res = posix_spawnp(&pid, args[0], NULL, &attr, args, NULL);
+	if (res < 0) {
+		perror(args[0]);
+		exit(127);
+	}
+
+	res = posix_spawnattr_destroy(&attr);
+	assert(res == 0);
+
+	dispatch_queue_t completion = dispatch_queue_create("completion", NULL);
+	
+	assert(pid > 0);
+
+	//
+	// Suspend the "completion" queue when each observer is created.
+	// Each observer resumes the queue when the child process exits.
+	// If the queue is resumed too few times (indicating that not all
+	// observers received the exit event) then the test case will not exit
+	// within the alloted time and result in failure.
+	//
+	
+	int i;
+	for (i = 0; i < PID_CNT; ++i) {
+		dispatch_suspend(completion);
+		proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+		test_ptr_notnull("DISPATCH_SOURCE_TYPE_PROC", proc);
+
+		dispatch_source_set_event_handler(proc, ^{
+			long flags = dispatch_source_get_data(proc);
+			test_long("DISPATCH_PROC_EXIT", flags, DISPATCH_PROC_EXIT);
+			event_cnt++;
+			dispatch_release(proc);
+			dispatch_resume(completion);
+		});
+
+		dispatch_resume(proc);
+	}
+
+
+	//
+	// The completion block will be pending on the completion queue until it
+	// has been fully resumed, at which point the test will exit successfully.
+	//
+
+	dispatch_async(completion, ^{
+		int status;
+		int res2 = waitpid(pid, &status, 0);
+		assert(res2 != -1);
+		test_long("Sub-process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
+		test_long("Event count", event_cnt, PID_CNT);
+		test_stop();
+	});
+
+	kill(pid, SIGCONT);
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/dispatch_read.c
===================================================================
--- trunk/testing/dispatch_read.c	                        (rev 0)
+++ trunk/testing/dispatch_read.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <sys/stat.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+static size_t bytes_total;
+static size_t bytes_read;
+
+int main(void)
+{
+	const char *path = "/usr/share/dict/words";
+	struct stat sb;
+
+	test_start("Dispatch Source Read");
+	
+	int infd = open(path, O_RDONLY);
+	if (infd == -1) {
+		perror(path);
+		exit(EXIT_FAILURE);
+	}
+	if (fstat(infd, &sb) == -1) {
+		perror(path);
+		exit(EXIT_FAILURE);
+	}
+	bytes_total = sb.st_size;
+
+	if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) {
+		perror(path);
+		exit(EXIT_FAILURE);
+	}
+
+	dispatch_queue_t main_q = dispatch_get_main_queue();
+	test_ptr_notnull("dispatch_get_main_queue", main_q);
+
+	dispatch_source_t reader;
+	
+	reader = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, main_q);
+	test_ptr_notnull("DISPATCH_SOURCE_TYPE_READ", reader);
+	
+	dispatch_source_set_event_handler(reader, ^{
+			size_t estimated = dispatch_source_get_data(reader);
+			printf("bytes available: %zu\n", estimated);
+			const ssize_t bufsiz = 1024*500; // 500 KB buffer
+			static char buffer[1024*500];	// 500 KB buffer
+			ssize_t actual = read(infd, buffer, sizeof(buffer));
+			bytes_read += actual;
+			printf("bytes read: %zd\n", actual);
+			if (actual < bufsiz) {
+				actual = read(infd, buffer, sizeof(buffer));
+				bytes_read += actual;
+				// confirm EOF condition
+				test_long("EOF", actual, 0);
+				dispatch_source_cancel(reader);
+				dispatch_release(reader);
+			}
+	});
+	
+	dispatch_source_set_cancel_handler(reader, ^{
+		test_long("Bytes read", bytes_read, bytes_total);
+		int res = close(infd);
+		test_errno("close", res == -1 ? errno : 0, 0);
+		test_stop();
+	});
+
+	dispatch_resume(reader);
+
+	dispatch_main();
+}

Added: trunk/testing/dispatch_readsync.c
===================================================================
--- trunk/testing/dispatch_readsync.c	                        (rev 0)
+++ trunk/testing/dispatch_readsync.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+#define LAPS 10000
+#define INTERVAL 100
+
+static size_t r_count = LAPS;
+static size_t w_count = LAPS / INTERVAL;
+
+static void
+writer(void *ctxt __attribute__((unused)))
+{
+	if (--w_count == 0) {
+		if (r_count == 0) {
+			test_stop();
+		}
+	}
+}
+
+static void
+reader(void *ctxt __attribute__((unused)))
+{
+	if (__sync_sub_and_fetch(&r_count, 1) == 0) {
+		if (r_count == 0) {
+			test_stop();
+		}
+	}
+}
+
+int
+main(void)
+{
+	dispatch_queue_t dq;
+
+	test_start("Dispatch Reader/Writer Queues");
+
+	dq = dispatch_queue_create("com.apple.libdispatch.test_readsync", NULL);
+	assert(dq);
+
+	dispatch_queue_set_width(dq, LONG_MAX);
+
+	dispatch_apply(LAPS, dispatch_get_global_queue(0, 0), ^(size_t idx) {
+		dispatch_sync_f(dq, NULL, reader);
+
+		if (idx % INTERVAL) {
+			dispatch_barrier_async_f(dq, NULL, writer);
+		}
+	});
+
+	dispatch_release(dq);
+
+	dispatch_main();
+}

Added: trunk/testing/dispatch_sema.c
===================================================================
--- trunk/testing/dispatch_sema.c	                        (rev 0)
+++ trunk/testing/dispatch_sema.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+#define LAPS 10000
+
+int
+main(void)
+{
+	static size_t total;
+	dispatch_semaphore_t dsema;
+
+	test_start("Dispatch Semaphore");
+
+	dsema = dispatch_semaphore_create(1);
+	assert(dsema);
+
+	dispatch_apply(LAPS, dispatch_get_global_queue(0, 0), ^(size_t idx __attribute__((unused))) {
+		dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
+		total++;
+		dispatch_semaphore_signal(dsema);
+	});
+
+	dispatch_release(dsema);
+
+	test_long("count", total, LAPS);
+	test_stop();
+
+	return 0;
+}

Added: trunk/testing/dispatch_starfish.c
===================================================================
--- trunk/testing/dispatch_starfish.c	                        (rev 0)
+++ trunk/testing/dispatch_starfish.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <dispatch/dispatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <TargetConditionals.h>
+
+#include "dispatch_test.h"
+
+#if TARGET_OS_EMBEDDED
+#define COUNT	300ul
+#define LAPS	10ul
+#else
+#define COUNT	1000ul
+#define LAPS	10ul
+#endif
+
+static dispatch_queue_t queues[COUNT];
+static size_t lap_count_down = LAPS;
+static size_t count_down;
+static uint64_t start;
+static mach_timebase_info_data_t tbi;
+
+static void do_test(void);
+
+static void
+collect(void *context __attribute__((unused)))
+{
+	uint64_t delta;
+	long double math;
+	size_t i;
+
+	if (--count_down) {
+		return;
+	}
+
+	delta = mach_absolute_time() - start;
+	delta *= tbi.numer;
+	delta /= tbi.denom;
+	math = delta;
+	math /= COUNT * COUNT * 2ul + COUNT * 2ul;
+
+	printf("lap: %ld\n", lap_count_down);
+	printf("count: %lu\n", COUNT);
+	printf("delta: %llu ns\n", delta);
+	printf("math: %Lf ns / lap\n", math);
+
+	for (i = 0; i < COUNT; i++) {
+		dispatch_release(queues[i]);
+	}
+
+	// our malloc could be a lot better,
+	// this result is really a malloc torture test
+	test_long_less_than("Latency" , (unsigned long)math, 1000);
+
+	if (--lap_count_down) {
+		return do_test();
+	}
+
+	// give the threads some time to settle before test_stop() runs "leaks"
+	// ...also note, this is a total cheat.   dispatch_after lets this
+	// thread go idle, so dispatch cleans up the continuations cache.
+	// Doign the "old style" sleep left that stuff around and leaks
+	// took a LONG TIME to complete.   Long enough that the test harness
+	// decided to kill us.
+	dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), NULL, test_stop_after_delay);
+}
+
+static void
+pong(void *context)
+{
+	dispatch_queue_t this_q = context;
+	size_t replies = (size_t)dispatch_get_context(this_q);
+
+	dispatch_set_context(this_q, (void *)--replies);
+	if (!replies) {
+		//printf("collect from: %s\n", dispatch_queue_get_label(dispatch_get_current_queue()));
+		dispatch_async_f(dispatch_get_main_queue(), NULL, collect);
+	}
+}
+
+static void
+ping(void *context)
+{
+	dispatch_queue_t reply_q = context;
+
+	dispatch_async_f(reply_q, reply_q, pong);
+}
+
+static void
+start_node(void *context)
+{
+	dispatch_queue_t this_q = context;
+	size_t i;
+
+	dispatch_set_context(this_q, (void *)COUNT);
+
+	for (i = 0; i < COUNT; i++) {
+		dispatch_async_f(queues[i], this_q, ping);
+	}
+}
+
+void
+do_test(void)
+{
+	size_t i;
+	kern_return_t kr;
+
+	count_down = COUNT;
+
+	kr = mach_timebase_info(&tbi);
+	assert(kr == 0);
+
+	start = mach_absolute_time();
+
+	for (i = 0; i < COUNT; i++) {
+		char buf[1000];
+		snprintf(buf, sizeof(buf), "com.example.starfish-node#%ld", i);
+		queues[i] = dispatch_queue_create(buf, NULL);
+		dispatch_suspend(queues[i]);
+	}
+
+	for (i = 0; i < COUNT; i++) {
+		dispatch_async_f(queues[i], queues[i], start_node);
+	}
+
+	for (i = 0; i < COUNT; i++) {
+		dispatch_resume(queues[i]);
+	}
+}
+
+int
+main(void)
+{
+    test_start("Dispatch Starfish");
+
+	do_test();
+
+	dispatch_main();
+}

Added: trunk/testing/dispatch_test.c
===================================================================
--- trunk/testing/dispatch_test.c	                        (rev 0)
+++ trunk/testing/dispatch_test.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,148 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/errno.h>
+#include <string.h>
+
+#include "dispatch_test.h"
+
+#define _test_print(_file, _line, _desc, \
+	_expr, _fmt1, _val1, _fmt2, _val2) do { \
+	const char* _exprstr = _expr ? "PASS" : "FAIL"; \
+	char _linestr[BUFSIZ]; \
+	if (!_expr) { \
+		snprintf(_linestr, sizeof(_linestr), \
+			" (%s:%ld)", _file, _line); \
+	} else { \
+		_linestr[0] = 0; \
+	} \
+	if (_fmt2 == 0) { \
+		printf("\tValue: " _fmt1 "\n"		\
+			"[%s] %s%s\n",			\
+			_val1,				\
+			_exprstr,			\
+			_desc,				\
+			_linestr); 			\
+	} else { \
+		printf("\tActual: " _fmt1 "\n"	   \
+			"\tExpected: " _fmt2 "\n"  \
+			"[%s] %s%s\n",		   \
+			_val1,			   \
+			_val2,			   \
+			_exprstr,		   \
+			_desc,			   \
+			_linestr);		   \
+	} \
+	if (!_expr) { \
+		printf("\t%s:%ld\n", _file, _line); \
+	} \
+	fflush(stdout); \
+} while (0);
+
+void
+test_start(const char* desc) {
+	printf("\n==================================================\n");
+	printf("[TEST] %s\n", desc);
+	printf("[PID] %d\n", getpid());
+	printf("==================================================\n\n");
+	usleep(100000);	// give 'gdb --waitfor=' a chance to find this proc
+}
+
+#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b)
+void
+_test_ptr_null(const char* file, long line, const char* desc, const void* ptr) {
+	_test_print(file, line, desc,
+		(ptr == NULL), "%p", ptr, "%p", (void*)0);
+}
+
+#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b)
+void
+_test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr) {
+	_test_print(file, line, desc,
+		(ptr != NULL), "%p", ptr, "%p", ptr ?: (void*)~0);
+}
+
+#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c)
+void
+_test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected) {
+	_test_print(file, line, desc,
+		(actual == expected), "%p", actual, "%p", expected);
+}
+
+#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c)
+void
+_test_long(const char* file, long line, const char* desc, long actual, long expected) {
+	_test_print(file, line, desc,
+		(actual == expected), "%ld", actual, "%ld", expected);
+}
+
+#define test_long_less_than(a, b, c) _test_long_less_than(__FILE__, __LINE__, a, b, c)
+void
+_test_long_less_than(const char* file, long line, const char* desc, long actual, long expected_max) {
+	_test_print(file, line, desc, (actual < expected_max), "%ld", actual, "<%ld", expected_max);
+}
+
+#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+void
+_test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected) {
+	_test_print(file, line, desc, (val < max_expected), "%f", val, "<%f", max_expected);
+}
+
+#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+void
+_test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected) {
+	_test_print(file, line, desc, (val <= max_expected), "%f", val, "<%f", max_expected);
+}
+
+#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c)
+void
+_test_errno(const char* file, long line, const char* desc, long actual, long expected) {
+	char* actual_str;
+	char* expected_str;
+	asprintf(&actual_str, "%ld\t%s", actual, actual ? strerror(actual) : "");
+	asprintf(&expected_str, "%ld\t%s", expected, expected ? strerror(expected) : "");
+	_test_print(file, line, desc,
+		(actual == expected), "%s", actual_str, "%s", expected_str);
+	free(actual_str);
+	free(expected_str);
+}
+
+#include <spawn.h>
+
+extern char **environ;
+
+void
+test_stop(void) {
+	test_stop_after_delay((void *)(intptr_t)0);
+}
+
+void
+test_stop_after_delay(void *delay) {
+	int res;
+	pid_t pid;
+	char pidstr[10];
+
+	if (delay != NULL) {
+		sleep((int)(intptr_t)delay);
+	}
+
+	if (getenv("NOLEAKS")) _exit(EXIT_SUCCESS);
+
+	/* leaks doesn't work against debug variant malloc */
+	if (getenv("DYLD_IMAGE_SUFFIX")) _exit(EXIT_SUCCESS);
+	
+	snprintf(pidstr, sizeof(pidstr), "%d", getpid());
+	char* args[] = { "./leaks-wrapper", pidstr, NULL };
+	res = posix_spawnp(&pid, args[0], NULL, NULL, args, environ);
+	if (res == 0 && pid > 0) {
+		int status;
+		waitpid(pid, &status, 0);
+		test_long("Leaks", status, 0);
+	} else {
+		perror(args[0]);
+	}
+	
+	_exit(EXIT_SUCCESS);
+}

Added: trunk/testing/dispatch_test.h
===================================================================
--- trunk/testing/dispatch_test.h	                        (rev 0)
+++ trunk/testing/dispatch_test.h	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,33 @@
+#include <errno.h>
+
+__BEGIN_DECLS
+
+void test_start(const char* desc);
+void test_stop(void);
+void test_stop_after_delay(void *delay);
+
+void _test_ptr_null(const char* file, long line, const char* desc, const void* ptr);
+#define test_ptr_null(a,b) _test_ptr_null(__FILE__, __LINE__, a, b)
+
+void _test_ptr_notnull(const char* file, long line, const char* desc, const void* ptr);
+#define test_ptr_notnull(a,b) _test_ptr_notnull(__FILE__, __LINE__, a, b)
+
+void _test_ptr(const char* file, long line, const char* desc, const void* actual, const void* expected);
+#define test_ptr(a,b,c) _test_ptr(__FILE__, __LINE__, a, b, c)
+
+void _test_long(const char* file, long line, const char* desc, long actual, long expected);
+#define test_long(a,b,c) _test_long(__FILE__, __LINE__, a, b, c)
+
+void _test_long_less_than(const char* file, long line, const char* desc, long actual, long max_expected);
+#define test_long_less_than(a,b,c) _test_long_less_than(__FILE__, __LINE__, a, b, c)
+
+void _test_double_less_than_or_equal(const char* file, long line, const char* desc, double val, double max_expected);
+#define test_double_less_than_or_equal(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+
+void _test_double_less_than(const char* file, long line, const char* desc, double val, double max_expected);
+#define test_double_less_than(d, v, m) _test_double_less_than(__FILE__, __LINE__, d, v, m)
+
+void _test_errno(const char* file, long line, const char* desc, long actual, long expected);
+#define test_errno(a,b,c) _test_errno(__FILE__, __LINE__, a, b, c)
+
+__END_DECLS

Added: trunk/testing/dispatch_timer_bit31.c
===================================================================
--- trunk/testing/dispatch_timer_bit31.c	                        (rev 0)
+++ trunk/testing/dispatch_timer_bit31.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+//
+// There were several bugs related to sign extension / integer overflow that
+// were encountered during development. Create a timer whose interval has the
+// 31st bit of the word set to verify that it behaves correctly.
+//
+// 2 s < 0x80000000ull ns < 4 s
+//
+
+int
+main(void)
+{
+	test_start("Dispatch Source Timer, bit 31");
+
+	dispatch_queue_t main_q = dispatch_get_main_queue();
+	test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+	dispatch_source_t timer;
+	struct timeval start_time;
+	
+	gettimeofday(&start_time, NULL);
+
+	timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_q);
+	test_ptr_notnull("DISPATCH_SOURCE_TYPE_TIMER", timer);
+
+	dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0x80000000ull), 0, 0);
+
+	dispatch_source_set_event_handler(timer, ^{
+		dispatch_source_cancel(timer);
+	});
+
+	dispatch_source_set_cancel_handler(timer, ^{
+		struct timeval end_time;
+		gettimeofday(&end_time, NULL);
+
+		test_long_less_than("elapsed time < 4s", end_time.tv_sec - start_time.tv_sec, 4);
+		test_long_less_than("elapsed time > 2s", 1, end_time.tv_sec - start_time.tv_sec);
+		test_stop();
+	});
+	
+	dispatch_resume(timer);
+	
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/dispatch_timer_bit63.c
===================================================================
--- trunk/testing/dispatch_timer_bit63.c	                        (rev 0)
+++ trunk/testing/dispatch_timer_bit63.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+//
+// There were several bugs related to sign extension / integer overflow that
+// were encountered during development. Create a timer whose interval has the
+// 63rd bit of the word set to verify that it behaves correctly.
+//
+// The expected side-effect of integer overflow would be for the timer to
+// fire immediately since its interval has "elapsed." If the timer does not
+// fire within 1s we can assume the arithmetic was performed correctly.
+//
+
+int
+main(void)
+{
+	test_start("Dispatch Source Timer, bit 63");
+
+	//uint64_t interval = 0xffffffffffffffffull;
+	uint64_t interval = 0x8000000000000001ull;
+
+	dispatch_queue_t mainq = dispatch_get_main_queue();
+
+	__block int i = 0;
+	struct timeval start_time;
+
+	gettimeofday(&start_time, NULL);
+
+	dispatch_source_t ds;
+	ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, mainq);
+	assert(ds);
+	dispatch_source_set_event_handler(ds, ^{
+		assert(i < 1);
+		printf("%d\n", i++);
+	});
+	dispatch_source_set_timer(ds, DISPATCH_TIME_NOW, interval, 0);
+	dispatch_resume(ds);
+
+	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC),
+		dispatch_get_main_queue(), ^{
+		test_stop();
+	});
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/dispatch_timer_set_time.c
===================================================================
--- trunk/testing/dispatch_timer_set_time.c	                        (rev 0)
+++ trunk/testing/dispatch_timer_set_time.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,60 @@
+#include <sys/time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+int main(void)
+{
+	test_start("Dispatch Update Timer");
+
+	dispatch_queue_t main_q = dispatch_get_main_queue();
+	test_ptr("dispatch_get_main_queue", main_q, dispatch_get_current_queue());
+
+	__block int i = 0;
+	struct timeval start_time;
+
+	gettimeofday(&start_time, NULL);
+	dispatch_source_attr_t attr = dispatch_source_attr_create();
+	dispatch_source_attr_set_finalizer(attr, ^(dispatch_source_t ds) {
+		struct timeval end_time;
+		gettimeofday(&end_time, NULL);
+		// Make sure we actually managed to adjust the interval
+		// duration.  Seven one second ticks would blow past
+		// this.
+		test_long_less_than("total duration", end_time.tv_sec - start_time.tv_sec, 3);
+		test_ptr_notnull("finalizer ran", ds);
+		test_stop();
+	});
+
+	dispatch_source_t s = dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL,
+		1000000000ull,
+		0,
+		attr,
+		main_q,
+		^(dispatch_event_t ev) {
+			long err;
+			if (dispatch_event_get_error(ev, &err)) {
+				test_errno("dispatch_event_get_error", err, ECANCELED);
+				dispatch_release(dispatch_event_get_source(ev));
+			} else {
+				fprintf(stderr, "%d\n", ++i);
+				if (i >= 7) {
+					dispatch_cancel(dispatch_event_get_source(ev));
+				} else if (i == 1) {
+					dispatch_source_timer_set_time(dispatch_event_get_source(ev), 100, 0);
+				}
+			}
+		});
+	test_ptr_notnull("dispatch_source_timer_create", s);
+
+	dispatch_release(attr);
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/fd_stress.c
===================================================================
--- trunk/testing/fd_stress.c	                        (rev 0)
+++ trunk/testing/fd_stress.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ * fd_stress.c
+ *
+ * Stress test for dispatch read and write sources.
+ */
+
+#include <dispatch/dispatch.h>
+
+#include <assert.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+static inline size_t max(size_t a, size_t b) {
+	return (a > b) ? a : b;
+}
+
+static inline size_t min(size_t a, size_t b) {
+	return (a < b) ? a : b;
+}
+
+int debug = 0;
+
+#define DEBUG(...) do { \
+		if (debug) fprintf(stderr, __VA_ARGS__); \
+	} while(0);
+
+#define assert_errno(str, expr) do { \
+	if (!(expr)) { \
+		fprintf(stderr, "%s: %s\n", (str), strerror(errno)); \
+		exit(1); \
+	} } while(0);
+
+#define assert_gai_errno(str, expr) do { \
+	if (!(expr)) { \
+		fprintf(stderr, "%s: %s\n", (str), gai_strerror(errno)); \
+		exit(1); \
+	} } while(0);
+
+
+/* sock_context
+ *
+ * Context structure used by the reader and writer queues.
+ *
+ * Writers begin by generating a random length and writing it to the descriptor.
+ * The write buffer is filled with a random byte value and written until empty
+ * or until the total length is reached. The write buffer is refilled with more
+ * random data when empty. Each write updates an MD5 digest which is written to
+ * the descriptor once the total length is reached.
+ *
+ * Readers begin by reading the total length of data. The read buffer is filled
+ * and an MD5 digest is computed on the bytes as they are received. Once the
+ * total length of data has be read, an MD5 digest is read from the descriptor
+ * and compared with the computed value.
+ */ 
+struct sock_context {
+	enum {
+		LENGTH,
+		DATA,
+		CKSUM,
+		DONE,
+	} state;
+	char label[64];
+	uint32_t len;
+	off_t offset;
+	char buf[8192];
+	size_t buflen;
+	CC_MD5_CTX md5ctx;
+	char md5[CC_MD5_DIGEST_LENGTH];
+};
+
+dispatch_source_t
+create_writer(int wfd, dispatch_block_t completion)
+{
+	dispatch_source_t ds;
+	struct sock_context *ctx = calloc(1, sizeof(struct sock_context));
+	assert(ctx);
+
+	snprintf(ctx->label, sizeof(ctx->label), "writer.fd.%d", wfd);
+	dispatch_queue_t queue = dispatch_queue_create(ctx->label, 0);
+	
+	ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, wfd, 0, queue);
+	assert(ds);
+	dispatch_release(queue);
+	
+	uint32_t len;
+	do {
+		len = (arc4random() & 0x7FFF);
+	} while (len == 0);
+	ctx->state = LENGTH;
+	CC_MD5_Init(&ctx->md5ctx);
+	ctx->len = len;
+	ctx->buflen = sizeof(len);
+	len = htonl(len);
+	memcpy(ctx->buf, &len, ctx->buflen);
+	DEBUG("%s: LENGTH %d\n", ctx->label, ctx->len);
+	
+	dispatch_source_set_event_handler(ds, ^{
+		DEBUG("%s: available %ld\n", ctx->label, dispatch_source_get_data(ds));
+		ssize_t res;
+		size_t wrsz = min(ctx->len, ctx->buflen);
+		res = write(wfd, &ctx->buf[ctx->offset], wrsz);
+		DEBUG("%s: write(%d, %p, %ld): %ld\n", ctx->label, wfd, &ctx->buf[ctx->offset], wrsz, res);
+		if (res > 0) {
+			if (ctx->state == DATA) {
+				CC_MD5_Update(&ctx->md5ctx, &ctx->buf[ctx->offset], res);
+				ctx->len -= res;
+			}
+			ctx->offset += res;
+			ctx->buflen -= res;
+			assert(ctx->offset >= 0);
+			assert(ctx->len >= 0);
+			assert(ctx->buflen >= 0);
+			if (ctx->buflen == 0 || ctx->len == 0) {
+				if (ctx->state == LENGTH) {
+					// finished writing length, move on to data.
+					ctx->state = DATA;
+					ctx->buflen = sizeof(ctx->buf);
+					char pattern = arc4random() & 0xFF;
+					memset(ctx->buf, pattern, ctx->buflen);
+				} else if (ctx->state == DATA && ctx->len == 0) {
+					// finished writing data, move on to cksum.
+					ctx->state = CKSUM;
+					ctx->len = sizeof(ctx->md5);
+					ctx->buflen = sizeof(ctx->md5);
+					CC_MD5_Final(ctx->md5, &ctx->md5ctx);
+					memcpy(ctx->buf, ctx->md5, ctx->buflen);
+				} else if (ctx->state == DATA) {
+					ctx->buflen = sizeof(ctx->buf);
+					char pattern = arc4random() & 0xFF;
+					memset(ctx->buf, pattern, ctx->buflen);
+				} else if (ctx->state == CKSUM) {
+					ctx->state = DONE;
+					dispatch_source_cancel(ds);
+				} else {
+					assert(0);
+				}
+				ctx->offset = 0;
+			}
+		} else if (res == 0) {
+			assert(ctx->state == DONE);
+			assert(0);
+		} else if (res == -1 && errno == EAGAIN) {
+			DEBUG("%s: EAGAIN\n", ctx->label);
+		} else {
+			assert_errno("write", res >= 0);
+		}
+	});
+	dispatch_source_set_cancel_handler(ds, ^{
+		DEBUG("%s: close(%d)\n", ctx->label, wfd);
+		int res = close(wfd);
+		assert_errno("close", res == 0);
+		completion();
+		dispatch_release(ds);
+		free(ctx);
+	});
+	dispatch_resume(ds);
+	return ds;
+}
+
+dispatch_source_t
+create_reader(int rfd, dispatch_block_t completion)
+{
+	dispatch_source_t ds;
+	struct sock_context *ctx = calloc(1, sizeof(struct sock_context));
+	assert(ctx);
+	
+	snprintf(ctx->label, sizeof(ctx->label), "reader.fd.%d", rfd);
+	dispatch_queue_t queue = dispatch_queue_create(ctx->label, 0);
+	
+	ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, rfd, 0, queue);
+	assert(ds);
+	dispatch_release(queue);
+	
+	ctx->state = LENGTH;
+	ctx->len = sizeof(ctx->len);
+	ctx->buflen = sizeof(ctx->len);
+	CC_MD5_Init(&ctx->md5ctx);
+	
+	dispatch_source_set_event_handler(ds, ^{
+		DEBUG("%s: available %ld\n", ctx->label, dispatch_source_get_data(ds));
+		ssize_t res;
+		size_t rdsz = min(ctx->len, ctx->buflen);
+		res = read(rfd, &ctx->buf[ctx->offset], rdsz);
+		DEBUG("%s: read(%d,%p,%ld): %ld\n", ctx->label, rfd, &ctx->buf[ctx->offset], rdsz, res);
+
+		// log unexpected data lengths...
+		long expected = dispatch_source_get_data(ds);
+		long actual = res;
+		if (actual >= 0 && (actual != expected && actual != rdsz)) {
+			fprintf(stderr, "%s: expected %ld, actual %ld (rdsz = %ld)\n", ctx->label, expected, actual, rdsz);
+		}
+
+		if (res > 0) {
+			if (ctx->state == DATA) {
+				CC_MD5_Update(&ctx->md5ctx, &ctx->buf[ctx->offset], res);
+				ctx->len -= res;
+			}
+			ctx->offset += res;
+			ctx->buflen -= res;
+			if (ctx->buflen == 0 || ctx->len == 0) {
+				if (ctx->state == LENGTH) {
+					// buffer is full, interpret as uint32_t
+					memcpy(&ctx->len, ctx->buf, sizeof(ctx->len));
+					ctx->len = ntohl(ctx->len);
+					ctx->buflen = sizeof(ctx->buf);
+					ctx->state = DATA;
+				} else if (ctx->state == DATA && ctx->len == 0) {
+					CC_MD5_Final(ctx->md5, &ctx->md5ctx);
+					ctx->state = CKSUM;
+					ctx->len = CC_MD5_DIGEST_LENGTH;
+					ctx->buflen = ctx->len;
+				} else if (ctx->state == DATA) {
+					ctx->buflen = sizeof(ctx->buf);
+				} else if (ctx->state == CKSUM) {
+					ctx->state = DONE;
+					res = memcmp(ctx->buf, ctx->md5, sizeof(ctx->md5));
+					if (res != 0) {
+						DEBUG("%s: MD5 FAILURE\n", ctx->label);
+					}
+					assert(res == 0);
+				}
+				ctx->offset = 0;
+			}
+		} else if (res == 0) {
+			assert(ctx->state == DONE);
+			DEBUG("%s: EOF\n", ctx->label);
+			dispatch_source_cancel(ds);
+		} else {
+			assert_errno("read", res >= 0);
+		}
+	});
+	dispatch_source_set_cancel_handler(ds, ^{
+		DEBUG("%s: close(%d)\n", ctx->label, rfd);
+		int res = close(rfd);
+		assert_errno("close", res == 0);
+		completion();
+		dispatch_release(ds);
+		free(ctx);
+	});
+	dispatch_resume(ds);
+	return ds;
+}
+
+void
+set_nonblock(int fd)
+{
+	int res, flags;
+	flags = fcntl(fd, F_GETFL);
+
+	flags |= O_NONBLOCK;
+	res = fcntl(fd, F_SETFL, flags);
+	assert_errno("fcntl(F_SETFL,O_NONBLOCK)", res == 0);
+}
+
+void
+create_fifo(int *rfd, int *wfd)
+{
+	int res;
+	char *name;
+	
+	char path[MAXPATHLEN];
+	strlcpy(path, "/tmp/fd_stress.fifo.XXXXXX", sizeof(path));
+	name = mktemp(path);
+	
+	res = unlink(name);
+
+	res = mkfifo(name, 0700);
+	assert_errno(name, res == 0);
+	
+	*rfd = open(name, O_RDONLY | O_NONBLOCK);
+	assert_errno(name, *rfd >= 0);
+	
+	*wfd = open(name, O_WRONLY | O_NONBLOCK);
+	assert_errno(name, *wfd >= 0);
+}
+
+void
+create_pipe(int *rfd, int *wfd)
+{
+	int res;
+	int fildes[2];
+	
+	res = pipe(fildes);
+	assert_errno("pipe", res == 0);
+	
+	*rfd = fildes[0];
+	*wfd = fildes[1];
+
+	set_nonblock(*rfd);
+	set_nonblock(*wfd);
+}
+
+void
+create_server_socket(int *rfd, struct sockaddr_in *sa)
+{
+	int res;
+	int value;
+	socklen_t salen = sizeof(*sa);
+
+	memset(sa, 0, salen);
+	sa->sin_len = salen;
+	sa->sin_family = AF_INET;
+	sa->sin_port = htons(12345);
+	sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+	*rfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	assert_errno("socket", *rfd >= 0);
+	
+	value = 1;
+	res = setsockopt(*rfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
+	assert_errno("setsockopt(SO_REUSEADDR)", res == 0);
+
+	value = 1;
+	res = setsockopt(*rfd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
+	assert_errno("setsockopt(SO_REUSEPORT)", res == 0);
+	
+	res = bind(*rfd, (const struct sockaddr *)sa, salen);
+	assert_errno("bind", res == 0);
+
+	res = listen(*rfd, 128);
+	assert_errno("listen", res == 0);
+}
+
+void
+create_client_socket(int *wfd, const struct sockaddr_in *sa)
+{
+	int res;
+
+	*wfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	assert_errno("socket", *wfd >= 0);
+	
+	set_nonblock(*wfd);
+
+	res = connect(*wfd, (const struct sockaddr *)sa, sa->sin_len);
+	assert_errno("connect", res == 0 || errno == EINPROGRESS);
+}
+
+extern int optind;
+
+void
+usage(void)
+{
+	fprintf(stderr, "usage: fd_stress [-d] iterations width\n");
+	exit(1);
+}
+
+int
+main(int argc, char* argv[])
+{
+	int serverfd;
+	struct sockaddr_in sa;
+	create_server_socket(&serverfd, &sa);
+
+	int ch;
+	
+	while ((ch = getopt(argc, argv, "d")) != -1) {
+		switch (ch) {
+			case 'd':
+				debug = 1;
+				break;
+			case '?':
+			default:
+				usage();
+				break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	
+	if (argc != 2) {
+		usage();
+	}
+	
+	size_t iterations = strtol(argv[0], NULL, 10);
+	size_t width = strtol(argv[1], NULL, 10);
+	
+	if (iterations == 0 || width == 0) {
+		usage();
+	}
+
+	fprintf(stdout, "pid %d\n", getpid());
+
+	dispatch_group_t group;
+	group = dispatch_group_create();
+	assert(group);
+
+#if 0
+	dispatch_queue_t queue = dispatch_queue_create("server", NULL);
+
+	dispatch_source_t ds;
+	ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, serverfd, 0, queue);
+	assert(ds);
+	dispatch_source_set_event_handler(ds, ^{
+		int res;
+		int fd;
+		struct sockaddr peer;
+		socklen_t peerlen;
+
+		fd = accept(serverfd, &peer, &peerlen);
+		assert_errno("accept", fd >= 0);
+
+		set_nonblock(fd);
+		
+		char host[NI_MAXHOST], serv[NI_MAXSERV];
+		host[0] = 0;
+		serv[0] = 0;
+		res = getnameinfo(&peer, peerlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST|NI_NUMERICSERV);
+		DEBUG("ACCEPTED %d (%s:%s)\n", fd, host, serv);
+		
+		create_reader(fd, ^{ dispatch_group_leave(group); });
+	});
+	dispatch_resume(ds);
+#endif
+
+	size_t i;
+	for (i = 1; i < iterations; ++i) {
+		fprintf(stderr, "iteration %ld\n", i);
+
+		size_t j;
+		for (j = 0; j < width; ++j) {
+			int rfd, wfd;
+			dispatch_group_enter(group);
+			create_pipe(&rfd, &wfd);
+			DEBUG("PIPE %d %d\n", rfd, wfd);
+			dispatch_source_t reader;
+			reader = create_reader(rfd, ^{ dispatch_group_leave(group); });
+			create_writer(wfd, ^{});
+		}
+		
+#if 0
+		int clientfd;
+		dispatch_group_enter(group);
+		create_client_socket(&clientfd, &sa);
+		DEBUG("CLIENT %d\n", clientfd);
+		create_writer(clientfd, ^{});
+
+		dispatch_group_enter(group);
+		create_fifo(&rfd, &wfd);
+		DEBUG("FIFO %d %d\n", rfd, wfd);
+		create_writer(wfd, ^{});
+		create_reader(rfd, ^{ dispatch_group_leave(group); });
+#endif
+
+		dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+	}
+	fprintf(stdout, "pid %d\n", getpid());
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/harness.c
===================================================================
--- trunk/testing/harness.c	                        (rev 0)
+++ trunk/testing/harness.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+#include <assert.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <mach/clock_types.h>
+
+#include "dispatch_test.h"
+
+extern char **environ;
+
+int
+main(int argc, char *argv[])
+{
+	dispatch_source_t proc;
+	dispatch_source_t sig;
+	int res;
+	pid_t pid;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: harness [...]\n");
+		exit(1);
+	}
+
+	//
+	// Start the test in a suspended state.  Will send SIGCONT once ready.
+	//
+
+	posix_spawnattr_t attr;
+	res = posix_spawnattr_init(&attr);
+	assert(res == 0);
+	res = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
+	assert(res == 0);
+
+	int i;
+	char** newargv = calloc(argc, sizeof(void*));
+	for (i = 1; i < argc; ++i) {
+		newargv[i-1] = argv[i];
+	}
+	newargv[i-1] = NULL;
+
+	res = posix_spawnp(&pid, newargv[0], NULL, &attr, newargv, environ);
+	if (res) {
+		errno = res;
+		perror(newargv[0]);
+		exit(EXIT_FAILURE);
+	}
+	//fprintf(stderr, "pid = %d\n", pid);
+	assert(pid > 0);
+
+	//
+	// Monitor the test process exit status.
+	//
+
+	dispatch_queue_t main_q = dispatch_get_main_queue();
+
+	proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, main_q);
+	assert(proc);
+	
+	dispatch_source_set_event_handler(proc, ^{
+		int status;
+		int res2 = waitpid(pid, &status, 0);
+		assert(res2 != -1);
+		test_long("Process exited", WEXITSTATUS(status) | WTERMSIG(status), 0);
+		exit(0);
+	});
+	
+	dispatch_resume(proc);
+	
+	//
+	// Forcibly stop currently running test after 30 second timeout.
+	//
+
+	uint64_t timeout = 30LL * NSEC_PER_SEC;
+
+	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout), main_q, ^{
+		kill(pid, SIGKILL);
+		fprintf(stderr, "Terminating unresponsive process (%0.1lfs)\n", (double)timeout/NSEC_PER_SEC);
+	});
+
+	//
+	// Control-c forcibly stops currently running test.
+	//
+
+	// Ignore the default signal handler so that dispatch can handle it.
+	signal(SIGINT, SIG_IGN);
+
+	sig = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGINT, 0, main_q);
+	assert(sig);
+
+	dispatch_source_set_event_handler(sig, ^{
+		fprintf(stderr, "Terminating process due to signal\n");
+		kill(pid, SIGKILL);
+	});
+	dispatch_resume(sig);
+
+	//
+	// Start the test.
+	//
+
+	kill(pid, SIGCONT);
+
+	dispatch_main();
+
+	return 0;
+}

Added: trunk/testing/leaks-wrapper
===================================================================
--- trunk/testing/leaks-wrapper	                        (rev 0)
+++ trunk/testing/leaks-wrapper	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+/usr/bin/leaks "$@" 2>&1 | tee "$@.leakslog" | grep -q " 0 leaks for 0 total leaked bytes"
+
+if [ $? -eq 0 ]; then
+    rm "$@.leakslog"
+    exit 0
+else
+    exit $?
+fi


Property changes on: trunk/testing/leaks-wrapper
___________________________________________________________________
Added: svn:executable
   + *

Added: trunk/testing/nsoperation.m
===================================================================
--- trunk/testing/nsoperation.m	                        (rev 0)
+++ trunk/testing/nsoperation.m	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <Foundation/Foundation.h>
+#include <dispatch/dispatch.h>
+
+#include "dispatch_test.h"
+
+ at interface MYOperation : NSOperation
+{
+}
+ at end
+
+ at implementation MYOperation
+
+- (id) init
+{
+	self = [super init];
+	return self;
+}
+
+- (void)main
+{
+	test_stop();
+}
+
+ at end
+
+int
+main(void)
+{
+	test_start("NSOperation");
+
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+	
+	NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
+	test_ptr_notnull("NSOperationQueue", queue);
+	
+	MYOperation *operation = [[MYOperation alloc] init];
+	test_ptr_notnull("NSOperation", operation);
+	
+	[queue addOperation:operation];
+	[operation release];
+	
+	[[NSRunLoop mainRunLoop] run];
+	
+	[pool release];
+	
+	return 0;
+}

Added: trunk/testing/queue_finalizer.c
===================================================================
--- trunk/testing/queue_finalizer.c	                        (rev 0)
+++ trunk/testing/queue_finalizer.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <dispatch/dispatch.h>
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dispatch_test.h"
+
+void *ctxt_magic;
+
+static void
+finalizer(void *ctxt)
+{
+	test_ptr("finalizer ran", ctxt, ctxt_magic);
+	test_stop();
+}
+
+int
+main(void) {
+	test_start("Dispatch Queue Finalizer");
+
+#ifdef __LP64__
+	ctxt_magic = (void*)((uintptr_t)arc4random() << 32 | arc4random());
+#else
+	ctxt_magic = (void*)arc4random();
+#endif
+
+	dispatch_queue_t q = dispatch_queue_create(NULL, NULL);
+	test_ptr_notnull("dispatch_queue_new", q);
+
+	dispatch_set_context(q, ctxt_magic);
+
+	dispatch_set_finalizer_f(q, finalizer);
+	
+	dispatch_release(q);
+
+	dispatch_main();
+	
+	return 0;
+}

Added: trunk/testing/summarize.c
===================================================================
--- trunk/testing/summarize.c	                        (rev 0)
+++ trunk/testing/summarize.c	2009-09-09 00:25:04 UTC (rev 13)
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+has_prefix(const char* str, const char* prefix) {
+	return (strncmp(str, prefix, strlen(prefix)) == 0);
+}
+
+int
+print_summary(FILE* f, long total, long pass, long fail) {
+	fprintf(f, "Total:  %ld\n", total);
+	fprintf(f, "Passed: %ld (%0.0lf%%)\n", pass, ((double)pass / (double)total) * (double)100.0);
+	fprintf(f, "Failed: %ld (%0.0lf%%)\n", fail, ((double)fail / (double)total) * (double)100.0);
+	fprintf(f, "\n");
+	return 0;
+}
+
+int main(int argc, char* argv[]) {
+	if (argc > 1) {
+		fprintf(stderr, "%s: usage: summarize\n", argv[0]);
+		exit(1);
+	}
+	
+	/*
+	FILE* f = fopen(argv[1], "w");
+	if (f == NULL) {
+		perror(argv[1]);
+		exit(1);
+	}
+	*/
+	FILE* f = stdout;
+
+	fprintf(f, "\n==================================================\n");
+	fprintf(f, "[SUMMARY] Test Summary\n");
+	fprintf(f, "==================================================\n\n");
+	
+	size_t len;
+	char* ln;
+	long total = 0;
+	long pass = 0;
+	long fail = 0;
+	long total_total = 0;
+	long total_pass = 0;
+	long total_fail = 0;
+	for(;;) {
+		ln = fgetln(stdin, &len);
+		//if (ln) fprintf(stdout, "%.*s", (int)len, ln);
+		if (ln == NULL || has_prefix(ln, "[TEST]")) {
+			if (total) {
+				print_summary(f, total, pass, fail);
+			}
+			total_total += total;
+			total_pass += pass;
+			total_fail += fail;
+			total = 0;
+			pass = 0;
+			fail = 0;
+			if (ln) {
+				fprintf(f, "%.*s", (int)len, ln);
+			} else {
+				fprintf(f, "[TOTAL]\n");
+				print_summary(f, total_total, total_pass, total_fail);
+				break;
+			}
+		} else if (has_prefix(ln, "[PASS]")) {
+			++total;
+			++pass;
+		} else if (has_prefix(ln, "[FAIL]")) {
+			++total;
+			++fail;
+		}
+	}
+	
+	return (total_fail ? EXIT_FAILURE : EXIT_SUCCESS);
+}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/libdispatch-changes/attachments/20090910/2aa74860/attachment-0001.html>


More information about the libdispatch-changes mailing list