Add test framework

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/tools/make/trunk@32010 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2011-02-07 20:41:58 +00:00
parent 97600a0e82
commit e2e760b00b
15 changed files with 1637 additions and 1 deletions

View file

@ -1,3 +1,22 @@
2011-01-07 Richard Frith-Macdonald <rfm@gnu.org>
* TestFramework/example1.m:
* TestFramework/example2.m:
* TestFramework/example3.m:
* TestFramework/example4.m:
* TestFramework/example5.m:
* TestFramework/example6.m:
* TestFramework/example7.m:
* TestFramework/GNUmakefile.in:
* TestFramework/gnustep-tests:
* TestFramework/ObjectTesting.h:
* TestFramework/README:
* TestFramework/runtest.sh:
* TestFramework/Testing.h:
Test framework scripts, headers, template, examples, documentation
* GNUmakefile.in:
Altered to install/uninstall test framework.
2010-12-22 Nicola Pero <nicola.pero@meta-innovation.com>
* configure.ac: Tidied up a few messages.

View file

@ -139,6 +139,9 @@ INSTANCE_SHARED_MAKE_FILES = bundle.make headers.make java.make \
INSTANCE_DOC_MAKE_FILES = autogsdoc.make gsdoc.make install_files.make \
javadoc.make latex.make texi.make
TEST_FRAMEWORK_FILES = \
GNUmakefile.in Testing.h ObjectTesting.h
# Decide which version of the GNUstep.conf file we are going to
# install; the standard one, or the strict gnustep-make v2 one ?
ifeq ($(GNUSTEP_MAKE_STRICT_V2_MODE),yes)
@ -172,7 +175,8 @@ install: generated-files
"$(makedir)/Master" \
"$(makedir)/Instance" \
"$(makedir)/Instance/Shared" \
"$(makedir)/Instance/Documentation")
"$(makedir)/Instance/Documentation" \
"$(makedir)/TestFramework")
$(EC)(echo "Installing GNUstep configuration file in $(GNUSTEP_CONFIG_FILE)"; \
"$(srcdir)/mkinstalldirs" "$(GNUSTEP_CONFIG_FILE_DIR)"; \
$(INSTALL_DATA) $(GNUSTEP_DOT_CONF_FILE) "$(GNUSTEP_CONFIG_FILE)")
@ -219,6 +223,13 @@ install: generated-files
$(INSTALL_DATA) config-noarch.make "$(makedir)"; \
$(INSTALL_DATA) filesystem.make "$(makedir)"; \
$(INSTALL_DATA) config.make "$(makedir)/$(GNUSTEP_TARGET_LDIR)")
$(EC)(echo "Installing Test Framework scripts"; \
$(INSTALL_PROGRAM) -m 755 TestFramework/runtests.sh "$(tooldir)"; \
$(INSTALL_PROGRAM) -m 755 TestFramework/runtest.sh "$(makedir)/TestFramework")
$(EC)(echo "Installing Test Framework support files"; \
for f in $(TEST_FRAMEWORK_FILES); do \
$(INSTALL_DATA) "$(srcdir)/TestFramework/$$f" "$(makedir)/TestFramework"; \
done)
$(EC)(echo "Installing (and compressing) manpages"; \
"$(srcdir)/mkinstalldirs" "$(mandir)/man1" \
"$(mandir)/man7"; \
@ -262,6 +273,8 @@ uninstall:
rm -f "$(tooldir)/debugapp"; \
rm -f "$(tooldir)/opentool"; \
rm -f "$(tooldir)/gnustep-config"; \
rm -f "$(tooldir)/runtests.sh"; \
rm -f "$(makedir)/TestFramework/runtest.sh"; \
for f in $(MAKE_FILES); do \
rm -f "$(makedir)/$$f"; \
done
@ -277,6 +290,9 @@ uninstall:
for f in $(INSTANCE_DOC_MAKE_FILES); do \
rm -f "$(makedir)/Instance/Documentation/$$f"; \
done
for f in $(TEST_FRAMEWORK_FILES); do \
rm -f "$(makedir)/TestFramework/$$f"; \
done
rm -f "$(makedir)/executable.template"
rm -f "$(makedir)/app-wrapper.template"
rm -f "$(makedir)/java-executable.template"
@ -287,6 +303,7 @@ uninstall:
rm -f "$(makedir)/$(GNUSTEP_TARGET_LDIR)/config.make"
rm -f "$(GNUSTEP_CONFIG_FILE)"
-rmdir "$(GNUSTEP_CONFIG_FILE_DIR)"
-rmdir "$(makedir)/TestFramework"
-rmdir "$(makedir)/Instance/Documentation"
-rmdir "$(makedir)/Instance/Shared"
-rmdir "$(makedir)/Instance"

View file

@ -0,0 +1,24 @@
# __GENERATED__ makefile marker
#
include $(GNUSTEP_MAKEFILES)/common.make
-include ../GNUmakefile.super
TEST_TOOL_NAME = @TESTNAME@
ADDITIONAL_OBJCFLAGS += -I@INCLUDEDIR@ -Wall
ifeq ($(gcov),yes)
ADDITIONAL_OBJCFLAGS += -ftest-coverage -fprofile-arcs
ADDITIONAL_LDFLAGS += -ftest-coverage -fprofile-arcs
ADDITIONAL_TOOL_LIBS+=-lgcov
endif
@TESTNAME@_OBJC_FILES = @FILENAME@
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/test-tool.make
-include GNUmakefile.postamble
test:
./$(GNUSTEP_OBJ_DIR_NAME)/@TESTNAME@

View file

@ -0,0 +1,426 @@
/* ObjectTesting - Include basic object tests for the GNUstep Testsuite
Copyright (C) 2005 Free Software Foundation, Inc.
Written by: Matt Rice?
This package is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
*/
#import <Testing.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSData.h>
#import <Foundation/NSArchiver.h>
#import <Foundation/NSKeyedArchiver.h>
/* WARNING ...
*
* You should look in Testing.h for the standard Test Framework API
* and its documentation (along with the README file).
*
* You should look in the example tests in the TestFramework subdirectory
* of the gnustep-make package to see how to use the API.
*
* This file contains old code from the earlier days of the testsuite
* and while a lot of it is useful stuff, it's not an example of how
* best to use the current API.
*/
/* Quick test to check that we have the class we expect.
*/
#define TEST_FOR_CLASS(aClassName, aClass, TestDescription) \
pass([aClass isKindOfClass: NSClassFromString(aClassName)], TestDescription)
/* Quick test to check for a non-empty string in the case where we don't
* actually know what value we should be expecting.
*/
#define TEST_STRING(code, description) \
{ \
NSString *_testString = code; \
pass(_testString != nil \
&& [_testString isKindOfClass: [NSString class]] \
&& [_testString length], description); \
}
/* DEPRECATED
* This is for backward compatibility, the original havng been replaced
* by two clearer/simpler macros.
* Please use PASS_EXCEPTION() for a test which is supposed to raise,
* or PASS_RUNS() to test that code runs without raising an exception.
*/
#define TEST_EXCEPTION(code, exceptionName, shouldRaise, description) \
if (shouldRaise) { PASS_EXCEPTION(code, exceptionName, description) } \
else { PASS_RUNS(code, description) }
/* DEPRECATED
START_TEST/END_TEST can be used if the code being tested could raise
and the exception should be considered a test failure. The exception
is not reraised to allow subsequent tests to execute. The START_TEST
macro takes an argument which will skip the test as UNSUPPORTED if it
evaluates to 0, allowing runtime control of whether the code block
should be executed. */
#define START_TEST(supported) if ((supported)) { NS_DURING
#define END_TEST(result, desc, args...) \
pass(result, desc, ## args); \
NS_HANDLER \
fprintf(stderr, "EXCEPTION: %s %s %s\n", \
[[localException name] UTF8String], \
[[localException reason] UTF8String], \
[[[localException userInfo] description] UTF8String]); \
pass (NO, desc, ## args); NS_ENDHANDLER } \
else unsupported (desc, ## args)
/* Functions to test basic capabilities expected of most classes.
*/
static void test_NSMutableCopying(NSString *iClassName,
NSString *mClassName,
NSArray *objects) __attribute__ ((unused));
static void test_alloc(NSString *className) __attribute__ ((unused));
static void test_alloc_only(NSString *className) __attribute__ ((unused));
static void test_NSObject(NSString *className,
NSArray *objects) __attribute__ ((unused));
static void test_NSCopying(NSString *iClassName,
NSString *mClassName,
NSArray *objects,
BOOL mustRetain,
BOOL mustCopy) __attribute__ ((unused));
static void test_NSCoding(NSArray *objects) __attribute__ ((unused));
static void test_keyed_NSCoding(NSArray *objects) __attribute__ ((unused));
/* perform basic allocation tests */
static void test_alloc(NSString *className)
{
Class theClass = NSClassFromString(className);
id obj0 = nil;
id obj1 = nil;
const char *prefix = [[NSString stringWithFormat: @"Class '%@'", className]
UTF8String];
NSZone *testZone = NSCreateZone(1024, 1024, 1);
pass(theClass != Nil, "%s exists", prefix);
obj0 = [theClass alloc];
pass(obj0 != nil, "%s has working alloc", prefix);
pass([obj0 isKindOfClass: theClass],
"%s alloc gives the correct class", prefix);
obj0 = [[theClass alloc] init];
pass([obj0 isKindOfClass: theClass], "%s has working init", prefix);
obj0 = [theClass new];
pass([obj0 isKindOfClass: theClass], "%s has working new", prefix);
obj1 = [theClass allocWithZone: testZone];
pass([obj1 isKindOfClass: theClass],"%s has working allocWithZone",prefix);
}
/* perform basic allocation tests without initialisation*/
static void test_alloc_only(NSString *className)
{
Class theClass = NSClassFromString(className);
id obj0 = nil;
id obj1 = nil;
const char *prefix = [[NSString stringWithFormat: @"Class '%@'", className]
UTF8String];
NSZone *testZone = NSCreateZone(1024, 1024, 1);
pass(theClass != Nil, "%s exists", prefix);
obj0 = [theClass alloc];
pass(obj0 != nil, "%s has working alloc", prefix);
pass([obj0 isKindOfClass: theClass],
"%s alloc gives the correct class", prefix);
PASS_EXCEPTION([obj0 description], NSInvalidArgumentException, "raises NSInvalidArgumentException in description")
PASS_EXCEPTION(if([obj0 init]==nil)[NSException raise: NSInvalidArgumentException format: @""],
NSInvalidArgumentException, "returns nil or raises NSInvalidArgumentException in init")
PASS_EXCEPTION(if([theClass new]==nil)[NSException raise: NSInvalidArgumentException format: @""],
NSInvalidArgumentException, "returns nil or raises NSInvalidArgumentException in new")
obj1 = [theClass allocWithZone: testZone];
pass([obj1 isKindOfClass: theClass],"%s has working allocWithZone",prefix);
}
/* test for the NSObject protocol */
/* TODO move to ProtocolTesting.h? */
static void test_NSObject(NSString *className, NSArray *objects)
{
int i;
Class theClass = Nil;
theClass = NSClassFromString(className);
pass(theClass != Nil, "%s is a known className", [className UTF8String]);
for (i = 0; i < [objects count]; i++)
{
id theObj = [objects objectAtIndex: i];
id mySelf = nil;
Class myClass = Nil;
int count0;
int count1;
int count2;
Class sup = Nil;
const char *prefix;
id r;
prefix = [[NSString stringWithFormat: @"Object %i of class '%@'",
i, className] UTF8String];
pass([theObj conformsToProtocol: @protocol(NSObject)],
"%s conforms to NSObject", prefix);
mySelf = [theObj self];
pass(mySelf == theObj, "%s can return self", prefix);
myClass = [theObj class];
pass(myClass != Nil, "%s can return own class", prefix);
pass([theObj isKindOfClass: theClass],
"%s object %.160s is of correct class", prefix,
[[theObj description] UTF8String]);
pass(mySelf == myClass ? ![theObj isMemberOfClass: myClass]
: [theObj isMemberOfClass: myClass], "%s isMemberOfClass works", prefix);
sup = [theObj superclass];
pass(theClass == NSClassFromString(@"NSObject") ? sup == nil
: (sup != nil && sup != myClass), "%s can return superclass", prefix);
pass([theObj respondsToSelector: @selector(hash)],
"%s responds to hash", prefix);
pass([theObj isEqual: theObj], "%s isEqual: to self", prefix);
pass([theObj respondsToSelector: @selector(self)],
"%s respondsToSelector: ", prefix);
[theObj isProxy];
r = [theObj retain];
pass(theObj == r, "%s handles retain", prefix);
[theObj release];
[theObj retain];
[theObj autorelease];
count0 = [theObj retainCount];
[theObj retain];
count1 = [theObj retainCount];
[theObj release];
count2 = [theObj retainCount];
pass((count0 == count2), "%s has working retainCount", prefix);
pass([[theObj description] isKindOfClass: [NSString class]],
"%s has NSString description", prefix);
pass([theObj performSelector: @selector(self)] == theObj,
"%s handles performSelector", prefix);
}
}
static void test_NSCoding(NSArray *objects)
{
int i;
for (i = 0; i < [objects count]; i++)
{
id obj = [objects objectAtIndex: i];
const char *prefix;
NSMutableData *data;
NSArchiver *archiver;
id decoded;
START_SET(YES);
pass([[[obj class] description] length],
"I can extract a class name for object");
prefix = [[NSString stringWithFormat: @"Object %i of class '%s'", i,
[NSStringFromClass([obj class]) UTF8String]] UTF8String];
pass([obj conformsToProtocol: @protocol(NSCoding)],
"conforms to NSCoding protocol");
data = (NSMutableData *)[NSMutableData data];
archiver = [[NSArchiver alloc] initForWritingWithMutableData: data];
pass(archiver != nil, "I am able to set up an archiver");
data = nil;
[archiver encodeRootObject: obj];
data = [archiver archiverData];
pass(data && [data length] > 0, "%s can be encoded", prefix);
decoded = [NSUnarchiver unarchiveObjectWithData: data];
pass(decoded != nil, "can be decoded");
pass([decoded isEqual: obj], "decoded object equals the original");
END_SET("test_NSCoding object %u", i);
}
}
static void test_keyed_NSCoding(NSArray *objects)
{
int i;
for (i = 0; i < [objects count]; i++)
{
id obj = [objects objectAtIndex: i];
const char *prefix;
NSData *data;
id decoded;
START_SET(YES);
pass([[[obj class] description] length],
"I can extract a class name for object");
prefix = [[NSString stringWithFormat: @"Object %i of class '%s'", i,
[NSStringFromClass([obj class]) UTF8String]] UTF8String];
pass([obj conformsToProtocol: @protocol(NSCoding)],
"conforms to NSCoding protocol");
data = [NSKeyedArchiver archivedDataWithRootObject: obj];
pass([data length] > 0, "%s can be encoded", prefix);
decoded = [NSKeyedUnarchiver unarchiveObjectWithData: data];
pass (decoded != nil, "can be decoded");
PASS_EQUAL(decoded, obj, "decoded object equals the original");
END_SET("test_keyed_NSCoding object %u", i);
}
}
static void test_NSCopying(NSString *iClassName,
NSString *mClassName,
NSArray *objects,
BOOL mustRetain,
BOOL mustCopy)
{
Class iClass = NSClassFromString(iClassName);
Class mClass = NSClassFromString(mClassName);
int i;
NSZone *testZone = NSCreateZone(1024, 1024, 1);
pass(iClass != Nil, "%s is a known class", [iClassName UTF8String]);
pass(mClass != Nil, "%s is a known class", [mClassName UTF8String]);
for (i = 0; i < [objects count]; i++)
{
BOOL immutable;
NSString *theName;
const char *prefix;
id theCopy = nil;
Class theClass = Nil;
id theObj = [objects objectAtIndex: i];
START_SET(YES);
if (iClass != mClass && [theObj isKindOfClass: mClass])
{
immutable = NO;
theName = iClassName;
theClass = iClass;
}
else
{
immutable = YES;
theName = mClassName;
theClass = mClass;
}
prefix = [[NSString stringWithFormat: @"Object %i of class '%s'",
i, [theName UTF8String]] UTF8String];
pass([theObj conformsToProtocol: @protocol(NSCopying)],
"conforms to NSCopying");
theCopy = [theObj copy];
pass(theCopy != nil, "%s understands -copy", prefix);
pass([theCopy isKindOfClass: iClass],
"%s copy is of correct type", prefix);
pass([theObj isEqual: theCopy], "%s original and copy are equal", prefix);
if (immutable)
{
if (YES == mustRetain)
{
pass(theCopy == theObj,
"%s is retained by copy with same zone", prefix);
}
else if (YES == mustCopy)
{
pass(theCopy != theObj,
"%s is not retained by copy with same zone", prefix);
}
}
if (theClass != iClass)
{
pass(![theCopy isKindOfClass: theClass],
"%s result of copy is not immutable", prefix);
}
theCopy = [theObj copyWithZone: testZone];
pass(theCopy != nil, "%s understands -copyWithZone", prefix);
pass([theCopy isKindOfClass: iClass],
"%s zCopy has correct type", prefix);
pass([theObj isEqual: theCopy],
"%s copy and original are equal", prefix);
if (immutable)
{
if (YES == mustRetain)
{
pass(theCopy == theObj,
"%s is retained by copy with other zone", prefix);
}
else if (YES == mustCopy)
{
pass(theCopy != theObj,
"%s is not retained by copy with other zone", prefix);
}
}
if (theClass != iClass)
pass(![theCopy isKindOfClass: theClass],
"%s result of copyWithZone: is not immutable", prefix);
END_SET("test_NSCopying object %u", i);
}
}
static void test_NSMutableCopying(NSString *iClassName,
NSString *mClassName,
NSArray *objects)
{
int i;
Class iClass = Nil;
Class mClass = Nil;
NSZone *testZone = NSCreateZone(1024, 1024, 1);
iClass = NSClassFromString(iClassName);
pass(iClass != Nil, "%s is a known class", [iClassName UTF8String]);
mClass = NSClassFromString(mClassName);
pass(mClass != Nil, "%s is a known class", [mClassName UTF8String]);
for (i = 0; i < [objects count]; i++)
{
id theObj = [objects objectAtIndex: i];
NSString *theName = nil;
const char *prefix;
BOOL immutable;
id theCopy = nil;
Class theClass = Nil;
START_SET(YES);
if (iClass == mClass && [theObj isKindOfClass: mClass])
immutable = NO;
else
immutable = YES;
if (immutable)
{
theName = iClassName;
theClass = iClass;
}
else
{
theName = mClassName;
theClass = mClass;
}
prefix = [[NSString stringWithFormat:
@"Object %i of class '%s'", i, [theName UTF8String]] UTF8String];
pass([theObj conformsToProtocol: @protocol(NSMutableCopying)],
"%s conforms to NSMutableCopying protocol", prefix);
theCopy = [theObj mutableCopy];
pass(theCopy != nil, "%s understands -mutableCopy", prefix);
pass([theCopy isKindOfClass: mClass],
"%s mutable copy is of correct type", prefix);
pass([theCopy isEqual: theObj], "%s copy equals original", prefix);
pass(theCopy != theObj,
"%s not retained by mutable copy in the same zone",
[mClassName UTF8String]);
theCopy = [theObj mutableCopyWithZone: testZone];
pass(theCopy != nil,
"%s understands mutableCopyWithZone", [mClassName UTF8String]);
pass(theCopy != theObj, "%s not retained by mutable copy in other zone",
[mClassName UTF8String]);
END_SET("test_NSMutableCopying object %u", i);
}
}

150
TestFramework/README Normal file
View file

@ -0,0 +1,150 @@
TestFramework
=============
This testsuite is a general framework for testing the core GNUstep
libraries. Since, in part, we are testing the very basic level of
an Objective-C runtime, we can't use more complete unit tests, such
as OCUnit <http://www.sente.ch/software/ocunit/>.
The aim of this framework is to provide a simple, yet reasonaply
comprehensive regression test mechanism for Objective-C development.
Please run the GNUstep testsuite (using this framework) often, when
adding new features, fixing bugs and running on new platforms.
Where working on features common to both Apple's Cocoa/iOS APIs and
to GNUstep, please try creating and running test cases in the Apple
environment before implementing/changing GNUstep code, so that you
are sure the behavior is the same in bioth cases.
License
-------
The testing framework and many of the test cases in the testsuite are
copyright by the FSF and distributed under the GPL. However, some tests
may not be copyright by the FSF, but retain the copyright of the original
owner (e.g tests submited as bug reports). You should feel free to add tests
that are not copyright by the FSF. The copyright of these tests should
be clearly stated, however, and they should still be distributed under the
GPL version 3 or later.
Running Tests
-------------
To run a testsuite, use the gnustep-tests script along with the name of
the project testsuite (directory) you with to test:
gnustep-tests base
or where a group of tests within a project is to be run:
gnustep-tests base/NSArray
You may run individual test files by using the runtest.sh script with the
name(s) of the Objective-C test source file(s):
gnustep-tests base/NSDate/general.m
gnustep-tests ./mytest.m base/NSDate/general.m
Alternatively, you may run tests from within a project/directory. eg.
cd base
gnustep-tests
Writing Tests
-------------
A minimal test should be a file importing the header "Testing.h"
(which defines global variables, functions, and standard test macros)
and containing a main() function implementation which executes the
actual test code.
Groups of tests should be placed between calls to the START_SET() and
END_SET() macros.
You should look at the example test files in the same directory as
this README for how to write test cases, and you should examine Testing.h
to see full documentation of the range of macros provided.
The main workhorse of the test framewrk is the pass() function, which has
two arguments ... first an integer expression, and second a string
describing what is being tested.
The function uses the global variable 'testHopeful' to decide whether a
test which did not pass is a 'FAIL' (when testHopeful==NO) or a 'DASHED'
hope (when testHopeful==YES).
The function sets the global variable 'testPassed' to a BOOL reflecting
the result of the test (YES if the test passed, NO otherwise).
The only other functions are for occasional use to report sections of
the testsuite as not having run for some reason.
There are just four test macros.
All have uppercase names beginning with 'PASS'.
All wrap test code and a call to the pass() function in exception handlers.
All are code blocks and do not need a semicolon terminator.
PASS passes if an expression resulting in an integer value is
non-zero
PASS_EQUAL passes if an expression resulting in an object is identical
to or -isEqual: to another object.
PASS_EXCEPTION passes if a code fragment raises an exception
PASS_RUNS passes if a code fragment runs without raising an exception
Tests are grouped together, along with any associated non-test code, between
paired calls to the START_SET and END_SET macros.
You can skip an entire set by supplying NO as the argument to the START_SET
macro, in which case the entire set will be reported as UNSUPPORTED.
Any uncaught exception (ie one which occurs outside a one of the four test
macros and is not caught by an exception handler you write yourself) will
cause the remaining tests in a set to be skipped. In this case the set
will be reported as UNRESOLVED.
You may also arrange to jump to the end of the set if a test fails by wrapping
the test in a NEED macro. Doing this also causes the set to be reported as
UNRESOLVED.
Advanced building
-----------------
In most cases, all you need to do is write an objective-c file as described
above, and the test framework will build it and run it for you automatically,
but occasionally you may need to use your own build process.
Where tests must make use of external resources or ensure that other tests
have already been run before they are run, you can make use of the gnustep
make package facilities to control dependencies etc.
Normally each test is built and run by generating a makefile in the directory
containing the test. This makefile uses the standard conventions of including
GNUmakefile.preamble before test-tool.make and including GNUmakefile.postamble
after test-tool.make, which gives you a high degree of control over how the
tests in the directory are built.
In addition to the preamble/postamble mechanism, the file ../GNUmakefile.super
is included at the start of the generated makefile (if it exists). This allows
all the tests in a suite to use a common makefile fragment which can (for
instance) build common resources before any tests are run.
For total control, the runtest.sh script checks to see if a 'Custom.mk' file
exists in the directory, and if it does it uses that file to build the tests
rather than generating its own make file.
You may also specify a GNUmakefile.tests in a project level directory (ie one
containing subdirectories of tests), and that makefile will be executed when
you run tests on the project.
Ignoring directories
--------------------
If, when given the name of a test to be run, the runtest.sh script finds a
file named 'IGNORE' in the same directory as the named file, it skips
running of the test. The effect of this is that the presence of an IGNORE
file causes a directory to be ignored. This is useful in conjunction
with ../GNUmakefile.super so that projects to build resources for other tests
can be ignored by the scripts running the tests, and just built as required
by ../GNUmakefile.super

379
TestFramework/Testing.h Normal file
View file

@ -0,0 +1,379 @@
/* Testing - Include basic tests macros for the GNUstep Testsuite
Copyright (C) 2005-2011 Free Software Foundation, Inc.
Written by: Alexander Malmberg <alexander@malmberg.org>
Updated by: Richard Frith-Macdonald <rfm@gnu.org>
This package is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
*/
#ifndef Testing_h
#define Testing_h
#include <stdio.h>
#include <stdarg.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSException.h>
#import <Foundation/NSGarbageCollector.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
/* A flag indicating that the testsuite is currently processing tests
* which are actually not expected to pass, but where we hope someone
* might have committed a bugfix.
* The state of this flag is preserved by sets ... on exit from a set
* it is restored to the state it had on entry.
*/
static BOOL testHopeful __attribute__((unused)) = NO;
/* A flag indicating whether the most recently executed test passed.
* This is set by the pass() function (and therefore by any test macro).
*/
static BOOL testPassed __attribute__((unused)) = NO;
/* A variable set whenever a test macro is executed. This contains
* the exception which terminated the test macro, or nil if no exception
* was raised.
*/
static NSException *testRaised __attribute__((unused)) = nil;
/* The pass() function is the low-level core of the testsuit.
*
* You call this with two arguments ... an integer expression indicating the
* success or failure of the testcase (0 is a failure) and a string which
* describes the testcase.
*
* The global variable 'testHopeful' can be set to a non-zero value before
* calling this function in order to specify that if the condition is
* not true it should be treated as a dashed hope rather than a failure.
*
* If there is a better higher-level test macro available, please use
* that instead. In particular, please use the PASS_EQUAL() macro wherever
* you wish to test the equality of a pair of objective-c objects.
*
* This function is the most efficient option for general use, but
* please don't use it if there is any change that the evaluation of
* the expression used as its first argument might cause an exception
* in any context where that might be a problem.
*/
static void pass(int testPassed, const char *format, ...) __attribute__((unused)) __attribute__ ((format(printf, 2, 3)));
static void pass(int testPassed, const char *format, ...)
{
va_list args;
va_start(args, format);
if (testPassed)
{
fprintf(stderr, "PASS: ");
testPassed = YES;
}
else if (YES == testHopeful)
{
fprintf(stderr, "DASHED: ");
testPassed = NO;
}
else
{
fprintf(stderr, "FAIL: ");
testPassed = NO;
}
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
va_end(args);
}
/* The unresolved() function is called with a single string argument to
* notify the testsuite that a test failed to complete for some reason.
* eg. You might call this if an earlier testcase failed and it makes no
* sense to run subsequent tests.
* This is called if a set is terminated before all the tests in it have
* been run.
*/
static void unresolved(const char *format, ...) __attribute__((unused)) __attribute__ ((format(printf, 1, 2), unused));
static void unresolved(const char *format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "UNRESOLVED: ");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
va_end(args);
}
/* The unsupported() function is called with a single string argument to
* notify the testsuite that a test could not be run because the capability
* it is testing does not exist on the current platform.
*/
static void unsupported(const char *format, ...) __attribute__((unused)) __attribute__ ((format(printf, 1, 2), unused));
static void unsupported(const char *format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "UNSUPPORTED: ");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
va_end(args);
}
/* Tests a code expression which evaluates to an integer value.
* If the expression evaluates to zero the test does not pass.
* If the expression causes an exception to be raised, the exception
* is caught and logged but the test does not pass.
* Otherwise, the test passes.
* Basically equivalent to pass() but with exception handling.
*/
#define PASS(expression, args...) \
NS_DURING \
{ \
int _cond; \
id _tmp = testRaised; testRaised = nil; [_tmp release]; \
[[NSGarbageCollector defaultCollector] collectExhaustively]; \
_cond = (int) expression; \
[[NSGarbageCollector defaultCollector] collectExhaustively]; \
pass(_cond, ## args); \
} \
NS_HANDLER \
testRaised = [localException retain]; \
pass(0, ## args); \
printf("%s: %s", [[testRaised name] UTF8String], \
[[testRaised description] UTF8String]); \
NS_ENDHANDLER
/* Tests a code expression which evaluates to an object value.
*
* Where the expression evaluates to an object which is identical to
* the expect value, or where [object isEqual: expect] returns YES,
* the test has passed.
*
* The particularly useful thing about this macro is that, if the
* results of the expression and the expected object are not equal,
* the string representation of both values is logged so that you
* can get a better idea of what went wrong.
*/
#define PASS_EQUAL(expression, expect, args...) \
NS_DURING \
{ \
int _cond; \
id _obj; \
id _tmp = testRaised; testRaised = nil; [_tmp release]; \
[[NSGarbageCollector defaultCollector] collectExhaustively]; \
_obj = ( expression );\
_cond = _obj == expect || [_obj isEqual: expect]; \
[[NSGarbageCollector defaultCollector] collectExhaustively]; \
pass(_cond, ## args); \
if (0 == _cond) \
{ \
NSString *s = [_obj description]; \
if ([s length] == 1) \
{ \
fprintf(stderr, \
"Expected '%s' and got '%s' (unicode codepoint %d)\n", \
[[expect description] UTF8String], [s UTF8String], \
[s characterAtIndex: 0]); \
} \
else \
{ \
fprintf(stderr, "Expected '%s' and got '%s'\n", \
[[expect description] UTF8String], [s UTF8String]); \
} \
} \
} \
NS_HANDLER \
testRaised = [localException retain]; \
pass(0, ## args); \
printf("%s: %s", [[testRaised name] UTF8String], \
[[testRaised description] UTF8String]); \
NS_ENDHANDLER
/* Please use the PASS_EXCEPTION() macro to handle any code where you
* want an exception to be thrown. The macro checks that the supplied
* code throws the specified exception. If the code fails to throw,
* or throws the wrong exception, then the code does not pass.
* You can supply nil for expectedExceptionName if you don't care about the
* type of exception.
*/
#define PASS_EXCEPTION(code, expectedExceptionName, args...) \
NS_DURING \
id _tmp = testRaised; testRaised = nil; [_tmp release]; \
{ code; } \
pass(0, ## args); \
NS_HANDLER \
testRaised = [localException retain]; \
pass((expectedExceptionName == nil \
|| [[testRaised name] isEqual: expectedExceptionName]), ## args); \
if (NO == [expectedExceptionName isEqual: [testRaised name]]) \
fprintf(stderr, "Expected '%s' and got '%s'\n", \
[expectedExceptionName UTF8String], \
[[testRaised name] UTF8String]); \
NS_ENDHANDLER
/* Please use the PASS_RUNS() macro to handle any code where you want the
* code to run to completion without an exception being thrown, but you don't
* have a particular expression to be checked.
*/
#define PASS_RUNS(code, args...) \
NS_DURING \
id _tmp = testRaised; testRaised = nil; [_tmp release]; \
{ code; } \
pass(1, ## args); \
NS_HANDLER \
testRaised = [localException retain]; \
pass(0, ## args); \
printf("%s: %s", [[testRaised name] UTF8String], \
[[testRaised description] UTF8String]); \
NS_ENDHANDLER
/* SETs are used to group multiple testcases or code which is outside of
* the scope of the current test but could raise exceptions that should
* be caught to allow further tests to run.
*
* You must pass a 'supported' flag to say whether the code should be run
* or not, if not then the set is reported as unsupported.
*
* The state of the 'testHopeful' flag is saved at the start of the set and
* restored at the end of the set, so you can start your code by setting
* 'testHopeful=YES;' to mark any tests within the set as being part of a group
* of tests we don't expect to pass.
*
* The tests within the set are enclosed in an autorelease pool, and any
* temporary objects are cleaned up at the end of the set.
*/
/* The START_SET() macro starts a set of grouped tests or, if the argument
* is false, skips the set and reports the set as unsupported.
*/
#define START_SET(supported) \
if ((supported)) \
{ \
BOOL save_hopeful = testHopeful; \
NS_DURING \
NSAutoreleasePool *_setPool = [NSAutoreleasePool new]; \
{
/* The END_SET() macro terminates a set of grouped tests. It's argument is
* a printf style format string and variable arguments to print a message
* describing the set.
*/
#define END_SET(desc, args...) \
} \
[_setPool release]; \
NS_HANDLER \
if (NO == [[localException name] isEqualToString: @"CheckSet"]) \
{ \
fprintf(stderr, "EXCEPTION: %s %s %s\n", \
[[localException name] UTF8String], \
[[localException reason] UTF8String], \
[[[localException userInfo] description] UTF8String]); \
} \
unresolved(desc, ## args); \
NS_ENDHANDLER \
testHopeful = save_hopeful; \
} \
else \
{ \
unsupported(desc, ## args); \
}
/* The NEED macro takes a test macro as an arugment and breaks out of a set
* and reports it as unresolved if test does not pass.
*/
#define NEED(testToTry) \
testToTry \
if (NO == testPassed) \
{ \
if (nil != testRaised) \
{ \
[testRaised raise]; \
} \
else \
{ \
[NSException raise: @"CheckSet" format: @"Test did not pass"]; \
} \
}
/* some good macros to compare floating point numbers */
#import <math.h>
#import <float.h>
#define EQ(x, y) (fabs((x) - (y)) <= fabs((x) + (y)) * (FLT_EPSILON * 100))
#define LE(x, y) ((x)<(y) || EQ(x, y))
#define GE(x, y) ((y)<(x) || EQ(x, y))
#define LT(x, y) (!GE(x, y))
#define GT(x, y) (!LE(x, y))
/* A convenience macro to pass an object as a string to a print function.
*/
#define POBJECT(obj) [[(obj) description] UTF8String]
#endif
#ifndef CREATE_AUTORELEASE_POOL
#define RETAIN(object) [object retain]
#define RELEASE(object) [object release]
#define AUTORELEASE(object) [object autorelease]
#define TEST_RETAIN(object) ({\
id __object = (id)(object); (__object != nil) ? [__object retain] : nil; })
#define TEST_RELEASE(object) ({\
id __object = (id)(object); if (__object != nil) [__object release]; })
#define TEST_AUTORELEASE(object) ({\
id __object = (id)(object); (__object != nil) ? [__object autorelease] : nil; })
#define ASSIGN(object,value) ({\
id __value = (id)(value); \
id __object = (id)(object); \
if (__value != __object) \
{ \
if (__value != nil) \
{ \
[__value retain]; \
} \
object = __value; \
if (__object != nil) \
{ \
[__object release]; \
} \
} \
})
#define ASSIGNCOPY(object,value) ({\
id __value = (id)(value); \
id __object = (id)(object); \
if (__value != __object) \
{ \
if (__value != nil) \
{ \
__value = [__value copy]; \
} \
object = __value; \
if (__object != nil) \
{ \
[__object release]; \
} \
} \
})
#define DESTROY(object) ({ \
if (object) \
{ \
id __o = object; \
object = nil; \
[__o release]; \
} \
})
#define CREATE_AUTORELEASE_POOL(X) \
NSAutoreleasePool *(X) = [NSAutoreleasePool new]
#define RECREATE_AUTORELEASE_POOL(X) \
if (X == nil) \
(X) = [NSAutoreleasePool new]
#endif

