add test suite (rudimentary)

This commit is contained in:
Laurent Sansonetti
2012-09-10 15:36:37 +02:00
parent eb59d3b2cc
commit 81a71a4c5c
24 changed files with 889 additions and 0 deletions

3
test/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.repl_history
build
resources/*.nib

10
test/Rakefile Normal file
View File

@@ -0,0 +1,10 @@
$:.unshift("../lib")
#$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'
Motion::Project::App.setup do |app|
# Use `rake config' to see complete project settings.
app.name = 'test'
app.frameworks += ['AddressBook', 'AddressBookUI', 'CoreData', 'CoreMIDI', 'GameKit']
app.vendor_project('vendor/code', :static)
end

5
test/app/app_delegate.rb Normal file
View File

@@ -0,0 +1,5 @@
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
true
end
end

View File

@@ -0,0 +1,25 @@
class TestArchiver
attr_accessor :name
def encodeWithCoder(encoder)
encoder.encodeObject(self.name, forKey: "name")
end
def initWithCoder(decoder)
if self.init
self.name = decoder.decodeObjectForKey("name")
end
self
end
end
describe "NSKeyedArchiver" do
it "works" do
m = TestArchiver.new
m.name = "test"
m2 = NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(m))
m2.class.should == TestArchiver
m2.name.should == m.name
end
end

22
test/spec/array_spec.rb Normal file
View File

@@ -0,0 +1,22 @@
describe "Array#delete_at" do
it "does not prematurely release the removed object" do
a = ['a', 'b', 'c']
o = a[1]
a.delete_at(1)
o.should == 'b'
a.should == ['a', 'c']
a.insert(1, o)
o.should == 'b'
a.should == ['a', 'b', 'c']
end
end
describe "Array#shift" do
it "should not prematurely release the removed object" do
@a = nil
RunloopYield.new do
@a = ['a', 'b', 'c'].shift
end
@a.should == 'a'
end
end

6
test/spec/const_spec.rb Normal file
View File

@@ -0,0 +1,6 @@
describe "iOS constants" do
it "have their values retrieved at demand" do
ABAddressBookCreate()
KABPersonFirstNameProperty.should != KABPersonLastNameProperty
end
end

View File

@@ -0,0 +1,6 @@
#describe "NSMigrationManager" do
# it "migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:" do
# mm = NSMigrationManager.new
# mm.migrateStoreFromURL(nil, type:nil, options:nil, withMappingModel:nil, toDestinationURL:nil, destinationType:nil, destinationOptions:nil, error:nil)
# end
#end

View File

@@ -0,0 +1,10 @@
describe "CoreMidi" do
it "can list devices" do
n = MIDIGetNumberOfDevices()
n.should > 0
n.times do |i|
device = MIDIGetDevice(i)
device.class.should == MIDIDeviceRef
end
end
end

View File

@@ -0,0 +1,16 @@
describe "NSExceptions" do
it "can be catched" do
exc_name = 'TestException'
exc_reason = 'some reason'
exc_userInfo = { 'One' => 1, 'Two' => 2, 'Three' => 3}
begin
NSException.exceptionWithName(exc_name, reason:exc_reason, userInfo:exc_userInfo).raise
rescue => e
nse = e.nsexception
nse.should != nil
nse.name.should == exc_name
nse.reason.should == exc_reason
nse.userInfo.should == exc_userInfo
end
end
end

View File

@@ -0,0 +1,5 @@
#desc "An inline function" do
# it "can be called" do
# TestInlineFunction(1, 2, 3).should == 1 + 2 + 3
# end
#end

8
test/spec/gcd_spec.rb Normal file
View File

@@ -0,0 +1,8 @@
=begin
describe "Group#notify" do
it "works" do
group = Dispatch::Group.new
group.notify (Dispatch::Queue.main) { p 42 }
end
end
=end

7
test/spec/hash_spec.rb Normal file
View File

@@ -0,0 +1,7 @@
describe "Hash" do
it 'can be created using []' do
h = Hash[a: 'a', b: 'b']
h[:a].should == 'a'
h[:b].should == 'b'
end
end

View File

@@ -0,0 +1,15 @@
class RunloopYield
def initialize(&b)
@b = b
run
end
def run
performSelectorOnMainThread(:'test_start', withObject:nil, waitUntilDone:false)
NSRunLoop.currentRunLoop.runUntilDate(NSDate.dateWithTimeIntervalSinceNow(0.1))
end
def test_start
@b.call
end
end

75
test/spec/kvo_spec.rb Normal file
View File

@@ -0,0 +1,75 @@
class Observed1
attr_accessor :name
def self.automaticallyNotifiesObserversForKey(theKey)
automatic = false
if (theKey == "name")
automatic = false
else
automatic = super
end
automatic
end
def name=(name)
self.willChangeValueForKey("name")
@name = name
self.didChangeValueForKey("name")
end
end
class Observed2
attr_accessor :name
end
class Observer
attr_reader :did_observe
def observe(observed)
observed.addObserver(self, forKeyPath: "name", options: 0, context: nil)
end
def unobserve(observed)
observed.removeObserver(self, forKeyPath: "name")
end
def observeValueForKeyPath(key_path, ofObject:object, change:change, context:context)
@did_observe = true
end
end
describe "KVO" do
it "works for objects that implement the KVO interface manually" do
observed = Observed1.new
observed.name = "first"
observer = Observer.new
observer.observe(observed)
observed.name = "second"
observer.did_observe.should == true
observer.unobserve(observed)
end
it "works for objects that use attr_accessor" do
observed = Observed2.new
observed.name = "first"
observer = Observer.new
observer.observe(observed)
observed.name = "second"
observer.did_observe.should == true
observer.unobserve(observed)
end
end
describe "KVC" do
it "works for objects that use attr_accessor" do
x = Observed2.new
x.name = 'foo'
x.valueForKey('name').should == 'foo'
x.setValue('bar', forKey:'name')
x.name == 'bar'
end
end

215
test/spec/main_spec.rb Normal file
View File

@@ -0,0 +1,215 @@
class MyTestMethod < TestMethod
def methodReturningCGSize
super
end
def methodReturningCGRect
super
end
end
describe 'A method accepting a 32-bit struct' do
it "can be called" do
s = MyStruct4C.new(1, 2, 3, 4)
TestMethod.testMethodAcceptingMyStruct4C(s).should == true
TestMethod.testMethodAcceptingMyStruct4C(s, another:s).should == true
end
end
describe 'A method returning a 64-bit struct' do
it "can be called" do
o = TestMethod.new
o.methodReturningCGSize.should == CGSize.new(1, 2)
end
it "can be defined" do
o = MyTestMethod.new
TestMethod.testMethodReturningCGSize(o).should == true
end
end
describe 'A method returning a 128-bit struct' do
it "can be called" do
o = TestMethod.new
o.methodReturningCGRect.should == CGRect.new(CGPoint.new(1, 2), CGSize.new(3, 4))
end
it "can be defined" do
o = MyTestMethod.new
TestMethod.testMethodReturningCGRect(o).should == true
end
end
describe 'A 3rd-party method accepting an iOS enum' do
it "can be called" do
TestMethod.testMethodAcceptingUIInterfaceOrientation(UIInterfaceOrientationPortrait).should == true
TestMethod.testMethodAcceptingUIInterfaceOrientation(UIInterfaceOrientationLandscapeLeft).should == false
end
end
describe 'A method accepting and returning UIEdgeInsets' do
it "can be called" do
s = UIEdgeInsetsMake(1, 2, 3, 4)
TestMethod.testMethodAcceptingUIEdgeInsets(s).should == true
end
end
describe 'A method accepting a block' do
it "can be called (1)" do
TestMethod.testMethodCallingBlock(lambda { 42 }).should == 42
TestMethod.testMethodCallingBlock(nil).should == nil
end
it "can be called (2)" do
res = []
[1, 2, 3, 4, 5].enumerateObjectsUsingBlock(lambda do |obj, idx, stop_ptr|
res << [obj, idx]
end)
res.should == [[1,0], [2,1], [3,2], [4,3], [5,4]]
res = []
[1, 2, 3, 4, 5].enumerateObjectsUsingBlock(lambda do |obj, idx, stop_ptr|
res << obj
stop_ptr[0] = true if idx == 2
end)
res.should == [1, 2, 3]
end
end
describe 'A method accepting a CF type' do
it "can be called (1)" do
s = CFStringCreateCopy(nil, 'foo')
TestMethod.testMethodAcceptingCFType(s).should == true
TestMethod.testMethodAcceptingCFType('foo').should == true
end
it "can be called (2)" do
controller = ABPersonViewController.alloc.init
person = ABPersonCreate()
controller.displayedPerson = person
controller.displayedPerson.should == person
end
end
describe 'A method returning a CF type' do
it "can be called" do
s = CFStringCreateCopy(nil, 'foo')
s.should == 'foo'
end
end
describe 'CFTypeRefs' do
it "are mapped as 'id' types" do
KSecAttrAccount.should == 'acct' # and not Pointer
end
end
describe 'RUBY_ENGINE' do
it "should be 'rubymotion'" do
RUBY_ENGINE.should == 'rubymotion'
end
end
describe "Objects conforming to NSFastEnumeration" do
it "can be iterated using #each" do
iter = TestIterator.new
enum = iter.to_enum
enum.to_a.should == ['1', '2', '3', '4', '5']
end
end
class TestAttrAlias
attr_accessor :foo
alias :bar :foo
end
describe "attr_" do
it "can be aliased" do
o = TestAttrAlias.new
o.foo = 42
o.bar.should == 42
end
it "return proper values when used by valueForKey:" do
o = TestAttrAlias.new
TestMethod.testValueForKey(o, expected:nil).should == true
o.foo = '42'
TestMethod.testValueForKey(o, expected:'42').should == true
end
end
describe "A method returning a big 32-bit integer" do
it "returns a Bignum" do
o = TestMethod.new.methodReturningLargeInt
o.class.should == Bignum
o.should == 2147483646
end
end
class TestNewInstance
end
describe "A method sending +new on a Ruby class" do
it "returns an instance" do
o = TestMethod.new.methodSendingNew(TestNewInstance)
o.class.should == TestNewInstance
end
end
class TestPrivateMethod
def foo; 42; end
private
def bar; 42; end
end
describe "A private method" do
it "cannot be called with #public_send" do
o = TestPrivateMethod.new
o.foo.should == 42
o.send(:foo).should == 42
o.public_send(:foo).should == 42
lambda { o.bar }.should.raise(NoMethodError)
o.send(:bar).should == 42
lambda { o.public_send(:bar) }.should.raise(NoMethodError)
end
end
describe "An informal protocol method with BOOL types" do
it "can be called" do
o = TestMethod.new
o.testProtocolFlag = true
o.testProtocolFlag.should == true
o.testProtocolFlag = false
o.testProtocolFlag.should == false
o = UITextField.new
o.enablesReturnKeyAutomatically = true
o.enablesReturnKeyAutomatically.should == true
end
end
describe "Constants starting with a lower-case character" do
it "can be accessed by upper-casing the first letter" do
LowerCaseConstant.should == 42
end
end
describe "Classes starting with a lower-case character" do
it "can be accessed by upper-casing the first letter" do
k = NSClassFromString('lowerCaseClass')
k.should != nil
LowerCaseClass.should == k
end
end
describe "Large unsigned ints (Bignum)" do
it "can be passed as 'unsigned ints'" do
NSNumber.numberWithUnsignedInt(4294967295).should == 4294967295
end
end
describe "Properties implemented using forwarders" do
it "can be called" do
player = GKLocalPlayer.localPlayer
player.alias.should == nil
player.alias = 'lol'
player.alias.should == 'lol'
end
end

189
test/spec/memory_spec.rb Normal file
View File

@@ -0,0 +1,189 @@
class BlockTestParam
def dealloc
$dealloc_test = true
super
end
def res
42
end
end
class BlockTest
def test_sync
o = BlockTestParam.new
1.times do
@res = o.res
end
end
def test_async
o = BlockTestParam.new
Dispatch::Queue.concurrent.async do
@res = o.res
end
end
def res
@res
end
end
describe "&block dvars" do
it "are properly retain/released (sync)" do
$dealloc_test = false
o = BlockTest.new
o.performSelectorOnMainThread(:'test_sync', withObject:nil, waitUntilDone:false)
NSRunLoop.currentRunLoop.runUntilDate(NSDate.dateWithTimeIntervalSinceNow(0.1))
o.res.should == 42
$dealloc_test.should == true
end
it "are properly retain/released (async)" do
$dealloc_test = false
o = BlockTest.new
o.performSelectorOnMainThread(:'test_async', withObject:nil, waitUntilDone:false)
NSRunLoop.currentRunLoop.runUntilDate(NSDate.dateWithTimeIntervalSinceNow(0.1))
o.res.should == 42
$dealloc_test.should == true
end
end
class DeallocTest
def self.test
DeallocTest.new
end
def dealloc
super
$dealloc_test = true
end
end
describe "dealloc" do
it "can be defined and is called" do
$dealloc_test = false
DeallocTest.performSelectorOnMainThread(:'test', withObject:nil, waitUntilDone:false)
NSRunLoop.currentRunLoop.runUntilDate(NSDate.dateWithTimeIntervalSinceNow(0.1))
$dealloc_test.should == true
end
end
$retain_test = false
class RetainTest
def retain
super
$retain_test = true
end
end
describe "retain and release" do
it "can be called directly" do
o = Object.new
o.retainCount.should == 1
o.retain
o.retainCount.should == 2
o.release
o.retainCount.should == 1
end
it "can be defined" do
o = RetainTest.new
$retain_test.should == false
NSArray.arrayWithObject(o)
$retain_test.should == true
end
end
describe "references" do
it "can be created using instance variables" do
o = Object.new
o.retainCount.should == 1
@tmpref = o
o.retainCount.should == 2
@tmpref = nil
o.retainCount.should == 1
end
it "can be created using constants" do
o = Object.new
o.retainCount.should == 1
ConstRef = o
o.retainCount.should == 2
end
end
class InitTest
def self.test_start
@o = InitTest.new
@o.instance_variable_set(:@foo, 42)
5.times { @o.init }
end
def self.test_res
@o
end
end
describe "init" do
it "can safely be called separately" do
InitTest.performSelectorOnMainThread(:'test_start', withObject:nil, waitUntilDone:false)
NSRunLoop.currentRunLoop.runUntilDate(NSDate.dateWithTimeIntervalSinceNow(0.1))
InitTest.test_res.instance_variable_get(:@foo).should == 42
end
end
class InitSuperTest
def init
if super
end
self
end
def dealloc
$dealloc_test = true
super
end
def self.test_start
InitSuperTest.alloc.init
nil
end
end
describe "init+super" do
it "returns an autoreleased object" do
$dealloc_test = false
InitSuperTest.performSelectorOnMainThread(:'test_start', withObject:nil, waitUntilDone:false)
NSRunLoop.currentRunLoop.runUntilDate(NSDate.dateWithTimeIntervalSinceNow(0.1))
$dealloc_test.should == true
end
end
class TestSetValueForKey
attr_accessor :foo
end
describe "setValue:forKey:" do
it "retains the value" do
o = TestSetValueForKey.new
val = Object.new
refcount = val.retainCount
o.setValue(val, forKey:'foo')
o.foo.should == val
val.retainCount.should >= refcount + 1
end
end
describe "setValuesForKeysWithDictionary:" do
it "retain the values" do
o = TestSetValueForKey.new
val = Object.new
refcount = val.retainCount
o.setValuesForKeysWithDictionary({'foo' => val})
val.retainCount.should >= refcount + 1
end
end
describe "Random" do
it "can be allocated" do
autorelease_pool do
100.times { Random.new }
end
1.should == 1
end
end

20
test/spec/module_spec.rb Normal file
View File

@@ -0,0 +1,20 @@
class TestModuleSubclass < Module
def initialize
@foo = 42
end
def foo
@foo
end
end
=begin
describe "Module" do
it "can be subclassed and mixed up" do
m = TestModuleSubclass.new
m.foo.should == 42
o = Object.new
o.extend(m)
o.foo.should == 42
end
end
=end

View File

@@ -0,0 +1,11 @@
describe "NSDecimal" do
it "can be created from NSDecimalNumber" do
dn = NSDecimalNumber.decimalNumberWithString('123.456')
d = dn.decimalValue
d.class.should == NSDecimal
NSDecimalNumber.decimalNumberWithDecimal(d).should == dn
ptr = Pointer.new(NSDecimal.type)
ptr[0] = d
NSDecimalString(ptr, nil).should == dn.stringValue
end
end

17
test/spec/nsvalue_spec.rb Normal file
View File

@@ -0,0 +1,17 @@
describe "NSValue objects" do
it "can be created manually" do
s = MyStruct4C.new(1, 2, 3, 4)
pointer = Pointer.new(MyStruct4C.type)
pointer[0] = s
val = NSValue.value(pointer, withObjCType:MyStruct4C.type)
TestMethod.testMethodAcceptingMyStruct4CValue(val).should == true
end
=begin
it "can be created from Boxed#to_value" do
val = MyStruct4C.new(1, 2, 3, 4).to_value
val.objcType.should == MyStruct4C.type
TestMethod.testMethodAcceptingMyStruct4CValue(val).should == true
end
=end
end

View File

@@ -0,0 +1,8 @@
describe "Pointer" do
it "can point to C strings" do
vals = (1..10).to_a
ptr = Pointer.new(:string, vals.size)
vals.each_with_index { |v, i| ptr[i] = v.to_s }
TestMethod.testPointerToStrings(ptr, length:vals.size).should == vals.inject(0) { |m, v| m + v }
end
end

View File

@@ -0,0 +1,20 @@
class TestConformsToProtocolObject1
def requiredMethod1; 42; end
def requiredMethod2; 42; end
end
class TestConformsToProtocolObject2
def requiredMethod1; 42; end
def requiredMethod2; 42; end
def optionalMethod3; 42; end
end
describe "conformsToProtocol:" do
it "works on Ruby objects implementing required methods" do
TestMethod.testConformsToProtocol(TestConformsToProtocolObject1.new).should == true
end
it "works on Ruby objects implementing all methods" do
TestMethod.testConformsToProtocol(TestConformsToProtocolObject2.new).should == true
end
end

10
test/spec/string_spec.rb Normal file
View File

@@ -0,0 +1,10 @@
describe "Strings containing null terminators" do
it "can be compiled and used" do
s = "\x00"
s.size.should == 1
s = "\x00\x00"
s.size.should == 2
s = "\x00\x00\x00"
s.size.should == 3
end
end

60
test/vendor/code/code.h vendored Normal file
View File

@@ -0,0 +1,60 @@
#import <UIKit/UIKit.h>
struct MyStruct4C {
char a, b, c, d;
};
typedef id (^MyBlock)(void);
@protocol TestProtocol <NSObject>
- (BOOL)testProtocolFlag;
- (void)setTestProtocolFlag:(BOOL)testFlag;
@end
@protocol TestConformsToProtocol <NSObject>
@required
- (int)requiredMethod1;
- (int)requiredMethod2;
@optional
- (int)optionalMethod3;
@end
@interface TestMethod : NSObject <TestProtocol>
{
BOOL _testProtocolFlag;
}
- (CGSize)methodReturningCGSize;
- (CGRect)methodReturningCGRect;
+ (BOOL)testMethodReturningCGSize:(TestMethod *)testMethod;
+ (BOOL)testMethodReturningCGRect:(TestMethod *)testMethod;
+ (BOOL)testMethodAcceptingUIInterfaceOrientation:(UIInterfaceOrientation)orientation;
+ (BOOL)testMethodAcceptingUIEdgeInsets:(UIEdgeInsets)insets;
+ (id)testMethodCallingBlock:(MyBlock)block;
+ (BOOL)testMethodAcceptingCFType:(CFStringRef)cfstring;
+ (BOOL)testMethodAcceptingMyStruct4C:(struct MyStruct4C)s;
+ (BOOL)testMethodAcceptingMyStruct4C:(struct MyStruct4C)s another:(struct MyStruct4C)s2;
+ (BOOL)testMethodAcceptingMyStruct4CValue:(NSValue *)val;
+ (BOOL)testValueForKey:(Class)klass expected:(id)expected;
+ (int)testPointerToStrings:(char **)strs length:(int)len;
- (int)methodReturningLargeInt;
- (id)methodSendingNew:(Class)klass;
+ (BOOL)testConformsToProtocol:(id <TestConformsToProtocol>)obj;
@end
@interface TestIterator : NSObject <NSFastEnumeration>
@end
static inline int TestInlineFunction(int x, int y, int z) {
return x + y + z;
}
extern int lowerCaseConstant;
@interface lowerCaseClass : NSObject
@end

126
test/vendor/code/code.m vendored Normal file
View File

@@ -0,0 +1,126 @@
#include "code.h"
@implementation TestMethod
- (CGSize)methodReturningCGSize
{
return CGSizeMake(1, 2);
}
- (CGRect)methodReturningCGRect
{
return CGRectMake(1, 2, 3, 4);
}
+ (BOOL)testMethodReturningCGSize:(TestMethod *)testMethod
{
CGSize size = [testMethod methodReturningCGSize];
return size.width == 1 && size.height == 2;
}
+ (BOOL)testMethodReturningCGRect:(TestMethod *)testMethod
{
CGRect rect = [testMethod methodReturningCGRect];
return rect.origin.x == 1 && rect.origin.y == 2 && rect.size.width == 3 && rect.size.height == 4;
}
+ (BOOL)testMethodAcceptingUIInterfaceOrientation:(UIInterfaceOrientation)orientation
{
return orientation == UIInterfaceOrientationPortrait;
}
+ (BOOL)testMethodAcceptingUIEdgeInsets:(UIEdgeInsets)insets
{
return YES;
}
+ (BOOL)testMethodAcceptingCFType:(CFStringRef)cfstring
{
return [(id)cfstring isEqualToString:@"foo"];
}
+ (BOOL)testMethodAcceptingMyStruct4C:(struct MyStruct4C)s
{
return s.a == 1 && s.b == 2 && s.c == 3 && s.d == 4;
}
+ (BOOL)testMethodAcceptingMyStruct4C:(struct MyStruct4C)s another:(struct MyStruct4C)s2
{
return [self testMethodAcceptingMyStruct4C:s] && [self testMethodAcceptingMyStruct4C:s2];
}
+ (BOOL)testMethodAcceptingMyStruct4CValue:(NSValue *)value
{
struct MyStruct4C s;
[value getValue:&s];
return [self testMethodAcceptingMyStruct4C:s];
}
+ (id)testMethodCallingBlock:(MyBlock)block
{
if (block != nil) {
return block();
}
return nil;
}
+ (BOOL)testValueForKey:(id)obj expected:(id)expected
{
id val = [obj valueForKey:@"foo"];
return val == expected || (val != nil && [val isEqual:expected]);
}
+ (int)testPointerToStrings:(char **)strs length:(int)len
{
int total = 0;
for (int i = 0; i < len; i++) {
assert(strs[i] != NULL);
total += atoi(strs[i]);
}
return total;
}
- (int)methodReturningLargeInt
{
return 2147483646;
}
- (id)methodSendingNew:(Class)klass
{
return [[klass new] autorelease];
}
- (BOOL)testProtocolFlag
{
return _testProtocolFlag;
}
- (void)setTestProtocolFlag:(BOOL)testFlag
{
_testProtocolFlag = testFlag;
}
+ (BOOL)testConformsToProtocol:(id <TestConformsToProtocol>)obj
{
return [obj conformsToProtocol:@protocol(TestConformsToProtocol)];
}
@end
@implementation TestIterator
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
{
static NSArray *content = nil;
if (content == nil) {
content = [[NSArray arrayWithObjects:@"1", @"2", @"3", @"4", @"5", nil] retain];
}
return [content countByEnumeratingWithState:state objects:stackbuf count:len];
}
@end
int lowerCaseConstant = 42;
@implementation lowerCaseClass
@end