summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ChangeLog7
-rw-r--r--tests/Makefile4
-rw-r--r--tests/condvar7.c226
-rw-r--r--tests/condvar8.c226
-rw-r--r--tests/condvar9.c231
-rw-r--r--tests/runall.bat82
-rw-r--r--tests/runtest.bat25
7 files changed, 766 insertions, 35 deletions
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 2283e3a..ee69577 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,10 @@
+Oct 14 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
+
+ * condvar7.c: New. Test broadcast after waiting thread is canceled.
+ * condvar8.c: New. Test multiple broadcasts.
+ * condvar9.c: New. Test multiple broadcasts with thread
+ cancelation.
+
Sep 16 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
* rwlock6.c: New test.
diff --git a/tests/Makefile b/tests/Makefile
index e2c298d..0263769 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -40,7 +40,7 @@ TESTS = mutex1 condvar1 condvar2 exit1 create1 equal1 \
exit2 exit3 \
join1 join2 mutex2 mutex3 \
count1 once1 tsd1 self1 self2 eyal1 \
- condvar3 condvar4 condvar5 condvar6 \
+ condvar3 condvar4 condvar5 condvar6 condvar7 condvar8 \
errno1 \
rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6
@@ -71,6 +71,8 @@ condvar3.pass: create1.pass
condvar4.pass: create1.pass
condvar5.pass: condvar4.pass
condvar6.pass: condvar5.pass
+condvar7.pass: condvar6.pass
+condvar8.pass: condvar6.pass
errno1.pass: mutex3.pass
rwlock1.pass: condvar6.pass
rwlock2.pass: rwlock1.pass
diff --git a/tests/condvar7.c b/tests/condvar7.c
new file mode 100644
index 0000000..d81dd78
--- /dev/null
+++ b/tests/condvar7.c
@@ -0,0 +1,226 @@
+/*
+ * File: condvar7.c
+ *
+ * Test Synopsis:
+ * - Test pthread_cond_broadcast with thread cancelation.
+ *
+ * Test Method (Validation or Falsification):
+ * - Validation
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * - Test broadcast with NUMTHREADS (=5) waiting CVs, one is canceled while waiting.
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * -
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#include "test.h"
+#include <sys/timeb.h>
+
+/*
+ * Create NUMTHREADS threads in addition to the Main thread.
+ */
+enum {
+ NUMTHREADS = 5
+};
+
+typedef struct bag_t_ bag_t;
+struct bag_t_ {
+ int threadnum;
+ int started;
+ /* Add more per-thread state variables here */
+};
+
+static bag_t threadbag[NUMTHREADS + 1];
+
+typedef struct cvthing_t_ cvthing_t;
+
+struct cvthing_t_ {
+ pthread_cond_t notbusy;
+ pthread_mutex_t lock;
+ int shared;
+};
+
+static cvthing_t cvthing = {
+ PTHREAD_COND_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER,
+ 0
+};
+
+static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;
+
+static struct timespec abstime = { 0, 0 };
+
+static int awoken;
+
+void *
+mythread(void * arg)
+{
+ bag_t * bag = (bag_t *) arg;
+
+ assert(bag == &threadbag[bag->threadnum]);
+ assert(bag->started == 0);
+ bag->started = 1;
+
+ /* Wait for the start gun */
+ assert(pthread_mutex_lock(&start_flag) == 0);
+ assert(pthread_mutex_unlock(&start_flag) == 0);
+
+ assert(pthread_mutex_lock(&cvthing.lock) == 0);
+
+ pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);
+
+ while (! cvthing.shared > 0)
+ assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
+
+ pthread_cleanup_pop(0);
+
+ assert(cvthing.shared > 0);
+
+ awoken++;
+
+ assert(pthread_mutex_unlock(&cvthing.lock) == 0);
+
+ return 0;
+}
+
+int
+main()
+{
+ int failed = 0;
+ int i;
+ pthread_t t[NUMTHREADS + 1];
+
+#if defined(__MINGW32__)
+ struct timeb currSysTime;
+#else
+ struct _timeb currSysTime;
+#endif
+ const DWORD NANOSEC_PER_MILLISEC = 1000000;
+
+ cvthing.shared = 0;
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);
+
+ assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);
+
+ assert(pthread_mutex_lock(&start_flag) == 0);
+
+ _ftime(&currSysTime);
+
+ abstime.tv_sec = currSysTime.time;
+ abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
+
+ abstime.tv_sec += 5;
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ awoken = 0;
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ threadbag[i].started = 0;
+ threadbag[i].threadnum = i;
+ assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
+ }
+
+ /*
+ * Code to control or munipulate child threads should probably go here.
+ */
+
+ assert(pthread_mutex_unlock(&start_flag) == 0);
+
+ /*
+ * Give threads time to start.
+ */
+ Sleep(2000);
+
+ assert(pthread_mutex_lock(&cvthing.lock) == 0);
+
+ cvthing.shared++;
+
+ assert(pthread_mutex_unlock(&cvthing.lock) == 0);
+
+ /*
+ * Cancel one of the threads.
+ */
+ assert(pthread_cancel(t[3]) == 0);
+ Sleep(500);
+
+ /*
+ * Signal all remaining waiting threads.
+ */
+ assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);
+
+ /*
+ * Give threads time to complete.
+ */
+ Sleep(2000);
+
+ /*
+ * Cleanup the CV.
+ */
+
+ assert(pthread_mutex_destroy(&cvthing.lock) == 0);
+
+ assert(cvthing.lock == NULL);
+
+ assert(pthread_cond_destroy(&cvthing.notbusy) == 0);
+
+ assert(cvthing.notbusy == NULL);
+
+ /*
+ * Standard check that all threads started.
+ */
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ failed = !threadbag[i].started;
+
+ if (failed)
+ {
+ fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
+ }
+ }
+
+ assert(!failed);
+
+ /*
+ * Check any results here.
+ */
+
+ assert(awoken == (NUMTHREADS - 1));
+
+ /*
+ * Success.
+ */
+ return 0;
+}
+
+
diff --git a/tests/condvar8.c b/tests/condvar8.c
new file mode 100644
index 0000000..65da040
--- /dev/null
+++ b/tests/condvar8.c
@@ -0,0 +1,226 @@
+/*
+ * File: condvar8.c
+ *
+ * Test Synopsis:
+ * - Test multiple pthread_cond_broadcasts.
+ *
+ * Test Method (Validation or Falsification):
+ * - Validation
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * - Make NUMTHREADS threads wait on CV, broadcast signal them, and then repeat.
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * -
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#include "test.h"
+#include <sys/timeb.h>
+
+/*
+ * Create NUMTHREADS threads in addition to the Main thread.
+ */
+enum {
+ NUMTHREADS = 5
+};
+
+typedef struct bag_t_ bag_t;
+struct bag_t_ {
+ int threadnum;
+ int started;
+ /* Add more per-thread state variables here */
+};
+
+static bag_t threadbag[NUMTHREADS + 1];
+
+typedef struct cvthing_t_ cvthing_t;
+
+struct cvthing_t_ {
+ pthread_cond_t notbusy;
+ pthread_mutex_t lock;
+ int shared;
+};
+
+static cvthing_t cvthing = {
+ PTHREAD_COND_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER,
+ 0
+};
+
+static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;
+
+static struct timespec abstime = { 0, 0 };
+
+static int awoken;
+
+static void *
+mythread(void * arg)
+{
+ bag_t * bag = (bag_t *) arg;
+
+ assert(bag == &threadbag[bag->threadnum]);
+ assert(bag->started == 0);
+ bag->started = 1;
+
+ /* Wait for the start gun */
+ assert(pthread_mutex_lock(&start_flag) == 0);
+ assert(pthread_mutex_unlock(&start_flag) == 0);
+
+ assert(pthread_mutex_lock(&cvthing.lock) == 0);
+
+ /*
+ * pthread_cond_timedwait is a cancelation point and we
+ * going to cancel one deliberately.
+ */
+ pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);
+
+ while (! cvthing.shared > 0)
+ assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
+
+ pthread_cleanup_pop(0);
+
+ assert(cvthing.shared > 0);
+
+ awoken++;
+
+ assert(pthread_mutex_unlock(&cvthing.lock) == 0);
+
+ return 0;
+}
+
+int
+main()
+{
+ int failed = 0;
+ int i;
+ int first, last;
+ pthread_t t[NUMTHREADS + 1];
+
+#if defined(__MINGW32__)
+ struct timeb currSysTime;
+#else
+ struct _timeb currSysTime;
+#endif
+ const DWORD NANOSEC_PER_MILLISEC = 1000000;
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);
+
+ assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);
+
+ _ftime(&currSysTime);
+
+ abstime.tv_sec = currSysTime.time;
+ abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
+
+ abstime.tv_sec += 5;
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ awoken = 0;
+
+ for (first = 1, last = NUMTHREADS / 2;
+ first < NUMTHREADS;
+ first = last + 1, last = NUMTHREADS)
+ {
+ assert(pthread_mutex_lock(&start_flag) == 0);
+
+ for (i = first; i <= last; i++)
+ {
+ threadbag[i].started = 0;
+ threadbag[i].threadnum = i;
+ assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
+ assert(pthread_detach(t[i]) == 0);
+ }
+
+ /*
+ * Code to control or munipulate child threads should probably go here.
+ */
+ cvthing.shared = 0;
+
+ assert(pthread_mutex_unlock(&start_flag) == 0);
+
+ /*
+ * Give threads time to start.
+ */
+ Sleep(1000);
+
+ assert(pthread_mutex_lock(&cvthing.lock) == 0);
+
+ cvthing.shared++;
+
+ assert(pthread_mutex_unlock(&cvthing.lock) == 0);
+
+ assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);
+
+ /*
+ * Give threads time to complete.
+ */
+ Sleep(1000);
+ }
+
+
+ /*
+ * Standard check that all threads started.
+ */
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ failed = !threadbag[i].started;
+
+ if (failed)
+ {
+ fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
+ }
+ }
+
+ /*
+ * Cleanup the CV.
+ */
+
+ assert(pthread_mutex_destroy(&cvthing.lock) == 0);
+
+ assert(cvthing.lock == NULL);
+
+ assert(pthread_cond_destroy(&cvthing.notbusy) == 0);
+
+ assert(cvthing.notbusy == NULL);
+
+ assert(!failed);
+
+ /*
+ * Check any results here.
+ */
+
+ assert(awoken == NUMTHREADS);
+
+ /*
+ * Success.
+ */
+ return 0;
+}
diff --git a/tests/condvar9.c b/tests/condvar9.c
new file mode 100644
index 0000000..49a8cab
--- /dev/null
+++ b/tests/condvar9.c
@@ -0,0 +1,231 @@
+/*
+ * File: condvar9.c
+ *
+ * Test Synopsis:
+ * - Test multiple pthread_cond_broadcasts with thread cancelation.
+ *
+ * Test Method (Validation or Falsification):
+ * - Validation
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * - Make NUMTHREADS threads wait on CV, cancel one, broadcast signal them,
+ * and then repeat.
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * -
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#include "test.h"
+#include <sys/timeb.h>
+
+/*
+ * Create NUMTHREADS threads in addition to the Main thread.
+ */
+enum {
+ NUMTHREADS = 9
+};
+
+typedef struct bag_t_ bag_t;
+struct bag_t_ {
+ int threadnum;
+ int started;
+ /* Add more per-thread state variables here */
+};
+
+static bag_t threadbag[NUMTHREADS + 1];
+
+typedef struct cvthing_t_ cvthing_t;
+
+struct cvthing_t_ {
+ pthread_cond_t notbusy;
+ pthread_mutex_t lock;
+ int shared;
+};
+
+static cvthing_t cvthing = {
+ PTHREAD_COND_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER,
+ 0
+};
+
+static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;
+
+static struct timespec abstime = { 0, 0 };
+
+static int awoken;
+
+static void *
+mythread(void * arg)
+{
+ bag_t * bag = (bag_t *) arg;
+
+ assert(bag == &threadbag[bag->threadnum]);
+ assert(bag->started == 0);
+ bag->started = 1;
+
+ /* Wait for the start gun */
+ assert(pthread_mutex_lock(&start_flag) == 0);
+ assert(pthread_mutex_unlock(&start_flag) == 0);
+
+ assert(pthread_mutex_lock(&cvthing.lock) == 0);
+
+ /*
+ * pthread_cond_timedwait is a cancelation point and we
+ * going to cancel one deliberately.
+ */
+ pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);
+
+ while (! cvthing.shared > 0)
+ assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
+
+ pthread_cleanup_pop(0);
+
+ assert(cvthing.shared > 0);
+
+ awoken++;
+
+ assert(pthread_mutex_unlock(&cvthing.lock) == 0);
+
+ return 0;
+}
+
+int
+main()
+{
+ int failed = 0;
+ int i;
+ int first, last;
+ int canceledThreads = 0;
+ pthread_t t[NUMTHREADS + 1];
+
+#if defined(__MINGW32__)
+ struct timeb currSysTime;
+#else
+ struct _timeb currSysTime;
+#endif
+ const DWORD NANOSEC_PER_MILLISEC = 1000000;
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);
+
+ assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);
+
+ _ftime(&currSysTime);
+
+ abstime.tv_sec = currSysTime.time;
+ abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
+
+ abstime.tv_sec += 5;
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ awoken = 0;
+
+ for (first = 1, last = NUMTHREADS / 2;
+ first < NUMTHREADS;
+ first = last + 1, last = NUMTHREADS)
+ {
+ assert(pthread_mutex_lock(&start_flag) == 0);
+
+ for (i = first; i <= last; i++)
+ {
+ threadbag[i].started = 0;
+ threadbag[i].threadnum = i;
+ assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
+ assert(pthread_detach(t[i]) == 0);
+ }
+
+ /*
+ * Code to control or munipulate child threads should probably go here.
+ */
+ cvthing.shared = 0;
+
+ assert(pthread_mutex_unlock(&start_flag) == 0);
+
+ /*
+ * Give threads time to start.
+ */
+ Sleep(1000);
+
+ assert(pthread_mutex_lock(&cvthing.lock) == 0);
+
+ cvthing.shared++;
+
+ assert(pthread_mutex_unlock(&cvthing.lock) == 0);
+
+ assert(pthread_cancel(t[(first + last) / 2]) == 0);
+ canceledThreads++;
+
+ assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);
+
+ /*
+ * Give threads time to complete.
+ */
+ Sleep(1000);
+ }
+
+
+ /*
+ * Standard check that all threads started.
+ */
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ failed = !threadbag[i].started;
+
+ if (failed)
+ {
+ fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
+ }
+ }
+
+ /*
+ * Cleanup the CV.
+ */
+
+ assert(pthread_mutex_destroy(&cvthing.lock) == 0);
+
+ assert(cvthing.lock == NULL);
+
+ assert(pthread_cond_destroy(&cvthing.notbusy) == 0);
+
+ assert(cvthing.notbusy == NULL);
+
+ assert(!failed);
+
+ /*
+ * Check any results here.
+ */
+
+ assert(awoken == NUMTHREADS - canceledThreads);
+
+ /*
+ * Success.
+ */
+ return 0;
+}
diff --git a/tests/runall.bat b/tests/runall.bat
index a1e3862..e3a927b 100644
--- a/tests/runall.bat
+++ b/tests/runall.bat
@@ -1,33 +1,55 @@
@echo off
-if x%1==x-f echo y | erase *.pass > nul:
+if NOT x%1==x-f goto noforce
+if EXIST *.pass echo y | erase *.pass > nul:
+if EXIST *.fail echo y | erase *.fail > nul:
+if EXIST *.notrun echo y | erase *.notrun > nul:
-call runtest cl mutex1
-call runtest cl mutex2
-call runtest cl exit1
-call runtest cl condvar1
-call runtest cl self1
-call runtest cl condvar2
-call runtest cl create1
-call runtest cl mutex3
-call runtest cl equal1
-call runtest cl exit2
-call runtest cl exit3
-call runtest cl join1
-call runtest cl join2
-call runtest cl count1
-call runtest cl once1
-call runtest cl tsd1
-call runtest cl self2
-call runtest cl eyal1
-call runtest cl condvar3
-call runtest cl condvar4
-call runtest cl condvar5
-call runtest cl condvar6
-call runtest cl errno1
-call runtest cl rwlock1
-call runtest cl rwlock2
-call runtest cl rwlock3
-call runtest cl rwlock4
-call runtest cl rwlock5
-call runtest cl rwlock6
+:noforce
+call runtest cl mutex1 _
+call runtest cl mutex2 _
+call runtest cl exit1 _
+call runtest cl condvar1 _
+call runtest cl self1 _
+call runtest cl condvar2 condvar1
+call runtest cl create1 mutex2
+call runtest cl mutex3 create1
+call runtest cl equal1 create1
+call runtest cl exit2 create1
+call runtest cl exit3 create1
+call runtest cl join1 create1
+call runtest cl join2 create1
+call runtest cl count1 join1
+call runtest cl once1 create1
+call runtest cl tsd1 join1
+call runtest cl self2 create1
+call runtest cl eyal1 tsd1
+call runtest cl condvar3 create1
+call runtest cl condvar4 create1
+call runtest cl condvar5 condvar4
+call runtest cl condvar6 condvar5
+call runtest cl condvar7 condvar6
+call runtest cl condvar8 condvar7
+call runtest cl condvar9 condvar8
+call runtest cl errno1 mutex3
+call runtest cl rwlock1 condvar6
+call runtest cl rwlock2 rwlock1
+call runtest cl rwlock3 rwlock2
+call runtest cl rwlock4 rwlock3
+call runtest cl rwlock5 rwlock4
+call runtest cl rwlock6 rwlock5
+
+if NOT EXIST *.notrun goto skip1
+echo The following tests did not run (because prerequisite didn't pass?):
+for %%f in (*.notrun) do echo %%f
+goto skip2
+:skip1
+echo All tests ran.
+:skip2
+if NOT EXIST *.fail goto skip3
+echo The following tests failed:
+for %%f in (*.fail) do echo %%f
+goto skip4
+:skip3
+echo No tests failed.
+:skip4
diff --git a/tests/runtest.bat b/tests/runtest.bat
index 4a06505..73fae97 100644
--- a/tests/runtest.bat
+++ b/tests/runtest.bat
@@ -1,7 +1,11 @@
@echo off
-REM Usage: runtest cl|gcc testname testarg ...
+REM Usage: runtest cl|gcc testname prerequisit testarg ...
+if %3==_ goto noprereq
+if NOT EXIST %3.pass goto needprereq
+
+:noprereq
if EXIST %2.pass goto bypass
REM Make sure we start with only those files we expect to need
@@ -30,11 +34,15 @@ REM erase ..\%2.%1log
echo TEST: %2 [%1]
REM Run the test case
-aout.exe %3 %4 %5 %6 %7 %8 %9
+if EXIST %2.pass erase %2.pass
+if EXIST %2.fail erase %2.fail
+if EXIST %2.notrun erase %2.notrun
+aout.exe %4 %5 %6 %7 %8 %9
set RESULT=%ERRORLEVEL%
-if %RESULT% EQU 0 echo Passed [%RESULT%] > ..\%2.pass
+if %RESULT% NEQ 0 echo Failed [%RESULT%] > ..\%2.fail
+if %RESULT% EQU 0 echo Passed > ..\%2.pass
:cleanup
@@ -43,6 +51,15 @@ cd ..
REM Clean up
if exist tmp\*.* echo y | erase tmp\*.* > nul:
+if EXIST %2.fail echo Failed [%RESULT%]
if EXIST %2.pass echo Passed [%RESULT%]
-:bypass \ No newline at end of file
+:bypass
+goto end
+
+:needprereq
+echo Test %2 requires %3 to pass before it can run.
+echo No Prereq > ..\%2.notrun
+goto end
+
+:end