16
TestFramework/example1.m Normal file
View file

@ -0,0 +1,16 @@
#import <ObjectTesting.h>
/* This is the absolute minimal test program ...
* a single test case involving plain C and no Objective-C code.
*
* If you run the test with 'gnustep-tests example1.m' it should
* report a single test file completed and a single test pass
*/
int
main()
{
/* This is too simple to really be useful, but it's a start.
*/
pass(1 == 1, "integer equality works");
return 0;
}

22
TestFramework/example2.m Normal file
View file

@ -0,0 +1,22 @@
#import <ObjectTesting.h>
/* A second test ... your first go at testing with ObjectiveC
*
* If you run the test with 'gnustep-tests example2.m' it should
* report a single test file completed and two test passes.
*/
int
main()
{
/* We start a set here ...
* Having a set means we do not need to bother creating an autorelease pool.
*/
START_SET(YES)
pass(1 == 1, "integer equality works");
pass([[NSObject new] autorelease] != nil, "+new creates an object");
END_SET("example set")
return 0;
}

41
TestFramework/example3.m Normal file
View file

@ -0,0 +1,41 @@
#import <ObjectTesting.h>
/* A third test ... using test macros.
*
* If you run the test with 'gnustep-tests example3.m' it should
* report a single test file completed, two test passes, and a test fail.
*/
/* Import a header because we want to use a method from it.
*/
#import <Foundation/NSDictionary.h>
int
main()
{
/* We start a set here ...
* Having a set means we do not need to bother creating an autorelease pool.
*/
START_SET(YES)
/* We use a macro here so that any exception in the expression we use
* will not break out of the set, and the two remaining tests will be
* run.
*/
PASS([(NSDictionary*)@"abc" objectForKey: @"xxx"],
"sending a bad message")
pass(1 == 1, "integer equality works");
/* And let's use a macro here too ... the expression is not going to
* raise an exception unless NSObject is somehow broken, and even if
* it did, this is the last test in the set, so it wouldn't matter,
* but it's good practice to code safely in case we move the code
* around in a later version of the program.
*/
PASS([[NSObject new] autorelease] != nil, "+new creates an object")
END_SET("example set")
return 0;
}

