paulanthonywilson-iphone_testify 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/README.rdoc +89 -0
- data/Rakefile +34 -0
- data/bin/iphone_testify +9 -0
- data/iphone_testify.gemspec +0 -0
- data/lib/iphone_testify/setup.rb +13 -0
- data/lib/iphone_testify.rb +49 -0
- data/skeleton/Rakefile +68 -0
- data/skeleton/autoiphonetest.rb +16 -0
- data/skeleton/google_testing/GTMDefines.h +166 -0
- data/skeleton/google_testing/GTMIPhoneUnitTestDelegate.h +29 -0
- data/skeleton/google_testing/GTMIPhoneUnitTestDelegate.m +160 -0
- data/skeleton/google_testing/GTMIPhoneUnitTestMain.m +33 -0
- data/skeleton/google_testing/GTMSenTestCase.h +1004 -0
- data/skeleton/google_testing/GTMSenTestCase.m +211 -0
- data/skeleton/google_testing/GoogleToolboxForMac.license +202 -0
- data/skeleton/google_testing/RunIPhoneUnitTest.sh +24 -0
- data/test/test_helper.rb +1 -0
- data/test/test_setup.rb +39 -0
- metadata +106 -0
data/History.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
iphone_testify
|
2
|
+
by Paul Wilson
|
3
|
+
http://merecomplexities.com
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Utility to help set up and iPhone project for testing using code from {The Google Toolbox for Mac}[http://code.google.com/p/google-toolbox-for-mac/].
|
8
|
+
|
9
|
+
== FEATURES:
|
10
|
+
|
11
|
+
* Copies google test files into the "google_testing" directory
|
12
|
+
* Creates a UnitTests directory
|
13
|
+
* Adds Rakefile for compiling (via xcode) and reporting test failures (on console and via Growl)
|
14
|
+
* Adds an 'autoiphonetest.rb' file for _autotest_ style running of all tests when a change has been detected.
|
15
|
+
|
16
|
+
== TODO:
|
17
|
+
|
18
|
+
There are a few more things I'd like this gem to do.
|
19
|
+
|
20
|
+
* Generation of test stubs
|
21
|
+
* Automatic setup of _Unit Test_ target through XCode (via automation?)
|
22
|
+
* Automatic addition of test files to _Unit Test_ target
|
23
|
+
|
24
|
+
== SYNOPSIS:
|
25
|
+
|
26
|
+
From console cd to your project root directory. Type
|
27
|
+
% iphone_testify
|
28
|
+
|
29
|
+
Unfortunately there are some manual stages needed to add a _Unit Test_ target to your Xcode project. This is based on the {Google Toolbox Wiki instructions}[http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting]
|
30
|
+
|
31
|
+
1. Create a new iPhone Target (Cocoa Touch Application) via "Project Menu > New Target..." called "Unit Test".
|
32
|
+
2. Add all the ObjectiveC files that have been copied to your 'google_testing' directory to this target
|
33
|
+
3. Add your project files to this target
|
34
|
+
4. Add a new 'run script' build phase as the last step of your target build via "Project Menu > New Build Phase > New Run Script Build Phase", and dragging it to the end of the build steps if needed.
|
35
|
+
5. Edit your Run Script Build Phase by double clicking it, and set the shell to "/bin/sh" and the script to "google_testing/RunIPhoneUnitTest.sh"
|
36
|
+
|
37
|
+
|
38
|
+
To autocompile/autorun all tests every time code changes
|
39
|
+
|
40
|
+
% ./autoiphonetest.rb
|
41
|
+
|
42
|
+
|
43
|
+
To compile and run tests if code has changed since the last run
|
44
|
+
|
45
|
+
% rake
|
46
|
+
|
47
|
+
To compile and run all tests even if nothing has changed since the last time
|
48
|
+
|
49
|
+
% rake test_all
|
50
|
+
|
51
|
+
|
52
|
+
== REQUIREMENTS:
|
53
|
+
|
54
|
+
* OS X Leopard
|
55
|
+
* XCode 3.1 (containing the iPhone SDK)
|
56
|
+
* Ruby (comes on Leopard)
|
57
|
+
|
58
|
+
== INSTALL:
|
59
|
+
|
60
|
+
% sudo gem sources -a http://gems.github.com # (you only need to do this once)
|
61
|
+
% sudo gem install paulanthonywilson-iphone_testify
|
62
|
+
|
63
|
+
== LICENSE:
|
64
|
+
|
65
|
+
Contains file taken from {The Google Toolbox for Mac}[http://code.google.com/p/google-toolbox-for-mac/]. See skeleton/google_testing/GoogleToolboxForMac.license which is also copied into the google_testing directory.
|
66
|
+
|
67
|
+
|
68
|
+
(The MIT License)
|
69
|
+
|
70
|
+
Copyright (c) 2008
|
71
|
+
|
72
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
73
|
+
a copy of this software and associated documentation files (the
|
74
|
+
'Software'), to deal in the Software without restriction, including
|
75
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
76
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
77
|
+
permit persons to whom the Software is furnished to do so, subject to
|
78
|
+
the following conditions:
|
79
|
+
|
80
|
+
The above copyright notice and this permission notice shall be
|
81
|
+
included in all copies or substantial portions of the Software.
|
82
|
+
|
83
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
84
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
85
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
86
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
87
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
88
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
89
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bones'
|
7
|
+
Bones.setup
|
8
|
+
rescue LoadError
|
9
|
+
begin
|
10
|
+
load 'tasks/setup.rb'
|
11
|
+
rescue LoadError
|
12
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
ensure_in_path 'lib'
|
17
|
+
require 'iphone_testify'
|
18
|
+
|
19
|
+
task :default => 'test:run'
|
20
|
+
|
21
|
+
PROJ.name = 'iphone_testify'
|
22
|
+
PROJ.authors = 'Paul Wilson'
|
23
|
+
PROJ.email = 'paul.wilson@merecomplexities.com'
|
24
|
+
PROJ.url = 'http://github.com/paulanthonywilson/iphone_testify/'
|
25
|
+
PROJ.version = IphoneTestify::VERSION
|
26
|
+
|
27
|
+
PROJ.exclude << '\.gitignore'
|
28
|
+
PROJ.notes.exclude = %w(^README\.txt$ ^data/)
|
29
|
+
PROJ.readme_file = 'README.rdoc'
|
30
|
+
|
31
|
+
depend_on "paulanthonywilson-osx_filewatcher"
|
32
|
+
depend_on "rake"
|
33
|
+
|
34
|
+
# EOF
|
data/bin/iphone_testify
ADDED
Binary file
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
module IphoneTestify
|
3
|
+
GEMDIR = File.expand_path(File.dirname(__FILE__) + "/..")
|
4
|
+
# :stopdoc:
|
5
|
+
VERSION = '0.0.1'
|
6
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
7
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
8
|
+
# :startdoc:
|
9
|
+
|
10
|
+
# Returns the version string for the library.
|
11
|
+
#
|
12
|
+
def self.version
|
13
|
+
VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the library path for the module. If any arguments are given,
|
17
|
+
# they will be joined to the end of the libray path using
|
18
|
+
# <tt>File.join</tt>.
|
19
|
+
#
|
20
|
+
def self.libpath( *args )
|
21
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the lpath for the module. If any arguments are given,
|
25
|
+
# they will be joined to the end of the path using
|
26
|
+
# <tt>File.join</tt>.
|
27
|
+
#
|
28
|
+
def self.path( *args )
|
29
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Utility method used to require all files ending in .rb that lie in the
|
33
|
+
# directory below this file that has the same name as the filename passed
|
34
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
35
|
+
# the _filename_ does not have to be equivalent to the directory.
|
36
|
+
#
|
37
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
38
|
+
dir ||= ::File.basename(fname, '.*')
|
39
|
+
search_me = ::File.expand_path(
|
40
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
41
|
+
|
42
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
43
|
+
end
|
44
|
+
|
45
|
+
end # module IphoneTestify
|
46
|
+
|
47
|
+
IphoneTestify.require_all_libs_relative_to(__FILE__)
|
48
|
+
|
49
|
+
# EOF
|
data/skeleton/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
BUILD_STATUS_FILE=".built"
|
3
|
+
|
4
|
+
class String
|
5
|
+
attr_accessor :colour
|
6
|
+
RESET="\e[00;m"
|
7
|
+
|
8
|
+
def coloured(colour = nil)
|
9
|
+
colour||=@colour
|
10
|
+
"#{colour_code(colour)}#{self}#{RESET}"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def colour_code colour
|
15
|
+
case colour
|
16
|
+
when :red : "\e[01;31m"
|
17
|
+
when :green : "\e[01;32m"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
file BUILD_STATUS_FILE => Dir.glob("Classes/*.[hm]") + Dir.glob("UnitTests/*.m") do
|
23
|
+
failure_line = test
|
24
|
+
if failure_line
|
25
|
+
notice = ['Fail', failure_line.chomp]
|
26
|
+
else
|
27
|
+
notice = ['Pass']
|
28
|
+
end
|
29
|
+
growl *notice
|
30
|
+
File.open(BUILD_STATUS_FILE, 'w') {|f| f.write(notice * ": ")}
|
31
|
+
end
|
32
|
+
|
33
|
+
task :remove_built_file do
|
34
|
+
FileUtils.rm(BUILD_STATUS_FILE)
|
35
|
+
end
|
36
|
+
|
37
|
+
task :test_all=>[:remove_built_file, :test]
|
38
|
+
|
39
|
+
task :test=>[BUILD_STATUS_FILE] do
|
40
|
+
out = File.open('.built') {|f| f.read}
|
41
|
+
print out.coloured(out =~ /Pass/ ? :green : :red) + "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
task :default => [:test]
|
47
|
+
|
48
|
+
def test
|
49
|
+
output = `xcodebuild -target "Unit Test" -configuration Debug -sdk iphonesimulator2.1`
|
50
|
+
failure_line = nil
|
51
|
+
output.each do |line|
|
52
|
+
if line =~ /error:|^Executed.*(\d+) failures/
|
53
|
+
if $1.nil? || $1.to_i > 0
|
54
|
+
failure_line||= line
|
55
|
+
line.colour = :red
|
56
|
+
else
|
57
|
+
line.colour = :green
|
58
|
+
end
|
59
|
+
end
|
60
|
+
print line.coloured
|
61
|
+
end
|
62
|
+
failure_line
|
63
|
+
end
|
64
|
+
|
65
|
+
def growl title, msg =""
|
66
|
+
img = "~/.autotest_images/#{title.downcase}.png"
|
67
|
+
`growlnotify -H localhost -n autotest --image #{img} -p 0 -m #{msg.inspect} #{title}`
|
68
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'osx_watchfolder'
|
5
|
+
gem 'rake'
|
6
|
+
|
7
|
+
|
8
|
+
ARGV.clear
|
9
|
+
|
10
|
+
fork do
|
11
|
+
ARGV << 'test_all'
|
12
|
+
load 'rake'
|
13
|
+
end
|
14
|
+
OsxWatchfolder::FolderWatcher.new("Classes", "UnitTests") do
|
15
|
+
fork{load 'rake'}
|
16
|
+
end.start
|
@@ -0,0 +1,166 @@
|
|
1
|
+
//
|
2
|
+
// GTMDefines.h
|
3
|
+
//
|
4
|
+
// Copyright 2008 Google Inc.
|
5
|
+
//
|
6
|
+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
7
|
+
// use this file except in compliance with the License. You may obtain a copy
|
8
|
+
// of the License at
|
9
|
+
//
|
10
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
//
|
12
|
+
// Unless required by applicable law or agreed to in writing, software
|
13
|
+
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
14
|
+
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
15
|
+
// License for the specific language governing permissions and limitations under
|
16
|
+
// the License.
|
17
|
+
//
|
18
|
+
|
19
|
+
// ============================================================================
|
20
|
+
|
21
|
+
// ----------------------------------------------------------------------------
|
22
|
+
// CPP symbols that can be overridden in a prefix to control how the toolbox
|
23
|
+
// is compiled.
|
24
|
+
// ----------------------------------------------------------------------------
|
25
|
+
|
26
|
+
|
27
|
+
// GTMHTTPFetcher will support logging by default but only hook its input
|
28
|
+
// stream support for logging when requested. You can control the inclusion of
|
29
|
+
// the code by providing your own definitions for these w/in a prefix header.
|
30
|
+
//
|
31
|
+
#ifndef GTM_HTTPFETCHER_ENABLE_LOGGING
|
32
|
+
# define GTM_HTTPFETCHER_ENABLE_LOGGING 1
|
33
|
+
#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
|
34
|
+
#ifndef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
|
35
|
+
# define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
|
36
|
+
#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
|
37
|
+
|
38
|
+
|
39
|
+
// _GTMDevLog & _GTMDevAssert
|
40
|
+
//
|
41
|
+
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
|
42
|
+
// developer level errors. This implementation simply macros to NSLog/NSAssert.
|
43
|
+
// It is not intended to be a general logging/reporting system.
|
44
|
+
//
|
45
|
+
// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
|
46
|
+
// for a little more background on the usage of these macros.
|
47
|
+
//
|
48
|
+
// _GTMDevLog log some error/problem in debug builds
|
49
|
+
// _GTMDevAssert assert if conditon isn't met w/in a method/function
|
50
|
+
// in all builds.
|
51
|
+
//
|
52
|
+
// To replace this system, just provide different macro definitions in your
|
53
|
+
// prefix header. Remember, any implementation you provide *must* be thread
|
54
|
+
// safe since this could be called by anything in what ever situtation it has
|
55
|
+
// been placed in.
|
56
|
+
//
|
57
|
+
|
58
|
+
// We only define the simple macros if nothing else has defined this.
|
59
|
+
#ifndef _GTMDevLog
|
60
|
+
|
61
|
+
#ifdef DEBUG
|
62
|
+
#define _GTMDevLog(...) NSLog(__VA_ARGS__)
|
63
|
+
#else
|
64
|
+
#define _GTMDevLog(...) do { } while (0)
|
65
|
+
#endif
|
66
|
+
|
67
|
+
#endif // _GTMDevLog
|
68
|
+
|
69
|
+
// Declared here so that it can easily be used for logging tracking if
|
70
|
+
// necessary. See GTMUnitTestDevLog.h for details.
|
71
|
+
@class NSString;
|
72
|
+
extern void _GTMUnittestDevLog(NSString *format, ...);
|
73
|
+
|
74
|
+
#ifndef _GTMDevAssert
|
75
|
+
// we directly invoke the NSAssert handler so we can pass on the varargs
|
76
|
+
// (NSAssert doesn't have a macro we can use that takes varargs)
|
77
|
+
#if !defined(NS_BLOCK_ASSERTIONS)
|
78
|
+
#define _GTMDevAssert(condition, ...) \
|
79
|
+
do { \
|
80
|
+
if (!(condition)) { \
|
81
|
+
[[NSAssertionHandler currentHandler] \
|
82
|
+
handleFailureInFunction:[NSString stringWithCString:__PRETTY_FUNCTION__] \
|
83
|
+
file:[NSString stringWithCString:__FILE__] \
|
84
|
+
lineNumber:__LINE__ \
|
85
|
+
description:__VA_ARGS__]; \
|
86
|
+
} \
|
87
|
+
} while(0)
|
88
|
+
#else // !defined(NS_BLOCK_ASSERTIONS)
|
89
|
+
#define _GTMDevAssert(condition, ...) do { } while (0)
|
90
|
+
#endif // !defined(NS_BLOCK_ASSERTIONS)
|
91
|
+
|
92
|
+
#endif // _GTMDevAssert
|
93
|
+
|
94
|
+
// _GTMCompileAssert
|
95
|
+
// _GTMCompileAssert is an assert that is meant to fire at compile time if you
|
96
|
+
// want to check things at compile instead of runtime. For example if you
|
97
|
+
// want to check that a wchar is 4 bytes instead of 2 you would use
|
98
|
+
// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
|
99
|
+
// Note that the second "arg" is not in quotes, and must be a valid processor
|
100
|
+
// symbol in it's own right (no spaces, punctuation etc).
|
101
|
+
|
102
|
+
// Wrapping this in an #ifndef allows external groups to define their own
|
103
|
+
// compile time assert scheme.
|
104
|
+
#ifndef _GTMCompileAssert
|
105
|
+
// We got this technique from here:
|
106
|
+
// http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html
|
107
|
+
|
108
|
+
#define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
|
109
|
+
#define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
|
110
|
+
#define _GTMCompileAssert(test, msg) \
|
111
|
+
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
|
112
|
+
#endif // _GTMCompileAssert
|
113
|
+
|
114
|
+
// ============================================================================
|
115
|
+
|
116
|
+
// ----------------------------------------------------------------------------
|
117
|
+
// CPP symbols defined based on the project settings so the GTM code has
|
118
|
+
// simple things to test against w/o scattering the knowledge of project
|
119
|
+
// setting through all the code.
|
120
|
+
// ----------------------------------------------------------------------------
|
121
|
+
|
122
|
+
// Provide a single constant CPP symbol that all of GTM uses for ifdefing
|
123
|
+
// iPhone code.
|
124
|
+
#include <TargetConditionals.h>
|
125
|
+
#if TARGET_OS_IPHONE // iPhone SDK
|
126
|
+
// For iPhone specific stuff
|
127
|
+
#define GTM_IPHONE_SDK 1
|
128
|
+
#else
|
129
|
+
// For MacOS specific stuff
|
130
|
+
#define GTM_MACOS_SDK 1
|
131
|
+
#endif
|
132
|
+
|
133
|
+
// To simplify support for 64bit (and Leopard in general), we provide the type
|
134
|
+
// defines for non Leopard SDKs
|
135
|
+
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
136
|
+
// NSInteger/NSUInteger and Max/Mins
|
137
|
+
#ifndef NSINTEGER_DEFINED
|
138
|
+
#if __LP64__ || NS_BUILD_32_LIKE_64
|
139
|
+
typedef long NSInteger;
|
140
|
+
typedef unsigned long NSUInteger;
|
141
|
+
#else
|
142
|
+
typedef int NSInteger;
|
143
|
+
typedef unsigned int NSUInteger;
|
144
|
+
#endif
|
145
|
+
#define NSIntegerMax LONG_MAX
|
146
|
+
#define NSIntegerMin LONG_MIN
|
147
|
+
#define NSUIntegerMax ULONG_MAX
|
148
|
+
#define NSINTEGER_DEFINED 1
|
149
|
+
#endif // NSINTEGER_DEFINED
|
150
|
+
// CGFloat
|
151
|
+
#ifndef CGFLOAT_DEFINED
|
152
|
+
#if defined(__LP64__) && __LP64__
|
153
|
+
// This really is an untested path (64bit on Tiger?)
|
154
|
+
typedef double CGFloat;
|
155
|
+
#define CGFLOAT_MIN DBL_MIN
|
156
|
+
#define CGFLOAT_MAX DBL_MAX
|
157
|
+
#define CGFLOAT_IS_DOUBLE 1
|
158
|
+
#else /* !defined(__LP64__) || !__LP64__ */
|
159
|
+
typedef float CGFloat;
|
160
|
+
#define CGFLOAT_MIN FLT_MIN
|
161
|
+
#define CGFLOAT_MAX FLT_MAX
|
162
|
+
#define CGFLOAT_IS_DOUBLE 0
|
163
|
+
#endif /* !defined(__LP64__) || !__LP64__ */
|
164
|
+
#define CGFLOAT_DEFINED 1
|
165
|
+
#endif // CGFLOAT_DEFINED
|
166
|
+
#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
@@ -0,0 +1,29 @@
|
|
1
|
+
//
|
2
|
+
// GTMIPhoneUnitTestDelegate.h
|
3
|
+
//
|
4
|
+
// Copyright 2008 Google Inc.
|
5
|
+
//
|
6
|
+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
7
|
+
// use this file except in compliance with the License. You may obtain a copy
|
8
|
+
// of the License at
|
9
|
+
//
|
10
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
//
|
12
|
+
// Unless required by applicable law or agreed to in writing, software
|
13
|
+
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
14
|
+
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
15
|
+
// License for the specific language governing permissions and limitations under
|
16
|
+
// the License.
|
17
|
+
//
|
18
|
+
|
19
|
+
// Application delegate that runs all test methods in registered classes
|
20
|
+
// extending SenTestCase. The application is terminated afterwards.
|
21
|
+
// You can also run the tests directly from your application by invoking
|
22
|
+
// gtm_runTests and clean up, restore data, etc. before the application
|
23
|
+
// terminates.
|
24
|
+
@interface GTMIPhoneUnitTestDelegate : NSObject
|
25
|
+
// Runs through all the registered classes and runs test methods on any
|
26
|
+
// that are subclasses of SenTestCase. Prints results and run time to
|
27
|
+
// the default output.
|
28
|
+
- (void)runTests;
|
29
|
+
@end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
//
|
2
|
+
// GTMIPhoneUnitTestDelegate.m
|
3
|
+
//
|
4
|
+
// Copyright 2008 Google Inc.
|
5
|
+
//
|
6
|
+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
7
|
+
// use this file except in compliance with the License. You may obtain a copy
|
8
|
+
// of the License at
|
9
|
+
//
|
10
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
//
|
12
|
+
// Unless required by applicable law or agreed to in writing, software
|
13
|
+
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
14
|
+
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
15
|
+
// License for the specific language governing permissions and limitations under
|
16
|
+
// the License.
|
17
|
+
//
|
18
|
+
|
19
|
+
#import "GTMIPhoneUnitTestDelegate.h"
|
20
|
+
|
21
|
+
#import "GTMDefines.h"
|
22
|
+
#if !GTM_IPHONE_SDK
|
23
|
+
#error GTMIPhoneUnitTestDelegate for iPhone only
|
24
|
+
#endif
|
25
|
+
#import <objc/runtime.h>
|
26
|
+
#import <stdio.h>
|
27
|
+
#import <UIKit/UIKit.h>
|
28
|
+
#import "GTMSenTestCase.h"
|
29
|
+
|
30
|
+
// Used for sorting methods below
|
31
|
+
static int MethodSort(const void *a, const void *b) {
|
32
|
+
const char *nameA = sel_getName(method_getName(*(Method*)a));
|
33
|
+
const char *nameB = sel_getName(method_getName(*(Method*)b));
|
34
|
+
return strcmp(nameA, nameB);
|
35
|
+
}
|
36
|
+
|
37
|
+
@interface UIApplication (iPhoneUnitTestAdditions)
|
38
|
+
// "Private" method that we need
|
39
|
+
- (void)terminate;
|
40
|
+
@end
|
41
|
+
|
42
|
+
@implementation GTMIPhoneUnitTestDelegate
|
43
|
+
|
44
|
+
// Return YES if class is subclass (1 or more generations) of SenTestCase
|
45
|
+
- (BOOL)isTestFixture:(Class)aClass {
|
46
|
+
BOOL iscase = NO;
|
47
|
+
Class testCaseClass = [SenTestCase class];
|
48
|
+
Class superclass;
|
49
|
+
for (superclass = aClass;
|
50
|
+
!iscase && superclass;
|
51
|
+
superclass = class_getSuperclass(superclass)) {
|
52
|
+
iscase = superclass == testCaseClass ? YES : NO;
|
53
|
+
}
|
54
|
+
return iscase;
|
55
|
+
}
|
56
|
+
|
57
|
+
// Run through all the registered classes and run test methods on any
|
58
|
+
// that are subclasses of SenTestCase. Terminate the application upon
|
59
|
+
// test completion.
|
60
|
+
- (void)applicationDidFinishLaunching:(UIApplication *)application {
|
61
|
+
[self runTests];
|
62
|
+
// Using private call to end our tests
|
63
|
+
[[UIApplication sharedApplication] terminate];
|
64
|
+
}
|
65
|
+
|
66
|
+
// Run through all the registered classes and run test methods on any
|
67
|
+
// that are subclasses of SenTestCase. Print results and run time to
|
68
|
+
// the default output.
|
69
|
+
- (void)runTests {
|
70
|
+
int count = objc_getClassList(NULL, 0);
|
71
|
+
Class *classes = (Class*)malloc(sizeof(Class) * count);
|
72
|
+
_GTMDevAssert(classes, @"Couldn't allocate class list");
|
73
|
+
objc_getClassList(classes, count);
|
74
|
+
int suiteSuccesses = 0;
|
75
|
+
int suiteFailures = 0;
|
76
|
+
int suiteTotal = 0;
|
77
|
+
NSString *suiteName = [[NSBundle mainBundle] bundlePath];
|
78
|
+
NSDate *suiteStartDate = [NSDate date];
|
79
|
+
NSString *suiteStartString = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n",
|
80
|
+
suiteName, suiteStartDate];
|
81
|
+
fputs([suiteStartString UTF8String], stderr);
|
82
|
+
fflush(stderr);
|
83
|
+
for (int i = 0; i < count; ++i) {
|
84
|
+
Class currClass = classes[i];
|
85
|
+
if ([self isTestFixture:currClass]) {
|
86
|
+
NSDate *fixtureStartDate = [NSDate date];
|
87
|
+
NSString *fixtureName = NSStringFromClass(currClass);
|
88
|
+
NSString *fixtureStartString = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n",
|
89
|
+
fixtureName, fixtureStartDate];
|
90
|
+
int fixtureSuccesses = 0;
|
91
|
+
int fixtureFailures = 0;
|
92
|
+
int fixtureTotal = 0;
|
93
|
+
fputs([fixtureStartString UTF8String], stderr);
|
94
|
+
fflush(stderr);
|
95
|
+
id testcase = [[currClass alloc] init];
|
96
|
+
_GTMDevAssert(testcase, @"Unable to instantiate Test Suite: '%@'\n",
|
97
|
+
fixtureName);
|
98
|
+
unsigned int methodCount;
|
99
|
+
Method *methods = class_copyMethodList(currClass, &methodCount);
|
100
|
+
// Sort our methods so they are called in Alphabetical order just
|
101
|
+
// because we can.
|
102
|
+
qsort(methods, methodCount, sizeof(Method), MethodSort);
|
103
|
+
for (size_t j = 0; j < methodCount; ++j) {
|
104
|
+
Method currMethod = methods[j];
|
105
|
+
SEL sel = method_getName(currMethod);
|
106
|
+
const char *name = sel_getName(sel);
|
107
|
+
// If it starts with test, run it.
|
108
|
+
if (strstr(name, "test") == name) {
|
109
|
+
fixtureTotal += 1;
|
110
|
+
BOOL failed = NO;
|
111
|
+
NSDate *caseStartDate = [NSDate date];
|
112
|
+
@try {
|
113
|
+
[testcase performTest:sel];
|
114
|
+
} @catch (NSException *exception) {
|
115
|
+
failed = YES;
|
116
|
+
}
|
117
|
+
if (failed) {
|
118
|
+
fixtureFailures += 1;
|
119
|
+
} else {
|
120
|
+
fixtureSuccesses += 1;
|
121
|
+
}
|
122
|
+
NSTimeInterval caseEndTime = [[NSDate date] timeIntervalSinceDate:caseStartDate];
|
123
|
+
NSString *caseEndString = [NSString stringWithFormat:@"Test Case '-[%@ %s]' %s (%0.3f seconds).\n",
|
124
|
+
fixtureName, name,
|
125
|
+
failed ? "failed" : "passed", caseEndTime];
|
126
|
+
fputs([caseEndString UTF8String], stderr);
|
127
|
+
fflush(stderr);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
if (methods) {
|
131
|
+
free(methods);
|
132
|
+
}
|
133
|
+
[testcase release];
|
134
|
+
NSDate *fixtureEndDate = [NSDate date];
|
135
|
+
NSTimeInterval fixtureEndTime = [fixtureEndDate timeIntervalSinceDate:fixtureStartDate];
|
136
|
+
NSString *fixtureEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n"
|
137
|
+
"Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n",
|
138
|
+
fixtureName, fixtureEndDate, fixtureTotal,
|
139
|
+
fixtureFailures, fixtureFailures,
|
140
|
+
fixtureEndTime, fixtureEndTime];
|
141
|
+
|
142
|
+
fputs([fixtureEndString UTF8String], stderr);
|
143
|
+
fflush(stderr);
|
144
|
+
suiteTotal += fixtureTotal;
|
145
|
+
suiteSuccesses += fixtureSuccesses;
|
146
|
+
suiteFailures += fixtureFailures;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
NSDate *suiteEndDate = [NSDate date];
|
150
|
+
NSTimeInterval suiteEndTime = [suiteEndDate timeIntervalSinceDate:suiteStartDate];
|
151
|
+
NSString *suiteEndString = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n"
|
152
|
+
"Executed %d tests, with %d failures (%d unexpected) in %0.3f (%0.3f) seconds\n",
|
153
|
+
suiteName, suiteEndDate, suiteTotal,
|
154
|
+
suiteFailures, suiteFailures,
|
155
|
+
suiteEndTime, suiteEndTime];
|
156
|
+
fputs([suiteEndString UTF8String], stderr);
|
157
|
+
fflush(stderr);
|
158
|
+
}
|
159
|
+
|
160
|
+
@end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
//
|
2
|
+
// GTMIPhoneUnitTestMain.m
|
3
|
+
//
|
4
|
+
// Copyright 2008 Google Inc.
|
5
|
+
//
|
6
|
+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
7
|
+
// use this file except in compliance with the License. You may obtain a copy
|
8
|
+
// of the License at
|
9
|
+
//
|
10
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
//
|
12
|
+
// Unless required by applicable law or agreed to in writing, software
|
13
|
+
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
14
|
+
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
15
|
+
// License for the specific language governing permissions and limitations under
|
16
|
+
// the License.
|
17
|
+
//
|
18
|
+
|
19
|
+
#import "GTMDefines.h"
|
20
|
+
#if !GTM_IPHONE_SDK
|
21
|
+
#error GTMIPhoneUnitTestMain for iPhone only
|
22
|
+
#endif
|
23
|
+
#import <UIKit/UIKit.h>
|
24
|
+
|
25
|
+
// Creates an application that runs all tests from classes extending
|
26
|
+
// SenTestCase, outputs results and test run time, and terminates right
|
27
|
+
// afterwards.
|
28
|
+
int main(int argc, char *argv[]) {
|
29
|
+
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
30
|
+
int retVal = UIApplicationMain(argc, argv, nil, @"GTMIPhoneUnitTestDelegate");
|
31
|
+
[pool release];
|
32
|
+
return retVal;
|
33
|
+
}
|