Initial import r208
authorWincent Colaiuta <win@wincent.com>
Sat, 14 Jul 2007 01:20:20 +0000 (03:20 +0200)
committerWincent Colaiuta <win@wincent.com>
Sat, 14 Jul 2007 01:20:20 +0000 (03:20 +0200)
Based on the current tip of the trunk (r208) in the old Subversion repository.

Signed-off-by: Wincent Colaiuta <win@wincent.com>
98 files changed:
Doxyfile [new file with mode: 0644]
INFO.txt [new file with mode: 0644]
Info.plist [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
NOTES.txt [new file with mode: 0644]
NSException+WOTest.h [new file with mode: 0644]
NSException+WOTest.m [new file with mode: 0644]
NSInvocation+WOTest.h [new file with mode: 0644]
NSInvocation+WOTest.m [new file with mode: 0644]
NSMethodSignature+WOTest.h [new file with mode: 0644]
NSMethodSignature+WOTest.m [new file with mode: 0644]
NSObject+WOTest.h [new file with mode: 0644]
NSObject+WOTest.m [new file with mode: 0644]
NSProxy+WOTest.h [new file with mode: 0644]
NSScanner+WOTest.h [new file with mode: 0644]
NSScanner+WOTest.m [new file with mode: 0644]
NSString+WOTest.h [new file with mode: 0644]
NSString+WOTest.m [new file with mode: 0644]
NSValue+WOTest.h [new file with mode: 0644]
NSValue+WOTest.m [new file with mode: 0644]
RunTests.sh [new file with mode: 0755]
TODO.txt [new file with mode: 0644]
TestBundle.icns [new file with mode: 0644]
Tests/Info.plist [new file with mode: 0644]
Tests/NSInvocationTests.h [new file with mode: 0644]
Tests/NSInvocationTests.m [new file with mode: 0644]
Tests/NSObjectTests.h [new file with mode: 0644]
Tests/NSObjectTests.m [new file with mode: 0644]
Tests/NSScannerTests.h [new file with mode: 0644]
Tests/NSScannerTests.m [new file with mode: 0644]
Tests/NSValueTests.h [new file with mode: 0644]
Tests/NSValueTests.m [new file with mode: 0644]
Tests/WOClassMockTests.h [new file with mode: 0644]
Tests/WOClassMockTests.m [new file with mode: 0644]
Tests/WOMockTests.h [new file with mode: 0644]
Tests/WOMockTests.m [new file with mode: 0644]
Tests/WOMultithreadedCrashTests.h [new file with mode: 0644]
Tests/WOMultithreadedCrashTests.m [new file with mode: 0644]
Tests/WOObjectMockTests.h [new file with mode: 0644]
Tests/WOObjectMockTests.m [new file with mode: 0644]
Tests/WOObjectStubTests.h [new file with mode: 0644]
Tests/WOObjectStubTests.m [new file with mode: 0644]
Tests/WOProtocolMockTests.h [new file with mode: 0644]
Tests/WOProtocolMockTests.m [new file with mode: 0644]
Tests/WOProtocolStubTests.h [new file with mode: 0644]
Tests/WOProtocolStubTests.m [new file with mode: 0644]
Tests/WOStubTests.h [new file with mode: 0644]
Tests/WOStubTests.m [new file with mode: 0644]
Tests/WOTestApplicationTestsControllerTests.h [new file with mode: 0644]
Tests/WOTestApplicationTestsControllerTests.m [new file with mode: 0644]
Tests/WOTestSelfTests.h [new file with mode: 0644]
Tests/WOTestSelfTests.m [new file with mode: 0644]
Tests/WincentTestBundle.icns [new file with mode: 0644]
WOClassMock.h [new file with mode: 0644]
WOClassMock.m [new file with mode: 0644]
WOEnumerate.h [new file with mode: 0644]
WOLightweightRoot.h [new file with mode: 0644]
WOLightweightRoot.m [new file with mode: 0644]
WOMock.h [new file with mode: 0644]
WOMock.m [new file with mode: 0644]
WOObjectMock.h [new file with mode: 0644]
WOObjectMock.m [new file with mode: 0644]
WOObjectStub.h [new file with mode: 0644]
WOObjectStub.m [new file with mode: 0644]
WOProtocolMock.h [new file with mode: 0644]
WOProtocolMock.m [new file with mode: 0644]
WOProtocolStub.h [new file with mode: 0644]
WOProtocolStub.m [new file with mode: 0644]
WOStub.h [new file with mode: 0644]
WOStub.m [new file with mode: 0644]
WOTest.h [new file with mode: 0644]
WOTest.xcodeproj/default.pbxuser [new file with mode: 0644]
WOTest.xcodeproj/project.pbxproj [new file with mode: 0644]
WOTestApplicationTestsController.h [new file with mode: 0644]
WOTestApplicationTestsController.m [new file with mode: 0644]
WOTestBundleInjector.h [new file with mode: 0644]
WOTestBundleInjector.m [new file with mode: 0644]
WOTestClass.h [new file with mode: 0644]
WOTestClass.m [new file with mode: 0644]
WOTestLowLevelException.h [new file with mode: 0644]
WOTestLowLevelException.m [new file with mode: 0644]
WOTestMacros.h [new file with mode: 0644]
WOTestRunner/WOTestRunner.1 [new file with mode: 0644]
WOTestRunner/WOTestRunner.h [new file with mode: 0644]
WOTestRunner/WOTestRunner.m [new file with mode: 0644]
WOTestRunner/WOTestRunner_Version.h [new file with mode: 0644]
WOTestSignalException.m [new file with mode: 0644]
WOTest_Version.h [new file with mode: 0644]
Xcode Integration/Library/Application Support/Apple/Developer Tools/File Templates/Cocoa/Objective-C WOTest test class.pbfiletemplate/TemplateInfo.plist [new file with mode: 0644]
Xcode Integration/Library/Application Support/Apple/Developer Tools/File Templates/Cocoa/Objective-C WOTest test class.pbfiletemplate/Tests.h [new file with mode: 0644]
Xcode Integration/Library/Application Support/Apple/Developer Tools/File Templates/Cocoa/Objective-C WOTest test class.pbfiletemplate/Tests.m [new file with mode: 0644]
en.lproj/InfoPlist.strings [new file with mode: 0644]
exc-notes.txt [new file with mode: 0644]
exc.h [new file with mode: 0644]
excServer.c [new file with mode: 0644]
excUser.c [new file with mode: 0644]
folder.icns [new file with mode: 0644]
version.plist [new file with mode: 0644]

diff --git a/Doxyfile b/Doxyfile
new file mode 100644 (file)
index 0000000..cb8a142
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,237 @@
+# Doxyfile 1.5.1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = WOTest
+PROJECT_NUMBER         = 1.0
+OUTPUT_DIRECTORY       = ../../build/WOTest-Documentation
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+USE_WINDOWS_ENCODING   = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = NO
+STRIP_FROM_PATH        = 
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                = startgroup=@{ \
+                         endgroup=@}
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+BUILTIN_STL_SUPPORT    = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = NO
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = .
+FILE_PATTERNS          = *.h \
+                         *.H
+RECURSIVE              = YES
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = NO
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = /usr/local/bin
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+MAX_DOT_GRAPH_DEPTH    = 1000
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/INFO.txt b/INFO.txt
new file mode 100644 (file)
index 0000000..70c6f2f
--- /dev/null
+++ b/INFO.txt
@@ -0,0 +1,18 @@
+WOTest is an Objective-C unit testing framework written by Wincent Colaiuta 
+<win@wincent.com>. More information available at:
+
+http://test.wincent.com/
+
+Copyright 2004-2007 Wincent Colaiuta.
+This program 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 program 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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
\ No newline at end of file
diff --git a/Info.plist b/Info.plist
new file mode 100644 (file)
index 0000000..39ce18e
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>en</string>
+       <key>CFBundleExecutable</key>
+       <string>WOTest</string>
+       <key>CFBundleIconFile</key>
+       <string>folder.icns</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.wincent.WOTest</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>FMWK</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>WO_INFO_PLIST_VERSION (WO_BUILDNUMBER)</string>
+       <key>NSPrincipalClass</key>
+       <string></string>
+       <key>WOCopyrightYear</key>
+       <string>WO_COPYRIGHT_YEAR</string>
+</dict>
+</plist>
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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 program 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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/NOTES.txt b/NOTES.txt
new file mode 100644 (file)
index 0000000..732aeac
--- /dev/null
+++ b/NOTES.txt
@@ -0,0 +1,481 @@
+$Id: NOTES.txt 147 2007-03-26 18:50:52Z wincent $
+
+================================================================================
+What is WOTest?
+================================================================================
+
+
+
+================================================================================
+WOTest quickstart (code snippets and questions)
+================================================================================
+
+Quick code snippets:
+
+// loading the framework
+WO_TEST_LOAD_FRAMEWORK;
+
+// testing to see if the framework is loaded
+if (!WO_TEST_FRAMEWORK_IS_LOADED)
+{
+    // ...
+}
+
+// performing some tests
+WO_TEST_EQUAL(object1, object2);
+WO_TEST_NOT_EQUAL(variable1, variable2);
+
+// shorthand form for tests
+WO_TEST_EQ(object1, object2);
+WO_TEST_NE(variable1, variable2);
+WO_TEST(assertion); // short form of WO_TEST_TRUE
+
+// writing a test method
+- (void)testNetworkHandler
+{
+    WO_TEST_TRUE([[ABNetworkManager sharedManager] isConnected]);
+}
+
+Quick questions and answers:
+
+Q: How do I install the WOTest.framework?
+A: You don't have to install the WOTest.framework.
+
+Q: How do I link against the WOTest.framework?
+A: You don't have to link against the framework.
+
+Q: Where do I put my tests? In a bundle? A framework? Inside my classes?
+A: You can put the tests anywhere you like.
+
+================================================================================
+Requirements
+================================================================================
+
+In order to build WOTest the Wincent Buildtools are required. They can be obtained from:
+
+http://wincent.com/a/products/buildtools/
+
+================================================================================
+Why YAUTF (Yet Another Unit Testing Framework)?
+================================================================================
+
+There are already a number of unit testing frameworks available for use with Objective-C (see "Other unit testing frameworks" for a possibly incomplete list). They each have distinct benefits to offer; some are very mature and in widespread use, others are especially "light weight", and others have a raft of functionality.
+
+But there are a lot of approaches (design philosophies) to unit testing and choosing between them is a very personal question. I chose to write WOTest because: firstly, none of the existing solutions matched my personal tastes; and secondly, because I see writing my own code as a valuable learning experience.
+
+Below is the set characteristics I wanted for WOTest which I couldn't find in an existing packages. In general I found that the available tools met some of these criteria but not all of them, and they generally forced me work in a way which I didn't really like. Given that the proposed design for WOTest was so light-weight, it seemed like a better idea to spend a few days writing the code rather than learn/tame any of the existing packages.
+
+1. Extremely light-weight. My unit testing needs are fairly modest. I don't do "Test-Driven Development" ("TDD"; where you write the tests before you write the program); rather I write the tests as I go along as a means of quality control (and of course, along the way there are "moments" of TDD). I don't need an exhaustive feature set.
+
+2. "Airtight" compartmentalization between product code ("the product") and the code that runs the tests ("the tests"). I don't want the test code to be interspersed throughout product's code. I want to be able to look at all the tests for a product in one location. There are three schools of thought on this:
+(a) That the tests should be right next to the code being tested, in the same file. This is the way I used to work, a strong argument for this method is that it is much easier to keep the tests updated as you modify the code, and much easier in general to switch back and forth between the tests and the code being tested. The main weakness of this method is that there is no easy way to see all the tests at once, and it is also very difficult to remove the tests or deactivate them without resorting to the use of ugly preprocessor macros;
+(b) That the tests should be in separate files that are closely related to the code being tested, for example, in subclasses that test their superclasses, or in categories that test their associated classes. Because the tests are in separate files they are easier to deactivate or excluded from code shipped to users, but you still can't see all the tests at once and the "parallel hierarchy problem" (see below) becomes more of an issue;
+(c) That the tests should be cleanly separated from the product code, together in one place (all in one file, if you desire). This is the method used by WOTest. The main benefits of this method is that you can easily see all the tests at once, the tests can be easily removed or deactivated, and the "parallel hierarchy problem" is avoided. The down side, in common with method (b), is that you must keep two editors open (one for the tests, one for the tested code) if you want to look at both at the same time. In reality this is not as bad as it sounds because firstly, method (b) is actually much more complicated because it requires you to look through multiple files in order to navigate through your test suite, and secondly, method (a) loses some of its simplicity when you're dealing with large and complicated classes (even when the tests are in the same file as the tested code, if there is a lot of code then the scrolling and navigation ceases to be trivial).
+
+3. Xcode integration. It should be easy to automatically run the test suite from within Xcode, and not just by performing a "Run" (Command-R) but as a part of the actual build process (Command-B). You should be able to click on failed test results and be taken to the corresponding line in the source code.
+
+4. Separation of test target and non-test target. It should be possible to have a target in Xcode that doesn't run any tests, and a separate target that does run the tests. 
+
+5. No parallel hierarchies (either class hierarchies or category hierarchies). It shouldn't be necessary to maintain two hierarchies of code (one for the product and one for the tests) which could get out of synchronization (see the following two points).
+
+6. No subclasses. It shouldn't be necessary to write subclasses (containing "the tests") in order to test classes (containing "the product"). I don't like this because it requires the incidental work of setting up interfaces and implementations. If the subclasses are in separate files then the work is greater because you have to worry about header files as well, and you've fall straight into the trap of maintaining parallel class hierarchies.
+
+7. No categories. It shouldn't be necessary to write categories (containing "the tests") in order to test classes (containing "the product"). Once again, I don't like the incidental work involved.
+
+8. Ability to test applications, frameworks (in isolation), or applications that link against frameworks.
+
+9. Ability to run unit tests from within the debugger.
+
+9. The code that ships should be tested, not the code that doesn't ship. I don't want to only test the "Development" build of the product and then ship an (untested!) "Deployment" build. Nor do I want to test a selection of classes from the product in isolation (for example, taking the source files for those selected classes and compiling them into my test suite). I want to test the exact code which will ship to customers. 
+
+10. Customers and beta-testers should be able to run the tests (if you want them to, that is). It's useful that third parties can run the tests for the purposes of support or squishing bugs. It is possible that the tests may pass "in house", but fail on other hardware or other configurations, and information gathered on those configurations is of obvious value.
+
+11. The testing mechanism should be transparent (invisible) to those who have no interest in using it. The option to run the tests should be exactly that: optional; and the tests and the option to run them itself should not even be visible to those customers who don't wish to run the tests. In effect this means that the test materials should be easily embeddable within the shipped product's bundle.
+
+12. Shipping the tests with the code should not affect the performance or memory use of the product. Because the framework is so light-weight (only a few kilobytes on disk), it can be shipped with the product without significantly contributing to the download size. The compartmentalization of the tests, combined with the fact that they are entirely optional, means that users not running the tests suffer no performance penalty (not only do the tests not run, the tests and the test framework are not even loaded into memory).
+
+13. Setting up test suites and adding tests should be extremely easy.
+
+14. Test framework should be embeddable or installable.
+
+15. Should be able to use the test framework without linking to it (ie. there should be no performance or memory penalty for those who do not wish to perform testing).
+
+Needless to say, WOTest is not the "Swiss Army Knife" of unit testing tools. It was not designed to be flexible and adapt to a huge range of different possible workflows. There are already enough tools out there for that. It is designed to exactly fit one workflow, mine, and I'm releasing it because there may be other people out there who like to work in the same way as I do.
+
+Despite this, it turns out that the design of WOTest allows you to do quite a lot more with it than implement the specific workflow for which I wrote it. For example, WOTest can do the following:
+
+- work as a separate project from the product to be tested; work as a separate target within the project of the product to be tested; work embedded in the actual target of the product to be tested;
+
+- perform the tests during a build phase within Xcode; perform the tests when run from within Xcode; perform the tests when run from the command-line;
+
+- find tests in separate bundles or frameworks; find tests in separate files within a project (subclasses, separate classes, categories, or methods added to an existing class); find tests embedded in the class files of a project (subclasses, separate classes, categories, or methods added to an existing class);
+
+This documentation focuses on my own workflow, but makes brief references to how WOTest can be used in the different ways referred to above (see the section "Other workflow permutations" below).
+
+================================================================================
+Background
+================================================================================
+
+When I first heard about Unit Testing it sounded like a great idea. I wasn't such a big fan of the so called "Extreme Programming" movement as a whole; the advocates' eyes would glaze over as they pounded and thumped their desks with their fists while extolling the merits of their guiding principles (a collection of acronyms and Wiki-ready phrases jammed together into long words), and clutched their Extreme Programming tomes to their chests as though they were sacred religious texts. Lots of solid, good ideas, but taken a little too seriously and fanatically for my liking (and I am a Mac user, by definition somewhat "fanatical"). And all too frequently they claimed that those who disagreed with them did so only because they didn't understand (or weren't capable of understanding) their point of view (in other words, that they hadn't yet attained "enlightenment"). You can't blame them for feeling enthusiastic about something, but their dogmatic, polemical style smacks of close-mindedness and an unwillingness to recognise that other people may have alternative and valid points of view.
+
+But yes, the idea of unit testing did appeal to me. I looked at the existing unit test solutions and didn't really like any of them. They all seemed highly complicated (at least to my beginner's eyes) and required me to installed third-party frameworks and command-line tools and goodness knows what else. It all seemed like overkill when all I really wanted to do was a series of very basic assertions, like the following pseudo-code:
+
+    if (result of function != expected result)
+        report error;
+
+There's a lot more to it than that, but this is what essentially lies at the heart of unit testing. So instead of grappling with any of those tools, I came up with a set of macros that enabled me to do unit testing. Here is a basic example:
+
+    #ifdef DEBUG
+    
+    /*! Test for a positive result (> 0) */
+    #define WO_TEST_POSITIVE(expr)                                          \
+    do {                                                                    \
+        if ((expr) <= 0)                                                    \
+            ELOG(@"Unit test failed (expected POSITIVE; obtained <= 0)");   \
+    } while (0)
+    
+    #else /* DEBUG not defined*/
+    
+    #define WO_TEST_POSITIVE(expr)
+    
+    #endif
+
+The ELOG macro was defined elsewhere, and logged the class, method, line number and so forth where the error was produced. Note that when the DEBUG flag is not defined (as is the case with "Deployment" builds), the tests produce no code. This means that the tests don't wind up in production code shipped to customers (which was in keeping with my goal at the time), but the down side is that the code that is tested is the "Development" build and not the "Deployment" build. It's conceivable (although it never happened to me) that tests could pass in the Development build yet the same function could fail in the "real world" deployment build.
+
+In the end I had quite an elaborate set of unit testing macros, all in all totalling a few hundred lines, that met my needs for a long time. These tests weren't bad; they were very easy to add to a project and they could be easily run in the debugger. On the down side, you had to run the product (specifically the "Development" build) in order to perform the tests, which means you couldn't click on the test results in Xcode and be taken to the corresponding line and file.
+
+Then while reading Apple's cocoa-dev mailing list I heard about UnitKit. I took a look at it. In my view, UnitKit is the most light-weight and easy-to-learn unit testing solution out there. UnitKit's integration with Xcode was great because the tests could be tacked on to the "Build" phase and the test results could easily be clicked on and you'd be automatically taken to the appropriately line and file.
+
+For reasons I've already stated above, I decided to build my own testing framework. I'm a perfectionist, and although UnitKit is a great little piece of software, it didn't work exactly how my personal tastes would dictate. Also, UnitKit development stopped once Apple started shipping OCUnit with its Xcode Tools. I wrote WOTest from scratch and didn't use any code from UnitKit. The truth is that I barely even looked at that code. But I still want to recognise the efforts of James Duncan Davidson (the principal author of UnitKit) because his work basically inspired me to write WOTest, and provided me with the key idea for how to achieve integration with Xcode.
+
+The internals of WOTest are a little more simple, it has quite a few more test types, but it sticks to the basic core idea of UnitKit that makes it so well intergrated with Xcode: using preprocessor macros so that the inbuilt __FILE__ and __LINE__ macros can be used to generate output that Xcode will interpret and allow users to click on results and be taken to the right file and line. Like UnitKit, WOTest tries to keep the minimum amount of test logic in the macros and keep most of the logic in a set up underlying Objective-C methods, because writing, testing and debugging methods is more straightforward than writing complex macros.
+
+
+
+
+================================================================================
+WOTest can test itself
+================================================================================
+
+Before I could use it to test itself, I had to complete a significant proportion of the code. "Test-Driven Development" was not an option, because until I had written the testing mechanism I didn't even know how I could write the tests. Now that WOTest is basically complete, it can be used to test itself. The key piece that needed to be written before I could start using WOTest to test itself was the WOTestRunner command-line executable.
+
+The tests appear in the file, "WOTestSelfTests.m". The file is a fairly comprehensive example of the kinds of tests that are possible with WOTest. Because the objective is to test the tests, all of the test methods include two approaches to the testing: one batch of tests that are expected to pass (in other words, if a test passes when it is expected to pass, then this is a pass); and a second batch of tests that are expected to fail (in other words, if a test fails a test when it is expected to fail, then this also is a pass). In this respect, this shows a highly atypical application of the tests; generally you would write code with the intention that all tests should pass, but in order to test that the tests work you must show that the both pass when you expect them to pass and fail when you expect them to fail. 
+
+However, the presence of all those failure warnings in the build log can make the test results hard to read. For this reason, WOTest implements a special variable (expectFailures) that should be used only during WOTest self-testing. When the flag is set, in addition to the standard "Failed" and "Passed" status messages, there are an additional two messages: "Failed (expected failure)" and "Passed (unexpected pass)". Normally in your own unit tests you wouldn't need to set the expectFailures variable; rather you would write tests such that an expected failure results in pass. This is much safer, because there's no risk of you setting the expectFailures variable to YES and later forgetting to set it back to NO.
+
+================================================================================
+Linking to WOTest.framework
+================================================================================
+
+You don't have to link to WOTest.framework, and there are a couple of reasons why you wouldn't want to. WOTest has been carefully designed so that you don't have to link if you don't want to.
+
+When you link to WOTest.framework you increase the size of your executable file on disk by a small amount (this is true of any framework). Furthermore, your executable will use more memory than it would have had you not linked to WOTest.framework. This is true even if you link but never make any calls to methods in the framework. This is bad because it means that if you decide to ship your product with the unit tests included, then all users will see a slight increase in disk and memory usage, even those who don't have any interest in running the tests. One way to work around this problem is to have two versions of the product, one for internal testing (that links against WOTest.framework) and one for external deployment, but that breaks one of the design goals for WOTest of being able to test the exact product (bit for bit) that gets shipped to customers.
+
+Luckily, thanks to the dynamic nature of the Objective-C runtime, it's possible to elegantly get around this problem. It's possible to have your product dynamically load the WOTest.framework (in other words, no linking) if and only if the user explicitly instructs it to do so; at all other times the framework doesn't get loaded. Because there's no linking, it means the executable size doesn't swell, and those users who don't want to run the tests don't see any increase in memory usage. You're then left with a simple choice: distribute the WOTest.framework along with your product if you want users to be able to test it; or just distribute the product on its own if you don't want to offer testing capability. In either case the product is exactly the same and performs exactly the same.
+
+To illustrate how this works, consider the following example. Imagine a tool that does not link against Apple's AppKit framework, but nevertheless wants to create an NSView object:
+
+#import <AppKit/AppKit.h>
+
+- (void)callAppKitMethod
+{
+    NSView *aView = [[NSView alloc] initWithFrame:NSZeroRect];
+    [aView release];
+}
+
+By including the AppKit header file you ensure that the compiler doesn't give you any warnings, but if you try to build such code the linker will give you an error that "NSView" is an undefined symbol. Of course, even if you could produce an executable and run it, it wouldn't work because the executable isn't linked to the AppKit.framework. One way to force the framework to load is to set environment variables and then run the product from the command-line. For example, in the bash shell:
+
+export DYLD_FRAMEWORK_PATH=/System/Library/Frameworks
+export DYLD_INSERT_LIBRARIES=/System/Library/Frameworks/AppKit.framework/AppKit 
+/path/to/executable
+
+On running, the executable has access to all of the AppKit.framework's classes, even though it didn't link to them. But this still doesn't solve the problem that the linker will complain about undefined symbols. One way around this is to pass "-undefined define_a_way" to the linker when building your executable (see the ld (1) man page for more options).
+
+A far more elegant way to solve this problem is to take advantage of the dynamic nature of the Objective-C runtime. The product doesn't need to link against the framework, there is no need to set linker options, export environment variables, or run the product from the command-line. The following code illustrates:
+
+#import <AppKit/AppKit.h>
+
+- (void)callAppKitMethod
+{
+    NSBundle *theBundle = 
+    [NSBundle bundleWithPath:@"/System/Library/Frameworks/AppKit.framework"];
+    Class   viewClass   = [theBundle classNamed:@"NSView"];
+    NSView  *aView      = [[viewClass alloc] initWithFrame:NSZeroRect];
+    [aView release];
+}
+
+No compiler warnings, no linker errors, everything just works. WOTest uses this technique to allow you to load code "on the fly" without having to link to it. All it has to do is look for bundles, load them, and then run the test methods on them. 
+
+================================================================================
+How to test frameworks
+================================================================================
+
+Let's take an example framework, "Example.framework". As a developer, you want to be able to build the framework and test it. You then want to ship the framework to customers. You don't want to have to engage in multiple builds (one for testing, one for shipping). The WOTest.framework is used to perform tests and report the results. The WOTestRunner commandline executable is the tool that loads the testing framework and the framework-to-be-tested into memory and then runs the tests.
+
+You have multiple options for where you put the tests:
+
+1. Embed them in Example.framework itself. This requires you to link against the WOTest.framework, otherwise you'll get warnings for undefined symbols. It will also swell the memory footprint of your framework even if the user never runs the tests.
+
+In this case you'll just run WOTestRunner and pass "Example.framework" as a command-line parameter.
+
+2. Stick them in separate framework or loadable bundle; call it "Tests.framework" or "Tests.bundle". Link against "WOTest.framework" and the framework you want to test so that the linker doesn't give you warnings about undefined symbols.
+
+In this case you'll run WOTestRunner and pass both "Example.framework" and "Tests.framework" or "Tests.bundle" as command-line parameters.
+
+WOTestRunner loads the code from the frameworks into memory and runs all eligible test, reporting the results.
+
+Framework search order:
+
+~/Library/Frameworks/
+~/Developer/Frameworks/
+/Library/Frameworks/
+/Developer/Frameworks/
+/Network/Library/Frameworks/
+/Network/Developer/Frameworks/
+/System/Library/Frameworks/
+
+0. These instructions assume a shared build products location. For example:
+
+~/build/WOTest.framework
+~/build/FrameworkBeingTested.framework
+~/build/FrameworkBeingTested.framework/Versions/A/Resources/FrameworkTests.bundle
+~/build/OtherFrameworkLinkedToByFrameworkBeingTested.framework
+
+Need to make sure these instructions work when embedded inside an application.
+And need to find a way to easily test everything at once for end-users... eg. user runs tests on application and application fires off tests on applications too
+
+1. In your framework project create a new group called "FrameworkTests" (or some other distinguishing label).
+
+2. From the "Project" menu select "New Target...".
+
+3. Choose "Loadable Bundle" from the "Cocoa" group.
+
+4. Name the target "FrameworkTests" (or some other distinguishing label).
+
+5. In the Target properties window that appears click the plus button to add the framework as a direct dependency of the test bundle. You can add WOTest.framework as a dependency as well if you want to make sure it gets built when required as part of the build process (rather than using a pre-built copy).
+
+5b. Add "-noprebind" to the "OTHER_LDFLAGS_ppc" build setting for the target if necessary to silence warnings.
+
+6. Xcode will create a plist file with a name like "FrameworkTests-Info.plist" which you can drag into the "FrameworkTests" group.
+
+7. Set the "CFBundleIdentifier" in the info plist file to something appropriate, like "com.wincent.SynergyAdvanceFrameworkTests".
+
+8. Select the "FrameworkTests" group and from the "File" menu choose "New File..."
+
+9. Choose "Objective-C class" from the "Cocoa" group and choose an appropriate filename such as "FrameworkTests.m".
+
+10. In the header ("FrameworkTests.h") mark your test class with the "<WOTest>" protocol marker and add this import statement.
+
+#import "WOTest/WOTest.h"
+
+11. In the implementation file write your tests. The methods should start with "- (void)test".
+
+12. Link the FrameworkTests bundle against Foundation.framework (or Cocoa.framework) or whatever other frameworks your test framework links to and the framework that you wish to test. It is not necessary to link to the WOTest.framework itself, but you can if you want to use the WOMock class.
+
+12b. Add WOTest to the project as a dependency, to ensure it gets built if required.
+
+12c. You may need to add "$(TARGET_BUILD_DIR)" to your "Header Search Paths" for the test target.
+
+13. Add a "Run Script" phase to the target that handles the packaging:
+
+# copy the test bundle into its final location
+${CP} -fRv "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" "${TARGET_BUILD_DIR}/SynergyAdvance.framework/Versions/A/Resources/"
+
+# make the frameworks folder relative to WOTestRunner (@executable_path/../Frameworks/)
+/bin/mkdir -pv "${TARGET_BUILD_DIR}/SynergyAdvance.framework/Versions/A/Frameworks"
+
+# provide a symlink so that the bundle can find the framework
+cd "${TARGET_BUILD_DIR}/SynergyAdvance.framework/Versions/A/Frameworks"
+/bin/ln -fsv ../../../../WOTest.framework WOTest.framework
+=====
+export DYLD_FRAMEWORK_PATH="${TARGET_BUILD_DIR}"
+
+"${TARGET_BUILD_DIR}/WOTest.framework/Versions/A/Resources/WOTestRunner" \
+--test-bundle="${TARGET_BUILD_DIR}/SynergyAdvance.framework/Versions/A/Resources/${FULL_PRODUCT_NAME}"
+=====
+
+
+include information on how to set this up as a custom executable too (works: see WODebug for an example)
+useful for running WOTestRunner under GDB
+
+
+================================================================================
+How to test applications
+================================================================================
+
+Testing frameworks is relatively easy because they are self-contained pieces of functionality. You can create a test bundle that links against the framework and then use the test runner to load the test bundle and run the tests, thus testing the framework.
+
+Testing an application is slightly different. You can't use the test runner to run an application. You can put your tests in a bundle and use the BUNDLE_LOADER build setting to inform that linker that it should look for any missing symbols in the executable (but not with dead code stripping turned on, unless you use the WO_TEST_NO_DEAD_STRIP_CLASS macro). You can then employ some dynamic loading trickery to load the bundle from the application and run the tests.
+
+Injecting a test bundle into a running application. Example taken from REnamer.app:
+
+export WOTestInjectBundle="/Users/wincent/trabajo/build/Release/REnamer.app/Contents/Resources/ApplicationTests.bundle"
+export DYLD_INSERT_LIBRARIES="/Users/wincent/trabajo/build/Release/REnamer.app/Contents/Frameworks/WOTest.framework/WOTest"
+export DYLD_PRINT_LIBRARIES=1
+/Users/wincent/trabajo/build/Release/REnamer.app/Contents/MacOS/REnamer 
+
+================================================================================
+Shipping testable production applications
+================================================================================
+
+================================================================================
+Shipping non-testable production applications
+================================================================================
+
+================================================================================
+Shipping testable frameworks
+================================================================================
+
+
+================================================================================
+Shipping non-testable frameworks
+================================================================================
+
+
+================================================================================
+Other workflow permutations
+================================================================================
+
+The suggested workflow that's documented above is based on adding a new target to the product that is to be tested and writing all of the tests inside of that target. WOTest was designed to enable this workflow. Nevertheless, the dynamic nature of the Objective-C runtime means that you can actually use WOTest in quite a few different ways and everything keeps on working as you would expect. Some alternative workflows are briefly discussed below.
+
+- Putting the tests inside an existing target
+
+- Putting the tests inside a separate project
+
+- Putting the tests inside subclasses (same files)
+
+- Putting the tests inside subclasses (separate files)
+
+- Putting the tests inside categories (same files)
+
+- Putting the tests inside categories (separate files)
+
+
+================================================================================
+FAQ (Frequently Asked Questions)
+================================================================================
+
+* How do I avoid the warning, "comparison between signed and unsigned, to avoid this warning use an explicit cast"?
+
+Either cast an unsigned value to a signed value:
+
+    number -> (int)number
+
+Or a signed value to an unsigned one:
+
+    number -> (unsigned)number
+
+A common cause of this warning is comparing an unsigned return value to a literal constant number:
+
+    WO_TEST_EQ([object length], 10);
+
+This is because the length method returns an unsigned value, but the compiler sees the number 10 as a signed value. One solution is:
+
+    WO_TEST_EQ([object length], 10U);
+
+================================================================================
+Donations
+================================================================================
+
+WOTest is free software released under the GPL license. If it is useful to you, please consider making a donation:
+
+https://www.paypal.com/xclick/business=win@wincent.com&item_name=WOTest+donation&no_note=1&currency_code=EUR&lc=en
+
+
+================================================================================
+Other unit testing frameworks that work with Objective-C
+================================================================================
+
+ObjCUnit:   http://oops.se/objcunit/                    (last updated 2002)
+
+OCUnit:     http://www.sente.ch/software/ocunit/        (included with Xcode Tools)
+
+TestKit:    http://testkit.sourceforge.net/             (last updated 2004)
+            http://sourceforge.net/projects/testkit
+
+UnitKit:    http://unitkit.org/                         (discontinued)
+
+
+================================================================================
+How it works
+================================================================================
+
+Project A
+--> Target: normal target
+
+--> Target: unit test target
+----> Dependency: normal target
+
+To perform the unit testing, build the unit test target:
+1. This first builds the project's normal target.
+2.A. Then it proceeds to build the unit test target.
+2.B. The unit test target contains a shell script phase that runs the WOTestRunner tool, passing the project's normal target bundle as a parameter.
+2.C. WOTestRunner loads the bundle and searches for all classes that correspond to the WOTest protocol.
+2.D. All conforming classes are searched for methods that begin with the word "test".
+2.E. Matching methods are invoked. Class methods are invoked first. Then, for each instance method, a new object is created using alloc/init, the test method is invoked, and the object is released. If a non-standard init method is required then the appropriate method should be invoked from within a class test method.
+
+An example class:
+
+class: WOFoo
+method: +testEncryption
+
+1. WOTestRunner loads WOFoo's bundle
+2. WOTestRunner finds WOFoo class
+3. WOTestRunner tests to see whether WOFoo conforms to the WOTest protocol
+4. WOTestRunner finds methods starting with "test"
+5. WOTestRunner runs the +testEncryption method
+6. The +testEncryption method contains a macro with the __FILE__ and __LINE__ passed as parameters
+7. The +testEncryption method calls a test method in the WOTest.framework
+8. If the test succeeds, a message is logged to stdout
+9. If the test fails, a message is logged to stderr in the appropriate format
+
+
+stderr reporting format: warnings:
+
+/full/path:line_number: warning: message
+
+errors:
+
+/full/path:line_number: error: message
+
+
+
+
+================================================================================
+Notes about mock objects
+================================================================================
+
+OCMock requires you to have a class which responds to all the selectors you want to mock. WOTest allows you to mock anything without writing anything. You do need to have the class that's being mocked available at runtime, but it can be totally empty.
+
+
+================================================================================
+Namespace issues
+================================================================================
+
+WOTest adds a number of methods to existing Foundation classes via categories. All added methods are prefixed with the "WOTest_" suffix to avoid namespace clashes with the software being tested. Yes it's ugly, and yes such names are not suitable for use in a Key/Value Coding context, but the methods are intended for internal use within WOTest only and so the tradeoff is deemed worthwhile.
+
+
+================================================================================
+Author contact and website
+================================================================================
+
+Author:             Wincent Colaiuta <win@wincent.com>
+Product website:    http://test.wincent.com/
+
+================================================================================
+Credits and thanks
+================================================================================
+
+Thanks James Duncan Davidson (UnitKit) for providing the inspiration to make a lean and elegant unit testing framework.
+
+http://unitkit.org/
+
+Thanks to Mulle Kybernetik (OCMock) for the idea of using the trampoline paradigm to elegantly implement mock objects.
+
+http://www.mulle-kybernetik.com/software/OCMock/
+
+================================================================================
+License and warranty
+================================================================================
+
+WOTest is licensed under the GNU GPL (General Public License). It 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 file LICENSE.txt for more details.
\ No newline at end of file
diff --git a/NSException+WOTest.h b/NSException+WOTest.h
new file mode 100644 (file)
index 0000000..eb1395a
--- /dev/null
@@ -0,0 +1,46 @@
+//
+//  NSException+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 19 October 2004.
+//
+//  Copyright 2004-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSException+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+//
+
+#import <Foundation/Foundation.h>
+
+#pragma mark -
+#pragma mark User info dictionary keys
+
+#define WO_TEST_USERINFO_LINE   @"WOTestLine"
+#define WO_TEST_USERINFO_PATH   @"WOTestPath"
+
+@interface NSException (WOTest) 
+
+/*! Cocoa allows any Objective-C object to be thrown in an exception. Although NSException objects are most commonly used for this purpose there is no limitation that prevents other objects from being used. These objects may or may not respond to the name, reason and userInfo methods that are implemented by NSException. This method accepts any object and attempts to produce a textual description, using the name and reason methods if implemented and otherwise resorting to the description method if implented and finally a basic description based on the name of the class if not. */
++ (NSString *)WOTest_descriptionForException:(id)exception;
+
+    /*! Returns the name of the passed exception, trying first to send a "name" selector, but falling back on the "description" selector and finally the class name itself. Returns the string "no exception" if passed nil. */
++ (NSString *)WOTest_nameForException:(id)exception;
+
+/*! Convenience method that invokes exceptionWithName:reason:userInfo:, packing the supplied path and line information into a userInfo dictionary. */
++ (NSException *)WOTest_exceptionWithName:(NSString *)name reason:(NSString *)reason inFile:(char *)path atLine:(int)line;
+
+/*! Convenience method that invokes exceptionWithName:reason:inFile:atLine: to create and raise an exception, packing the supplied path and line information into a userInfo dictionary. */
++ (void)WOTest_raise:(NSString *)name reason:(NSString *)reason inFile:(char *)path atLine:(int)line;
+
+@end
diff --git a/NSException+WOTest.m b/NSException+WOTest.m
new file mode 100644 (file)
index 0000000..cff79ff
--- /dev/null
@@ -0,0 +1,138 @@
+//
+//  NSException+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 19 October 2004.
+//
+//  Copyright 2004-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSException+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+//
+
+#import "NSException+WOTest.h"
+#import "NSObject+WOTest.h"
+
+@implementation NSException (WOTest)
+
++ (NSString *)WOTest_descriptionForException:(id)exception
+{
+    @try
+    {
+        if ([NSObject WOTest_object:exception isKindOfClass:[NSException class]])
+            return [NSString stringWithFormat:@"%@: %@", [exception name], [exception reason]];
+        else if ([NSObject WOTest_object:exception respondsToSelector:@selector(name)])
+        {
+            NSString *name = nil; // attempt to get name
+            
+            if ([NSObject WOTest_objectReturnsId:exception forSelector:@selector(name)])
+            {
+                name = [exception name];
+                if (![NSObject WOTest_object:name isKindOfClass:[NSString class]])
+                    @throw self; // the returned id is not an NSString; bail
+            }
+            else if ([NSObject WOTest_objectReturnsCharacterString:exception forSelector:@selector(name)] ||
+                     [NSObject WOTest_objectReturnsConstantCharacterString:exception forSelector:@selector(name)])
+            {
+                const char *charString = (const char *)[exception name];
+                name = [NSString stringWithUTF8String:charString];
+            }
+            
+            NSString *reason = nil; // attempt to get reason
+            if ([NSObject WOTest_object:exception respondsToSelector:@selector(reason)])
+            {
+                if ([NSObject WOTest_objectReturnsId:exception forSelector:@selector(reason)])
+                {
+                    reason = [exception reason];
+                    if (![NSObject WOTest_object:reason isKindOfClass:[NSString class]])
+                        @throw self; // the returned id is not an NSString; bail
+                }
+                else if ([NSObject WOTest_objectReturnsCharacterString:exception forSelector:@selector(reason)] ||
+                         [NSObject WOTest_objectReturnsConstantCharacterString:exception forSelector:@selector(reason)])
+                {
+                    const char *charString = (const char *)[exception reason];
+                    reason = [NSString stringWithUTF8String:charString];
+                }
+            }
+            
+            if (name && reason)
+                return [NSString stringWithFormat:@"%@: %@", name, reason];
+            else if (name)
+                return [NSString stringWithFormat:@"%@ (%x)", name, exception];
+            else if (reason)
+                return [NSString stringWithFormat:@"%@ (%x)", reason, exception];
+        }
+        else
+        {
+            return [NSObject WOTest_descriptionForObject:exception];
+        }            
+    }
+    @catch (id e)
+    {
+        // fall through
+    }
+    
+    // last resort
+    return [NSString stringWithFormat:@"unknown exception (%x)", exception];
+}
+
++ (NSString *)WOTest_nameForException:(id)exception
+{
+    NSString *returnString = nil;
+    @try
+    {
+        if (!exception)
+            returnString = @"no exception";
+        else if ([NSObject WOTest_object:exception isKindOfClass:[NSException class]])
+            returnString = [exception name];
+        else if ([NSObject WOTest_object:exception respondsToSelector:@selector(name)])
+        {
+            if ([NSObject WOTest_objectReturnsId:exception forSelector:@selector(name)])
+            {
+                returnString = [exception name];
+                if (![NSObject WOTest_object:returnString isKindOfClass:[NSString class]])
+                    @throw self; // the returned id is not an NSString; bail
+            }
+            else if ([NSObject WOTest_objectReturnsCharacterString:exception forSelector:@selector(name)] ||
+                     [NSObject WOTest_objectReturnsConstantCharacterString:exception forSelector:@selector(name)])
+            {
+                const char *charString = (const char *)[exception name];
+                returnString = [NSString stringWithUTF8String:charString];
+            }
+        }
+        else 
+            returnString = [NSObject WOTest_descriptionForObject:exception];
+    }
+    @catch (id e)
+    {
+        returnString = @"(exception caught trying to determine exception name)";
+    }    
+    return returnString;
+}
+
+// TODO: replace with var_args versions
++ (NSException *)WOTest_exceptionWithName:(NSString *)aName reason:(NSString *)aReason inFile:(char *)path atLine:(int)line
+{
+    NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+        [NSString stringWithUTF8String:path],   WO_TEST_USERINFO_PATH,
+        [NSNumber numberWithInt:line],          WO_TEST_USERINFO_LINE, nil];
+    return [self exceptionWithName:aName reason:aReason userInfo:theUserInfo];
+}
+
++ (void)WOTest_raise:(NSString *)aName reason:(NSString *)aReason inFile:(char *)path atLine:(int)line
+{
+    [[self WOTest_exceptionWithName:aName reason:aReason inFile:path atLine:line] raise];
+}
+
+@end
diff --git a/NSInvocation+WOTest.h b/NSInvocation+WOTest.h
new file mode 100644 (file)
index 0000000..04233aa
--- /dev/null
@@ -0,0 +1,41 @@
+//
+//  NSInvocation+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 30 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSInvocation+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+
+@interface NSInvocation (WOTest)
+
+//! Need a method for checking invocation equality (NSInvocation isEqual: method always returns NO).
+- (BOOL)WOTest_isEqualToInvocation:(NSInvocation *)anInvocation;
+
+//! A method for checking invocation equality which ignores arguments.
+- (BOOL)WOTest_isEqualToInvocationIgnoringArguments:(NSInvocation *)anInvocation;
+
+//! Convenience method for extracting arguments from invocations, returning them as NSValues.
+//! \throws NSInternalInconsistencyException thrown if \p index is outside the range of arguments in the receiver.
+- (NSValue *)WOTest_valueForArgumentAtIndex:(unsigned)index;
+
+//! Convenience method for inserting arguments passed as NSValues into invocations
+//! \throws NSInternalInconsistencyException thrown if \p index is outside the range of arguments in the receiver.
+- (void)WOTest_setArgumentValue:(NSValue *)aValue atIndex:(unsigned)index;
+
+@end
diff --git a/NSInvocation+WOTest.m b/NSInvocation+WOTest.m
new file mode 100644 (file)
index 0000000..827a7d2
--- /dev/null
@@ -0,0 +1,98 @@
+//
+//  NSInvocation+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 30 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSInvocation+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSInvocation+WOTest.h"
+#import "WOTest.h"
+
+@implementation NSInvocation (WOTest)
+
+- (BOOL)WOTest_isEqualToInvocation:(NSInvocation *)anInvocation
+{
+    // first do a basic comparison without arguments
+    if (![self WOTest_isEqualToInvocationIgnoringArguments:anInvocation]) return NO;
+    
+    // compare arguments (skip first two: self and _cmd)
+    NSMethodSignature *aSignature = [self methodSignature];
+    NSMethodSignature *otherSignature = [anInvocation methodSignature];
+    for (unsigned i = 2, max = [aSignature numberOfArguments]; i < max; i++)
+    {
+        const char *aType = [aSignature getArgumentTypeAtIndex:i];
+        const char *otherType = [otherSignature getArgumentTypeAtIndex:i];
+        if (strcmp(aType, otherType) != 0) return NO;
+        
+        // compare the two values
+        return [[self WOTest_valueForArgumentAtIndex:i] isEqual:[anInvocation WOTest_valueForArgumentAtIndex:i]];
+    }
+    
+    return YES; // if get this far, all equality tests passed (no arguments)
+}
+
+- (BOOL)WOTest_isEqualToInvocationIgnoringArguments:(NSInvocation *)anInvocation
+{
+    // basic checks: compare against nil and against self
+    if (!anInvocation) return NO;
+    if (anInvocation == self) return YES;
+    
+    // compare selectors
+    if ([self selector] != [anInvocation selector]) return NO;
+    
+    // compare signatures
+    NSMethodSignature *aSignature = [self methodSignature];
+    NSMethodSignature *otherSignature = [anInvocation methodSignature];
+    if (![aSignature isEqual:otherSignature]) return NO;
+        
+    return YES; // if get this far, all equality tests passed (no arguments)
+}
+
+- (NSValue *)WOTest_valueForArgumentAtIndex:(unsigned)index
+{
+    NSMethodSignature *methodSignature = [self methodSignature];
+    NSParameterAssert(index < [methodSignature numberOfArguments]);
+    const char *type = [methodSignature getArgumentTypeAtIndex:index];
+
+    // no way to find out size needed for argument, so allocate enough for all
+    size_t bufferSize = (size_t)[methodSignature frameLength];
+    void *buffer = malloc(bufferSize);
+    NSAssert1((buffer != NULL), @"malloc() failed (size %d)", bufferSize);
+    [self getArgument:&buffer atIndex:index];
+    NSValue *aValue = [NSValue value:&buffer withObjCType:type];
+
+    // following line yields a runtime error:
+    // malloc: ***  Deallocation of a pointer not malloced: 0x26a45c; This could be a double free(), or free() called with the middle of an allocated block; Try setting environment variable MallocHelp to see tools to help debug
+    //free(buffer); // is NSValue freeing automatically?
+    return aValue;
+}
+
+- (void)WOTest_setArgumentValue:(NSValue *)aValue atIndex:(unsigned)index
+{
+    NSMethodSignature *methodSignature = [self methodSignature];
+    NSParameterAssert(index < [methodSignature numberOfArguments]);
+    NSParameterAssert(aValue != nil);
+    size_t bufferSize = [aValue WOTest_bufferSize];
+    void *buffer = malloc(bufferSize);
+    NSAssert1((buffer != NULL), @"malloc() failed (size %d)", bufferSize);
+    [aValue getValue:buffer];
+    [self setArgument:buffer atIndex:index];
+    free(buffer);
+}
+
+@end
diff --git a/NSMethodSignature+WOTest.h b/NSMethodSignature+WOTest.h
new file mode 100644 (file)
index 0000000..bd885ed
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  NSMethodSignature+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 30 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSMethodSignature+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+
+@interface NSMethodSignature (WOTest) 
+
++ (id)WOTest_signatureBasedOnObjCTypes:(const char *)types;
+
+@end
diff --git a/NSMethodSignature+WOTest.m b/NSMethodSignature+WOTest.m
new file mode 100644 (file)
index 0000000..353e7c3
--- /dev/null
@@ -0,0 +1,139 @@
+//
+//  NSMethodSignature+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 30 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSMethodSignature+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSMethodSignature+WOTest.h"
+#import <objc/objc-class.h>
+
+/*
+ The real 10.4 NSMethodSignature API (obtained using class-dump; TODO: run class-dump to get 10.5 API):
+@interface NSMethodSignature : NSObject
+{
+    char *_types;
+    int _nargs;
+    unsigned int _sizeofParams;
+    unsigned int _returnValueLength;
+    void *_parmInfoP;
+    int *_fixup;
+    void *_reserved;
+}
+
++ (id)signatureWithObjCTypes:(const char *)fp8;
+- (BOOL)_isReturnStructInRegisters;
+- (id)retain;
+- (void)release;
+- (unsigned int)retainCount;
+- (const char *)methodReturnType;  // PUBLIC
+- (unsigned int)methodReturnLength; // PUBLIC
+- (BOOL)isOneway; // PUBLIC
+- (const char *)getArgumentTypeAtIndex:(unsigned int)fp8; // PUBLIC
+- (struct _arginfo)_argumentInfoAtIndex:(unsigned int)fp8;
+- (unsigned int)frameLength; // PUBLIC
+- (unsigned int)numberOfArguments; // PUBLIC
+- (id)description;
+- (id)debugDescription;
+
+@end
+
+The public API:
+
+@interface NSMethodSignature : NSObject {
+    @private
+    const char *_types;
+    int                _nargs;
+    unsigned   _sizeofParams;
+    unsigned   _returnValueLength;
+    void       *_parmInfoP;
+    int                *_fixup;
+    void       *_reserved;
+}
+
+- (unsigned)numberOfArguments;
+- (const char *)getArgumentTypeAtIndex:(unsigned)index;
+- (unsigned)frameLength;
+- (BOOL)isOneway;
+- (const char *)methodReturnType;
+- (unsigned)methodReturnLength;
+
+@end
+
+Many public implementations of that use NSProxy subclasses to implement trampolines make use of the signatureWithObjCTypes: private API.
+
+Examples: OCMock
+http://www.cocoadev.com/index.pl?LSTrampoline
+http://www.cocoadev.com/index.pl?BSTrampoline
+
+See also http://www.stuffonfire.com/2005/12/signaturewithobjctypes_is_stil.html
+
+ */
+
+@interface NSMethodSignature (WOApplePrivate)
+
+/*! Private Apple API. */
++ (id)signatureWithObjCTypes:(const char *)fp8;
+
+@end
+
+@interface NSMethodSignature (WOPrivate)
+
+- (id)initWithObjCTypes:(const char *)types;
+
+@end
+
+@implementation NSMethodSignature (WOTest)
+
++ (id)WOTest_signatureBasedOnObjCTypes:(const char *)types
+{
+    NSParameterAssert(types != NULL);
+
+#ifdef WO_USE_OWN_METHOD_SIGNATURE_IMPLEMENTATION
+    
+    return [[[self alloc] initWithObjCTypes:types] autorelease];
+    
+#else /* use private Apple API */
+    
+    NSAssert([self respondsToSelector:@selector(signatureWithObjCTypes:)],
+             @"signatureWithObjCTypes: selector not recognized");
+    return [self signatureWithObjCTypes:types];
+
+#endif
+    
+}
+
+- (id)initWithObjCTypes:(const char *)types
+{
+    NSParameterAssert(types != NULL);
+    if ((self = [super init]))
+    {
+        // TODO: finish implementation
+        // loop through args
+/*        unsigned method_getNumberOfArguments(Method);
+        unsigned method_getSizeOfArguments(Method); */
+        
+        // I hate using private Apple APIs, even ones that appear stable, but
+        // not sure that meddling with these instance variables is a good idea        
+    }
+    return self;
+}
+
+@end
diff --git a/NSObject+WOTest.h b/NSObject+WOTest.h
new file mode 100644 (file)
index 0000000..78bf318
--- /dev/null
@@ -0,0 +1,76 @@
+//
+//  NSObject+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 12 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSObject+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+
+/*! WOTest is designed to work with any Objective-C class or object. You are not limited to working only with classes that derive from the Apple root classes (NSObject and NSProxy) or the other root classes (such as Object and NSZombie) that are implemented in libobjc (libobjc is part of Darwin and open source but the headers do not ship with Mac OS X). In most Objective-C programming you can assume that the objects with which you are working descend from NSObject and implement the NSObject protocol. This means that you can avoid exceptions by testing objects to see whether they implement selectors before sending messages; NSObject methods such as conformsToProtocol:, respondsToSelector: and performSelector: are frequently used for this purpose. 
+
+If you try sending these messages to objects which do not implement them then you could cause a non-catchable exception (generated in _objc_error and _objc_trap) which will terminate the testing process. One of the goals of WOTest is to be extremely robust; exceptions should be caught and reported and they should not crash the program. It is true that custom root classes are extremely uncommon but WOTest nevertheless has been designed to cope with them. WOTest interacts with the Objective-C runtime at a low level and implements a number of convenience wrapper routines that enable the framework to work with any Objective-C object without fear of provoking uncatchable exceptions. The wrapper methods are described in this section. In simple cases (for example when invoking the objc_msgSend function) the framework calls the function directly without a wrapper. */
+
+@interface NSObject (WOTest) 
+
+/*! Substitute for NSObject description method. Returns "(nil)" if \p anObject is nil. */
++ (NSString *)WOTest_descriptionForObject:(id)anObject;
+
+/*! Returns YES if \p aClass is a class definition registered with the runtime. Returns NO otherwise. Does not raise an exception if passed a non-class pointer (in fact, the purpose of this method is to provide a way for checking for valid class pointers). */
++ (BOOL)WOTest_isRegisteredClass:(Class)aClass;
+
+/*! Returns YES if aClass is the metaclass of a class that is registered with the runtime. */
++ (BOOL)WOTest_isMetaClass:(Class)aClass;
+
+/*! Substitute for NSObject isKindOfClass: method. */
++ (BOOL)WOTest_object:(id)anObject isKindOfClass:(Class)aClass;
+
+/*! Substitute for NSObject isKindOfClass: method. */
++ (BOOL)WOTest_instancesOfClass:(Class)aClass areKindOfClass:(Class)otherClass;
+
++ (BOOL)WOTest_class:(Class)aClass respondsToSelector:(SEL)aSelector;
+
+/*! WOTest can work with objects that do not derive from any of the Apple root classes (NSObject, NSProxy) or implement the standard protocols (such as NSObject). This means that it is possible that the objects could be passed that do not even implement the respondsToSelector: method. If such objects are sent the selector then the program running the tests could crash. The WOTest_object:respondsToSelector: method avoids these crashes by providing a wrapper for the low level class_getInstanceMethod function. */
++ (BOOL)WOTest_object:(id)anObject respondsToSelector:(SEL)aSelector;
+
+/*! Similar to WOTest_object:respondsToSelector:. */
++ (BOOL)WOTest_instancesOfClass:(Class)aClass respondToSelector:(SEL)aSelector;
+
+/*! Similar to WOTest_instancesOfClass:respondToSelector:. */
++ (BOOL)WOTest_instancesOfClass:(Class)aClass conformToProtocol:(Protocol *)aProtocol;
+
++ (NSString *)WOTest_returnTypeForClass:(Class)aClass selector:(SEL)aSelector;
+
++ (NSString *)WOTest_returnTypeForObject:(id)anObject selector:(SEL)aSelector;
+
++ (BOOL)WOTest_isIdReturnType:(NSString *)returnType;
+
++ (BOOL)WOTest_isCharacterStringReturnType:(NSString *)returnType;
+
++ (BOOL)WOTest_isConstantCharacterStringReturnType:(NSString *)returnType;
+
+/*! Raises if \p anObject is nil or \p aSelector is NULL. Returns no if \p aSelector is not implemented by \p anObject. */
++ (BOOL)WOTest_objectReturnsId:(id)anObject forSelector:(SEL)aSelector;
+
+/*! Raises if \p anObject is nil or \p aSelector is NULL. Returns no if \p aSelector is not implemented by \p anObject. */
++ (BOOL)WOTest_objectReturnsCharacterString:(id)anObject forSelector:(SEL)aSelector;
+
+/*! Raises if \p anObject is nil or \p aSelector is NULL. Returns no if \p aSelector is not implemented by \p anObject. */
++ (BOOL)WOTest_objectReturnsConstantCharacterString:(id)anObject forSelector:(SEL)aSelector;
+
+@end
diff --git a/NSObject+WOTest.m b/NSObject+WOTest.m
new file mode 100644 (file)
index 0000000..54ac582
--- /dev/null
@@ -0,0 +1,308 @@
+//
+//  NSObject+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 12 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSObject+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+
+// category header
+#import "NSObject+WOTest.h"
+
+// system headers
+#import <objc/objc-runtime.h>
+#import <objc/Protocol.h>
+
+// other headers
+#import "NSScanner+WOTest.h"
+#import "NSValue+WOTest.h"
+
+@implementation NSObject (WOTest)
+
++ (NSString *)WOTest_descriptionForObject:(id)anObject
+{
+    if (!anObject) return @"(nil)";
+    
+    @try
+    {
+        // special case handling for NSValue objects
+        if ([self WOTest_object:anObject isKindOfClass:[NSValue class]])
+            return [(NSValue *)anObject WOTest_description];
+        
+        // fallback case: try "description" selector
+        if ([self WOTest_object:anObject respondsToSelector:@selector(description)])
+        {
+            NSString *returnType = [self WOTest_returnTypeForObject:anObject selector:@selector(description)];
+            
+            if ([self WOTest_isIdReturnType:returnType])
+            {
+                NSString *description = [anObject description];
+                if ([self WOTest_object:description isKindOfClass:[NSString class]])
+                    return description;
+            }
+            else if ([self WOTest_isCharacterStringReturnType:returnType] || 
+                     [self WOTest_isConstantCharacterStringReturnType:returnType])
+            {
+                const char *charString = (const char *)[anObject description];
+                return [NSString stringWithUTF8String:charString];
+            }
+        }
+    }
+    @catch (id e) 
+    {
+        // fall through
+    }
+    return NSStringFromClass(object_getClass(anObject)); // last resort
+}
+
++ (BOOL)WOTest_isRegisteredClass:(Class)aClass
+{
+    if (!aClass) return NO;
+    
+    BOOL    isRegisteredClass   = NO;
+    int     numClasses          = 0;
+    int     newNumClasses       = objc_getClassList(NULL, 0);
+    Class   *classes            = NULL;
+    
+    // get list of all classes on the system
+    while (numClasses < newNumClasses)
+    {
+        numClasses          = newNumClasses;
+        size_t bufferSize   = sizeof(Class) * numClasses;
+        classes             = realloc(classes, bufferSize);
+        NSAssert1((classes != NULL), @"realloc() failed (size %d)", bufferSize);
+        newNumClasses       = objc_getClassList(classes, numClasses);
+    }
+    
+    if (classes)
+    {
+        for (int i = 0; i < newNumClasses; i++)
+        { 
+            if (aClass == classes[i])   // found in list!
+            {
+                isRegisteredClass = YES;
+                break;
+            }
+        }
+        free(classes);
+    }
+    return isRegisteredClass;
+}
+
+/*! Returns YES if aClass is the metaclass of a class that is registered with the runtime. */
++ (BOOL)WOTest_isMetaClass:(Class)aClass
+{
+    if (!aClass) return NO;
+    
+    BOOL    isMetaClass     = NO;
+    int     numClasses      = 0;
+    int     newNumClasses   = objc_getClassList(NULL, 0);
+    Class   *classes        = NULL;
+    
+    // get a list of all classes on the system
+    // get list of all classes on the system
+    while (numClasses < newNumClasses)
+    {
+        numClasses          = newNumClasses;
+        size_t bufferSize   = sizeof(Class) * numClasses;
+        classes             = realloc(classes, bufferSize);
+        NSAssert1((classes != NULL), @"realloc() failed (size %d)", bufferSize);
+        newNumClasses       = objc_getClassList(classes, numClasses);
+    }
+    
+    if (classes)
+    {
+        for (int i = 0; i < newNumClasses; i++)
+        {
+            if (class_isMetaClass(classes[i]))  // looking at a meta class
+            {
+                if (aClass == classes[i])              // found in list!
+                {
+                    isMetaClass = YES;
+                    break;
+                }
+            }
+            else // not looking at a meta class, look up its meta class
+            {
+                if (aClass == object_getClass(classes[i])) // found it!
+                {
+                    isMetaClass = YES;
+                    break;
+                }
+            }
+        }
+        free(classes);
+    }
+    return isMetaClass;    
+}
+
++ (BOOL)WOTest_object:(id)anObject isKindOfClass:(Class)aClass
+{
+    if (!aClass)    return NO;
+    NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
+    if (!anObject)  return NO;
+    Class objectClass = object_getClass(anObject);
+    
+    if (objectClass == aClass)
+        return YES;
+    else
+    {
+               // check superclass
+        Class superClass = class_getSuperclass(objectClass);
+               if (superClass)
+                       return [self WOTest_instancesOfClass:superClass areKindOfClass:aClass];                
+    }
+    
+    return NO; // give up
+}
+
++ (BOOL)WOTest_instancesOfClass:(Class)aClass areKindOfClass:(Class)otherClass
+{
+    if (!aClass)        return NO;
+    NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
+    if (!otherClass)    return NO;
+    NSParameterAssert([self WOTest_isRegisteredClass:otherClass] || [self WOTest_isMetaClass:otherClass]);
+    
+    if (aClass == otherClass)
+        return YES;
+       
+       Class superClass = class_getSuperclass(aClass);
+    if (superClass)
+        return [self WOTest_instancesOfClass:superClass areKindOfClass:otherClass];
+    
+    return NO; // give up
+}
+
++ (BOOL)WOTest_class:(Class)aClass respondsToSelector:(SEL)aSelector
+{
+    if (!aClass) return NO;
+    NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
+    if (!aSelector) return NO;
+    return (class_getClassMethod(aClass, aSelector) ? YES : NO);
+}
+
++ (BOOL)WOTest_object:(id)anObject respondsToSelector:(SEL)aSelector
+{
+    if (!anObject)  return NO;
+    if (!aSelector) return NO;
+    Class theClass = object_getClass(anObject);
+    if (class_isMetaClass(theClass))
+        return (class_getClassMethod(theClass, aSelector) ? YES : NO);
+    else
+        return (class_getInstanceMethod(theClass, aSelector) ? YES : NO);
+}
+
++ (BOOL)WOTest_instancesOfClass:(Class)aClass respondToSelector:(SEL)aSelector
+{
+    if (!aClass) return NO;
+    NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
+    if (!aSelector) return NO;
+    return (class_getInstanceMethod(aClass, aSelector) ? YES : NO);
+}
+
++ (BOOL)WOTest_instancesOfClass:(Class)aClass conformToProtocol:(Protocol *)aProtocol
+{
+    if (!aClass)    return NO;
+    NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
+    if (!aProtocol) return NO;
+       if (class_conformsToProtocol(aClass, aProtocol))
+               return YES;
+       Class superClass = class_getSuperclass(aClass);
+       if (superClass)
+        return [self WOTest_instancesOfClass:superClass conformToProtocol:aProtocol];
+    
+    return NO;  // give up
+}
+
++ (NSString *)WOTest_returnTypeForClass:(Class)aClass selector:(SEL)aSelector
+{
+    NSParameterAssert(aClass != NULL);
+    NSParameterAssert([self WOTest_isRegisteredClass:aClass] || [self WOTest_isMetaClass:aClass]);
+    NSParameterAssert(aSelector != NULL);
+    Method theMethod = class_getClassMethod(aClass, aSelector);
+    
+    if (theMethod == NULL) // class does not respond to this selector
+        return nil;
+
+    // get return type and list of arguments
+       char *returnType = method_copyReturnType(theMethod);
+       if (returnType)
+       {
+               NSString *returnString = [[NSString alloc] initWithUTF8String:returnType];
+               free(returnType);
+               return returnString;
+       }
+       return nil;
+}
+
++ (NSString *)WOTest_returnTypeForObject:(id)anObject selector:(SEL)aSelector
+{
+    NSParameterAssert(anObject != nil);
+    NSParameterAssert(aSelector != NULL);
+    Method theMethod = class_getInstanceMethod(object_getClass(anObject), aSelector);
+               
+       if (theMethod == NULL) // object does not respond to this selector
+        return nil;
+       
+    // get return type and list of arguments
+       char *returnType = method_copyReturnType(theMethod);
+       if (returnType)
+       {
+               NSString *typeString = [NSString stringWithUTF8String:returnType];
+               free(returnType);
+               return typeString;
+       }
+       return nil;    
+}
+
++ (BOOL)WOTest_isIdReturnType:(NSString *)returnType
+{
+    return [NSValue WOTest_typeIsObject:returnType];
+}
+
++ (BOOL)WOTest_isCharacterStringReturnType:(NSString *)returnType
+{
+    return [NSValue WOTest_typeIsCharacterString:returnType];
+}
+
++ (BOOL)WOTest_isConstantCharacterStringReturnType:(NSString *)returnType
+{
+    return [NSValue WOTest_typeIsConstantCharacterString:returnType];
+}
+
++ (BOOL)WOTest_objectReturnsId:(id)anObject forSelector:(SEL)aSelector
+{
+    NSParameterAssert(anObject != nil);
+    NSParameterAssert(aSelector != NULL);
+    return [self WOTest_isIdReturnType:[self WOTest_returnTypeForObject:anObject selector:aSelector]];
+}
+
++ (BOOL)WOTest_objectReturnsCharacterString:(id)anObject forSelector:(SEL)aSelector
+{
+    NSParameterAssert(anObject != nil);
+    NSParameterAssert(aSelector != NULL);
+    return [self WOTest_isCharacterStringReturnType:[self WOTest_returnTypeForObject:anObject selector:aSelector]];
+}
+
++ (BOOL)WOTest_objectReturnsConstantCharacterString:(id)anObject forSelector:(SEL)aSelector
+{
+    NSParameterAssert(anObject != nil);
+    NSParameterAssert(aSelector != NULL);
+    return [self WOTest_isConstantCharacterStringReturnType:[self WOTest_returnTypeForObject:anObject selector:aSelector]];    
+}
+
+@end
diff --git a/NSProxy+WOTest.h b/NSProxy+WOTest.h
new file mode 100644 (file)
index 0000000..6da9c01
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  NSProxy+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 12 July 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSProxy+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import <objc/objc-class.h>
+
+//! The runtime requires that all root classes implement the forward:: method. To my knowledge this is not officially documented anywhere, but can at least be confirmed by examining the Darwin source code. This header declares the method as part of a category; include the header to silence compiler warnings when trying to call forward:: on NSProxy or a subclass.
+//! \sa http://darwinsource.opendarwin.org/10.4.3/objc4-267/runtime/Messengers.subproj/objc-msg-ppc.s
+@interface NSProxy (WOTestPrivate)
+
+- forward:(SEL)sel :(marg_list)args;
+
+@end
\ No newline at end of file
diff --git a/NSScanner+WOTest.h b/NSScanner+WOTest.h
new file mode 100644 (file)
index 0000000..d0a1a42
--- /dev/null
@@ -0,0 +1,112 @@
+//
+//  NSScanner+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 12 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSScanner+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+
+/*! 
+ Example method signatures and types:
+ The documentation notes the following: "The compiler generates the method type encodings in a format that includes information on the size of the stack and the size occupied by the arguments. These numbers appear after each encoding in the method_types string. However, because the compiler historically generates them incorrectly, and because they differ depending on the CPU type, the runtime ignores them if they are present. These numbers are not required by the Objective-C runtime in Mac OS X v10.0 or later."
+ The first entry indicates the type of the return value.
+ This is followed by the first argument which is always \@0 or \@8 (self).
+ The second argument is always :4 or :12 (_cmd).
+ Any subsequent arguments follow.
+ + new  : \@8\@0:4
+ + (const char *) name  : r*8\@0:4
+ - (const char *) name : r*8\@0:4
+ - (void *)zone :  ^v8\@0:4
+ - (BOOL) conformsTo: (Protocol *)aProtocolObject : c12\@0:4\@8
+ + (id)allocWithZone:(NSZone *)zone : \@12\@0:4^{_NSZone=}8
+ + (Class)class : #8\@0:4
+ + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget 
+ selector:(SEL)aSelector object:(id)anArgument
+ v20\@0:4\@8:12\@16
+ + (NSRect)contentRectForFrameRect:(NSRect)frameRect styleMask:(unsigned int)aStyle
+ {_NSRect={_NSPoint=ff}{_NSSize=ff}}28\@0:4{_NSRect={_NSPoint=ff}{_NSSize=ff}}8I24    
+ + (id)stringWithString:(NSString *)aString : \@12\@0:4\@8
+ + (NSString *)localizedNameOfStringEncoding:(NSStringEncoding)encoding
+ \@12\@0:4\@8
+ See objc/objc-class.h for macros 
+Additional codes used by runtime as described here: file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/RuntimeOverview/chapter_4_section_6.html */
+
+#define WO_ENCODING_QUALIFIER_CONST     'r'
+#define WO_ENCODING_QUALIFIER_IN        'n'
+#define WO_ENCODING_QUALIFIER_INOUT     'N'
+#define WO_ENCODING_QUALIFIER_OUT       'o'
+#define WO_ENCODING_QUALIFIER_BYCOPY    'O'
+#define WO_ENCODING_QUALIFIER_BYREF     'R'
+#define WO_ENCODING_QUALIFIER_ONEWAY    'V'
+
+@interface NSScanner (WOTest) 
+
+/*! Raises an NSInternalInconsistencyException if value is NULL. */
+- (BOOL)WOTest_peekCharacter:(unichar *)value;
+
+/*! Pass NULL if you merely wish to scan past a character. */
+- (BOOL)WOTest_scanCharacter:(unichar *)value;
+
+/*! Pass NULL if you merely wish to scan past a character from the set. */
+- (BOOL)WOTest_scanCharacterFromSet:(NSCharacterSet *)scanSet intoChar:(unichar *)value;
+
+/*! Scans a return type into a string. Return types must appear at the beginning of the string. Pass nil if you simply wish to scan past a return type. */
+- (BOOL)WOTest_scanReturnTypeIntoString:(NSString **)stringValue;
+
+/*! Scans a type (simple or compound). Pass nil if you simply wish to scan past a type. */
+- (BOOL)WOTest_scanTypeIntoString:(NSString **)stringValue;
+
+/*! Scans one or more qualifieres into a string. Pass nil if you simply wish to scan past the qualifiers. */
+- (BOOL)WOTest_scanQualifiersIntoString:(NSString **)stringValue;
+
+/*! Scans a simple (non-compound) type into a string. Pass nil if you simply wish to scan past a type. */
+- (BOOL)WOTest_scanNonCompoundTypeIntoString:(NSString **)stringValue;
+
+/*! Scans a bitfield into a string. Pass nil if you simply wish to scan past a bitfield. */
+- (BOOL)WOTest_scanBitfieldIntoString:(NSString **)stringValue;
+
+/*! Scans an array into a string. Pass nil if you simply wish to scan past an array. */
+- (BOOL)WOTest_scanArrayIntoString:(NSString **)stringValue;
+
+/*! Scans an identifier into a string. Pass nil if you simply wish to scan past an identifier. */
+- (BOOL)WOTest_scanIdentifierIntoString:(NSString **)stringValue;
+
+/*! Scans a struct into a string. Pass nil if you simply wish to scan past a struct. */
+- (BOOL)WOTest_scanStructIntoString:(NSString **)stringValue;
+
+/*! Scans a union into a string. Pass nil if you simply wish to scan past a union. */
+- (BOOL)WOTest_scanUnionIntoString:(NSString **)stringValue;
+
+/*! Scans a pointer into a string. Pass nil if you simply wish to scan past a pointer. */
+- (BOOL)scanPointerIntoString:(NSString **)stringValue;
+
+@end
diff --git a/NSScanner+WOTest.m b/NSScanner+WOTest.m
new file mode 100644 (file)
index 0000000..dc99e09
--- /dev/null
@@ -0,0 +1,327 @@
+//
+//  NSScanner+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 12 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSScanner+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSScanner+WOTest.h"
+#import <objc/objc-class.h>
+#import "NSString+WOTest.h"
+#import "NSValue+WOTest.h"
+
+#define WO_LEGAL_IDENTIFIER_CHARACTERS  @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
+
+@implementation NSScanner (WOTest)
+
+- (BOOL)WOTest_peekCharacter:(unichar *)value;
+{
+    NSParameterAssert(value != NULL);
+    unsigned scanLocation   = [self scanLocation];
+    NSString *string        = [self string];
+    if (string && ([string length] > scanLocation))
+    {
+        *value = [string characterAtIndex:scanLocation];
+        return YES;
+    }   
+    return NO;
+}
+
+- (BOOL)WOTest_scanCharacter:(unichar *)value;
+{    
+    unichar  character;
+    if ([self WOTest_peekCharacter:&character])
+    {
+        if (value)
+            *value = character;
+        [self setScanLocation:[self scanLocation] + 1];
+        return YES;
+    }
+    return NO;
+}
+
+- (BOOL)WOTest_scanCharacterFromSet:(NSCharacterSet *)scanSet intoChar:(unichar *)value;
+{
+    if (!scanSet) return NO; // nothing to do
+    
+    unsigned    scanLocation = [self scanLocation];
+    unichar     character;
+    if ([self WOTest_scanCharacter:&character] && [scanSet characterIsMember:character])
+    {
+        if (value)
+            *value = character;
+        return YES;
+    }
+    
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+- (BOOL)WOTest_scanReturnTypeIntoString:(NSString **)stringValue
+{
+    if ([self scanLocation] != 0)
+        return NO; // the return type must be at the start of the string
+    
+    // scan a single type (the first one will be the return type)
+    return [self WOTest_scanTypeIntoString:stringValue];
+}
+
+- (BOOL)WOTest_scanTypeIntoString:(NSString **)stringValue
+{
+    unsigned    scanLocation        = [self scanLocation];
+    NSString    *qualifiers         = nil;
+    NSString    *type               = nil;
+    
+    // scan any qualifers that are present
+    if (![self WOTest_scanQualifiersIntoString:&qualifiers]) qualifiers = @"";
+    
+    // scan type
+    if ([self WOTest_scanBitfieldIntoString:&type]          ||
+        [self scanPointerIntoString:&type]                  ||
+        [self WOTest_scanArrayIntoString:&type]             ||
+        [self WOTest_scanStructIntoString:&type]            ||
+        [self WOTest_scanUnionIntoString:&type]             ||
+        [self WOTest_scanNonCompoundTypeIntoString:&type])
+    {
+        // success
+        if (stringValue)
+            *stringValue = 
+                [NSString stringWithFormat:@"%@%@", qualifiers, type];
+        return YES;        
+    }
+    
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+- (BOOL)WOTest_scanQualifiersIntoString:(NSString **)stringValue
+{    
+    NSCharacterSet *qualifiersSet = 
+    [NSCharacterSet characterSetWithCharactersInString:
+        [NSString stringWithFormat:@"%C%C%C%C%C%C%C",
+            WO_ENCODING_QUALIFIER_CONST,    WO_ENCODING_QUALIFIER_IN,
+            WO_ENCODING_QUALIFIER_INOUT,    WO_ENCODING_QUALIFIER_OUT,
+            WO_ENCODING_QUALIFIER_BYCOPY,   WO_ENCODING_QUALIFIER_BYREF,
+            WO_ENCODING_QUALIFIER_ONEWAY]];
+    
+    return [self scanCharactersFromSet:qualifiersSet
+                            intoString:stringValue];
+}
+
+- (BOOL)WOTest_scanNonCompoundTypeIntoString:(NSString **)stringValue
+{
+    NSCharacterSet *nonCompoundSet = 
+    [NSCharacterSet characterSetWithCharactersInString:
+        [NSString stringWithFormat:@"%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C",
+            _C_ID,      _C_CLASS,   _C_SEL,     _C_CHR,     _C_UCHR,    
+            _C_SHT,     _C_USHT,    _C_INT,     _C_UINT,    _C_LNG,
+            _C_ULNG,    _C_LNGLNG,  _C_ULNGLNG, _C_FLT,     _C_DBL,     
+            _C_99BOOL,  _C_VOID,    _C_UNDEF,   _C_CHARPTR]];
+    
+    unichar character;
+    if ([self WOTest_scanCharacterFromSet:nonCompoundSet intoChar:&character])
+    {
+        if (stringValue)
+            *stringValue = [NSString WOTest_stringWithCharacter:character]; 
+        return YES;
+    }
+     
+    return NO;
+}
+
+- (BOOL)WOTest_scanBitfieldIntoString:(NSString **)stringValue
+{
+    unsigned scanLocation = [self scanLocation];
+    
+    unichar marker; // look for bitfield marker
+    int num;        // scan number of bits
+    if ([self WOTest_scanCharacter:&marker] && (marker == _C_BFLD) && [self scanInt:&num])
+    {
+        if (stringValue)
+            *stringValue = [NSString stringWithFormat:@"%C%d", marker, num];
+        return YES;
+    }
+    
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+- (BOOL)WOTest_scanArrayIntoString:(NSString **)stringValue
+{
+    unsigned scanLocation = [self scanLocation];
+    
+    unichar startMarker, endMarker; // look for array start marker
+    int num;                        // scan number of elements
+    NSString *type;                 // scan type of elements
+    
+    if ([self WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) &&
+        [self scanInt:&num] && [self WOTest_scanTypeIntoString:&type] &&
+        [self WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E))
+    {
+        if (stringValue)
+            *stringValue = [NSString stringWithFormat:@"%C%d%@%C", startMarker, num, type, endMarker];
+        return YES;
+    }
+    
+    
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+- (BOOL)WOTest_scanIdentifierIntoString:(NSString **)stringValue
+{
+    unsigned scanLocation = [self scanLocation];
+    
+    unichar firstChar, equalsChar;
+    if ([self WOTest_peekCharacter:&firstChar])
+    {
+        // identifiers must begin with a letter or underscore (no numbers!)
+        if (((firstChar >= 'a') && (firstChar <= 'z')) || 
+            ((firstChar >= 'A') && (firstChar <= 'Z')) ||
+            (firstChar == '_'))
+        {
+            NSString *identifier;   // scan identifier
+            if ([self scanCharactersFromSet:
+                [NSCharacterSet characterSetWithCharactersInString:
+                    WO_LEGAL_IDENTIFIER_CHARACTERS]
+                                 intoString:&identifier])
+            {
+                if ([self WOTest_peekCharacter:&equalsChar] && (equalsChar == '='))
+                {
+                    if (stringValue)
+                        *stringValue = identifier;
+                    return YES;
+                }
+            }
+        }
+        else // special case: check for anonymous/unknown identifiers ('?')
+        {
+            if ([self WOTest_scanCharacter:&firstChar] && (firstChar == _C_UNDEF) &&
+                [self WOTest_peekCharacter:&equalsChar] && (equalsChar == '='))
+            {
+                if (stringValue)
+                    *stringValue = [NSString WOTest_stringWithCharacter:_C_UNDEF];
+                return YES;
+            }
+        }
+    }
+    
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+- (BOOL)WOTest_scanStructIntoString:(NSString **)stringValue
+{
+    unsigned scanLocation = [self scanLocation];
+    
+    unichar startMarker, endMarker;
+    NSString *identifier;
+    if ([self WOTest_scanCharacter:&startMarker] && (startMarker == _C_STRUCT_B))
+    {
+        // scan optional identifier
+        if ([self WOTest_scanIdentifierIntoString:&identifier])
+        {
+            [self WOTest_scanCharacter:NULL]; // scan past "="
+            
+            // prepare identifier for later insertion (append equals sign)
+            identifier = [NSString stringWithFormat:@"%@=", identifier];
+        }
+        else // optional identifier not found
+            identifier = @"";
+        
+        // scan types until you hit end marker
+        NSMutableString *types = [NSMutableString string];
+        NSString *type;
+        while ([self WOTest_scanTypeIntoString:&type])
+        {
+            [types appendString:type];
+            [self scanInt:nil]; // skip over any superfluous numbers in string
+        }
+        
+        // scan end marker
+        if ([self WOTest_scanCharacter:&endMarker] && (endMarker == _C_STRUCT_E))
+        {
+            if (stringValue)
+                *stringValue = [NSString stringWithFormat:@"%C%@%@%C", startMarker, identifier, types, endMarker];
+            return YES;
+        }
+    }
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+- (BOOL)WOTest_scanUnionIntoString:(NSString **)stringValue
+{
+    unsigned scanLocation = [self scanLocation];
+    
+    unichar startMarker, equalsMarker, endMarker;
+    NSString *identifier;
+    if ([self WOTest_scanCharacter:&startMarker] && (startMarker == _C_UNION_B))
+    {
+        // scan optional identifier
+        unsigned identifierLocation = [self scanLocation];
+        if ([self WOTest_scanIdentifierIntoString:&identifier] &&
+            [self WOTest_scanCharacter:&equalsMarker] && (equalsMarker == '='))
+            // prepare identifier for later insertion (append equals sign)
+            identifier = [NSString stringWithFormat:@"%@=", identifier];
+        else // optional identifier not found
+        {
+            identifier = @"";
+            [self setScanLocation:identifierLocation];
+        }
+
+        // scan types until you hit end marker
+        NSMutableString *types = [NSMutableString string];
+        NSString *type;
+        while ([self WOTest_scanTypeIntoString:&type])
+        {
+            [types appendString:type];
+            [self scanInt:nil]; // skip over any superfluous numbers in string
+        }
+        
+        // scan end marker
+        if ([self WOTest_scanCharacter:&endMarker] && (endMarker == _C_UNION_E))
+        {
+            if (stringValue)
+                *stringValue = [NSString stringWithFormat:@"%C%@%@%C", startMarker, identifier, types, endMarker];
+            return YES;
+        }
+    }
+        
+    [self setScanLocation:scanLocation]; // revert
+    return NO;    
+}
+
+- (BOOL)scanPointerIntoString:(NSString **)stringValue
+{
+    unsigned scanLocation = [self scanLocation];
+    
+    unichar marker; // look for pointer marker
+    NSString *type; // scan type to which pointer points
+    if ([self WOTest_scanCharacter:&marker] && (marker == _C_PTR) && [self WOTest_scanTypeIntoString:&type])
+    {
+        if (stringValue)
+            *stringValue = [NSString stringWithFormat:@"%C%@", marker, type];
+        return YES;
+    }
+    
+    [self setScanLocation:scanLocation]; // revert
+    return NO;
+}
+
+@end
diff --git a/NSString+WOTest.h b/NSString+WOTest.h
new file mode 100644 (file)
index 0000000..9072b11
--- /dev/null
@@ -0,0 +1,47 @@
+//
+//  NSString+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 07 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSString+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+
+/*! This function is an alternative to NSLog that accepts the same kinds of format specifiers (including the "%@" format specifier to print object descriptions) but which omits the prelimary information that is prepended by NSLog (date, time, process name, process number). Named with a preceding underscore to avoid namespace clash with the WOLog global variable defined in the WOLogManager class of the WODebug framework. */
+void _WOLog(NSString *format, ...);
+
+/*! This function is an alternative to NSLogv that accepts the same kinds of format specifiers (including the "%@" format specifier to print object descriptions) but which omits the prelimary information that is prepended by NSLogv (date, time, process name, process number). Named with a preceding underscore for consistency with the _WOLog function. */
+void _WOLogv(NSString *format, va_list args);
+
+@interface NSString (WOTest) 
+
++ (NSString *)WOTest_stringWithFormat:(NSString *)format arguments:(va_list)argList;
+
+/*! Convenience method that returns an NSString based on a single character of type unichar. */
++ (NSString *)WOTest_stringWithCharacter:(unichar)character;
+
+- (NSString *)WOTest_stringByConvertingToAbsolutePath;
+
+/*! Returns an immutable, autoreleased string created by "collapsing" all of the whitespace in the receiver into single spaces. All newlines are converted into spaces and consecutive spaces are "collapsed" into a single space. */
+- (NSString *)WOTest_stringByCollapsingWhitespace;
+
+
+/*! Returns an immutable, autoreleased string created by appending a single character of type unichar to the receiver. */
+- (NSString *)WOTest_stringByAppendingCharacter:(unichar)character;
+
+@end
diff --git a/NSString+WOTest.m b/NSString+WOTest.m
new file mode 100644 (file)
index 0000000..253a176
--- /dev/null
@@ -0,0 +1,105 @@
+//
+//  NSString+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 07 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSString+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSString+WOTest.h"
+
+void _WOLog(NSString *format, ...)
+{
+    if (!format) return; // bail
+    va_list args;
+    va_start(args, format);
+    NSString *string = [[NSString alloc] initWithFormat:format arguments:args];
+    if (string)
+    {
+        fprintf(stdout, "%s\n", [string UTF8String]);
+        fflush(NULL); // flush all open streams (not just stdout)
+        [string release];
+    }
+    va_end(args);
+}
+
+void _WOLogv(NSString *format, va_list args)
+{
+    if (!format) return; // bail
+    NSString *string = [[NSString alloc] initWithFormat:format arguments:args];
+    if (string)
+    {
+        fprintf(stdout, "%s\n", [string UTF8String]);
+        fflush(NULL); // flush all open streams (not just stdout)
+        [string release]; 
+    }
+}
+
+@implementation NSString (WOTest)
+
++ (NSString *)WOTest_stringWithFormat:(NSString *)format arguments:(va_list)argList
+{
+    return [[[NSString alloc] initWithFormat:format 
+                                   arguments:argList] autorelease];
+}
+
++ (NSString *)WOTest_stringWithCharacter:(unichar)character
+{
+    return [NSString stringWithFormat:@"%C", character];
+}
+
+- (NSString *)WOTest_stringByConvertingToAbsolutePath
+{
+    if ([self isAbsolutePath])
+        return self;
+    NSString *path = [[NSFileManager defaultManager] currentDirectoryPath];
+    // TODO: strictly speaking, should write a method stringByAppendPathComponents (see draft above)
+    return [path stringByAppendingPathComponent:self];
+}
+
+- (NSString *)WOTest_stringByCollapsingWhitespace
+{
+    NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+    NSMutableString *temp = [NSMutableString stringWithString:self];
+    unsigned int length = [temp length];
+    for (unsigned int i = 0; i < length; i++)
+    {
+        if ([whitespace characterIsMember:[temp characterAtIndex:i]])
+        {
+            // convert newslines, tabs etc to spaces
+            [temp replaceCharactersInRange:NSMakeRange(i, 1) withString:@" "];
+            
+            // was the last character also a space?
+            if ((i > 0) && ([temp characterAtIndex:i - 1] == ' '))
+            {
+                // two consecutive whitespace characters, delete the second
+                [temp deleteCharactersInRange:NSMakeRange(i - 1, 1)];
+                i--;
+                length--;
+            }
+        }
+    }
+    return [NSString stringWithString:temp]; // return immutable, autoreleased
+}
+
+- (NSString *)WOTest_stringByAppendingCharacter:(unichar)character
+{
+    return [self stringByAppendingFormat:@"%C", character];
+}
+
+@end
diff --git a/NSValue+WOTest.h b/NSValue+WOTest.h
new file mode 100644 (file)
index 0000000..0896714
--- /dev/null
@@ -0,0 +1,767 @@
+//
+//  NSValue+WOTest.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 09 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSValue+WOTest.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+
+#import <objc/objc-class.h>
+
+#ifndef _C_LNGLNG
+
+/*! Type macros missing from objc-class.h at the time of writing; definition taken from docs file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/index.html */
+#define _C_LNGLNG 'q' 
+
+#endif
+
+#ifndef _C_ULNGLNG
+
+/*! Type macros missing from objc-class.h at the time of writing; definition taken from docs file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/index.html */
+#define _C_ULNGLNG 'Q'
+
+#endif
+
+#ifndef _C_99BOOL
+
+/*! Type macros missing from objc-class.h at the time of writing; definition taken from docs file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ObjectiveC/index.html */
+#define _C_99BOOL 'B'
+
+#endif
+
+/*! Macro that compares two scalar values, \p a and \p b, using standard relational operators and returns NSOrderedSame, NSOrderedAscending or NSOrderedDescending. */
+#define WO_COMPARE_SCALARS(a, b) \
+((a) == (b) ? NSOrderedSame : ((a) < (b) ? NSOrderedAscending : NSOrderedDescending))
+
+/*! This category adds unit testing methods to the NSValue class. It provides methods for comparing NSValue objects for equality/non-equality and ordering.
+
+The base NSValue class adopts a stricter view of equality that makes it hard to compare numeric scalar values (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double and C99 _Bool) because those values must be of the same type or class. For example, an NSValue that contains an int (10) and another that contains a long (10) would be considered unequal according the default implementation of the isEqualTo: method because they are not of the same type. Likewise an NSValue containing an NSMutableString (@"string") and another containing an NSString (@"string") would also be considered unequal because they are of different classes.
+
+This category adds a number of high-level methods for use in unit testing that implement a more flexible comparison method. Any numeric scalar value can be compared to any other scalar value and the values will be appropriately cast before comparison. In cases where GCC would issue a warning (for example, when comparing a signed and an unsigned type) a warning will be printed to the console (and will appear in the Xcode build results).
+
+Any object can be compared to any other object for equality provided that it implements the isEqualTo: method. If it does not implement the method then the WOTest_testIsEqualToValue: method will return NO. If both ids are nil then the method returns YES.
+
+Objects can be compared to one another for ordering provided that they are of the same class (or at least have a super-subclass relationship) and at least one of the objects implement the compare: method. If neither of these conditions are true then an NSInvalidArgument exception will be raised.
+
+Class objects, method selectors (SEL), void types, character strings (char *), arrays, structs, unions and pointers have no special behaviour implemented in this category. If you try to compare them the standard isEqualTo: NSValue method will be used and testing the ordering will raise an NSInvalidArgument exception. */
+@interface NSValue (WOTest) 
+
+#pragma mark -
+#pragma mark Value creation methods
+
++ (NSValue *)WOTest_valueWithChar:(char)aChar;
+
++ (NSValue *)WOTest_valueWithInt:(int)anInt;
+
++ (NSValue *)WOTest_valueWithShort:(short)aShort;
+
++ (NSValue *)WOTest_valueWithLong:(long)aLong;
+
++ (NSValue *)WOTest_valueWithLongLong:(long long)aLongLong;
+
++ (NSValue *)WOTest_valueWithUnsignedChar:(unsigned char)anUnsignedChar;
+
++ (NSValue *)WOTest_valueWithUnsignedInt:(unsigned int)anUnsignedInt;
+
++ (NSValue *)WOTest_valueWithUnsignedShort:(unsigned short)anUnsignedShort;
+
++ (NSValue *)WOTest_valueWithUnsignedLong:(unsigned long)anUnsignedLong;
+
++ (NSValue *)WOTest_valueWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong;
+
++ (NSValue *)WOTest_valueWithFloat:(float)aFloat;
+
++ (NSValue *)WOTest_valueWithDouble:(double)aDouble;
+
++ (NSValue *)WOTest_valueWithC99Bool:(_Bool)aC99Bool;
+
++ (NSValue *)WOTest_valueWithConstantCharacterString:(const char *)aConstantCharString;
+
++ (NSValue *)WOTest_valueWithCharacterString:(char *)aCharacterString;
+
+/*! Unlike Cocoa's valueWithNonretainedObject method, which is equivalent to calling:
+
+    \code
+    NSValue *theValue = [NSValue value:&anObject withObjCType:@encode(void  *)];
+    \endcode
+
+                                               This method is equivalent to invoking:
+
+    \code
+    NSValue *theValue = [NSValue value:&anObject withObjCType:@encode(id)];
+    \endcode
+
+    The object is not retained.
+
+    */
++ (NSValue *)WOTest_valueWithObject:(id)anObject;
+
++ (NSValue *)WOTest_valueWithClass:(Class)aClass;
+
++ (NSValue *)WOTest_valueWithSelector:(SEL)aSelector;
+
+#pragma mark -
+#pragma mark Parsing type strings
+
+/*!
+\name Parsing type strings
+\startgroup
+*/
+
+/*! Used to determine the maximum number of bytes needed to store the type represented by \p typeString when it is embedded inside a composite type (a struct, array or union). It is possible that compiler pragmas or flags may lead to data being aligned differently and packed in a more compact form, but this method is guaranteed to return the maximum amount of space that would be required to store the type under the least-compact alignment conditions. The alignments and embedding rules are taken from "The alignments taken from Apple's "Mac OS X ABI Function Call Guide". Throws an exception if \p typeString is nil. */
++ (size_t)WOTest_maximumEmbeddedSizeForType:(NSString *)typeString;
+
+/*! This method returns the minimum buffer size required to safely store an object of the type represented by \p typeString. Because it is impossible to know if any compiler pragmas or flags were used to change the default alignment behaviour of composite  types (structs, arrays, unions), this method bases its calculations on the space that would be required if the least-packed alignment were chosen. As such the values returned by this function may be greater than or equal to those returned by the sizeof compiler directive, but never less than. Throws an exception if \p typeString is nil. */
++ (size_t)WOTest_sizeForType:(NSString *)typeString;
+
+/*! Returns YES if \p typeString contains a numeric scalar value (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, C99 _Bool). Returns NO if the receiver contains any other type, object or pointer (id, Class, SEL, void, char *, as well as arrays, structures and pointers). */
++ (BOOL)WOTest_typeIsNumericScalar:(NSString *)typeString;
+
+/*! Returns YES if \p typeString represents a compound type (struct, union or array). */
++ (BOOL)WOTest_typeIsCompound:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsChar:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsInt:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsShort:(NSString *)typeString;  
+
++ (BOOL)WOTest_typeIsLong:(NSString *)typeString;      
+
++ (BOOL)WOTest_typeIsLongLong:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsUnsignedChar:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsUnsignedInt:(NSString *)typeString;    
+
++ (BOOL)WOTest_typeIsUnsignedShort:(NSString *)typeString;  
+
++ (BOOL)WOTest_typeIsUnsignedLong:(NSString *)typeString;   
+
++ (BOOL)WOTest_typeIsUnsignedLongLong:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsFloat:(NSString *)typeString;          
+
++ (BOOL)WOTest_typeIsDouble:(NSString *)typeString;  
+
++ (BOOL)WOTest_typeIsC99Bool:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsVoid:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsConstantCharacterString:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsCharacterString:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsObject:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsClass:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsSelector:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsPointerToVoid:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsPointer:(NSString *)typeString;
+    
++ (BOOL)WOTest_typeIsArray:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsStruct:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsUnion:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsBitfield:(NSString *)typeString;
+
++ (BOOL)WOTest_typeIsUnknown:(NSString *)typeString;
+
+/*! \endgroup */
+
+#pragma mark - 
+#pragma mark High-level test methods
+
+/*!
+\name High-level test methods
+\startgroup
+*/
+
+/*! Compares the receiver with \p aValue for equality and returns YES if equal, NO otherwise. When both the receiver and \p aValue are objects tries to send an isEqualTo: message to the receiver; if the receiver does not implement that selector then the default NSValue isEqualToValue: method is invoked. When both the receiver and \p aValue represent numeric scalars casting is performed as already described. There is one special case where an object to numeric scalar comparison is permitted and that is when the numeric value is both the same size and value as a nil pointer (in other words an int with value 0), such as the relatively common case in which the caller might try to compare an object to nil (in this case the C preprocessor will have substituted "0" for "nil" which means that the NSValue object will record it as an representing an integer rather than an id). This special case is necessary so that test macros such as the following work as expected:
+
+\code
+WO_TEST_EQUAL(nil, theObject);          // looks like comparing an id with an id
+WO_TEST_NOT_EQUAL(otherObject, nil);    // looks like comparing an id with an id
+
+// which are equivalent to:
+WO_TEST_EQUAL(0, theObject);            // actually comparing an int with an id
+WO_TEST_NOT_EQUAL(otherObject, 0);      // actually comparing an id with an int
+\endcode
+
+Note that the following example would work even without this special handling because in this case there is enough information present at compile time to prevent the nil value from being interpreted as an int:
+
+\code
+NSString *aString = nil;
+WO_TEST_EQUAL(aString, otherString);    // comparing an id with and id
+\endcode
+
+Special case handling notwithstanding, attempting to compare a non-zero int with an object will raise an exception:
+
+\code
+WO_TEST_EQUAL(25, aString);             // raises
+\endcode
+
+In all other cases the default NSValue isEqualToValue: method is used as a fallback. 
+
+<table>
+<tr>
+<td>Compare value:</td>
+<td>With value:</td>
+<td>Result:</td>
+</tr>
+<tr>
+<td>numeric scalar</td>
+<td>numeric scalar</td>
+<td>automated casting takes place prior to comparison, warning printed to console at runtime if risky casts are required (for example signed and unsigned) </td>
+</tr>
+<tr>
+<td>id</td>
+<td>id</td>
+<td>compared using isEqual: if right-handle value implements that method, otherwise default NSValue isEqualToValue: behaviour used </td>
+</tr>
+<tr>
+<td>id</td>
+<td>numeric scalar</td>
+<td>comparison allowed if and only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
+</tr>
+<tr>
+<td>numeric scalar</td>
+<td>id</td>
+<td>comparison allowed if and only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
+</tr>
+<tr>
+<td>void * </td>
+<td>numeric scalar </td>
+<td>comparison allowed if and only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
+</tr>
+<tr>
+<td>numeric scalar </td>
+<td>void * </td>
+<td>comparison allowed if any only if the numeric scalar value is equivalent to "nil", otherwise throws exception </td>
+</tr>
+<tr>
+<td>char*</td>
+<td>char[]</td>
+<td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
+</tr>
+<tr>
+<td>char[]</td>
+<td>char * </td>
+<td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
+</tr>
+<tr>
+<td>const char* </td>
+<td>char[]</td>
+<td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
+</tr>
+<tr>
+<td>char[]</td>
+<td>const char* </td>
+<td>if possible creates NSString representations of the two values and compares them; if comparison not possible falls back to default NSValue isEqualToValue: behaviour</td>
+</tr>
+</table>
+
+*/
+- (BOOL)WOTest_testIsEqualToValue:(NSValue *)aValue;
+
+// TODO: write tests that make use of these methods? so far only use the isEqual variant
+- (BOOL)WOTest_testIsGreaterThanValue:(NSValue *)aValue;
+
+- (BOOL)WOTest_testIsLessThanValue:(NSValue *)aValue;
+
+- (BOOL)WOTest_testIsNotGreaterThanValue:(NSValue *)aValue;
+
+- (BOOL)WOTest_testIsNotLessThanValue:(NSValue *)aValue;
+
+/*! The table below indicates the warnings that GCC issues when trying to compare numeric scalars of different types. "-" indicates that no warning is issued and the compiler performs an implicit cast. "W" indicates that the compiler issues a warning about signed/unsigned comparison; in this case WOTest performs an explicit cast and prints a warning to the console (visible in the Xcode build results window) at runtime.
+
+<table>
+  <tr>
+    <td>type</td>
+    <td>char</td>
+    <td>int</td>
+    <td>short</td>
+    <td>long</td>
+    <td>long long </td>
+    <td>unsigned char </td>
+    <td>unsigned int</td>
+    <td>unsigned short </td>
+    <td>unsigned long </td>
+    <td>unsigned long long </td>
+    <td>float</td>
+    <td>double</td>
+    <td>C99 _Bool </td>
+  </tr>
+  <tr>
+    <td>char</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>W</td>
+    <td>-</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>int</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>W</td>
+    <td>-</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>short</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>W</td>
+    <td>-</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>long</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>W</td>
+    <td>-</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>long long </td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>unsigned char </td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>unsigned int </td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>unsigned short </td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>unsigned long </td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>unsigned long long </td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>W</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>float</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>double</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+  <tr>
+    <td>C99 _Bool</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+    <td>-</td>
+  </tr>
+</table>
+
+Table corresponds to the warnings produced by the version of GCC 4.0 that ships with Apple's Xcode 2.1, "powerpc-apple-darwin8-gcc-4.0.0 (GCC) 4.0.0 (Apple Computer, Inc. build 5026)". */
+- (NSComparisonResult)WOTest_compare:(NSValue *)aValue;
+
+/*! \endgroup */
+
+#pragma mark - 
+#pragma mark Utility methods
+
+//! \name Utility methods
+//! \startgroup
+
+//! Returns the buffer size necessary to hold the contents of the receiver. For use in conjunction with the getValue method.
+- (size_t)WOTest_bufferSize;
+
+//! Prints a warning to the console about a signed-to-unsigned comparison together with information about the last known location (file and line).
+- (void)WOTest_printSignCompareWarning:(NSString *)warning;
+
+//! \endgroup
+
+#pragma mark -
+#pragma mark Convenience methods
+
+/*!
+\name Convenience methods
+\startgroup
+*/
+
+/*! Returns the Objective-C type of the receiver as an NSString. */
+- (NSString *)WOTest_objCTypeString;
+
+/*! Returns a human-readable description of the receiver. */
+- (NSString *)WOTest_description;
+
+/*! \endgroup */
+
+#pragma mark -
+#pragma mark Identifying generic types
+
+/*!
+\name Identifying generic types
+\startgroup
+*/
+
+/*! Returns YES if the receiver contains a numeric scalar value (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, C99 _Bool). Returns NO if the receiver contains any other type, object or pointer (id, Class, SEL, void, char *, as well as arrays, structures and pointers). */
+- (BOOL)WOTest_isNumericScalar;
+
+- (BOOL)WOTest_isPointer;
+
+- (BOOL)WOTest_isArray;
+
+/*! Returns the count of items in the array stored by the receiver. Raises an exception if the receiver does not store an array. */
+- (unsigned)WOTest_arrayCount;
+
+/*! Returns the type string of the elements in the array stored by the receiver. Raises an exception if the receiver does not store an array. */
+- (NSString *)WOTest_arrayType;
+
+- (BOOL)WOTest_isStruct;
+
+- (BOOL)WOTest_isUnion;
+
+- (BOOL)WOTest_isBitfield;
+
+/*! True if the receiver contains a value of type "unknown" (indicated by "?"). Function pointers, for example, are encoded with this type by the \@encode() compiler directive. */
+- (BOOL)WOTest_isUnknown;
+
+/*! \endgroup */
+
+#pragma mark -
+#pragma mark Identifying and retrieving specific types
+
+/*!
+\name Identifying and retrieving specific types
+\startgroup
+*/
+
+/*! Returns YES if the receiver contains a char. */
+- (BOOL)WOTest_isChar;
+
+/*! Returns YES if the receiver contains an int. */
+- (BOOL)WOTest_isInt;
+
+/*! Returns YES if the receiver contains a short. */
+- (BOOL)WOTest_isShort;
+
+/*! Returns YES if the receiver contains a long. */
+- (BOOL)WOTest_isLong;
+
+/*! Returns YES if the receiver contains a long long. */
+- (BOOL)WOTest_isLongLong;
+
+/*! Returns YES if the receiver contains an unsigned char. */
+- (BOOL)WOTest_isUnsignedChar;
+
+/*! Returns YES if the receiver contains an unsigned int. */
+- (BOOL)WOTest_isUnsignedInt;
+
+/*! Returns YES if the receiver contains an unsigned short. */
+- (BOOL)WOTest_isUnsignedShort;
+
+/*! Returns YES if the receiver contains an unsigned long. */
+- (BOOL)WOTest_isUnsignedLong;
+
+/*! Returns YES if the receiver contains an unsigned long long. */
+- (BOOL)WOTest_isUnsignedLongLong;
+
+/*! Returns YES if the receiver contains a float. */
+- (BOOL)WOTest_isFloat;
+
+/*! Returns YES if the receiver contains a double. */
+- (BOOL)WOTest_isDouble;
+
+/*! Returns YES if the receiver contains a C99 _Bool. */
+- (BOOL)WOTest_isC99Bool;
+
+/*! Returns YES if the receiver contains a void. */
+- (BOOL)WOTest_isVoid;
+
+/*! Returns YES if the receiver contains a constant character string (const char *). */
+- (BOOL)WOTest_isConstantCharacterString;
+
+/*! Returns YES if the receiver contains a character string (char *). */
+- (BOOL)WOTest_isCharacterString;
+
+/*! Returns YES if the receiver contains an (id or statically typed) object. */
+- (BOOL)WOTest_isObject;
+
+/*! Returns YES if the receiver contains a Class object. */
+- (BOOL)WOTest_isClass;
+
+/*! Returns YES if the receiver contains a method selector (SEL). */
+- (BOOL)WOTest_isSelector;
+
+/*! Returns YES if the receiver contains a pointer to void. */
+- (BOOL)WOTest_isPointerToVoid;
+
+/*! If the receiver was created to hold a char-sized data item, returns that item as a char. Otherwise, the result is undefined. */
+- (char)WOTest_charValue;
+
+/*! If the receiver was created to hold an int-sized data item, returns that item as an int. Otherwise, the result is undefined. */
+- (int)WOTest_intValue;
+
+/*! If the receiver was created to hold a short-sized data item, returns that item as a short. Otherwise, the result is undefined. */
+- (short)WOTest_shortValue;
+
+/*! If the receiver was created to hold a long-sized data item, returns that item as a long. Otherwise, the result is undefined. */
+- (long)WOTest_longValue;
+
+/*! If the receiver was created to hold a long long-sized data item, returns that item as a long long. Otherwise, the result is undefined. */
+- (long long)WOTest_longLongValue;
+
+/*! If the receiver was created to hold an unsigned char-sized data item, returns that item as an unsigned char. Otherwise, the result is undefined. */
+- (unsigned char)WOTest_unsignedCharValue;
+
+/*! If the receiver was created to hold an unsigned int-sized data item, returns that item as an unsigned int. Otherwise, the result is undefined. */
+- (unsigned int)WOTest_unsignedIntValue;
+
+/*! If the receiver was created to hold an unsigned short-sized data item, returns that item as an unsigned short. Otherwise, the result is undefined. */
+- (unsigned short)WOTest_unsignedShortValue;
+
+/*! If the receiver was created to hold an unsigned long-sized data item, returns that item as an unsigned long. Otherwise, the result is undefined. */
+- (unsigned long)WOTest_unsignedLongValue;
+
+/*! If the receiver was created to hold an unsigned long long-sized data item, returns that item as an unsigned long long. Otherwise, the result is undefined. */
+- (unsigned long long)WOTest_unsignedLongLongValue;
+
+/*! If the receiver was created to hold a float-sized data item, returns that item as a float. Otherwise, the result is undefined. */
+- (float)WOTest_floatValue;
+
+/*! If the receiver was created to hold a double-sized data item, returns that item as a double. Otherwise, the result is undefined. */
+- (double)WOTest_doubleValue;
+
+/*! If the receiver was created to hold a C99 _Bool-sized data item, returns that item as a C99 _Bool. Otherwise, the result is undefined. */
+- (_Bool)WOTest_C99BoolValue;
+
+/*! If the receiver was created to hold a constant character string-sized data item, returns that item as a constant character string. Otherwise, the result is undefined. */
+- (const char *)WOTest_constantCharacterStringValue;
+
+/*! If the receiver was created to hold a character string-sized data item, returns that item as a character string. Otherwise, the result is undefined. */
+- (char *)WOTest_characterStringValue;
+
+/*! If the receiver was created to hold an id-sized data item, returns that item as an id. Otherwise, the result is undefined. */
+- (id)WOTest_objectValue;
+
+/*! If the receiver was created to hold a Class-sized data item, returns that item as a Class. Otherwise, the result is undefined. */
+- (Class)WOTest_classValue;
+
+/*! If the receiver was created to hold a SEL-sized data item, returns that item as a SEL. Otherwise, the result is undefined. */
+- (SEL)WOTest_selectorValue;
+
+- (void *)WOTest_pointerToVoidValue;
+
+/*! Returns YES if the receiver is an array of characters (of type char). This method looks for encoding strings of the form "[1c]", "[2c]", "[3c]" and so forth. There are no guarantees that such arrays are null-terminated. Such encodings are produced when passing constant strings to the WOTest macros as illustrated below:
+
+\code
+WO_TEST_NOT_EQUAL("foo", "bar");
+\endcode
+
+When encoded as NSValues the two constant strings in the example above would be encoded as having type "[4c]" (3 chars plus a terminating byte). */
+- (BOOL)WOTest_isCharArray;
+
+/*! If WOTest_isCharacterString returns YES then this method attempts to return an NSString representation of the content of the receiver; returns nil if no representation could be produced. If WOTest_isCharArray returns YES then attempts to do the same, returning nil if not possible. */
+- (NSString *)WOTest_stringValue;
+
+/*! \endgroup */
+
+#pragma mark - 
+#pragma mark Low-level test methods
+
+/*!
+\name Low-level test methods
+\startgroup
+*/
+
+- (NSComparisonResult)WOTest_compareWithChar:(char)other;
+
+- (NSComparisonResult)WOTest_compareWithInt:(int)other;
+
+- (NSComparisonResult)WOTest_compareWithShort:(short)other;
+
+- (NSComparisonResult)WOTest_compareWithLong:(long)other;
+
+- (NSComparisonResult)WOTest_compareWithLongLong:(long long)other;
+
+- (NSComparisonResult)WOTest_compareWithUnsignedChar:(unsigned char)other;
+
+- (NSComparisonResult)WOTest_compareWithUnsignedInt:(unsigned int)other;
+
+- (NSComparisonResult)WOTest_compareWithUnsignedShort:(unsigned short)other;
+
+- (NSComparisonResult)WOTest_compareWithUnsignedLong:(unsigned long)other;
+
+- (NSComparisonResult)WOTest_compareWithUnsignedLongLong:(unsigned long long)other;
+
+- (NSComparisonResult)WOTest_compareWithFloat:(float)other;
+
+- (NSComparisonResult)WOTest_compareWithDouble:(double)other;
+
+- (NSComparisonResult)WOTest_compareWithC99Bool:(_Bool)other;
+
+/*! \endgroup */
+
+@end
diff --git a/NSValue+WOTest.m b/NSValue+WOTest.m
new file mode 100644 (file)
index 0000000..f4c4d73
--- /dev/null
@@ -0,0 +1,1814 @@
+//
+//  NSValue+WOTest.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 09 June 2005.
+//
+//  Copyright 2005-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSValue+WOTest.m 208 2007-07-07 19:02:28Z wincent $
+
+// category header
+#import "NSValue+WOTest.h"
+
+// system headers
+#import <objc/objc-runtime.h>
+
+// framework headers
+#import "WOTest.h"
+#import "NSObject+WOTest.h"
+#import "NSScanner+WOTest.h"
+#import "NSString+WOTest.h"
+
+@implementation NSValue (WOTest)
+
+#pragma mark -
+#pragma mark Value creation methods
+
++ (NSValue *)WOTest_valueWithChar:(char)aChar
+{
+    return [NSValue value:&aChar withObjCType:@encode(char)];
+}
+
++ (NSValue *)WOTest_valueWithInt:(int)anInt
+{
+    return [NSValue value:&anInt withObjCType:@encode(int)];
+}
+
++ (NSValue *)WOTest_valueWithShort:(short)aShort
+{
+    return [NSValue value:&aShort withObjCType:@encode(short)];
+}
+
++ (NSValue *)WOTest_valueWithLong:(long)aLong
+{
+    return [NSValue value:&aLong withObjCType:@encode(long)];
+}
+
++ (NSValue *)WOTest_valueWithLongLong:(long long)aLongLong
+{
+    return [NSValue value:&aLongLong withObjCType:@encode(long long)];
+}
+
++ (NSValue *)WOTest_valueWithUnsignedChar:(unsigned char)anUnsignedChar
+{
+    return [NSValue value:&anUnsignedChar withObjCType:@encode(unsigned char)];
+}
+
++ (NSValue *)WOTest_valueWithUnsignedInt:(unsigned int)anUnsignedInt
+{
+    return [NSValue value:&anUnsignedInt withObjCType:@encode(unsigned int)];
+}
+
++ (NSValue *)WOTest_valueWithUnsignedShort:(unsigned short)anUnsignedShort
+{
+    return [NSValue value:&anUnsignedShort withObjCType:@encode(unsigned short)];
+}
+
++ (NSValue *)WOTest_valueWithUnsignedLong:(unsigned long)anUnsignedLong
+{
+    return [NSValue value:&anUnsignedLong withObjCType:@encode(unsigned long)];
+}
+
++ (NSValue *)WOTest_valueWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong
+{
+    return [NSValue value:&anUnsignedLongLong withObjCType:@encode(unsigned long long)]; 
+}
+
++ (NSValue *)WOTest_valueWithFloat:(float)aFloat
+{
+    return [NSValue value:&aFloat withObjCType:@encode(float)];
+}
+
++ (NSValue *)WOTest_valueWithDouble:(double)aDouble
+{
+    return [NSValue value:&aDouble withObjCType:@encode(double)];
+}
+
++ (NSValue *)WOTest_valueWithC99Bool:(_Bool)aC99Bool
+{
+    return [NSValue value:&aC99Bool withObjCType:@encode(_Bool)];
+}
+
++ (NSValue *)WOTest_valueWithConstantCharacterString:(const char *)aConstantCharString
+{
+    return [NSValue value:&aConstantCharString withObjCType:@encode(const char *)]; 
+}
+
++ (NSValue *)WOTest_valueWithCharacterString:(char *)aCharacterString
+{
+    return [NSValue value:&aCharacterString withObjCType:@encode(char *)];
+}
+
++ (NSValue *)WOTest_valueWithObject:(id)anObject
+{
+    return [NSValue value:&anObject withObjCType:@encode(id)];
+}
+
++ (NSValue *)WOTest_valueWithClass:(Class)aClass
+{
+    return [NSValue value:&aClass withObjCType:@encode(Class)];
+}
+
++ (NSValue *)WOTest_valueWithSelector:(SEL)aSelector
+{
+    return [NSValue value:&aSelector withObjCType:@encode(SEL)];
+}
+
+#pragma mark -
+#pragma mark Parsing type strings
+
+// TODO: investigate whether NSGetSizeAndAlignment() could save some code here...
++ (size_t)WOTest_maximumEmbeddedSizeForType:(NSString *)typeString
+{
+    NSParameterAssert(typeString != nil);
+    size_t size = 16; // worst case scenario
+
+    if ([self WOTest_typeIsCompound:typeString])
+        return [self WOTest_sizeForType:typeString];
+
+    if ([self WOTest_typeIsBitfield:typeString])
+        return sizeof(int);
+    
+#if defined (__i386__)
+
+    if ([self WOTest_typeIsC99Bool:typeString]                         ||
+        [self WOTest_typeIsUnsignedChar:typeString]                    ||
+        [self WOTest_typeIsChar:typeString]                            ||
+        [self WOTest_typeIsUnsignedChar:typeString])
+        size = 1;   // scalars of size/alignment 1
+    else if ([self WOTest_typeIsUnsignedShort:typeString]              ||
+             [self WOTest_typeIsShort:typeString])
+        size = 2;   // scalars of size/alignment 2
+    else if ([self WOTest_typeIsUnsignedInt:typeString]                ||
+             [self WOTest_typeIsInt:typeString]                        ||
+             [self WOTest_typeIsUnsignedLong:typeString]               ||
+             [self WOTest_typeIsLong:typeString]                       ||
+             [self WOTest_typeIsFloat:typeString])
+        size = 4;   // scalars of size/alignment 4
+    else if ([self WOTest_typeIsUnsignedLongLong:typeString]           ||
+             [self WOTest_typeIsLongLong:typeString]                   ||
+             [self WOTest_typeIsDouble:typeString])
+        size = 8;   // scalars of size/alignment 8
+    else if ([self WOTest_typeIsPointer:typeString]                    ||
+             [self WOTest_typeIsPointerToVoid:typeString]              ||
+             [self WOTest_typeIsObject:typeString]                     ||
+             [self WOTest_typeIsClass:typeString]                      ||
+             [self WOTest_typeIsSelector:typeString]                   ||
+             [self WOTest_typeIsCharacterString:typeString]            ||
+             [self WOTest_typeIsConstantCharacterString:typeString])
+        size = 4;   // pointers (size/alignment 4)
+    else
+        // documented in "Mac OS X ABI Function Call Guide" but not supported:
+        // long double        16 bytes
+        // vector (64 bits)   8 bytes
+        // vector (128 bits)  16 bytes
+        [NSException raise:NSInternalInconsistencyException 
+                    format:@"Type %@ not supported by WOTest_maximumEmbeddedSizeForType:", typeString];
+                        
+#elif defined (__ppc__)
+    
+    if ([self WOTest_typeIsUnsignedChar:typeString]                    ||
+        [self WOTest_typeIsChar:typeString])
+        size = 1;   // scalars of size/alignment 1
+    else if ([self WOTest_typeIsUnsignedShort:typeString]              ||
+             [self WOTest_typeIsShort:typeString])
+        size = 2;   // scalars of size/alignment 2
+    else if ([self WOTest_typeIsC99Bool:typeString]                    ||
+             [self WOTest_typeIsUnsignedInt:typeString]                ||
+             [self WOTest_typeIsInt:typeString]                        ||
+             [self WOTest_typeIsUnsignedLong:typeString]               ||
+             [self WOTest_typeIsLong:typeString]                       ||
+             [self WOTest_typeIsFloat:typeString])
+        size = 4;   // scalars of size/alignment 4
+    else if ([self WOTest_typeIsUnsignedLongLong:typeString]           ||
+             [self WOTest_typeIsLongLong:typeString]                   ||
+             [self WOTest_typeIsDouble:typeString])
+        size = 8;   // scalars of size/alignment 8
+    else if ([self WOTest_typeIsPointer:typeString]                    ||
+             [self WOTest_typeIsPointerToVoid:typeString]              ||
+             [self WOTest_typeIsObject:typeString]                     ||
+             [self WOTest_typeIsClass:typeString]                      ||
+             [self WOTest_typeIsSelector:typeString]                   ||
+             [self WOTest_typeIsCharacterString:typeString]            ||
+             [self WOTest_typeIsConstantCharacterString:typeString])
+        size = 4;   // pointers (size/alignment 4)
+    else
+        // documented in "Mac OS X ABI Function Call Guide" but not supported:
+        // long double  8 bytes (Mac OS X < 10.4, GCC < 4.0)
+        // long double  16 bytes (Mac OS X >= 10.4, GCC >= 4.0)
+        // vector       16 bytes
+        [NSException raise:NSInternalInconsistencyException 
+                    format:@"Type %@ not supported by WOTest_maximumEmbeddedSizeForType:", typeString];
+
+#elif defined (__ppc64__)
+
+    if ([self WOTest_typeIsC99Bool:typeString]                         ||
+        [self WOTest_typeIsUnsignedChar:typeString]                    ||
+        [self WOTest_typeIsChar:typeString])
+        size = 1;   // scalars of size/alignment 1
+    else if ([self WOTest_typeIsUnsignedShort:typeString]              ||
+             [self WOTest_typeIsShort:typeString])
+        size = 2;   // scalars of size/alignment 2
+    else if ([self WOTest_typeIsUnsignedInt:typeString]                ||
+             [self WOTest_typeIsInt:typeString]                        ||
+             [self WOTest_typeIsFloat:typeString])
+        size = 4;   // scalars of size/alignment 4
+    else if ([self WOTest_typeIsUnsignedLong:typeString]               ||
+             [self WOTest_typeIsLong:typeString]                       ||
+             [self WOTest_typeIsUnsignedLongLong:typeString]           ||
+             [self WOTest_typeIsLongLong:typeString]                   ||
+             [self WOTest_typeIsDouble:typeString])
+        size = 8;   // scalars of size/alignment 8
+    else if ([self WOTest_typeIsPointer:typeString]                    ||
+             [self WOTest_typeIsPointerToVoid:typeString]              ||
+             [self WOTest_typeIsObject:typeString]                     ||
+             [self WOTest_typeIsClass:typeString]                      ||
+             [self WOTest_typeIsSelector:typeString]                   ||
+             [self WOTest_typeIsCharacterString:typeString]            ||
+             [self WOTest_typeIsConstantCharacterString:typeString])
+        size = 8;   // pointers (size/alignment 8)
+    else
+        // documented in "Mac OS X ABI Function Call Guide" but not supported:
+        // long double  16 bytes
+        // vector       16 bytes
+        [NSException raise:NSInternalInconsistencyException 
+                    format:@"Type %@ not supported by WOTest_maximumEmbeddedSizeForType:", typeString];
+                        
+#else
+                        
+#error Unsupported architecture
+
+#endif
+                            
+    return size;
+}
+
++ (size_t)WOTest_sizeForType:(NSString *)typeString
+{
+    NSParameterAssert(typeString != nil);
+    size_t size = 0;
+    
+    if ([self WOTest_typeIsChar:typeString])
+        size = sizeof(char);
+    else if ([self WOTest_typeIsInt:typeString])
+        size = sizeof(int);
+    else if ([self WOTest_typeIsShort:typeString])  
+        size = sizeof(short);
+    else if ([self WOTest_typeIsLong:typeString])      
+        size = sizeof(long);
+    else if ([self WOTest_typeIsLongLong:typeString])
+        size = sizeof(long long);
+    else if ([self WOTest_typeIsUnsignedChar:typeString])
+        size = sizeof(unsigned char);
+    else if ([self WOTest_typeIsUnsignedInt:typeString])    
+        size = sizeof(unsigned int);
+    else if ([self WOTest_typeIsUnsignedShort:typeString])  
+        size = sizeof(unsigned short);
+    else if ([self WOTest_typeIsUnsignedLong:typeString])   
+        size = sizeof(unsigned long);
+    else if ([self WOTest_typeIsUnsignedLongLong:typeString])
+        size = sizeof(unsigned long long);
+    else if ([self WOTest_typeIsFloat:typeString])          
+        size = sizeof(float);
+    else if ([self WOTest_typeIsDouble:typeString])  
+        size = sizeof(double);
+    else if ([self WOTest_typeIsC99Bool:typeString])
+        size = sizeof(_Bool);
+    else if ([self WOTest_typeIsVoid:typeString])
+        size = sizeof(void);
+    else if ([self WOTest_typeIsConstantCharacterString:typeString])
+        size = sizeof(const char *);
+    else if ([self WOTest_typeIsCharacterString:typeString])
+        size = sizeof(char *);
+    else if ([self WOTest_typeIsObject:typeString])
+        size = sizeof(id);
+    else if ([self WOTest_typeIsClass:typeString])
+        size = sizeof(Class);
+    else if ([self WOTest_typeIsSelector:typeString])
+        size = sizeof(SEL);
+    else if ([self WOTest_typeIsPointerToVoid:typeString])
+        size = sizeof(void *);
+    else // handle complex types and special cases
+    {
+        // if is pointer
+        if ([self WOTest_typeIsPointer:typeString])
+            size = sizeof(void *);
+        else if ([self WOTest_typeIsArray:typeString])
+        {
+            NSScanner *scanner = [NSScanner scannerWithString:typeString];
+            unichar startMarker, endMarker;
+            int count;
+            NSString *elementType;
+            if ([scanner WOTest_scanCharacter:&startMarker] && 
+                (startMarker == _C_ARY_B) && [scanner scanInt:&count] && 
+                [scanner WOTest_scanTypeIntoString:&elementType] &&
+                [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
+                [scanner isAtEnd])
+            {
+                // recursion
+                size = [self WOTest_sizeForType:elementType] * count;
+            }
+            else
+                [NSException raise:NSInternalInconsistencyException 
+                            format:@"scanner error in sizeForType for type %@", typeString];
+        }
+        else if ([self WOTest_typeIsStruct:typeString])
+        {
+            NSScanner *scanner = [NSScanner scannerWithString:typeString];
+            unichar startMarker, endMarker;
+            
+            if ([scanner WOTest_scanCharacter:&startMarker] && 
+                (startMarker == _C_STRUCT_B))
+            {
+                // scan optional identifier
+                if ([scanner WOTest_scanIdentifierIntoString:nil]) 
+                    [scanner WOTest_scanCharacter:NULL]; // scan past "="
+                                
+                NSString    *memberType;
+                size_t      largestMember   = 0;
+                while ([scanner WOTest_scanTypeIntoString:&memberType])
+                {
+                    size_t memberSize = [self WOTest_maximumEmbeddedSizeForType:memberType];
+                    largestMember = MAX(largestMember, memberSize);
+                    
+                    if (memberSize != 0) // watch out for division by zero
+                    {
+                        // check for alignment gap
+                        size_t modulo = (size % memberSize);
+                        if (modulo != 0) // fill alignment gap
+                            size += (memberSize - modulo);
+                    }
+                    
+                    size += memberSize;
+                }
+                
+#if defined (__i386__) || defined (__ppc64)
+                    
+                // Special rules for i386:
+                // 1. Composite data types (structs/arrays/unions) take on the alignment of the member with the highest alignment
+                // 2. Size of composite type is a multiple of its alignment 
+
+                // Special rules for ppc64 (equivalent):
+                // 1. Embedding alignment of composite types (array/struct) is same as largest embedding align of members.
+                // 2. Total size of the composite is rounded up to multiple of its embedding alignment.
+                
+                // Special rules for ppc: None.
+                
+                if (largestMember != 0) // watch out for division by zero
+                {
+                    // check for alignment gap
+                    size_t modulo = (size % largestMember);
+                    if (modulo != 0) // fill alignment gap
+                        size += (largestMember - modulo);
+                }
+                        
+#endif
+                
+                if ([scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_STRUCT_E) && [scanner isAtEnd])
+                    return size; // all done
+            }
+                
+            [NSException raise:NSInternalInconsistencyException format:@"scanner error in sizeForType for type %@", typeString];
+        }
+        else if ([self WOTest_typeIsUnion:typeString])
+        {
+            NSScanner *scanner = [NSScanner scannerWithString:typeString];
+            unichar startMarker, endMarker;
+            
+            if ([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_UNION_B)) 
+            {
+                // scan optional identifier
+                if ([scanner WOTest_scanIdentifierIntoString:nil]) 
+                    [scanner WOTest_scanCharacter:NULL]; // scan past "="
+                
+                NSString *memberType;
+                while ([scanner WOTest_scanTypeIntoString:&memberType])
+                    // size of union is size of largest type in the union
+                    size = MAX(size, [self WOTest_maximumEmbeddedSizeForType:memberType]); 
+                
+                if ([scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_UNION_E) && [scanner isAtEnd])
+                    return size; // all done
+            }
+            
+            [NSException raise:NSInternalInconsistencyException format:@"scanner error in sizeForType for type %@", typeString];
+        }
+        else if ([self WOTest_typeIsBitfield:typeString])
+            size = sizeof(int);
+        else if ([self WOTest_typeIsUnknown:typeString])
+        {
+            // could be a function pointer, but could be something else
+            [NSException raise:NSInternalInconsistencyException format: @"Cannot calculate buffer size for type %@", typeString]; 
+        }
+        else // we officially have no idea whatsoever
+            [NSException raise:NSInternalInconsistencyException 
+                        format:@"Cannot calculate buffer size for unknown type %@", typeString];
+    }
+    
+    return size;    
+}
+
+/*! Returns YES if \p typeString contains a numeric scalar value (char, int, short, long, long long, unsigned char, unsigned int, unsigned short, unsigned long, unsigned long long, float, double, C99 _Bool). Returns NO if the receiver contains any other type, object or pointer (id, Class, SEL, void, char *, as well as arrays, structures and pointers). */
++ (BOOL)WOTest_typeIsNumericScalar:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    return ([self WOTest_typeIsChar:typeString]                || 
+            [self WOTest_typeIsInt:typeString]                 ||
+            [self WOTest_typeIsShort:typeString]               || 
+            [self WOTest_typeIsLong:typeString]                ||
+            [self WOTest_typeIsLongLong:typeString]            || 
+            [self WOTest_typeIsUnsignedChar:typeString]        ||
+            [self WOTest_typeIsUnsignedInt:typeString]         || 
+            [self WOTest_typeIsUnsignedShort:typeString]       ||
+            [self WOTest_typeIsUnsignedLong:typeString]        || 
+            [self WOTest_typeIsUnsignedLongLong:typeString]    ||
+            [self WOTest_typeIsFloat:typeString]               || 
+            [self WOTest_typeIsDouble:typeString]              ||
+            [self WOTest_typeIsC99Bool:typeString]);
+}
+
++ (BOOL)WOTest_typeIsCompound:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    return ([self WOTest_typeIsStruct:typeString] || [self WOTest_typeIsUnion:typeString] || [self WOTest_typeIsArray:typeString]);
+}
+
++ (BOOL)WOTest_typeIsChar:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_CHR);
+}
+
++ (BOOL)WOTest_typeIsInt:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_INT);
+}
+
++ (BOOL)WOTest_typeIsShort:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_SHT);   
+}
+
++ (BOOL)WOTest_typeIsLong:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_LNG);   
+}
+
++ (BOOL)WOTest_typeIsLongLong:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_LNGLNG);
+}
+
++ (BOOL)WOTest_typeIsUnsignedChar:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_UCHR);
+}
+
++ (BOOL)WOTest_typeIsUnsignedInt:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_UINT);
+}
+
++ (BOOL)WOTest_typeIsUnsignedShort:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_USHT);
+}
+
++ (BOOL)WOTest_typeIsUnsignedLong:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_ULNG);
+}
+
++ (BOOL)WOTest_typeIsUnsignedLongLong:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_ULNGLNG);   
+}
+
++ (BOOL)WOTest_typeIsFloat:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_FLT);
+}
+
++ (BOOL)WOTest_typeIsDouble:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_DBL);   
+}
+
++ (BOOL)WOTest_typeIsC99Bool:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_99BOOL);  
+}
+
++ (BOOL)WOTest_typeIsVoid:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_VOID);
+}
+
++ (BOOL)WOTest_typeIsConstantCharacterString:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 2) && (strcmp(type, "r*") == 0));
+}
+
++ (BOOL)WOTest_typeIsCharacterString:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_CHARPTR);
+}
+
++ (BOOL)WOTest_typeIsObject:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_ID);
+}
+
++ (BOOL)WOTest_typeIsClass:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_CLASS);
+}
+
++ (BOOL)WOTest_typeIsSelector:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_SEL);
+}
+
++ (BOOL)WOTest_typeIsPointerToVoid:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 2) && (strcmp(type, "^v") == 0));
+}
+
++ (BOOL)WOTest_typeIsPointer:(NSString *)typeString
+{
+    NSScanner *scanner = [NSScanner scannerWithString:typeString];
+    return [scanner scanPointerIntoString:nil];
+}
+
++ (BOOL)WOTest_typeIsArray:(NSString *)typeString
+{
+    NSScanner *scanner = [NSScanner scannerWithString:typeString];
+    return [scanner WOTest_scanArrayIntoString:nil];
+}
+
++ (BOOL)WOTest_typeIsStruct:(NSString *)typeString
+{
+    NSScanner *scanner = [NSScanner scannerWithString:typeString];
+    return [scanner WOTest_scanStructIntoString:nil];
+}
+
++ (BOOL)WOTest_typeIsUnion:(NSString *)typeString
+{
+    NSScanner *scanner = [NSScanner scannerWithString:typeString];
+    return [scanner WOTest_scanUnionIntoString:nil];
+}
+
++ (BOOL)WOTest_typeIsBitfield:(NSString *)typeString
+{
+    NSScanner *scanner = [NSScanner scannerWithString:typeString];
+    return [scanner WOTest_scanBitfieldIntoString:nil];
+}
+
++ (BOOL)WOTest_typeIsUnknown:(NSString *)typeString
+{
+    if (!typeString) return NO;
+    const char *type = [typeString UTF8String];
+    return ((strlen(type) == 1) && *type == _C_UNDEF);
+}
+
+#pragma mark - 
+#pragma mark High-level test methods
+
+- (BOOL)WOTest_testIsEqualToValue:(NSValue *)otherValue
+{
+    if (!otherValue) return NO;
+
+    if ([self WOTest_isObject])
+    {
+        if ([otherValue WOTest_isObject])
+        {
+            // can only compare objects with objects
+            id selfObject   = [self nonretainedObjectValue];
+            id otherObject  = [otherValue nonretainedObjectValue];
+            @try {
+                if (selfObject && otherObject && [NSObject WOTest_object:selfObject respondsToSelector:@selector(isEqual:)]) 
+                    return [selfObject isEqual:otherObject];
+            }
+            @catch (id e) {
+                // fall through
+            }
+        }
+        else if ([otherValue WOTest_isNumericScalar])
+        {
+            // check for special case: comparing an object with nil
+            if (strcmp([otherValue objCType], @encode(typeof(nil))) == 0)
+            {
+                typeof(nil) nilId = nil;
+                NSValue *nilValue = [NSValue valueWithBytes:&nilId objCType:@encode(typeof(nil))]; 
+                if ([otherValue WOTest_compare:nilValue] == NSOrderedSame) // comparing other value (nil) with self object
+                    return ([self nonretainedObjectValue] == nil);
+            }
+            
+            // will raise exception (comparing numeric scalar with object)
+            return ([self WOTest_compare:otherValue] == NSOrderedSame);
+        }
+    }
+    else if ([self WOTest_isNumericScalar])
+    {
+        // check for special case: comparing with nil
+        typeof(nil) nilId = nil;
+        NSValue *nilValue = [NSValue valueWithBytes:&nilId objCType:@encode(typeof(nil))];         
+        if ((strcmp([self objCType], @encode(typeof(nil))) == 0) && ([self WOTest_compare:nilValue] == NSOrderedSame)) 
+        {
+            // self is nil (or at least looks like nil)
+            if ([otherValue WOTest_isObject])                          // comparing self (nil) with otherObject
+                return ([otherValue nonretainedObjectValue] == nil);
+            else if ([otherValue WOTest_isPointerToVoid])              // special case can compare to pointer to void if zero
+                return ((id)[otherValue pointerValue] == nil);
+        }
+        
+        // could raise exception (comparing numeric scalar with object)
+        return ([self WOTest_compare:otherValue] == NSOrderedSame);
+    }
+    else if ([self WOTest_isPointerToVoid])
+    {
+        // check for special case: comparing with nil
+        typeof(nil) nilId = nil;
+        NSValue *nilValue = [NSValue valueWithBytes:&nilId objCType:@encode(typeof(nil))];
+        
+        if ((strcmp([otherValue objCType], @encode(typeof(nil))) == 0) && ([otherValue WOTest_compare:nilValue] == NSOrderedSame))
+            // other value is nil (or at least looks like nil)
+            return ((id)[self pointerValue] == nil);    
+        
+        // will raise exception (comparing pointer to void with other)
+        return ([self WOTest_compare:otherValue] == NSOrderedSame);
+    }
+    else if (([self WOTest_isCharArray] || [self WOTest_isCharacterString] || [self WOTest_isConstantCharacterString]) && 
+             ([otherValue WOTest_isCharArray] || [otherValue WOTest_isCharacterString] || 
+              [otherValue WOTest_isConstantCharacterString]))
+    {
+        // another special case
+        NSString *selfString    = [self WOTest_stringValue];
+        NSString *otherString   = [otherValue WOTest_stringValue];
+        if (selfString && otherString)
+            return [selfString isEqualToString:otherString];
+    }
+
+    // fallback case (Class objects, SEL, structs, unions, pointers etc)
+    return [self isEqualToValue:otherValue];
+}
+
+- (BOOL)WOTest_testIsGreaterThanValue:(NSValue *)aValue
+{
+    return ([self WOTest_compare:aValue] == NSOrderedDescending);
+}
+
+- (BOOL)WOTest_testIsLessThanValue:(NSValue *)aValue
+{
+    return ([self WOTest_compare:aValue] == NSOrderedAscending);
+}
+
+- (BOOL)WOTest_testIsNotGreaterThanValue:(NSValue *)aValue
+{
+    return ([self WOTest_compare:aValue] != NSOrderedDescending);
+}
+
+- (BOOL)WOTest_testIsNotLessThanValue:(NSValue *)aValue
+{
+    return ([self WOTest_compare:aValue] != NSOrderedAscending);
+}
+
+- (NSComparisonResult)WOTest_compare:(NSValue *)aValue
+{
+    if (!aValue)
+        [NSException raise:NSInvalidArgumentException format:@"cannot compare to nil"];
+    
+    // object case
+    if ([self WOTest_isObject])
+    {
+        if (![aValue WOTest_isObject])
+            [NSException raise:NSInvalidArgumentException format:@"cannot compare object with non-object"];
+        
+        id selfObject   = [self nonretainedObjectValue];
+        id otherObject  = [aValue nonretainedObjectValue];
+        
+        if ([selfObject isKindOfClass:[otherObject class]] && [selfObject respondsToSelector:@selector(compare:)])
+            return ((NSComparisonResult (*)(id, SEL, id))objc_msgSend)(selfObject, @selector(compare:), otherObject); 
+        else if ([otherObject isKindOfClass:[selfObject class]] && [otherObject respondsToSelector:@selector(compare:)])
+            return ((NSComparisonResult (*)(id, SEL, id))objc_msgSend)(otherObject, @selector(compare:), selfObject);
+        
+        [NSException raise:NSInvalidArgumentException format:@"compared objects must be of same class and implement compare:"];
+    }
+    
+    // numeric scalar case
+    if ([self WOTest_isNumericScalar] && [aValue WOTest_isNumericScalar])
+    {
+        if ([aValue WOTest_isChar])
+            return [self WOTest_compareWithChar:[aValue WOTest_charValue]];
+        else if ([aValue WOTest_isInt])
+            return [self WOTest_compareWithInt:[aValue WOTest_intValue]];
+        else if ([aValue WOTest_isShort])
+            return [self WOTest_compareWithShort:[aValue WOTest_shortValue]];
+        else if ([aValue WOTest_isLong])
+            return [self WOTest_compareWithLong:[aValue WOTest_longValue]];
+        else if ([aValue WOTest_isLongLong])
+            return [self WOTest_compareWithLongLong:[aValue WOTest_longLongValue]];
+        else if ([aValue WOTest_isUnsignedChar])
+            return [self WOTest_compareWithUnsignedChar:[aValue WOTest_unsignedCharValue]];
+        else if ([aValue WOTest_isUnsignedInt])
+            return [self WOTest_compareWithUnsignedInt:[aValue WOTest_unsignedIntValue]];
+        else if ([aValue WOTest_isUnsignedShort])
+            return [self WOTest_compareWithUnsignedShort:[aValue WOTest_unsignedShortValue]];
+        else if ([aValue WOTest_isUnsignedLong])
+            return [self WOTest_compareWithUnsignedLong:[aValue WOTest_unsignedLongValue]];
+        else if ([aValue WOTest_isUnsignedLongLong])
+            return [self WOTest_compareWithUnsignedLongLong:
+                [aValue WOTest_unsignedLongLongValue]];
+        else if ([aValue WOTest_isFloat])
+            return [self WOTest_compareWithFloat:[aValue WOTest_floatValue]];
+        else if ([aValue WOTest_isDouble])
+            return [self WOTest_compareWithDouble:[aValue WOTest_doubleValue]];
+        else if ([aValue WOTest_isC99Bool])
+            return [self WOTest_compareWithC99Bool:[aValue WOTest_C99BoolValue]];
+    }
+    
+    [NSException raise:NSInvalidArgumentException format:@"non-numeric value(s) passed"];
+
+    // never reached, but necessary to suppress compiler warning
+    return NSOrderedSame;  
+}
+
+#pragma mark - 
+#pragma mark Utility methods
+
+- (size_t)WOTest_bufferSize
+{
+    return [[self class] WOTest_sizeForType:[self WOTest_objCTypeString]];
+}
+
+- (void)WOTest_printSignCompareWarning:(NSString *)warning
+{
+    NSParameterAssert(warning != nil);
+    
+    // this conditional in here so that the warnings can be turned off in WOTest self-testing
+    if ([[WOTest sharedInstance] warnsAboutSignComparisons])
+    {
+        [[WOTest sharedInstance] writeWarning:warning];
+        [[WOTest sharedInstance] writeLastKnownLocation];
+    }
+}
+
+#pragma mark -
+#pragma mark Convenience methods
+
+/*! Returns the Objective-C type of the receiver as an NSString. */
+- (NSString *)WOTest_objCTypeString
+{
+    return [NSString stringWithUTF8String:[self objCType]];
+}
+
+- (NSString *)WOTest_description
+{
+    // these special handlings exist because NSValue's description is not very human-friendly
+    // it gets even worse when running on Intel: for example, an integer like 500,000 (0x0007a120 hex) is displayed as "<20a10700>"
+    if ([self WOTest_isObject])
+    {
+        // look for objects that are either (NSString objects) or objects that respond to "description"
+        id valueContents = [self WOTest_objectValue];
+        if (valueContents)
+        {
+            // for NSString objects: just return content
+            if ([NSObject WOTest_object:valueContents isKindOfClass:[NSString class]])
+                return [[valueContents retain] autorelease];
+            else if ([NSObject WOTest_object:valueContents respondsToSelector:@selector(description)] &&
+                     [NSObject WOTest_isIdReturnType:[NSObject WOTest_returnTypeForObject:valueContents 
+                                                                                 selector:@selector(description)]])
+            {
+                NSString *description = objc_msgSend(valueContents, @selector(description));
+                if (description && [NSObject WOTest_object:description isKindOfClass:[NSString class]])
+                    return description;
+            }
+        }
+    }
+    // TODO: write unit tests to confirm that NSString supports all of these printf format markers and modifiers
+    else if ([self WOTest_isChar])
+    {
+        char character = [self WOTest_charValue];
+        if (character == 32)                                                // the space character
+            return @"(space)";
+        else if ((character >= 33) && (character <= 126))                   // other printable characters printed as an ASCII char
+            return [NSString stringWithFormat:@"'%C'", (unichar)character];
+        else
+            return [NSString stringWithFormat:@"(char)%hhd", character];    // all others printed as signed numbers
+    }
+    else if ([self WOTest_isInt])
+        return [NSString stringWithFormat:@"(int)%d", [self WOTest_intValue]];
+    else if ([self WOTest_isShort])
+        return [NSString stringWithFormat:@"(short)%hi", [self WOTest_shortValue]];
+    else if ([self WOTest_isLong])
+        return [NSString stringWithFormat:@"(long)%ld", [self WOTest_longValue]];
+    else if ([self WOTest_isLongLong])
+        return [NSString stringWithFormat:@"(long long)%lld", [self WOTest_longLongValue]];
+    else if ([self WOTest_isUnsignedChar])
+    {
+        unsigned char character = [self WOTest_unsignedCharValue];
+        if (character == 32)                                        // the space character
+            return @"(space)";
+        else if ((character >= 33) && (character <= 126))           // other printable characters printed as an ASCII char
+            return [NSString stringWithFormat:@"%C", (unichar)character];
+        else
+            // all others printed as unsigned numbers
+            return [NSString stringWithFormat:@"(unsigned char)%hhu", [self WOTest_unsignedCharValue]];
+    }
+    else if ([self WOTest_isUnsignedInt])
+        return [NSString stringWithFormat:@"(unsigned int)%u", [self WOTest_unsignedIntValue]];
+    else if ([self WOTest_isUnsignedShort])
+        return [NSString stringWithFormat:@"(unsigned short)%hu", [self WOTest_unsignedShortValue]];
+    else if ([self WOTest_isUnsignedLong])
+        return [NSString stringWithFormat:@"(unsigned long)%lu", [self WOTest_unsignedLongValue]];
+    else if ([self WOTest_isUnsignedLongLong])
+        return [NSString stringWithFormat:@"(unsigned long long)%llu", [self WOTest_unsignedLongLongValue]];
+    else if ([self WOTest_isFloat])
+        return [NSString stringWithFormat:@"(float)%f", [self WOTest_floatValue]];
+    else if ([self WOTest_isDouble])
+        return [NSString stringWithFormat:@"(double)%f", [self WOTest_doubleValue]];
+    else if ([self WOTest_isC99Bool])
+        return [NSString stringWithFormat:@"(_Bool)%@", [self WOTest_C99BoolValue] ? @"true" : @"false"];
+    else if ([self WOTest_isVoid])
+        return @"(void)";
+    else if ([self WOTest_isConstantCharacterString])
+        return [NSString stringWithFormat:@"\"%s\"", [self WOTest_constantCharacterStringValue]];
+    else if ([self WOTest_isCharacterString])
+        return [NSString stringWithFormat:@"\"%s\"", [self WOTest_characterStringValue]];
+    else if ([self WOTest_isClass])
+        return [NSString stringWithFormat:@"(Class)%@", NSStringFromClass([self WOTest_classValue])];
+    else if ([self WOTest_isSelector])
+        return [NSString stringWithFormat:@"(SEL)%@", NSStringFromSelector([self WOTest_selectorValue])];
+    else if ([self WOTest_isPointerToVoid])
+        return [NSString stringWithFormat:@"(void *)%#08x", [self WOTest_pointerToVoidValue]];    
+    return [self description];  // fallback case
+}
+
+#pragma mark -
+#pragma mark Identifying generic types
+
+- (BOOL)WOTest_isNumericScalar
+{
+    return ([self WOTest_isChar]           || [self WOTest_isInt]                 ||
+            [self WOTest_isShort]          || [self WOTest_isLong]                ||
+            [self WOTest_isLongLong]       || [self WOTest_isUnsignedChar]        ||
+            [self WOTest_isUnsignedInt]    || [self WOTest_isUnsignedShort]       ||
+            [self WOTest_isUnsignedLong]   || [self WOTest_isUnsignedLongLong]    ||
+            [self WOTest_isFloat]          || [self WOTest_isDouble]              ||
+            [self WOTest_isC99Bool]);
+}
+
+- (BOOL)WOTest_isPointer
+{
+    return [[self class] WOTest_typeIsPointer:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isArray
+{
+    return [[self class] WOTest_typeIsArray:[self WOTest_objCTypeString]];
+}
+
+- (unsigned)WOTest_arrayCount
+{
+    NSAssert([self WOTest_isArray], 
+             @"WOTest_arrayCount sent but receiver does not contain an array");
+    NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
+    unichar startMarker;
+    int count = 0;
+    
+    // attempt the scan: it should work
+    if (!([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) && [scanner scanInt:&count])) 
+        [NSException raise:NSInternalInconsistencyException format:@"scanner error in WOTest_arrayCount"];
+
+    return (unsigned)count;
+}
+
+- (NSString *)WOTest_arrayType
+{
+    NSAssert([self WOTest_isArray], @"WOTest_arrayType sent but receiver does not contain an array"); 
+    NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
+    [scanner setScanLocation:1];
+    NSString *typeString;
+    if (!([scanner scanInt:nil] && [scanner WOTest_scanTypeIntoString:&typeString]))
+        [NSException raise:NSInternalInconsistencyException format:@"scanner error in WOTest_arrayType"]; 
+    return typeString;
+}
+
+- (BOOL)WOTest_isStruct
+{
+    return [[self class] WOTest_typeIsStruct:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnion
+{
+    return [[self class] WOTest_typeIsUnion:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isBitfield
+{
+    return [[self class] WOTest_typeIsBitfield:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnknown
+{
+    return [[self class] WOTest_typeIsUnknown:[self WOTest_objCTypeString]];
+}
+
+#pragma mark -
+#pragma mark Identifying and retrieving specific types
+
+- (BOOL)WOTest_isChar
+{
+    return [[self class] WOTest_typeIsChar:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isInt
+{
+    return [[self class] WOTest_typeIsInt:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isShort
+{
+    return [[self class] WOTest_typeIsShort:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isLong
+{
+    return [[self class] WOTest_typeIsLong:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isLongLong
+{
+    return [[self class] WOTest_typeIsLongLong:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnsignedChar
+{
+    return [[self class] WOTest_typeIsUnsignedChar:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnsignedInt
+{
+    return [[self class] WOTest_typeIsUnsignedInt:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnsignedShort
+{
+    return [[self class] WOTest_typeIsUnsignedShort:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnsignedLong
+{
+    return [[self class] WOTest_typeIsUnsignedLong:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isUnsignedLongLong
+{
+    return [[self class] WOTest_typeIsUnsignedLongLong:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isFloat
+{
+    return [[self class] WOTest_typeIsFloat:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isDouble
+{
+    return [[self class] WOTest_typeIsDouble:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isC99Bool
+{
+    return [[self class] WOTest_typeIsC99Bool:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isVoid
+{
+    return [[self class] WOTest_typeIsVoid:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isConstantCharacterString
+{
+    return [[self class] WOTest_typeIsConstantCharacterString:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isCharacterString
+{
+    return [[self class] WOTest_typeIsCharacterString:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isObject
+{
+    return [[self class] WOTest_typeIsObject:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isClass
+{
+    return [[self class] WOTest_typeIsClass:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isSelector
+{
+    return [[self class] WOTest_typeIsSelector:[self WOTest_objCTypeString]];
+}
+
+- (BOOL)WOTest_isPointerToVoid
+{
+    return [[self class] WOTest_typeIsPointerToVoid:[self WOTest_objCTypeString]];
+}
+
+- (char)WOTest_charValue
+{
+    char value;
+    [self getValue:&value];
+    return value;
+}
+
+- (int)WOTest_intValue
+{
+    int value;
+    [self getValue:&value];
+    return value;
+}
+
+- (short)WOTest_shortValue
+{
+    short value;
+    [self getValue:&value];
+    return value;
+}
+
+- (long)WOTest_longValue
+{
+    long value;
+    [self getValue:&value];
+    return value;
+}
+
+- (long long)WOTest_longLongValue
+{
+    long long value;
+    [self getValue:&value];
+    return value;
+}
+
+- (unsigned char)WOTest_unsignedCharValue
+{
+    unsigned char value;
+    [self getValue:&value];
+    return value;
+}
+
+- (unsigned int)WOTest_unsignedIntValue
+{
+    unsigned int value;
+    [self getValue:&value];
+    return value;
+}
+
+- (unsigned short)WOTest_unsignedShortValue
+{
+    unsigned short value;
+    [self getValue:&value];
+    return value;
+}
+
+- (unsigned long)WOTest_unsignedLongValue
+{
+    unsigned long value;
+    [self getValue:&value];
+    return value;
+}
+
+- (unsigned long long)WOTest_unsignedLongLongValue
+{
+    unsigned long long value;
+    [self getValue:&value];
+    return value;
+}
+
+- (float)WOTest_floatValue
+{
+    float value;
+    [self getValue:&value];
+    return value;
+}
+
+- (double)WOTest_doubleValue
+{
+    double value;
+    [self getValue:&value];
+    return value;
+}
+
+- (_Bool)WOTest_C99BoolValue
+{
+    _Bool value;
+    [self getValue:&value];
+    return value;
+}
+
+- (const char *)WOTest_constantCharacterStringValue
+{
+    const char *value;
+    [self getValue:&value];
+    return value;
+}
+
+- (char *)WOTest_characterStringValue
+{
+    char *value;
+    [self getValue:&value];
+    return value;
+}
+
+- (id)WOTest_objectValue
+{
+    id value;
+    [self getValue:&value];
+    return value;
+}
+
+- (Class)WOTest_classValue
+{
+    Class value;
+    [self getValue:&value];
+    return value;
+}
+
+- (SEL)WOTest_selectorValue
+{
+    SEL value;
+    [self getValue:&value];
+    return value;
+}
+
+- (void *)WOTest_pointerToVoidValue
+{
+    void *value;
+    [self getValue:&value];
+    return value;
+}
+
+- (BOOL)WOTest_isCharArray
+{
+    // look for string of form "[4c]"
+    NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]];
+    unichar startMarker, flag, endMarker;
+    int count;
+    return ([scanner WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) &&
+            [scanner scanInt:&count] && 
+            [scanner WOTest_scanCharacter:&flag] && (flag == _C_CHR) && 
+            [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
+            [scanner isAtEnd]);
+}
+
+- (NSString *)WOTest_stringValue
+{
+    @try {
+        if ([self WOTest_isCharacterString] || [self WOTest_isConstantCharacterString])
+            return [NSString stringWithUTF8String:(const char *)[self pointerValue]]; 
+        else // see if this is a char array
+        {
+            NSScanner *scanner = [NSScanner scannerWithString:[self WOTest_objCTypeString]]; 
+            unichar startMarker, flag, endMarker;
+            int count;
+            if ([scanner WOTest_scanCharacter:&startMarker] && 
+                (startMarker == _C_ARY_B) && [scanner scanInt:&count] && 
+                [scanner WOTest_scanCharacter:&flag] && (flag == _C_CHR) && 
+                [scanner WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E) &&
+                [scanner isAtEnd])
+            {
+                // is char array
+                if (count > 0)
+                {
+                    char *buffer = malloc(count * sizeof(char));
+                    NSAssert1(buffer != NULL, @"malloc() failed (size %d)", 
+                              (count * sizeof(char)));
+                    [self getValue:buffer];
+                    
+                    // confirm that this is a null-terminated string
+                    for (int i = 0; i < count; i++)
+                    {
+                        if (buffer[i] == 0)
+                            return [NSString stringWithUTF8String:buffer];
+                    }
+                    free(buffer);
+                }
+            }
+        }
+    }
+    @catch (id e) {
+        // fall through
+    }
+    return nil;    
+}
+
+#pragma mark - 
+#pragma mark Low-level test methods
+
+/* Unfortunately there is a lot of very similar code repeated across these methods but it seems to be a necessary evil (600 lines of necessary evil). Firstly, it's necessary to explicitly declare the type of the right-hand value of the comparison. There are lots of permuations for implicit casts, explicit casts (and warnings), and GCC seems to warn about signed to unsigned comparisons differently depending on the types. */
+- (NSComparisonResult)WOTest_compareWithChar:(char)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // no cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        // implicit cast
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
+    else if ([self WOTest_isUnsignedInt])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned char)other); // explicit cast 
+    }
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned char)other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedLongLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        // explicit cast
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned char)other);
+    }
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+         return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithInt:(int)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // no cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+                                    // implicit cast
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
+    else if ([self WOTest_isUnsignedInt])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned int)other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned int)other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedLongLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned int)other);  // explicit cast
+    }
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithShort:(short)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // no cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+                                    // implicit cast
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
+    else if ([self WOTest_isUnsignedInt])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned short)other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned short)other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedLongLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned short)other);  // explicit cast
+    }
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithLong:(long)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // no cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+                                    // implicit cast
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other);
+    else if ([self WOTest_isUnsignedInt])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], (unsigned long)other); // explicit cast 
+    }
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], (unsigned long)other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedLongLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        // explicit cast
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned long)other);
+    }
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithLongLong:(long long)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // no cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], (unsigned long long)other);  // explicit cast
+    }
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithUnsignedChar:(unsigned char)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // no cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithUnsignedInt:(unsigned int)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // explicit cast
+    }
+    else if ([self WOTest_isInt])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
+    }
+    else if ([self WOTest_isShort])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
+    }
+    else if ([self WOTest_isLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
+    }
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // no cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithUnsignedShort:(unsigned short)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // no cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+
+    // all other cases
+    [NSException raise:NSInvalidArgumentException  
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithUnsignedLong:(unsigned long)other
+{
+    if ([self WOTest_isChar]) // char (also BOOL)
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // expicit cast
+    }
+    else if ([self WOTest_isInt]) // int
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
+    }
+    else if ([self WOTest_isShort]) // short
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
+    }
+    else if ([self WOTest_isLong]) // long
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
+    }
+    else if ([self WOTest_isLongLong]) // long long
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // unsigned char (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt]) // unsigned int
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort]) // unsigned short
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong]) // unsigned long
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // no cast
+    else if ([self WOTest_isUnsignedLongLong]) // unsigned long long
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat]) // float
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble]) // double
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool]) // C99 _Bool
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithUnsignedLongLong:(unsigned long long)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned char)[self WOTest_charValue], other); // explicit cast 
+    }
+    else if ([self WOTest_isInt])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned int)[self WOTest_intValue], other); // explicit cast
+    }
+    else if ([self WOTest_isShort])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned short)[self WOTest_shortValue], other); // explicit cast
+    }
+    else if ([self WOTest_isLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned long)[self WOTest_longValue], other); // explicit cast
+    }
+    else if ([self WOTest_isLongLong])
+    {
+        [self WOTest_printSignCompareWarning:@"comparison between signed and unsigned, to avoid this warning use an explicit cast"];
+        return WO_COMPARE_SCALARS((unsigned long long)[self WOTest_longLongValue], other); // explicit cast
+    }
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // no cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithFloat:(float)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // no cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithDouble:(double)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // no cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // implicit cast
+
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;   // never reached, but necessary to suppress compiler warning
+}
+
+- (NSComparisonResult)WOTest_compareWithC99Bool:(_Bool)other
+{
+    if ([self WOTest_isChar]) // (also BOOL)
+        return WO_COMPARE_SCALARS([self WOTest_charValue], other); // implicit cast
+    else if ([self WOTest_isInt])
+        return WO_COMPARE_SCALARS([self WOTest_intValue], other); // implicit cast
+    else if ([self WOTest_isShort])
+        return WO_COMPARE_SCALARS([self WOTest_shortValue], other); // implicit cast
+    else if ([self WOTest_isLong])
+        return WO_COMPARE_SCALARS([self WOTest_longValue], other); // implicit cast
+    else if ([self WOTest_isLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_longLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedChar]) // (also Boolean)
+        return WO_COMPARE_SCALARS([self WOTest_unsignedCharValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedInt])
+        return WO_COMPARE_SCALARS ([self WOTest_unsignedIntValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedShort])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedShortValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongValue], other); // implicit cast
+    else if ([self WOTest_isUnsignedLongLong])
+        return WO_COMPARE_SCALARS([self WOTest_unsignedLongLongValue], other); // implicit cast
+    else if ([self WOTest_isFloat])
+        return WO_COMPARE_SCALARS([self WOTest_floatValue], other); // implicit cast
+    else if ([self WOTest_isDouble])
+        return WO_COMPARE_SCALARS([self WOTest_doubleValue], other); // implicit cast
+    else if ([self WOTest_isC99Bool])
+        return WO_COMPARE_SCALARS([self WOTest_C99BoolValue], other); // no cast
+    
+    // all other cases
+    [NSException raise:NSInvalidArgumentException 
+                format:@"cannot compare type \"%s\" with type \"%s\"", [self objCType], @encode(typeof(other))];
+    
+    return NSOrderedSame;       // never reached, but necessary to suppress compiler warning
+}
+
+@end
diff --git a/RunTests.sh b/RunTests.sh
new file mode 100755 (executable)
index 0000000..fddd08f
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+# RunTests.sh
+# WOTest
+#
+# Created by Wincent Colaiuta on 01 March 2006.
+#
+# Copyright 2006-2007 Wincent Colaiuta.
+# This program 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 program 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# $Id: RunTests.sh 208 2007-07-07 19:02:28Z wincent $
+
+#
+# Defaults
+#
+
+# test bundle path relative to this script
+TEST_BUNDLE="WOTestSelfTests.bundle"
+
+# WOTest.framework path relative to this script
+TEST_FRAMEWORK="../../../../WOTest.framework"
+
+#
+# Functions
+#
+
+check_error_status()
+{
+  ERR=$?
+  if [ $ERR -ne 0 ]; then
+    FAILURE=$ERR
+  fi
+}
+
+#
+# Main
+#
+
+set -e
+
+STARTING_DIRECTORY=`/bin/pwd`
+builtin echo "Saved current working directory: ${STARTING_DIRECTORY}"
+
+SCRIPT_DIRECTORY=$(/usr/bin/dirname "$0")
+builtin echo "Changing to script directory: ${SCRIPT_DIRECTORY}"
+cd "${SCRIPT_DIRECTORY}"
+SCRIPT_DIRECTORY=$(/bin/pwd)
+builtin echo "Script directory with symlinks resolved: ${SCRIPT_DIRECTORY}"
+
+if [ "${DYLD_FRAMEWORK_PATH}" != "" ]; then
+  SAVE_DLYD_FRAMEWORK_PATH=`declare -p DYLD_FRAMEWORK_PATH`
+  builtin echo "Saved old DYLD_FRAMEWORK_PATH: ${SAVE_DLYD_FRAMEWORK_PATH}"
+else
+  SAVE_DLYD_FRAMEWORK_PATH=""
+fi
+
+DYLD_FRAMEWORK_PATH=$(/usr/bin/dirname "${SCRIPT_DIRECTORY}/${TEST_FRAMEWORK}")
+cd "${DYLD_FRAMEWORK_PATH}"
+export DYLD_FRAMEWORK_PATH=$(/bin/pwd)
+cd -
+builtin echo "DYLD_FRAMEWORK_PATH set to ${DYLD_FRAMEWORK_PATH}"
+
+builtin echo "Launching test runner for bundle: ${SCRIPT_DIRECTORY}/${TEST_BUNDLE}"
+"${SCRIPT_DIRECTORY}/${TEST_FRAMEWORK}/Versions/A/Resources/WOTestRunner" --test-bundle="${SCRIPT_DIRECTORY}/${TEST_BUNDLE}"
+check_error_status
+builtin echo "Test run complete"
+
+builtin echo "Restoring old DYLD_FRAMEWORK_PATH"
+eval "${SAVE_DLYD_FRAMEWORK_PATH}"
+
+builtin echo "Returning to old working directory"
+cd "${STARTING_DIRECTORY}"
+
+if [ $FAILURE ]; then
+  exit $FAILURE
+else
+  exit 0
+fi
diff --git a/TODO.txt b/TODO.txt
new file mode 100644 (file)
index 0000000..2378d61
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,35 @@
+$Id: TODO.txt 153 2007-04-24 12:32:52Z wincent $
+
+- method for converting command line arguments into absolute paths if they don't start with a / (based on CWD env variable? or NSProcessInfo?)
+
+- Add information on code coverage testing to docs:
+
+    http://www.supermegaultragroovy.com/Software%20Development/xcode_code_coverage_howto
+
+- Describe how to run tests under a debugger? WOTestRunner
+
+- for consistency, make a WOClassStub class (inherits from WOObjectStub) to mirror set up in WOClassMock and WOObjectMock
+
+- further to thoughts on Higher-Order Messaging here:
+
+http://wincent.com/a/about/wincent/weblog/archives/2006/08/thoughts_on_hig.php
+
+Think about alternative syntax for mock objects; instead of:
+
+    [[mock expect] returning:@"foo"] substringfromIndex:3];
+
+Consider:
+
+    [mock expectSelector:@selector(substringFromIndex:)
+                  return:@"foo" 
+               arguments:3];
+
+Regressions:
+
+- write WOMultithreadedCrash class for WOTest (regression test for multithreaded low level exception handling failure); for now just 
+WO_TEST_FAIL
+
+- also write WOLocaleFormatterCrash class (copy and refactor WOLocaleFormatter class and tests so that the WOCommon crash problem 
+can be manifested  in the WOTest tests); again, for now will just be WO_TEST_FAIL
+
+- for Leopard: use RubyCocoa bridge for writing specs in Ruby
\ No newline at end of file
diff --git a/TestBundle.icns b/TestBundle.icns
new file mode 100644 (file)
index 0000000..fbf0090
Binary files /dev/null and b/TestBundle.icns differ
diff --git a/Tests/Info.plist b/Tests/Info.plist
new file mode 100644 (file)
index 0000000..a989d91
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>en</string>
+       <key>CFBundleExecutable</key>
+       <string>WOTestSelfTests</string>
+       <key>CFBundleIconFile</key>
+       <string>WincentTestBundle.icns</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.wincent.WOTest.tests</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>WO_INFO_PLIST_VERSION (WO_BUILDNUMBER)</string>
+       <key>WOCopyrightYear</key>
+       <string>WO_COPYRIGHT_YEAR</string>
+</dict>
+</plist>
diff --git a/Tests/NSInvocationTests.h b/Tests/NSInvocationTests.h
new file mode 100644 (file)
index 0000000..0262d32
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  NSInvocationTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 30 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSInvocationTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest.h"
+
+@interface NSInvocationTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/NSInvocationTests.m b/Tests/NSInvocationTests.m
new file mode 100644 (file)
index 0000000..f9e4825
--- /dev/null
@@ -0,0 +1,32 @@
+//
+//  NSInvocationTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 30 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSInvocationTests.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSInvocationTests.h"
+
+@implementation NSInvocationTests
+
+- (void)testNSInvocationCategory
+{
+    // WOTest_valueForArgumentAtIndex should throw for out-of-range index values
+}
+
+@end
diff --git a/Tests/NSObjectTests.h b/Tests/NSObjectTests.h
new file mode 100644 (file)
index 0000000..68b61bc
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  NSObjectTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 10 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSObjectTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest.h"
+
+@interface NSObjectTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/NSObjectTests.m b/Tests/NSObjectTests.m
new file mode 100644 (file)
index 0000000..6d0b203
--- /dev/null
@@ -0,0 +1,365 @@
+//
+//  NSObjectTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 10 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSObjectTests.m 208 2007-07-07 19:02:28Z wincent $
+
+// class header
+#import "NSObjectTests.h"
+
+// framework headers
+#import "WOLightweightRoot.h"
+
+@implementation NSObjectTests
+
+- (void)testDescriptionForObject
+{
+    // description for nil should be "(nil)"
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:nil], @"(nil)");
+    
+    // NSString objects should be returned as-is
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:@"foo"], @"foo");
+    
+    // NSNumber is a special case:
+    // when an NSValue is initialized with an int NSNumber, the NSValue behaves as though it were initialized with an int
+    NSNumber *number = [NSNumber numberWithInt:1];
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:number], @"(int)1");
+    
+    // other objects should return "description"
+    NSButton *button = [[NSButton alloc] init];
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:button], [button description]);
+    
+    // custom classes that do not respond to "description" return class name    
+    WOLightweightRoot *root = [WOLightweightRoot newLightweightRoot];
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:root], @"WOLightweightRoot");
+    [root dealloc];
+    
+    // special case: NSValues that contain NSStrings should return the string
+    NSValue *value = [NSValue WOTest_valueWithObject:@"foo"];
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:value], @"foo");
+    
+    // standard case: other NSValues should return "description"
+    NSRange range;
+    range.location  = 0; // can't use NSMakeRange (Intel release warnings)
+    range.length    = 0;
+    value = [NSValue valueWithRange:range];
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:value], [value WOTest_description]);
+    
+    // pointers to void should be formatted as "(void *)<00000000 >", as returned by WOTest_description
+    value = [NSValue valueWithNonretainedObject:@"bar"];
+    WO_TEST_EQ([NSObject WOTest_descriptionForObject:value], [value WOTest_description]);
+}
+
+- (void)testIsRegisteredClass
+{
+    WO_TEST([NSObject WOTest_isRegisteredClass:[self class]]);
+    WO_TEST_FALSE([NSObject WOTest_isRegisteredClass:(Class)self]); // shouldn't raise
+}
+
+- (void)testIsMetaClass
+{
+    WO_TEST([NSObject WOTest_isMetaClass:objc_getMetaClass("NSObjectTests")]);
+    WO_TEST_FALSE([NSObject WOTest_isMetaClass:[self class]]);
+}
+
+- (void)testObjectIsKindOfClass
+{
+    // preliminaries
+    id object       = @"foo";
+    id subobject    = [NSMutableString stringWithString:@"bar"];
+    id otherObject  = [WOLightweightRoot newLightweightRoot];
+    
+    // should raise if passed a non-class pointer
+    WO_TEST_THROWS([NSObject WOTest_object:self isKindOfClass:(Class)self]);
+    WO_TEST_DOES_NOT_THROW([NSObject WOTest_object:self isKindOfClass:[self class]]);
+    
+    // nil object or NULL class should always return NO
+    WO_TEST_FALSE([NSObject WOTest_object:nil isKindOfClass:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_object:self isKindOfClass:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_object:nil isKindOfClass:[self class]]);
+    
+    // basic test cases
+    WO_TEST([NSObject WOTest_object:object isKindOfClass:[NSString class]]);
+    WO_TEST_FALSE([NSObject WOTest_object:object isKindOfClass:[NSNumber class]]);        
+    
+    // subclass should be considered of same kind as superclass
+    WO_TEST([NSObject WOTest_object:subobject isKindOfClass:[NSString class]]);
+    
+    // superclass should not be considered as same kind as subclass
+    WO_TEST_FALSE([NSObject WOTest_object:[[[NSObject alloc] init] autorelease]
+                     isKindOfClass:[self class]]);
+    
+    // initial attempt at this test failed because the NSString object had a
+    // superclass of "%NSCFString", which happened to match NSMutableString!
+    //WO_TEST_FALSE([NSObject WOTest_object:object
+    //                 isKindOfClass:[NSMutableString class]]);
+    // note that the Cocoa isKindOfClass: method also produces this behaviour:
+    //WO_TEST_FALSE([@"constant string" isKindOfClass:[NSMutableString class]]);
+        
+    // should handle custom root classes without problems
+    WO_TEST([NSObject WOTest_object:otherObject 
+               isKindOfClass:NSClassFromString(@"WOLightweightRoot")]);
+    WO_TEST_FALSE([NSObject WOTest_object:otherObject isKindOfClass:[NSString class]]);
+    WO_TEST_FALSE([NSObject WOTest_object:self 
+                     isKindOfClass:NSClassFromString(@"WOLightweightRoot")]);
+    
+    // cleanup
+    [otherObject dealloc];
+}
+
+- (void)testInstancesOfClassAreKindOfClass
+{
+    Class class         = [NSString class];
+    Class subclass      = [NSMutableString class];
+    Class otherClass    = NSClassFromString(@"WOLightweightRoot");
+    
+    // should raise if passed non-class pointers
+    WO_TEST_THROWS([NSObject WOTest_instancesOfClass:(Class)self areKindOfClass:(Class)self]);
+    WO_TEST_THROWS([NSObject WOTest_instancesOfClass:(Class)self areKindOfClass:[NSString class]]);
+    WO_TEST_THROWS([NSObject WOTest_instancesOfClass:[NSString class] areKindOfClass:(Class)self]);
+    WO_TEST_DOES_NOT_THROW([NSObject WOTest_instancesOfClass:[self class] areKindOfClass:[NSString class]]);
+    
+    // if either class is NULL should always return NO
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NULL areKindOfClass:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[self class] areKindOfClass:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NULL areKindOfClass:[self class]]);
+    
+    // basic tests
+    WO_TEST([NSObject WOTest_instancesOfClass:[NSString class] areKindOfClass:[NSString class]]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[NSString class] areKindOfClass:[NSNumber class]]);
+        
+    // a subclass should be considered of same kind as superclass
+    WO_TEST([NSObject WOTest_instancesOfClass:subclass areKindOfClass:class]);
+    
+    // a superclass should not be considered as same kind as subclass
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:class areKindOfClass:subclass]);
+    
+    // should handle custom root classes without problems
+    WO_TEST([NSObject WOTest_instancesOfClass:otherClass areKindOfClass:otherClass]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:class areKindOfClass:otherClass]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:otherClass areKindOfClass:class]);
+}
+
+- (void)testObjectRespondsToSelector
+{
+    // preliminaries
+    id object       = @"foobar";
+    id subobject    = [NSMutableString stringWithString:object];
+    id root         = [WOLightweightRoot newLightweightRoot];
+    
+    // test returns NO for nil object or NULL selector
+    WO_TEST_FALSE([NSObject WOTest_object:nil respondsToSelector:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_object:nil respondsToSelector:@selector(length)]);
+    WO_TEST_FALSE([NSObject WOTest_object:self respondsToSelector:NULL]);
+    
+    // basic tests
+    WO_TEST([NSObject WOTest_object:self 
+          respondsToSelector:@selector(testObjectRespondsToSelector)]);
+    WO_TEST_FALSE([NSObject WOTest_object:self respondsToSelector:@selector(foo)]);
+    
+    // should work for subclasses
+    WO_TEST([NSObject WOTest_object:object respondsToSelector:@selector(length)]);
+    WO_TEST([NSObject WOTest_object:subobject respondsToSelector:@selector(length)]);
+    WO_TEST_FALSE([NSObject WOTest_object:object respondsToSelector:@selector(bar)]);
+    WO_TEST_FALSE([NSObject WOTest_object:subobject 
+                respondsToSelector:@selector(bar)]);
+    
+    // should handle custom root classes without problems
+    WO_TEST([NSObject WOTest_object:root respondsToSelector:@selector(dealloc)]);
+    WO_TEST_FALSE([NSObject WOTest_object:root respondsToSelector:@selector(foobar)]);
+    
+    // cleanup
+    [root dealloc];
+}
+
+- (void)testClassRespondsToSelector
+{
+    // should raise if passed non-class pointer
+    WO_TEST_THROWS([NSObject WOTest_class:(Class)self respondsToSelector:@selector(init)]);
+    WO_TEST_DOES_NOT_THROW([NSObject WOTest_class:[self class] respondsToSelector:@selector(init)]);
+    
+    // test that NULL class or NULL selector return NO
+    WO_TEST_FALSE([NSObject WOTest_class:NULL respondsToSelector:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_class:NULL respondsToSelector:@selector(init)]);
+    WO_TEST_FALSE([NSObject WOTest_class:[self class] respondsToSelector:NULL]);
+    
+    // basic tests
+    WO_TEST([NSObject WOTest_class:[self class] respondsToSelector:@selector(initialize)]);
+    WO_TEST_FALSE([NSObject WOTest_class:[self class] respondsToSelector:@selector(unimplimentedClassMethod)]);
+}
+
+- (void)testInstancesOfClassRespondToSelector
+{
+    // should raise if passed non-class pointer
+    WO_TEST_THROWS([NSObject WOTest_instancesOfClass:(Class)self respondToSelector:@selector(init)]);
+    WO_TEST_DOES_NOT_THROW([NSObject WOTest_instancesOfClass:[self class] respondToSelector:@selector(init)]);               
+    
+    // test that NULL class or NULL selector return NO
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NULL respondToSelector:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NULL respondToSelector:@selector(length)]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[self class] respondToSelector:NULL]);
+    
+    // basic tests
+    WO_TEST([NSObject WOTest_instancesOfClass:[NSString class] respondToSelector:@selector(length)]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[NSString class] respondToSelector:@selector(longitude)]);
+    
+    // subclasses should work as well
+    WO_TEST([NSObject WOTest_instancesOfClass:[NSMutableString class] respondToSelector:@selector(length)]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[NSMutableString class] respondToSelector:@selector(longitude)]);
+    
+    // should work with custom root classes
+    WO_TEST([NSObject WOTest_instancesOfClass:NSClassFromString(@"WOLightweightRoot") respondToSelector:@selector(forward::)]);
+    WO_TEST([NSObject WOTest_instancesOfClass:NSClassFromString(@"WOLightweightRoot") respondToSelector:@selector(dealloc)]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NSClassFromString(@"WOLightweightRoot")
+                                  respondToSelector:@selector(conformsToProtocol:)]);
+}
+
+- (void)testInstancesOfClassConformToProtocol
+{
+    // should throw if passed non-class pointer
+    WO_TEST_THROWS([NSObject WOTest_instancesOfClass:(Class)self conformToProtocol:@protocol(NSLocking)]);
+    WO_TEST_DOES_NOT_THROW([NSObject WOTest_instancesOfClass:[self class] conformToProtocol:@protocol(NSObject)]);
+    
+    // test that NULL class or NULL protocol return NO
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NULL conformToProtocol:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[self class] conformToProtocol:NULL]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:NULL conformToProtocol:@protocol(WOTest)]);
+    
+    // basic tests
+    WO_TEST([NSObject WOTest_instancesOfClass:[NSLock class] conformToProtocol:@protocol(NSLocking)]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[NSString class] conformToProtocol:@protocol(NSLocking)]);
+    WO_TEST([NSObject WOTest_instancesOfClass:[self class] conformToProtocol:@protocol(WOTest)]);
+    WO_TEST_FALSE([NSObject WOTest_instancesOfClass:[self class] conformToProtocol:@protocol(NSTextAttachmentCell)]);
+    
+    // test with subclasses (subclasses should inherit protocol conformance)
+    WO_TEST([NSObject WOTest_instancesOfClass:[NSMutableString class] conformToProtocol:@protocol(NSCopying)]);
+    
+    // should handle custom root classes
+    WO_TEST_FALSE
+        ([NSObject WOTest_instancesOfClass:NSClassFromString(@"WOLightweightRoot") conformToProtocol:@protocol(NSLocking)]);
+}
+
+- (void)testReturnTypeForClassSelector
+{
+    // raises if passed NULL class or NULL selector
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForClass:NULL selector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForClass:NULL selector:@selector(init)]);
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForClass:[self class] selector:NULL]);
+    
+    // raises if passed non-class pointer
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForClass:(Class)self selector:@selector(init)]);
+    
+    // basic test
+    WO_TEST_EQ([NSObject WOTest_returnTypeForClass:[self class] selector:@selector(initialize)], @"v");
+    
+    // returns nil for unrecognized selector
+    WO_TEST_EQ([NSObject WOTest_returnTypeForClass:[self class] selector:@selector(poodle)], nil);
+}
+
+- (void)testReturnTypeForObjectSelector
+{
+    // raises if passed nil object or NULL selector
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForObject:nil selector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForObject:self selector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_returnTypeForObject:nil selector:@selector(init)]);
+    
+    // basic test
+    WO_TEST_EQ([NSObject WOTest_returnTypeForObject:self selector:@selector(init)], @"@");
+    
+    // returns nil for unrecognized selector
+    WO_TEST_EQ([NSObject WOTest_returnTypeForObject:self selector:@selector(beagle:dog:)], nil);
+}
+
+- (void)testIsIdReturnType
+{
+    WO_TEST([NSObject WOTest_isIdReturnType:@"@"]);
+    WO_TEST_FALSE([NSObject WOTest_isIdReturnType:@"v"]);
+
+    // passing nil should return NO, not raise an exception
+    WO_TEST_FALSE([NSObject WOTest_isIdReturnType:nil]);
+}
+
+- (void)testIsCharacterStringReturnType
+{
+    WO_TEST([NSObject WOTest_isCharacterStringReturnType:@"*"]);
+    WO_TEST_FALSE([NSObject WOTest_isCharacterStringReturnType:@"r*"]);
+
+    // passing nil should return NO, not raise an exception
+    WO_TEST_FALSE([NSObject WOTest_isCharacterStringReturnType:nil]);
+}
+
+- (void)testIsConstantCharacterStringReturnType
+{
+    WO_TEST([NSObject WOTest_isConstantCharacterStringReturnType:@"r*"]);
+    WO_TEST_FALSE([NSObject WOTest_isConstantCharacterStringReturnType:@"^v"]);
+    
+    // passing nil should return NO, not raise an exception
+    WO_TEST_FALSE([NSObject WOTest_isConstantCharacterStringReturnType:nil]);
+}
+
+- (void)testObjectReturnsIdForSelector
+{
+    // should raise for nil object or NULL selector
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsId:nil forSelector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsId:self forSelector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsId:nil forSelector:@selector(init)]);
+    
+    // basic tests
+    WO_TEST([NSObject WOTest_objectReturnsId:self forSelector:@selector(init)]);
+    WO_TEST_FALSE([NSObject WOTest_objectReturnsId:self forSelector:@selector(dealloc)]);
+    
+    // passing unrecognized selector should return NO, not raise an exception
+    WO_TEST_FALSE([NSObject WOTest_objectReturnsId:self forSelector:@selector(initWithChicken:)]);
+}
+
+- (void)testObjectReturnsCharacterStringForSelector
+{
+    // should raise for nil object or NULL selector
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsCharacterString:nil forSelector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsCharacterString:self forSelector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsCharacterString:nil forSelector:@selector(init)]);
+    
+    // basic tests
+    char *string = "foo";    
+    NSValue *value = [NSValue WOTest_valueWithCharacterString:string];
+    WO_TEST([NSObject WOTest_objectReturnsCharacterString:value forSelector:@selector(WOTest_characterStringValue)]);
+    WO_TEST_FALSE([NSObject WOTest_objectReturnsCharacterString:self forSelector:@selector(init)]);
+    
+    // passing unrecognized selector should return NO, not raise an exception
+    WO_TEST_FALSE([NSObject WOTest_objectReturnsCharacterString:self forSelector:@selector(initWithChicken:)]);
+}
+
+- (void)testObjectReturnsConstantCharacterString
+{
+    // should raise for nil object or NULL selector
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsConstantCharacterString:nil forSelector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsConstantCharacterString:self forSelector:NULL]);
+    WO_TEST_THROWS([NSObject WOTest_objectReturnsConstantCharacterString:nil forSelector:@selector(init)]);
+    
+    // basic tests
+    NSValue *value      = [NSValue WOTest_valueWithConstantCharacterString:"foobar"];
+    SEL     selector    = @selector(WOTest_constantCharacterStringValue);
+    WO_TEST([NSObject WOTest_objectReturnsConstantCharacterString:value forSelector:selector]);
+    WO_TEST_FALSE([NSObject WOTest_objectReturnsConstantCharacterString:self forSelector:@selector(init)]);
+    
+    // passing unrecognized selector should return NO, not raise an exception
+    WO_TEST_FALSE([NSObject WOTest_objectReturnsConstantCharacterString:self forSelector:@selector(getTurkey)]);
+}
+
+@end
diff --git a/Tests/NSScannerTests.h b/Tests/NSScannerTests.h
new file mode 100644 (file)
index 0000000..523ce50
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  NSScannerTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 01 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSScannerTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest.h"
+
+@interface NSScannerTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/NSScannerTests.m b/Tests/NSScannerTests.m
new file mode 100644 (file)
index 0000000..76f9269
--- /dev/null
@@ -0,0 +1,266 @@
+//
+//  NSScannerTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 01 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSScannerTests.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSScannerTests.h"
+
+@implementation NSScannerTests
+
+- (void)testPeekCharacter
+{
+    // preliminaries
+    NSString    *string     = @"foobar";
+    NSScanner   *scanner    = [NSScanner scannerWithString:string];
+    unichar     character;
+    
+    WO_TEST_THROWS([scanner WOTest_peekCharacter:NULL]);        // test response to NULL
+    [scanner setScanLocation:0];                                // move to start
+    WO_TEST([scanner WOTest_peekCharacter:&character]);         // scans
+    WO_TEST_EQ(character, 'f');                                 // gets correct result
+    WO_TEST_EQ([scanner scanLocation], 0U);                     // doesn't move location
+    [scanner setScanLocation:[string length]];                  // move to end
+    WO_TEST_FALSE([scanner WOTest_peekCharacter:&character]);   // doesn't scan past end
+}
+
+- (void)testScanCharacter
+{
+    // preliminaries
+    NSString    *string     = @"foobar";
+    NSScanner   *scanner    = [NSScanner scannerWithString:string];
+    unichar     character   = 0;
+    
+    WO_TEST_DOES_NOT_THROW([scanner WOTest_scanCharacter:NULL]);    // test with NULL
+    [scanner setScanLocation:0];                                    // move to start
+    WO_TEST_TRUE([scanner WOTest_scanCharacter:&character]);        // scans
+    WO_TEST_EQ(character, 'f');                                     // gets correct result
+    WO_TEST_EQ([scanner scanLocation], 1U);                         // does move location
+    [scanner setScanLocation:[string length]];                      // move to end
+    WO_TEST_FALSE([scanner WOTest_scanCharacter:&character]);       // doesn't scan past end
+}
+
+- (void)testScanCharacterFromSetIntoChar
+{
+    // handles NULL correctly
+}
+
+- (void)testScanReturnTypeIntoString
+{
+    // handles nil correctly
+    
+    // return types must be at beginning of string
+}
+
+- (void)testScanTypeIntoString
+{
+    // handles nil correctly
+    
+    
+    
+}
+
+- (void)testScanQualifiersIntoString
+{
+    // handles nil correctly
+    
+}
+
+- (void)testScanNonCompoundTypeIntoString
+{
+    // test handles nil correctly
+    
+}
+
+- (void)testScanBitfieldIntoString
+{
+    // test handles nil correctly
+    
+}
+
+- (void)testScanArrayIntoString
+{
+    // test handles nil correctly
+    
+}
+
+- (void)testScanIdentifierIntoString
+{
+    // test handles nil correctly
+    
+}
+
+- (void)testScanStructIntoString
+{
+    // test handles nil correctly
+    
+}
+
+- (void)testScanUnionIntoString
+{
+    // test handles nil correctly
+    
+}
+
+- (void)testScanPointerIntoString
+{
+    // preliminaries
+    NSString    *string     = @"^i";
+    NSScanner   *scanner    = [NSScanner scannerWithString:string];
+    NSString    *result     = nil;
+    
+    // test handles nil correctly
+    WO_TEST_TRUE([scanner scanPointerIntoString:nil]);
+    
+    // test scanning a pointer to something
+    [scanner setScanLocation:0];
+    WO_TEST_TRUE([scanner scanPointerIntoString:&result]);
+    WO_TEST_EQUAL(string, result);
+    
+    // test scanning a pointer to a pointer to something
+    string  = @"^^{WOStruct=fi@}";
+    scanner = [NSScanner scannerWithString:string];
+    result  = nil;
+    WO_TEST_TRUE([scanner scanPointerIntoString:&result]);
+    WO_TEST_EQUAL(string, result);
+    
+    // test against a non-pointer
+    string  = @"{WOStruct=^fi@}";
+    scanner = [NSScanner scannerWithString:string];
+    result  = nil;
+    WO_TEST_FALSE([scanner scanPointerIntoString:&result]);
+}
+
+- (void)testMethodSignatureParsing
+{
+    // these type strings taken from the runtime
+    NSScanner *scanner1 = [NSScanner scannerWithString:@"@8@0:4"];
+    NSScanner *scanner2 = [NSScanner scannerWithString:@"r*8@0:4"];
+    NSScanner *scanner3 = [NSScanner scannerWithString:@"^v8@0:4"];
+    NSScanner *scanner4 = [NSScanner scannerWithString:@"c12@0:4@8"];
+    NSScanner *scanner5 = [NSScanner scannerWithString:@"@12@0:4^{_NSZone=}8"];
+    NSScanner *scanner6 = [NSScanner scannerWithString:@"#8@0:4"];
+    NSScanner *scanner7 = [NSScanner scannerWithString:@"v20@0:4@8:12@16"];
+    NSScanner *scanner8 = [NSScanner scannerWithString:
+        (@"{_NSRect={_NSPoint=ff}{_NSSize=ff}}"
+         @"28@0:4{_NSRect={_NSPoint=ff}{_NSSize=ff}}8I24")];
+    NSScanner *scanner9 = [NSScanner scannerWithString:@"@12@0:4@8"];
+    
+    // WOTest_peekCharacter: and WOTest_scanCharacter:
+    unichar character;
+    [scanner1 WOTest_scanCharacter:&character];
+    WO_TEST_EQUAL(character, '@'); 
+    WO_TEST_EQUAL([scanner1 scanLocation], (unsigned)1);        // should advance
+    [scanner1 WOTest_peekCharacter:&character];
+    WO_TEST_EQUAL(character, '8');
+    WO_TEST_EQUAL([scanner1 scanLocation], (unsigned)1);        // should not
+    [scanner1 WOTest_scanCharacter:&character];
+    WO_TEST_EQUAL(character, '8');
+    [scanner1 setScanLocation:5];
+    [scanner1 WOTest_scanCharacter:&character];
+    WO_TEST_EQUAL(character, '4');
+    WO_TEST_FALSE([scanner1 WOTest_peekCharacter:&character]);  // atEnd
+    WO_TEST_FALSE([scanner1 WOTest_scanCharacter:&character]);  // atEnd
+    [scanner1 setScanLocation:0];                               // reset
+    
+    // using higher level scanning methods to parse: @8@0:4
+    NSString *type;
+    WO_TEST_TRUE([scanner1 WOTest_scanNonCompoundTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"@");
+    [scanner1 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_TRUE([scanner1 WOTest_scanTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"@");
+    [scanner1 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_TRUE([scanner1 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"@");
+    WO_TEST_FALSE([scanner1 WOTest_scanReturnTypeIntoString:&type]);
+    [scanner1 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_FALSE([scanner1 WOTest_scanQualifiersIntoString:&type]);
+    WO_TEST_FALSE([scanner1 WOTest_scanBitfieldIntoString:&type]);
+    WO_TEST_FALSE([scanner1 WOTest_scanArrayIntoString:&type]);
+    WO_TEST_FALSE([scanner1 WOTest_scanIdentifierIntoString:&type]);
+    WO_TEST_FALSE([scanner1 WOTest_scanStructIntoString:&type]);
+    WO_TEST_FALSE([scanner1 WOTest_scanUnionIntoString:&type]);
+    WO_TEST_FALSE([scanner1 scanPointerIntoString:&type]);
+    WO_TEST_EQUAL([scanner1 scanLocation], (unsigned)0);    // still at start
+    
+    // parsing: r*8@0:4
+    WO_TEST_FALSE([scanner2 WOTest_scanNonCompoundTypeIntoString:&type]);
+    WO_TEST_TRUE([scanner2 WOTest_scanTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"r*");
+    [scanner2 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_TRUE([scanner2 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"r*");
+    WO_TEST_FALSE([scanner2 WOTest_scanReturnTypeIntoString:&type]);
+    [scanner2 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_TRUE([scanner2 WOTest_scanQualifiersIntoString:&type]);
+    [scanner2 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_FALSE([scanner2 WOTest_scanBitfieldIntoString:&type]);
+    WO_TEST_FALSE([scanner2 WOTest_scanArrayIntoString:&type]);
+    WO_TEST_FALSE([scanner2 WOTest_scanIdentifierIntoString:&type]);
+    [scanner2 setScanLocation:0];                           // reset
+    type = nil;
+    WO_TEST_FALSE([scanner2 WOTest_scanStructIntoString:&type]);
+    WO_TEST_FALSE([scanner2 WOTest_scanUnionIntoString:&type]);
+    WO_TEST_FALSE([scanner2 scanPointerIntoString:&type]);
+    WO_TEST_EQUAL([scanner2 scanLocation], (unsigned)0);    // still at start
+    
+    // parse: ^v8@0:4
+    type = nil;
+    WO_TEST_TRUE([scanner3 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"^v");
+    
+    // parse: c12@0:4@8
+    type = nil;
+    WO_TEST_TRUE([scanner4 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"c");
+    
+    // parse: @12@0:4^{_NSZone=}8
+    type = nil;
+    WO_TEST_TRUE([scanner5 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"@");
+    
+    // parse: #8@0:4
+    type = nil;
+    WO_TEST_TRUE([scanner6 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"#");
+    
+    // parse: v20@0:4@8:12@16
+    type = nil;
+    WO_TEST_TRUE([scanner7 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"v");
+    
+    // parse: {_NSRect={_NSPoint=ff}{_NSSize=ff}}28@0:4{_NSRect={_NSPoint=f...
+    type = nil;
+    WO_TEST_TRUE([scanner8 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"{_NSRect={_NSPoint=ff}{_NSSize=ff}}");
+    
+    // parse: @12@0:4@8
+    type = nil;
+    WO_TEST_TRUE([scanner9 WOTest_scanReturnTypeIntoString:&type]);
+    WO_TEST_EQUAL(type, @"@");
+}
+
+@end
diff --git a/Tests/NSValueTests.h b/Tests/NSValueTests.h
new file mode 100644 (file)
index 0000000..907f4f2
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  NSValueTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 31 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSValueTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest.h"
+
+@interface NSValueTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/NSValueTests.m b/Tests/NSValueTests.m
new file mode 100644 (file)
index 0000000..beae135
--- /dev/null
@@ -0,0 +1,1484 @@
+//
+//  NSValueTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 31 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: NSValueTests.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "NSValueTests.h"
+
+#pragma mark -
+#pragma mark typedefs for testing size calculations
+
+typedef union WOSimpleStruct {
+    long                one;
+    short               two;
+} WOSimpleStruct;
+
+typedef struct WOSubstruct {
+    id                  foo;
+    long long           bar;
+    SEL                 fee;
+    unsigned short      fye;
+    NSRect              foe;
+    NSPoint             fum;
+} WOSubstruct; 
+
+typedef union WOSubunion {
+    NSPoint             a;
+    int                 b;
+    long long           c;
+    WOSubstruct         d;
+    double              e;
+} WOSubunion;
+
+typedef struct WOComplicatedStruct {
+    unsigned long long  a;
+    WOSubstruct         b;
+    NSRect              c;
+    Class               d;
+    char                e[100];
+    int                 f;
+    WOSubunion          g;
+    unsigned int        bitfield_a          : 3;
+    signed int          bitfield_b          : 5;
+    unsigned int        /* no identifier */ : 7;
+    unsigned int        bitfield_d          : 1;
+    long                h;
+} WOComplicatedStruct;
+
+typedef union WOSimpleUnion {
+    long                one;
+    short               two;
+} WOSimpleUnion;
+
+typedef union WOComplicatedUnion {
+    int                 alice;
+    id                  bob;
+    WOSubunion          eva;
+    WOComplicatedStruct mallory;
+    signed int          bitfield_a          : 2;
+    unsigned int        bitfield_b          : 3;
+    unsigned int        /* no identifier */ : 6;
+    unsigned int        bitfield_d          : 2;
+    float               xavier;
+    char                zach;               
+} WOComplicatedUnion;
+
+// anonymous struct (no identifier)
+typedef struct {
+    _Bool               a;
+    unsigned long long  b;
+    id                  c;
+} WOAnonymousStruct;
+
+// anonymous union (no identifier)
+typedef struct {
+    BOOL                a;
+    double              b;
+    id                  c[20];
+} WOAnonymousUnion;
+
+@implementation NSValueTests
+
+- (void)testAssumptions
+{
+    // numeric scalar types
+    _Bool               b;
+    char                c;
+    unsigned char       uc;
+    short               s;
+    unsigned short      us;
+    int                 i;
+    unsigned int        ui;
+    long                l;
+    unsigned long       ul;
+    long long           ll;
+    unsigned long long  ull;
+    float               f;
+    double              d;
+    
+    // pointers etc
+    id                  object;
+    Class               class;
+    SEL                 selector;
+    void                *pVoid;
+    char                *pChar;
+    const char          *pConstChar;
+    int                 *pInt;
+    
+    // compound types
+    WOComplicatedStruct aStruct;
+    WOComplicatedUnion  aUnion;
+    
+    // special case: function pointers
+    float               (*pFunction)(float, float, float);
+    
+    // test assumption that @encode() returns the same as @encode(typeof())
+    WO_TEST_EQ(@encode(_Bool), @encode(typeof(b)));
+    WO_TEST_EQ(@encode(char), @encode(typeof(c)));
+    WO_TEST_EQ(@encode(unsigned char), @encode(typeof(uc)));
+    WO_TEST_EQ(@encode(short), @encode(typeof(s)));
+    WO_TEST_EQ(@encode(unsigned short), @encode(typeof(us)));
+    WO_TEST_EQ(@encode(int), @encode(typeof(i)));
+    WO_TEST_EQ(@encode(unsigned int), @encode(typeof(ui)));
+    WO_TEST_EQ(@encode(long), @encode(typeof(l)));
+    WO_TEST_EQ(@encode(unsigned long), @encode(typeof(ul)));
+    WO_TEST_EQ(@encode(long long), @encode(typeof(ll)));
+    WO_TEST_EQ(@encode(unsigned long long), @encode(typeof(ull)));
+    WO_TEST_EQ(@encode(float), @encode(typeof(f)));
+    WO_TEST_EQ(@encode(double), @encode(typeof(d)));
+    WO_TEST_EQ(@encode(id), @encode(typeof(object)));
+    WO_TEST_EQ(@encode(Class), @encode(typeof(class)));
+    WO_TEST_EQ(@encode(SEL), @encode(typeof(selector)));
+    WO_TEST_EQ(@encode(void *), @encode(typeof(pVoid)));
+    WO_TEST_EQ(@encode(char *), @encode(typeof(pChar)));
+    WO_TEST_EQ(@encode(const char*), @encode(typeof(pConstChar)));
+    WO_TEST_EQ(@encode(int *), @encode(typeof(pInt)));
+    WO_TEST_EQ(@encode(WOComplicatedStruct), @encode(typeof(aStruct)));
+    WO_TEST_EQ(@encode(WOComplicatedUnion), @encode(typeof(aUnion)));
+    WO_TEST_EQ(@encode(float (*)(float, float, float)), @encode(typeof(pFunction)));
+}
+
+#pragma mark - 
+#pragma mark Creation and retrieval convenience methods
+
+- (void)testCharConvenienceMethods
+{
+    // test valueWithChar
+    char aChar = 'a';
+    NSValue *value = [NSValue WOTest_valueWithChar:aChar];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(char)); 
+    
+    // check that the value extracts as expected
+    char extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aChar);
+    
+    // also test WOTest_charValue method
+    WO_TEST_EQ([value WOTest_charValue], aChar);
+}
+
+- (void)testIntConvenienceMethods
+{
+    // test valueWithInt
+    int anInt = 20;
+    NSValue *value = [NSValue WOTest_valueWithInt:anInt];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(int)); 
+    
+    // check that the value extracts as expected
+    int extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, anInt);
+    
+    // also test WOTest_intValue method
+    WO_TEST_EQ([value WOTest_intValue], anInt);
+}
+
+- (void)testShortConvenienceMethods
+{
+    // test valueWithShort
+    short aShort = 100;
+    NSValue *value = [NSValue WOTest_valueWithShort:aShort];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(short)); 
+    
+    // check that the value extracts as expected
+    short extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aShort);
+    
+    // also test WOTest_shortValue method
+    WO_TEST_EQ([value WOTest_shortValue], aShort);
+}
+
+- (void)testLongConvenienceMethods
+{
+    // test valueWithLong
+    long aLong = 200;
+    NSValue *value = [NSValue WOTest_valueWithLong:aLong];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(long)); 
+    
+    // check that the value extracts as expected
+    long extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aLong);
+    
+    // also test WOTest_longValue method
+    WO_TEST_EQ([value WOTest_longValue], aLong);
+}
+
+- (void)testLongLongConvenienceMethods
+{
+    // test valueWithLongLong
+    long long aLongLong = 5;
+    NSValue *value = [NSValue WOTest_valueWithLongLong:aLongLong];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(long long)); 
+    
+    // check that the value extracts as expected
+    long long extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aLongLong);
+    
+    // also test WOTest_longLongValue method
+    WO_TEST_EQ([value WOTest_longLongValue], aLongLong);
+}
+
+- (void)testUnsignedCharConvenienceMethods
+{
+    // test valueWithUnsignedChar
+    unsigned char anUnsignedChar = 'a';
+    NSValue *value = [NSValue WOTest_valueWithUnsignedChar:anUnsignedChar];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(unsigned char)); 
+    
+    // check that the value extracts as expected
+    unsigned char extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, anUnsignedChar);
+    
+    // also test WOTest_unsignedCharValue method
+    WO_TEST_EQ([value WOTest_unsignedCharValue], anUnsignedChar);
+}
+
+- (void)testUnsignedIntConvenienceMethods
+{
+    // test valueWithUnsignedInt
+    unsigned int anUnsignedInt = 100;
+    NSValue *value = [NSValue WOTest_valueWithUnsignedInt:anUnsignedInt];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(unsigned int)); 
+    
+    // check that the value extracts as expected
+    unsigned int extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, anUnsignedInt);
+    
+    // also test WOTest_unsignedIntValue method
+    WO_TEST_EQ([value WOTest_unsignedIntValue], anUnsignedInt);
+}
+
+- (void)testUnsignedShortConvenienceMethods
+{
+    // test valueWithUnsignedShort
+    unsigned short anUnsignedShort = 40;
+    NSValue *value = [NSValue WOTest_valueWithUnsignedShort:anUnsignedShort];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(unsigned short)); 
+    
+    // check that the value extracts as expected
+    unsigned short extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, anUnsignedShort);
+    
+    // also test WOTest_unsignedShortValue method
+    WO_TEST_EQ([value WOTest_unsignedShortValue], anUnsignedShort);
+}
+
+- (void)testUnsignedLongConvenienceMethods
+{
+    // test valueWithUnsignedLong
+    unsigned long anUnsignedLong = 2000;
+    NSValue *value = [NSValue WOTest_valueWithUnsignedLong:anUnsignedLong];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(unsigned long)); 
+    
+    // check that the value extracts as expected
+    unsigned long extracted = 0;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, anUnsignedLong);
+    
+    // also test WOTest_unsignedLongValue method
+    WO_TEST_EQ([value WOTest_unsignedLongValue], anUnsignedLong);
+}
+
+- (void)testUnsignedLongLongConvenienceMethods
+{
+    // test valueWithUnsignedLongLong
+    unsigned long long anUnsignedLongLong = 20, extracted = 0;
+    NSValue *value = [NSValue WOTest_valueWithUnsignedLongLong:anUnsignedLongLong];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(unsigned long long)); 
+    
+    // check that the value extracts as expected
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, anUnsignedLongLong);
+    
+    // also test WOTest_unsignedLongLongValue method
+    WO_TEST_EQ([value WOTest_unsignedLongLongValue], anUnsignedLongLong);
+}
+
+- (void)testFloatConvenienceMethods
+{
+    // test valueWithFloat
+    float aFloat = 10.0, extracted = 0.0;
+    NSValue *value = [NSValue WOTest_valueWithFloat:aFloat];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(float)); 
+    
+    // check that the value extracts as expected
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aFloat);
+    
+    // also test WOTest_floatValue method
+    WO_TEST_EQ([value WOTest_floatValue], aFloat);
+}
+
+- (void)testDoubleConvenienceMethods
+{
+    double aDouble = 20.0, extracted = 0.0;
+    NSValue *value = [NSValue WOTest_valueWithDouble:aDouble];
+    WO_TEST_EQ([value objCType], @encode(double));  // check type
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aDouble);                 // check extracts
+    WO_TEST_EQ([value WOTest_doubleValue], aDouble);       // also test WOTest_doubleValue
+}
+
+- (void)testC99BoolConvenienceMethods
+{
+    _Bool aC99Bool = 1, extracted = 0;
+    NSValue *value = [NSValue WOTest_valueWithC99Bool:aC99Bool];
+    WO_TEST_EQ([value objCType], @encode(_Bool));   // check type
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, aC99Bool);                // check extracts
+    WO_TEST_EQ([value WOTest_C99BoolValue], aC99Bool);     // also test WOTest_C99BoolValue
+}
+
+- (void)testConstantCharacterStringConvenienceMethods
+{
+    // test valueWithObject
+    const char *string = "foo";
+    NSValue *value = [NSValue WOTest_valueWithConstantCharacterString:string];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(const char *)); 
+    
+    // check that the value extracts as expected
+    const char *extracted;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, string);
+    
+    // also test WOTest_constantCharacterStringValue method
+    WO_TEST_EQ([value WOTest_constantCharacterStringValue], string);
+}
+
+- (void)testCharacterStringConvenienceMethods
+{
+    // test valueWithObject
+    char *string = "foo";
+    NSValue *value = [NSValue WOTest_valueWithCharacterString:string];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(char *)); 
+    
+    // check that the value extracts as expected
+    char *extracted = NULL;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, string);
+    
+    // also test WOTest_characterStringValue method
+    WO_TEST_EQ([value WOTest_characterStringValue], string);
+}
+
+- (void)testObjectConvenienceMethods
+{
+    // test valueWithObject
+    NSValue *value = [NSValue WOTest_valueWithObject:self];
+
+    // check type
+    WO_TEST_EQ([value objCType], @encode(id)); 
+    
+    // check that the value extracts as expected
+    id extracted = nil;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, self);
+    
+    // also test WOTest_objectValue method
+    WO_TEST_EQ([value WOTest_objectValue], self);
+}
+- (void)testClassConvenienceMethods
+{
+    // test valueWithClass
+    NSValue *value = [NSValue WOTest_valueWithClass:[self class]];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(Class));
+    
+    // check that the value extracts as expected
+    Class extracted = NULL;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, [self class]);
+    
+    // also test WOTest_classValue method
+    WO_TEST_EQ([value WOTest_classValue], [self class]);
+}
+
+- (void)testSelectorConvenienceMethods
+{
+    // test valueWithSelector
+    NSValue *value = [NSValue WOTest_valueWithSelector:_cmd];
+    
+    // check type
+    WO_TEST_EQ([value objCType], @encode(SEL));
+    
+    // check that the value extracts as expected
+    SEL extracted = NULL;
+    [value getValue:&extracted];
+    WO_TEST_EQ(extracted, _cmd);
+    
+    // also test WOTest_selectorValue method
+    WO_TEST_EQ([value WOTest_selectorValue], _cmd);
+}
+
+- (void)testSizeCalculationsForScalars
+{
+    // preliminaries
+    NSValue *value = nil;
+    
+    // test with int
+    int i = 0;
+    value = [NSValue value:&i withObjCType:@encode(int)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(int));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(int));
+
+    // test with unsigned int
+    unsigned int ui = 0;
+    value = [NSValue value:&ui withObjCType:@encode(unsigned int)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(unsigned int));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(unsigned int));
+    
+    // test with short
+    short s = 0;
+    value = [NSValue value:&s withObjCType:@encode(short)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(short));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(short));
+
+    // test with unsigned short
+    unsigned short us = 0;
+    value = [NSValue value:&us withObjCType:@encode(unsigned short)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(unsigned short));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(unsigned short));
+    
+    // test with long
+    long l = 0;
+    value = [NSValue value:&l withObjCType:@encode(int)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(int));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(int));
+    
+    // test with unsigned long
+    unsigned long ul = 0;
+    value = [NSValue value:&ul withObjCType:@encode(unsigned long)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(unsigned long));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(unsigned long));
+    
+    // test with long long
+    long long ll = 0;
+    value = [NSValue value:&ll withObjCType:@encode(long long)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(long long));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(long long));
+    
+    // test with unsigned long long
+    unsigned long long ull = 0;
+    value = [NSValue value:&ull withObjCType:@encode(unsigned long long)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(unsigned long long));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(unsigned long long));
+    
+    // test with float
+    float f = 0;
+    value = [NSValue value:&f withObjCType:@encode(float)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(float));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(float));
+    
+    // test with double
+    double d = 0;
+    value = [NSValue value:&d withObjCType:@encode(double)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(double));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(double));
+
+    // test with char
+    char c = 0;
+    value = [NSValue value:&c withObjCType:@encode(char)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(char));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(char));
+    
+    // test with unsigned char
+    unsigned char uc = 0;
+    value = [NSValue value:&uc withObjCType:@encode(unsigned char)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(unsigned char));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(unsigned char));
+    
+    // test with C99 _Bool
+    _Bool b = 0;
+    value = [NSValue value:&b withObjCType:@encode(_Bool)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(_Bool));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(_Bool));
+}   
+    
+- (void)testSizeCalculationsForStructs
+{
+    // preliminaries
+    NSValue *value = nil;
+
+    // test with NSRange 
+    NSRange range = NSMakeRange(100, 100);
+    value = [NSValue valueWithRange:range];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(NSRange));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(NSRange));
+    
+    // test with NSPoint
+    NSPoint point;
+    point.x = 100.0f; // can't use NSMakePoint (warnings on Intel release builds)
+    point.y = 100.0f;
+    value = [NSValue valueWithPoint:point];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(NSPoint));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(NSPoint));
+    
+    // test with NSRect
+    NSRect rect = NSMakeRect(100.0, 100.0, 100.0, 100.0);
+    value = [NSValue valueWithRect:rect];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(NSRect));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(NSRect));
+    
+    // test with simple custom struct
+    WOSimpleStruct simple;
+    value = [NSValue valueWithBytes:&simple objCType:@encode(WOSimpleStruct)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOSimpleStruct));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOSimpleStruct));
+    
+    // test with complicated custom structs
+    WOSubstruct substruct;
+    value = [NSValue valueWithBytes:&substruct objCType:@encode(WOSubstruct)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOSubstruct));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOSubstruct));
+    
+    WOComplicatedStruct complicated;
+    value = [NSValue valueWithBytes:&complicated objCType:@encode(WOComplicatedStruct)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOComplicatedStruct));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOComplicatedStruct));
+    
+    // test with anonymous struct
+    WOAnonymousStruct anonymous;
+    value = [NSValue valueWithBytes:&anonymous objCType:@encode(WOAnonymousStruct)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOAnonymousStruct));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOAnonymousStruct));
+}
+
+- (void)testSizeCalculationsForUnions
+{
+    // preliminaries
+    NSValue *value = nil;
+
+    // test with WOSimpleUnion
+    WOSimpleUnion simple;
+    value = [NSValue valueWithBytes:&simple objCType:@encode(WOSimpleUnion)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOSimpleUnion));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOSimpleUnion));
+    
+    // test with WOSubunion
+    WOSubunion subunion;
+    value = [NSValue valueWithBytes:&subunion objCType:@encode(WOSubunion)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOSubunion));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOSubunion));
+    
+    // test with WOComplicatedUnion
+    WOComplicatedUnion complicated;
+    value = [NSValue valueWithBytes:&complicated objCType:@encode(WOComplicatedUnion)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOComplicatedUnion));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOComplicatedUnion));    
+
+    // test with anonymous union
+    WOAnonymousUnion anonymous;
+    value = [NSValue valueWithBytes:&anonymous objCType:@encode(WOAnonymousUnion)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(WOAnonymousUnion));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(WOAnonymousUnion));
+}    
+
+// tests fo id, Class, SEL (really just pointers)
+- (void)testSizeCalculationsForObjects
+{
+    // preliminaries
+    NSValue *value = nil;
+
+    // test with id
+    id i = self;
+    value = [NSValue valueWithBytes:&i objCType:@encode(id)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(id));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(id));
+    
+    // test with Class 
+    Class c = [self class];
+    value = [NSValue valueWithBytes:&c objCType:@encode(Class)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(Class));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(Class));
+    
+    // test with SEL
+    SEL s = _cmd;
+    value = [NSValue valueWithBytes:&s objCType:@encode(SEL)];
+    WO_TEST_GTE([NSValue WOTest_sizeForType:[value WOTest_objCTypeString]], sizeof(SEL));
+    WO_TEST_GTE([value WOTest_bufferSize], sizeof(SEL));
+}    
+
+- (void)testSizeCalculationsForPointers
+{
+    // preliminaries
+    NSValue *value = nil;
+
+    // test with function pointers
+    
+    
+    
+}    
+
+- (void)testSizeCalculationsForArrays
+{
+    // preliminaries
+    NSValue *value = nil;
+
+}
+
+- (void)testTypeStringMethods
+{
+    // preliminaries
+    NSString            *typeString;
+    
+    // numeric scalar types
+    _Bool               b;
+    char                c;
+    unsigned char       uc;
+    short               s;
+    unsigned short      us;
+    int                 i;
+    unsigned int        ui;
+    long                l;
+    unsigned long       ul;
+    long long           ll;
+    unsigned long long  ull;
+    float               f;
+    double              d;
+
+    // pointers etc
+    id                  object;
+    Class               class;
+    SEL                 selector;
+    void                *pVoid;
+    char                *pChar;
+    const char          *pConstChar;
+    int                 *pInt;
+        
+    // compound types
+    WOComplicatedStruct aStruct;
+    WOComplicatedUnion  aUnion;
+    char                anArray[100];
+
+    // special case: function pointers
+    float               (*pFunction)(float, float, float);
+    
+    // test type is _Bool
+    typeString = [NSString stringWithUTF8String:@encode(typeof(b))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is char
+    typeString = [NSString stringWithUTF8String:@encode(typeof(c))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is unsigned char
+    typeString = [NSString stringWithUTF8String:@encode(typeof(uc))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is short
+    typeString = [NSString stringWithUTF8String:@encode(typeof(s))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is unsigned short
+    typeString = [NSString stringWithUTF8String:@encode(typeof(us))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is int
+    typeString = [NSString stringWithUTF8String:@encode(typeof(i))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is unsigned int
+    typeString = [NSString stringWithUTF8String:@encode(typeof(ui))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is long
+    typeString = [NSString stringWithUTF8String:@encode(typeof(l))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is unsigned long
+    typeString = [NSString stringWithUTF8String:@encode(typeof(ul))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is long long
+    typeString = [NSString stringWithUTF8String:@encode(typeof(ll))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is unsigned long long
+    typeString = [NSString stringWithUTF8String:@encode(typeof(ull))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is float
+    typeString = [NSString stringWithUTF8String:@encode(typeof(f))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is double
+    typeString = [NSString stringWithUTF8String:@encode(typeof(d))];
+    WO_TEST([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is id
+    typeString = [NSString stringWithUTF8String:@encode(typeof(object))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is Class
+    typeString = [NSString stringWithUTF8String:@encode(typeof(class))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is SEL
+    typeString = [NSString stringWithUTF8String:@encode(typeof(selector))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is pointer to void
+    typeString = [NSString stringWithUTF8String:@encode(typeof(pVoid))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is character string
+    typeString = [NSString stringWithUTF8String:@encode(typeof(pChar))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is constant character string
+    typeString = [NSString stringWithUTF8String:@encode(typeof(pConstChar))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is pointer to int
+    typeString = [NSString stringWithUTF8String:@encode(typeof(pInt))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is struct
+    typeString = [NSString stringWithUTF8String:@encode(typeof(aStruct))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is union
+    typeString = [NSString stringWithUTF8String:@encode(typeof(aUnion))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is array
+    typeString = [NSString stringWithUTF8String:@encode(typeof(anArray))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+    
+    // test type is a function pointer ("^?")
+    typeString = [NSString stringWithUTF8String:@encode(typeof(pFunction))];
+    WO_TEST_FALSE([NSValue WOTest_typeIsNumericScalar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCompound:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedChar:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedInt:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedShort:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnsignedLongLong:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsFloat:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsDouble:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsC99Bool:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsVoid:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsConstantCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsCharacterString:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsObject:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsClass:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsSelector:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsPointerToVoid:typeString]);
+    WO_TEST([NSValue WOTest_typeIsPointer:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsArray:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsStruct:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnion:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsBitfield:typeString]);
+    WO_TEST_FALSE([NSValue WOTest_typeIsUnknown:typeString]);
+}
+
+- (void)testBugBadCastsToUnsignedChar
+{
+    // preliminaries
+    BOOL warns = [WO_TEST_SHARED_INSTANCE warnsAboutSignComparisons];
+    [WO_TEST_SHARED_INSTANCE setWarnsAboutSignComparisons:NO];
+    
+    // due to some bad casts, comparing values too large to fit in an unsigned char was failing in specific cases
+    // the signed value was being cast to unsigned char in these cases, thus getting truncated
+    // this meant that tests would fail with messages like "expected (int)1000, got (unsigned int)1000"
+
+    WO_TEST_EQ(1000U, 1000);            // this is the unsigned int versus int case
+    WO_TEST_EQ(1000UL, 1000);           // same bug for unsigned long compared with int
+
+    unsigned long long ullValue = 1000;
+    WO_TEST_EQ(ullValue, 1000);         // same bug for unsigned long long compared with int
+    
+    short shortValue = 1000;
+    WO_TEST_EQ(1000U, shortValue);      // same bug for unsigned compared with short
+    WO_TEST_EQ(1000UL, shortValue);     // and unsigned long compared with short
+    WO_TEST_EQ(ullValue, shortValue);   // and unsigned long long compared with short
+    
+    WO_TEST_EQ(1000U, 1000L);           // and unsigned int versus long
+    WO_TEST_EQ(1000UL, 1000L);          // and unsigned long compared with long
+    WO_TEST_EQ(ullValue, 1000L);        // and unsigned long long compared with long
+    
+    long long llValue = 1000;
+    WO_TEST_EQ(ullValue, llValue);      // and unsigned long long compared with long long
+    
+    // cleanup
+    [WO_TEST_SHARED_INSTANCE setWarnsAboutSignComparisons:warns];
+}
+
+@end
diff --git a/Tests/WOClassMockTests.h b/Tests/WOClassMockTests.h
new file mode 100644 (file)
index 0000000..454f331
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  WOClassMockTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 10 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOClassMockTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest.h"
+
+@interface WOClassMockTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/WOClassMockTests.m b/Tests/WOClassMockTests.m
new file mode 100644 (file)
index 0000000..648d593
--- /dev/null
@@ -0,0 +1,68 @@
+//
+//  WOClassMockTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 10 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOClassMockTests.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "WOClassMockTests.h"
+
+@implementation WOClassMockTests
+
+- (void)testInitWithClass
+{
+    // preliminaries
+    WOClassMock *mock = nil;
+    
+    // should throw if passed NULL
+    mock = [WOClassMock alloc];
+    WO_TEST_THROWS([mock initWithClass:NULL]);
+    [mock release];
+    
+    // should throw if passed non-class pointer
+    mock = [WOClassMock alloc];
+    WO_TEST_THROWS([mock initWithClass:(Class)self]);
+    [mock release];
+    
+    // otherwise should work
+    WO_TEST_DOES_NOT_THROW
+        ([[[WOClassMock alloc] initWithClass:[self class]] autorelease]);
+    
+    // should throw if passed a meta class
+    Class class     = [NSString class];
+    Class metaclass = object_getClass(class);
+    mock = [WOClassMock alloc];
+    WO_TEST_THROWS([mock initWithClass:metaclass]);
+    [mock release];
+}
+
+- (void)testAccept
+{
+    // should work with class methods
+    id mock = [WOClassMock mockForClass:[NSString class]];
+    WO_TEST_DOES_NOT_THROW([[mock accept] stringWithString:@"foo"]);
+    WO_TEST_DOES_NOT_THROW([mock stringWithString:@"foo"]);
+    
+    // should throw for instance methods
+    WO_TEST_THROWS([[mock accept] lowercaseString]);
+    
+    // should throw for unknown methods
+    WO_TEST_THROWS(objc_msgSend([mock accept], @selector(foobar)));
+}
+
+@end
diff --git a/Tests/WOMockTests.h b/Tests/WOMockTests.h
new file mode 100644 (file)
index 0000000..99493c7
--- /dev/null
@@ -0,0 +1,22 @@
+//
+//  WOMockTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 10 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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
+//  in the accompanying file, "LICENSE.txt", for more details.
+//
+//  $Id: WOMockTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest.h"
+
+@interface WOMockTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/WOMockTests.m b/Tests/WOMockTests.m
new file mode 100644 (file)
index 0000000..2f09d2e
--- /dev/null
@@ -0,0 +1,180 @@
+//
+//  WOMockTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 10 February 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOMockTests.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "WOMockTests.h"
+
+@implementation WOMockTests
+
+// no real tests in this method: examples of how to use mocks without warnings
+- (void)testExample
+{
+    // using mocks in the following way will cause a compiler warning:
+    //WOObjectMock *mock = [WOMock mockForObjectClass:[NSString class]];
+    //[[mock expect] lowercaseString];
+    //[mock lowercaseString]; // "warning: 'WOObjectMock' may not respond to '-lowercaseString'"
+    //[mock verify];        
+    
+    // one way of avoiding compiler warnings when using mocks: cast to id
+    WOObjectMock *mock1 = [WOMock mockForObjectClass:[NSString class]];
+    [[mock1 expect] lowercaseString];
+    [(id)mock1 lowercaseString];
+    [mock1 verify];
+    
+    // another way of avoiding compiler warnings: use id type from beginning
+    id mock2 = [WOMock mockForObjectClass:[NSString class]];
+    [[mock2 expect] lowercaseString];
+    [mock2 lowercaseString];
+    [mock2 verify];
+    
+    // another way: cast to mocked class
+    NSString *mock3 = [WOMock mockForObjectClass:[NSString class]];
+    [[(WOMock *)mock3 expect] lowercaseString];
+    [mock3 lowercaseString];
+    [(WOMock *)mock3 verify];
+    
+    // another way: alternative way of casting to mocked class
+    WOObjectMock *mock4 = [WOMock mockForObjectClass:[NSString class]];
+    [[mock4 expect] lowercaseString];
+    [(NSString *)mock4 lowercaseString];
+    [mock4 verify];
+    
+    // yet another way: use objc_msgSend
+    WOObjectMock *mock5 = [WOMock mockForObjectClass:[NSString class]];
+    [[mock5 expect] lowercaseString];
+    objc_msgSend(mock5, @selector(lowercaseString));
+    [mock5 verify];
+}
+
+- (void)testMockForObjectClass
+{
+    WOObjectMock *mock = [WOMock mockForObjectClass:[self class]];
+    
+    // make sure WOObjectMock class is returned
+    WO_TEST_EQ([mock class], [WOObjectMock class]);
+    
+    // make sure mocked class is correctly set
+    WO_TEST_EQ([mock mockedClass], [self class]);
+    
+    // should throw exception instead of entering infinite loop
+    WO_TEST_THROWS([WOObjectMock mockForObjectClass:[self class]]);
+}
+
+- (void)testMockForClass
+{
+    WOClassMock *mock = [WOMock mockForClass:[self class]];
+    
+    // make sure WOClassMock class is returned
+    WO_TEST_EQ([mock class], [WOClassMock class]);
+    
+    // make sure mocked class is correctly set
+    Class class     = [self class];
+    Class metaclass = object_getClass(class);
+    WO_TEST_EQ([mock mockedClass], metaclass);
+    
+    // should throw exception instead of entering infinite loop
+    // cannot test this because subclass implements that method directly
+    //WO_TEST_THROWS([WOClassMock mockForClass:[self class]]);
+}
+
+- (void)testMockForProtocol
+{
+    WOProtocolMock *mock = [WOMock mockForProtocol:@protocol(WOTest)];
+    
+    // make sure WOProtocolMock class is returned
+    WO_TEST_EQ([mock class], [WOProtocolMock class]);
+    
+    // make sure mocked protocol is correctly set
+    WO_TEST_EQ([mock mockedProtocol], @protocol(WOTest));
+    
+    // should throw exception instead of entering infinite loop
+    // cannot test this because subclass implements that method directly
+    //WO_TEST_THROWS([WOProtocolMock mockForProtocol:@protocol(WOTest)]);
+}
+
+- (void)testInitWithObjectClass
+{
+    WOObjectMock *mock = 
+        [[[WOMock alloc] initWithObjectClass:[self class]] autorelease];
+    
+    // make sure WOObjectMock class is returned
+    WO_TEST_EQ([mock class], [WOObjectMock class]);
+    
+    // make sure mocked class is correctly set
+    WO_TEST_EQ([mock mockedClass], [self class]);
+    
+    // should throw exception instead of entering infinite loop
+    mock = [WOObjectMock alloc];
+    WO_TEST_THROWS([mock initWithObjectClass:[self class]]);
+    [mock release];
+}
+
+- (void)testInitWithClass
+{
+    WOClassMock *mock = 
+        [[[WOMock alloc] initWithClass:[self class]] autorelease];
+    
+    // make sure WOClassMock class is returned
+    WO_TEST_EQ([mock class], [WOClassMock class]);
+    
+    // make sure mocked class is correctly set
+    Class class     = [self class];
+    Class metaclass = object_getClass(class);
+    WO_TEST_EQ([mock mockedClass], metaclass);
+    
+    // should throw exception instead of entering infinite loop
+    // cannot test this because subclass implements that method directly
+    //mock = [WOClassMock alloc];
+    //WO_TEST_THROWS([mock initWithClass:[self class]]);
+    //[mock release];
+}
+
+- (void)testInitWithProtocol
+{
+    WOProtocolMock *mock = 
+        [[[WOMock alloc] initWithProtocol:@protocol(WOTest)] autorelease];
+    
+    // make sure WOProtocolMock class is returned
+    WO_TEST_EQ([mock class], [WOProtocolMock class]);
+    
+    // make sure mocked protocol is correctly set
+    WO_TEST_EQ([mock mockedProtocol], @protocol(WOTest));
+    
+    // should throw exception instead of entering infinite loop
+    // cannot test this because subclass implements that method directly
+    //mock = [WOProtocolMock alloc];
+    //WO_TEST_THROWS([mock initWithProtocol:@protocol(WOTest)]);
+    //[mock release];
+}
+
+- (void)testRecordingMethods
+{
+    // all recording methods should throw an exception (use subclasses instead)
+    WOMock *mock = [[[WOMock alloc] init] autorelease];
+    WO_TEST_THROWS([mock reject]);
+    WO_TEST_THROWS([mock expectInOrder]);
+    WO_TEST_THROWS([mock expectOnce]);
+    WO_TEST_THROWS([mock expect]);
+    WO_TEST_THROWS([mock acceptOnce]);
+    WO_TEST_THROWS([mock accept]);
+}
+
+@end
diff --git a/Tests/WOMultithreadedCrashTests.h b/Tests/WOMultithreadedCrashTests.h
new file mode 100644 (file)
index 0000000..8e2750c
--- /dev/null
@@ -0,0 +1,29 @@
+//
+//  WOMultithreadedCrashTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 26 November 2006.
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOMultithreadedCrashTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Cocoa/Cocoa.h>
+#import "WOTest/WOTest.h"
+
+@interface WOMultithreadedCrashTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/WOMultithreadedCrashTests.m b/Tests/WOMultithreadedCrashTests.m
new file mode 100644 (file)
index 0000000..650b506
--- /dev/null
@@ -0,0 +1,68 @@
+//
+//  WOMultithreadedCrashTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 26 November 2006.
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOMultithreadedCrashTests.m 208 2007-07-07 19:02:28Z wincent $
+
+// class header
+#import "WOMultithreadedCrashTests.h"
+
+@implementation WOMultithreadedCrashTests
+
+- (void)preflight
+{
+    [WO_TEST_SHARED_INSTANCE setExpectLowLevelExceptions:YES];
+}
+
+- (void)postflight
+{
+    [WO_TEST_SHARED_INSTANCE setExpectLowLevelExceptions:YES];
+}
+
+#pragma mark -
+#pragma mark Helper methods
+
+- (void)secondaryThreadCrasher:(id)sender
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    
+    // Apple's InstallExceptionHandler doesn't catch crashes on secondary threads
+    // - in the case of Carbon threads a separate handler is automatically installed for each thread
+    // - pthreads and Cocoa threads don't get extra handlers automatically installed
+    // - these are per-thread handlers because the per-process handler port is used by the crash reporter
+    
+    // TODO: write a Mach per-process exception handler
+    return;                                                     // don't continue (would crash WOTestRunner)
+    
+    WO_TEST_PASS;                                               // force update of "lastKnownLocation"
+    id *object = NULL;                                          // cause a crash, but WOTest should keep running
+    *object = @"foo";                                           // SIGBUS here
+    WO_TEST_FAIL;                                               // this line never reached
+    [pool release];                                             // nor this one, but pools are in a stack no problem
+}
+
+#pragma mark -
+#pragma mark Test methods
+
+- (void)testCrashOnSecondaryThread
+{
+    [WO_TEST_SHARED_INSTANCE setExpectLowLevelExceptions:YES];  // will be reset to NO in preflight prior to next method
+    [NSThread detachNewThreadSelector:@selector(secondaryThreadCrasher:) toTarget:self withObject:self];
+}
+
+@end
diff --git a/Tests/WOObjectMockTests.h b/Tests/WOObjectMockTests.h
new file mode 100644 (file)
index 0000000..e2ca340
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  WOObjectMockTests.h
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 29 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOObjectMockTests.h 208 2007-07-07 19:02:28Z wincent $
+
+#import <Foundation/Foundation.h>
+#import "WOTest/WOTest.h"
+
+@interface WOObjectMockTests : NSObject <WOTest> {
+
+}
+
+@end
diff --git a/Tests/WOObjectMockTests.m b/Tests/WOObjectMockTests.m
new file mode 100644 (file)
index 0000000..52d097a
--- /dev/null
@@ -0,0 +1,541 @@
+//
+//  WOObjectMockTests.m
+//  WOTest
+//
+//  Created by Wincent Colaiuta on 29 January 2006.
+//
+//  Copyright 2006-2007 Wincent Colaiuta.
+//  This program 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 program 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.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+//  $Id: WOObjectMockTests.m 208 2007-07-07 19:02:28Z wincent $
+
+#import "WOObjectMockTests.h"
+
+@implementation WOObjectMockTests
+
+#pragma mark - 
+#pragma mark High-level tests
+
+- (void)testMockForClass
+{
+    // should throw if passed NULL
+    WO_TEST_THROWS([WOObjectMock mockForClass:NULL]);
+    
+    // should throw if passed non-class pointer
+    WO_TEST_THROWS([WOObjectMock mockForClass:(Class)self]);
+    
+    // otherwise should work
+    WO_TEST_DOES_NOT_THROW([WOObjectMock mockForClass:[self class]]);
+    
+    // should throw if passed a meta class
+    Class class     = [NSString class];
+    Class metaclass = object_getClass(class);
+    WO_TEST_THROWS([WOObjectMock mockForClass:metaclass]);
+}
+
+- (void)testInitWithClass
+{
+    // preliminaries
+    WOObjectMock *mock = nil;
+    
+    // should throw if passed NULL
+    mock = [WOObjectMock alloc];
+    WO_TEST_THROWS([mock initWithClass:NULL]);
+    [mock release];
+    
+    // should throw if passed non-class pointer
+    mock = [WOObjectMock alloc];
+    WO_TEST_THROWS([mock initWithClass:(Class)self]);
+    [mock release];
+    
+    // otherwise should work
+    WO_TEST_DOES_NOT_THROW
+        ([[[WOObjectMock alloc] initWithClass:[self class]] autorelease]);
+    
+    // should throw if passed a meta class
+    Class class     = [NSString class];
+    Class metaclass = object_getClass(class);
+    mock = [WOObjectMock alloc];
+    WO_TEST_THROWS([mock initWithClass:metaclass]);
+    [mock release];
+}
+
+- (void)testMockExpectInOrder
+{
+    // basic test
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    
+    [[mock expectInOrder] lowercaseString];
+    [[mock expectInOrder] uppercaseString];
+    [[mock expectInOrder] stringByExpandingTildeInPath];
+    [[mock expectInOrder] uppercaseString];
+    
+    [mock lowercaseString];
+    [mock uppercaseString];
+    [mock stringByExpandingTildeInPath];
+    [mock uppercaseString];
+    
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // repeat test: this time omit one of the expected methods
+    [mock clear];
+    [[mock expectInOrder] lowercaseString];
+    [[mock expectInOrder] uppercaseString];
+    [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
+    [[mock expectInOrder] uppercaseString];
+    
+    [mock lowercaseString];
+    [mock uppercaseString];
+    [mock stringByAbbreviatingWithTildeInPath];
+    
+    WO_TEST_THROWS([mock verify]);
+
+    // repeat test: this time invoke methods in wrong order
+    [mock clear];
+    [[mock expectInOrder] lowercaseString];
+    [[mock expectInOrder] uppercaseString];
+    [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
+    [[mock expectInOrder] uppercaseString];
+    
+    [mock lowercaseString];
+    WO_TEST_THROWS([mock stringByAbbreviatingWithTildeInPath]);
+    
+    // test with arguments
+    [mock clear];
+    [[mock expectInOrder] stringByAppendingFormat:@"foobar"];
+    [[mock expectInOrder] uppercaseString];
+    [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
+    [[mock expectInOrder] uppercaseString];
+    
+    [mock stringByAppendingFormat:@"foobar"];
+    [mock uppercaseString];
+    [mock stringByAbbreviatingWithTildeInPath];
+    [mock uppercaseString];
+    
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // repeat test: this time pass unexpected argument
+    [mock clear];
+    [[mock expectInOrder] stringByAppendingFormat:@"foobar"];
+    [[mock expectInOrder] uppercaseString];
+    [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
+    [[mock expectInOrder] uppercaseString];
+    
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"other"]);
+    
+    // test with object return value
+    NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
+    [mock clear];
+    [[[mock expectInOrder] returning:value] stringByAppendingString:@"bar"];
+    WO_TEST_EQ(@"foobar", [mock stringByAppendingString:@"bar"]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // test with scalar return value
+    value = [NSValue WOTest_valueWithUnsignedInt:6];
+    [mock clear];
+    [[[mock expectInOrder] returning:value] length];
+    WO_TEST_EQ((unsigned int)6, [mock length]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // test with raising exception
+    [mock clear];
+    [[[mock expectInOrder] raising:self] lowercaseString];
+    WO_TEST_THROWS([mock lowercaseString]);
+    
+    // test raising named exception
+    NSException *exception = [NSException exceptionWithName:@"Robert" 
+                                                     reason:@"Robert exception"
+                                                   userInfo:nil];
+    [mock clear];
+    [[[mock expectInOrder] raising:exception] lowercaseString];
+    WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
+}
+
+- (void)testMockExpectOnce
+{
+    // basic test
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    [[mock expectOnce] lowercaseString];
+    WO_TEST_THROWS([mock verify]); // was bug
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+    WO_TEST_THROWS([mock lowercaseString]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // test with arguments
+    [mock clear];
+    [[mock expectOnce] stringByAppendingFormat:@"foo"];
+    WO_TEST_THROWS([mock verify]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // repeat test: this time pass unexpected argument
+    [mock clear];
+    [[mock expectOnce] stringByAppendingFormat:@"foo"];
+    WO_TEST_THROWS([mock verify]);
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
+    WO_TEST_THROWS([mock verify]);
+    
+    // test with return value
+    NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
+    [mock clear];
+    [[[mock expectOnce] returning:value] stringByAppendingString:@"bar"];
+    WO_TEST_THROWS([mock verify]);
+    WO_TEST_EQ(@"foobar", [mock stringByAppendingString:@"bar"]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    // test with raising exception
+    [mock clear];
+    [[[mock expectOnce] raising:self] lowercaseString];
+    WO_TEST_THROWS([mock verify]);
+    WO_TEST_THROWS([mock lowercaseString]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // test raising named exception
+    NSException *exception = [NSException exceptionWithName:@"Robert" 
+                                                     reason:@"Robert exception"
+                                                   userInfo:nil];
+    [mock clear];
+    [[[mock expectOnce] raising:exception] lowercaseString];
+    WO_TEST_THROWS([mock verify]);
+    WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+}
+
+- (void)testMockExpect
+{
+    // basic test
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    [[mock expect] lowercaseString];
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    WO_TEST_THROWS([mock uppercaseString]);
+    
+    // test with return value (no parameters)
+    NSValue *value = [NSValue WOTest_valueWithObject:@".txt"];
+    [mock clear];
+    [[[mock expect] returning:value] stringByAppendingPathExtension:@"txt"];
+    WO_TEST_EQ([mock stringByAppendingPathExtension:@"txt"], @".txt");
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    
+    // test with parameters and return value
+    [mock clear];
+    value = [NSValue WOTest_valueWithObject:@"foo.txt"];
+    [[[mock expect] returning:value] stringByAppendingFormat:@".txt"];
+    WO_TEST_EQ([mock stringByAppendingFormat:@".txt"], @"foo.txt");
+    WO_TEST_THROWS([mock stringByAppendingFormat:@".m"]); // wrong argument
+    
+    // test with parameters but without return value
+    [mock clear];
+    [[mock expect] stringByAppendingFormat:@".mov"];
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@".mov"]);
+    WO_TEST_THROWS([mock stringByAppendingFormat:@".mpeg"]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@".mov"]);
+}
+
+- (void)testMockAcceptOnce
+{
+    // basic test
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    [[mock acceptOnce] lowercaseString];
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+    WO_TEST_THROWS([mock lowercaseString]);        
+    
+    // test with arguments
+    [mock clear];
+    [[mock acceptOnce] stringByAppendingFormat:@"foo"];
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"foo"]);
+    
+    // repeat test: this time pass unexpected argument
+    [mock clear];
+    [[mock acceptOnce] stringByAppendingFormat:@"foo"];
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"foo"]);          
+    
+    // test with return value
+    NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
+    [mock clear];
+    [[[mock acceptOnce] returning:value] stringByAppendingFormat:@"bar"];
+    WO_TEST_EQ([mock stringByAppendingFormat:@"bar"], @"foobar");    
+    WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
+    
+    // test raising exception
+    [mock clear];
+    [[[mock acceptOnce] raising:@"foo"] lowercaseString];
+    WO_TEST_THROWS([mock lowercaseString]);
+    
+    // test raising named exception
+    NSException *exception = [NSException exceptionWithName:@"Robert" 
+                                                     reason:@"Robert exception"
+                                                   userInfo:nil];    
+    [mock clear];
+    [[[mock acceptOnce] raising:exception] lowercaseString];
+    WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
+}
+
+- (void)testMockAccept
+{
+    // preliminaries
+    id mock = nil;
+    
+    // should throw (NSString instances do not respond to -stringWithString)
+    // they do respond to the +stringWithString class method
+    mock = [WOObjectMock mockForClass:[NSString class]];
+    WO_TEST_THROWS([[mock accept] stringWithString:@"Hello"]);
+    WO_TEST_THROWS([mock stringWithString:@"Hello"]);
+    
+    // should throw even for valid selectors if you haven't set them up first
+    mock = [WOObjectMock mockForClass:[NSString class]];
+    [[mock expect] lowercaseString];        // a valid NSString selector
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+    WO_TEST_DOES_NOT_THROW([mock verify]);
+    WO_TEST_DOES_NOT_THROW([mock retain]);  // ok (inherited from NSProxy)
+    WO_TEST_DOES_NOT_THROW([mock release]); // ok (inherited from NSProxy)
+    WO_TEST_THROWS([mock uppercaseString]); // fail (not explicitly expected)
+    
+    // should throw for class methods
+    mock = [WOObjectMock mockForClass:[NSString class]];
+    WO_TEST_THROWS([[mock expect] stringWithString:@"foo"]);
+    WO_TEST_THROWS([mock stringWithString:@"foo"]);
+}
+
+- (void)testClear
+{
+    // test that clear actually clears
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    [[mock accept] lowercaseString];
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]); // send as many times as
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]); // you like... should work
+    [mock clear];
+    WO_TEST_THROWS([mock lowercaseString]);         // but fail on clear
+    
+    // should be able to clear and re-set the same selector (was a bug)
+    [[mock accept] lowercaseString];
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+    [mock clear];
+    [[mock accept] lowercaseString];
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+    [mock clear];
+    
+    // same for acceptOnce
+    [[mock acceptOnce] lowercaseString];
+    [mock clear];
+    WO_TEST_THROWS([mock lowercaseString]);
+    
+    // expect
+    [[mock expect] lowercaseString];
+    [mock clear];
+    WO_TEST_THROWS([mock lowercaseString]);
+    
+    // expectOnce
+    [[mock expectOnce] lowercaseString];
+    [mock clear];
+    WO_TEST_THROWS([mock lowercaseString]);
+    
+    // expectInOrder
+    [[mock expectInOrder] lowercaseString];
+    [mock clear];
+    WO_TEST_THROWS([mock lowercaseString]);
+}
+
+- (void)testAnyArguments
+{
+    // by default should require exactly matching arguments
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    [[mock accept] stringByAppendingString:@"foo"];
+    WO_TEST_THROWS([mock stringByAppendingString:@"bar"]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"foo"]);
+    WO_TEST_THROWS([mock stringByAppendingString:nil]);
+    
+    // but should also be able to accept any argument
+    [mock clear];
+    [[[mock accept] anyArguments] stringByAppendingString:@"irrelevant"];
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"irrelevant"]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"bar"]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"foo"]);
+    WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:nil]);
+}
+
+- (void)testAcceptsByDefault
+{
+    id mock = [WOObjectMock mockForClass:[NSString class]];
+    [mock setAcceptsByDefault:YES];
+    WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
+
+    // unrecognised selector should always throw
+    WO_TEST_THROWS(objc_msgSend(mock, @selector(foobar)));
+    
+    [mock setAcceptsByDefault:NO];