30
TestFramework/example4.m Normal file
View file

@ -0,0 +1,30 @@
#import <ObjectTesting.h>
/* A fourth test ... testing for an exception.
*
* If you run the test with 'gnustep-tests example4.m' it should
* report a single test file completed, three test passes.
*/
/* Import a header because we want to use a method from it.
*/
#import <Foundation/NSDictionary.h>
int
main()
{
START_SET(YES)
/* We test for the code fragment raising an exception. We don't care
* about the particular exception, so we pass nil as the expected exception
* name.
*/
PASS_EXCEPTION([(NSDictionary*)@"abc" objectForKey: @"xxx"], nil,
"sending a bad message causes an exception")
pass(1 == 1, "integer equality works");
PASS([[NSObject new] autorelease] != nil, "+new creates an object")
END_SET("example set")
return 0;
}

35
TestFramework/example5.m Normal file
View file

@ -0,0 +1,35 @@
#import <ObjectTesting.h>
/* A fifth test ... hope.
*
* If you run the test with 'gnustep-tests example5.m' it should
* report a single test file completed, one hope dashed, two test passes,
* and one set unresolved.
*/
int
main()
{
START_SET(YES)
/* First set a flag to say that we are not expecting tests to
* actually pass.
*/
testHopeful = YES;
/* Here the test should result in a dashed hope rather than a fail.
*/
PASS(1 == 0, "silly test which we don't expect to pass")
/* This test should simply pass of course.
*/
PASS(1 == 1, "integer equality works")
END_SET("example set")
/* And here we demonstrate that on exit from the set, the global
* variable is restored to its state on entry.
*/
pass(NO == testHopeful, "the flag state is restored outside the set");
return 0;
}

40
TestFramework/example6.m Normal file
View file

@ -0,0 +1,40 @@
#import <ObjectTesting.h>
/* A sixth test ... need.
*
* If you run the test with 'gnustep-tests example6.m' it should
* report a single test file completed, one hope dashed, one test pass,
* and one set unresolved.
*/
int
main()
{
START_SET(YES)
/* First set a flag to say that we are not expecting tests to
* actually pass.
*/
testHopeful = YES;
/* Here we demonstate the NEED macro. This says that a need must be met
* in order to continue with the set ... if the test does not pass
* we can't complete the set and we skip to the end.
*/
NEED(PASS(1 == 0, "silly test which we don't expect to pass"))
/* This test should never be reached because the previous test needs to
* pass, but will not do so.
*/
PASS(1 == 1, "integer equality works")
/* The set should be unresolved, because the earlier need was not met.
*/
END_SET("example set")
/* And here we demonstrate that on exit from the set, the global
* variable is restored to its state on entry.
*/
pass(NO == testHopeful, "the flag state is restored outside the set");
return 0;
}

44
TestFramework/example7.m Normal file
View file

@ -0,0 +1,44 @@
#import <ObjectTesting.h>
/* A seventh test ... nesting sets.
*
* If you run the test with 'gnustep-tests example7.m' it should
* report a single test file completed, one test fail, two test passes,
* and one set unresolved.
*/
int
main()
{
/* Start a set.
*/
START_SET(YES)
/* Our first test in this set will pass.
*/
PASS(1 == 1, "integer equality works")
/* Now we start a set nested inside the first one.
*/
START_SET(YES)
/* And we say we need a test to pass, but it's actually a faulty one
* which will fail, causing the set to be terminated.
*/
NEED(PASS_EQUAL(@"hello", @"there", "faulty string equality test"))
/* Heres a correct string equality test, but it's never reached because
* the earlier test was needed.
*/
PASS_EQUAL(@"there", @"there", "NSString equality works")
END_SET("inner set")
/* And here's another correct test which *is* reached because it's
* in a different set.
*/
PASS_EQUAL(@"there", @"there", "NSString equality works")
END_SET("outer set")
return 0;
}

208
TestFramework/gnustep-tests Executable file
View file

@ -0,0 +1,208 @@
#!/bin/sh
#
# Runs tests for the GNUstep Testsuite
#
# Copyright (C) 2005-2011 Free Software Foundation, Inc.
#
# Written by: Alexander Malmberg <alexander@malmberg.org>
# Updates by: Richard Frith-Macdonald <rfm@gnu.org>
#
# This package is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
#
# Usage: gnustep-tests [directory | test1.m [test2.m ...]]
#
# Runs the tests in the specified directory (or those in the individual files)
# or all the tests in subdirectories of the current directory if no arguments
# are given.
# A summary is written to tests.sum, a log to tests.log, and a brief
# summary to stdout.
# The log and summary from the previous testrun are renamed to
# oldtests.log and oldtests.sum, available for comparison.
if test -z "$GNUSTEP_MAKEFILES"; then
GNUSTEP_MAKEFILES=`gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null`
if test -z "$GNUSTEP_MAKEFILES"; then
echo "You need to have GNUstep-make installed and set up."
echo "Did you remember to source GNUstep.sh?"
else
export GNUSTEP_MAKEFILES
. $GNUSTEP_MAKEFILES/GNUstep.sh
fi
fi
# Argument checking
while test $# != 0
do
gs_option=
case $1 in
--help | -h)
echo
echo "$0: Script to run the GNUstep testsuite"
echo "Usage: gnustep-tests [directory | test1.m [test2.m ...]]"
echo "Runs the specified tests, or any in subdirectoried of the"
echo "current directory if no arguments are given."
echo
echo "Interpreting the output"
echo "-----------------------"
echo "The summary output lists all test failures ... there should not"
echo "be any. If a test fails then either there is a problem in the"
echo "software being tested, or a problem in the test itsself. Either"
echo "way, you should try to fix the problem and provide a patch, or"
echo "at least report it at: https://savannah.gnu.org/bugs/?group=gnustep"
echo
echo "After the listing of any failures is a summary of counts of events:"
echo "COMPLETED: The number of separate test files which were run."
echo "COMPILEFAIL: The number of separate test files which failed to run."
echo "DASHED: The number of hopes dashed ... tests which did not"
echo " pass, but which were not expected to pass (known"
echo " bugs etc)."
echo "FAIL: The number of individual tests failed"
echo "PASS: The number of individual tests passed"
echo "UNRESOLVED: The number of unresolved tests ... tests which have"
echo " been omitted because of an earlier failure etc."
echo "UNSUPPORTED: The number of unsupported tests ... those for features"
echo " which work on some platforms, but not on yours."
echo
exit 0
;;
--debug | -d) # ignore for backward compatibility.
;;
*)
break
;;
esac
shift
done
if [ ! "$MAKE_CMD" ]
then
gmake --version > /dev/null 2>&1
if [ $? = 0 ]
then
MAKE_CMD=gmake
else
MAKE_CMD=make
fi
fi
export MAKE_CMD
TEMP=`echo *`
TESTDIRS=
for file in $TEMP
do
if [ -d $file -a $file != CVS -a $file != obj ]
then
TESTDIRS="$TESTDIRS $file"
fi
done
if [ x$1 != x ]
then
if [ -d $1 ]
then
# Only find in the directories specified.
TESTDIRS=$*
else
TESTDIRS=
TESTS=$*
fi
fi
CWD=`pwd`
TOP=$GNUSTEP_MAKEFILES/TestFramework
export TOP
RUNCMD=$TOP/runtest.sh
cd $CWD
run_test_file ()
{
echo >> $CWD/tests.log
echo Testing $TESTFILE... >> $CWD/tests.log
echo >> $CWD/tests.sum
# Run the test. Log everything to a temporary file.
$RUNCMD $run_args $TESTFILE > $CWD/tests.tmp 2>&1
# Add the information to the detailed log.
cat $CWD/tests.tmp >> $CWD/tests.log
# Extract the summary information and add it to the summary file.
grep "^\(PASS\|FAIL\|COMPILEFAIL\|COMPLETED\|DASHED\|UNRESOLVED\|UNSUPPORTED\)" $CWD/tests.tmp > $CWD/tests.sum.tmp
cat $CWD/tests.sum.tmp >> $CWD/tests.sum
# If there were failures or unresolved tests then report them...
if grep -L "^\(COMPILEFAIL\|FAIL\|UNRESOLVED\)" $CWD/tests.sum.tmp > /dev/null; then
echo
echo $TESTFILE:
grep "^\(COMPILEFAIL\|FAIL\|UNRESOLVED\)" $CWD/tests.sum.tmp
fi
}
# Delete the old files.
if [ -f tests.log ]
then
mv tests.log oldtests.log
fi
if [ -f tests.sum ]
then
mv tests.sum oldtests.sum
fi
if [ x"$TESTDIRS" = x ]
then
# I think we should depreciate this part, but for now...
for TESTFILE in $TESTS
do
run_test_file
done
else
for dir in $TESTDIRS
do
echo "--- Running tests in $dir ---"
TESTS=`find $dir -name \*.m | sort | sed -e 's/\(^\| \)X[^ ]*//g'`
# If there is a GNUmakefile.tests in the directory, run it first.
# Unless ... we are at the top level, in which case that file is
# our template.
cd $dir
if [ $TOP != `pwd` ]
then
if [ -f GNUmakefile.tests ]
then
$MAKE_CMD -f GNUmakefile.tests $MAKEFLAGS debug=yes 2>&1
fi
fi
cd $CWD
for TESTFILE in $TESTS
do
run_test_file
done
done
fi
# Make some stats.
if [ -r tests.sum ]
then
grep "^[A-Z]*:" tests.sum | cut -d: -f1 | sort | uniq -c > tests.tmp
else
echo "No tests found." > tests.tmp
fi
echo >> tests.sum
cat tests.tmp >> tests.sum
echo
cat tests.tmp
# Delete the temporary file.
rm -f tests.tmp tests.sum.tmp

185
TestFramework/runtest.sh Executable file
View file

@ -0,0 +1,185 @@
#!/bin/sh
#
# run tests script for the GNUstep Testsuite
#
# Copyright (C) 2005-2011 Free Software Foundation, Inc.
#
# Written by: Alexander Malmberg <alexander@malmberg.org>
# Updated by: Richard Frith-Macdonald <rfm@gnu.org>
#
# This package is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
#
# Usage: ./runtest.sh test_name.m
#
# Compiles and runs the test test_name. Detailed logging should go to stdout;
# only summary information should go to stderr.
USEDEBUG=YES
# Argument checking
while test $# != 0
do
gs_option=
case $1 in
--help | -h)
echo "$0: Script to run a test a GNUstep testsuite program"
echo "Usage: ./runtest.sh test_name.m"
echo "Options:"
echo " --help - Print help"
echo
exit 0
;;
--debug | -d) # ignore for backward compatibility
;;
*)
break
;;
esac
shift
done
if [ x$1 = x ]
then
echo "ERROR: $0: No test given"
exit 1
fi
if [ $# != 1 ]
then
echo "ERROR: $0: Too many arguments (single test file name expected)"
exit 1
fi
if [ -e $1 ]
then
if [ ! -f $1 ]
then
echo "ERROR: $0: Argument ($1) is not the name of a regular file"
exit 1
fi
if [ ! -r $1 ]
then
echo "ERROR: $0: Test file ($1) is not readable by you"
exit 1
fi
else
echo "ERROR: $0: Test file ($1) does not exist"
exit 1
fi
if test -z "$GNUSTEP_MAKEFILES"; then
GNUSTEP_MAKEFILES=`gnustep-config --variable=GNUSTEP_MAKEFILES 2>/dev/null`
if test -z "$GNUSTEP_MAKEFILES"; then
echo "You need to have GNUstep-make installed and set up."
echo "Did you remember to source GNUstep.sh?"
else
. $GNUSTEP_MAKEFILES/GNUstep.sh
fi
fi
# Move to the test's directory.
DIR=`dirname $1`
if [ ! -d $DIR ]; then
echo "Unable to proceed ... $DIR is not a directory"
exit 1
fi
cd $DIR
DIR=`pwd`
# Check that we are not in the top level ... where creating a GNUmakefile
# would trash an existing makefile.
if [ -f GNUmakefile ]; then
echo "Unable to proceed ... a GNUmakefile already exsts"
exit 1
fi
if [ ! "$MAKE_CMD" ]
then
MAKE_CMD=gmake
$MAKE_CMD --version > /dev/null 2>&1
if [ $? != 0 ]
then
MAKE_CMD=make
fi
fi
NAME=`basename $1`
if [ ! -f IGNORE ]
then
# Remove the extension, if there is one. If there is no extension, add
# .obj .
TESTNAME=`echo $NAME | sed -e"s/^\([^.]*\)$/\1.obj./;s/\.[^.]*//g"`
# Check for a custom makefile, if it exists use it.
if [ -r Custom.mk ]
then
if [ $NAME = "Custom.mk" ]
then
echo "include Custom.mk" >>GNUmakefile
else
exit 0
fi
else
# Create the GNUmakefile by filling in the name of the test.
sed -e "s/@TESTNAME@/$TESTNAME/;s/@FILENAME@/$NAME/;s^@INCLUDEDIR@^$TOP^" < $TOP/GNUmakefile.in > GNUmakefile
fi
# Clean up to avoid contamination by previous tests. (Optimistically) assume
# that this will never fail in any interesting way.
$MAKE_CMD clean >/dev/null 2>&1
# Compile it. Redirect errors to stdout so it shows up in the log, but not
# in the summary.
$MAKE_CMD $MAKEFLAGS messages=yes debug=yes 2>&1
if [ $? != 0 ]
then
echo COMPILEFAIL: $1 >&2
else
# We want aggressive memory checking.
# Tell glibc to check for malloc errors, and to crash if it detects
# any.
MALLOC_CHECK_=2
export MALLOC_CHECK
# Tell GNUstep-base to check for messages sent to deallocated objects
# and crash if it happens.
NSZombieEnabled=YES
CRASH_ON_ZOMBIE=YES
export NSZombieEnabled CRASH_ON_ZOMBIE
echo Running $1...
# Run it. If it terminates abnormally, mark it as a crash (unless we have
# a special file to mark it as being expected to abort).
$MAKE_CMD -s test
if [ $? != 0 ]
then
if [ -r $NAME.abort ]
then
echo COMPLETED: $1 >&2
else
echo FAIL: $1 >&2
fi
else
echo COMPLETED: $1 >&2
fi
fi
rm -f GNUmakefile
# Clean up to avoid contaminating later tests. (Optimistically) assume that
# this will never fail in any interesting way.
rm -f core
#$MAKE_CMD clean >/dev/null 2>&1
fi