Implement multi-file Obj-C interop tests (interop_objc_tests task)
Extract most of interop_objc_smoke tests
This commit is contained in:
committed by
SvyatoslavScherbina
parent
f8e1409376
commit
cd6840a817
@@ -3445,6 +3445,11 @@ if (PlatformInfo.isAppleTarget(project)) {
|
||||
it.headers "$projectDir/interop/objc/smoke.h"
|
||||
}
|
||||
|
||||
createInterop("objcTests") {
|
||||
it.defFile 'interop/objc/objcTests.def'
|
||||
it.headers fileTree("$projectDir/interop/objc/tests") { include '**/*.h' }
|
||||
}
|
||||
|
||||
createInterop("objcMisc") {
|
||||
it.defFile 'interop/objc_with_initializer/objc_misc.def'
|
||||
it.headers "$projectDir/interop/objc_with_initializer/objc_misc.h"
|
||||
@@ -3475,7 +3480,7 @@ createInterop("withSpaces") {
|
||||
/**
|
||||
* Creates a task for the interop test. Configures runner and adds library and test build tasks.
|
||||
*/
|
||||
Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
||||
Task interopTestBase(String name, boolean multiFile, Closure<KonanInteropTest> configureClosure) {
|
||||
return KotlinNativeTestKt.createTest(project, name, KonanInteropTest) { task ->
|
||||
task.configure(configureClosure)
|
||||
if (task.enabled) {
|
||||
@@ -3487,7 +3492,7 @@ Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
||||
libraries {
|
||||
artifact lib
|
||||
}
|
||||
srcFiles task.getSources()
|
||||
srcFiles multiFile ? fileTree(task.source) { include '**/*.kt' } : task.getSources()
|
||||
baseDir "$testOutputLocal/$name"
|
||||
linkerOpts "-L$buildDir"
|
||||
extraOpts task.flags
|
||||
@@ -3498,6 +3503,13 @@ Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
||||
}
|
||||
}
|
||||
|
||||
Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
|
||||
return interopTestBase(name, false, configureClosure)
|
||||
}
|
||||
|
||||
Task interopTestMultifile(String name, Closure<KonanInteropTest> configureClosure) {
|
||||
return interopTestBase(name, true, configureClosure)
|
||||
}
|
||||
|
||||
standaloneTest("interop_objc_allocException") {
|
||||
dependsOnPlatformLibs(it)
|
||||
@@ -3721,6 +3733,26 @@ if (PlatformInfo.isAppleTarget(project)) {
|
||||
}
|
||||
}
|
||||
|
||||
interopTestMultifile("interop_objc_tests") {
|
||||
source = "interop/objc/tests/"
|
||||
interop = 'objcTests'
|
||||
flags = ['-tr', '-e', 'main']
|
||||
|
||||
doBeforeBuild {
|
||||
mkdir(buildDir)
|
||||
execKonanClang(project.target) {
|
||||
args fileTree("$projectDir/interop/objc/tests/") { include '**/*.m' }
|
||||
args "-lobjc", '-fobjc-arc'
|
||||
args '-fPIC', '-shared', '-o', "$buildDir/libobjctests.dylib"
|
||||
// Enable ARC optimizations to prevent some objects from leaking in Obj-C code due to exceptions:
|
||||
args '-O2'
|
||||
}
|
||||
if (project.target instanceof KonanTarget.IOS_X64) {
|
||||
UtilsKt.codesign(project, "$buildDir/libobjctests.dylib")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interopTest("interop_objc_global_initializer") {
|
||||
goldValue = "OK\n"
|
||||
source = "interop/objc_with_initializer/objc_test.kt"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
language = Objective-C
|
||||
headers = Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h Foundation/NSBundle.h
|
||||
headerFilter = **/interop/objc/tests/**.h \
|
||||
Foundation/NSArray.h Foundation/NSValue.h Foundation/NSString.h Foundation/NSStream.h \
|
||||
objc/objc.h Foundation/NSBundle.h
|
||||
linkerOpts = -lobjctests
|
||||
@@ -76,24 +76,6 @@ id createObjectWithFactory(id<ObjectFactory> factory) {
|
||||
return [factory create];
|
||||
}
|
||||
|
||||
@protocol BlockProvider
|
||||
@required
|
||||
-(int (^)(int)) block;
|
||||
@end;
|
||||
|
||||
int callProvidedBlock(id<BlockProvider> blockProvider, int argument) {
|
||||
return [blockProvider block](argument);
|
||||
}
|
||||
|
||||
@protocol BlockConsumer
|
||||
@required
|
||||
-(int)callBlock:(int (^)(int))block argument:(int)argument;
|
||||
@end;
|
||||
|
||||
int callPlusOneBlock(id<BlockConsumer> blockConsumer, int argument) {
|
||||
return [blockConsumer callBlock:^int(int p) { return p + 1; } argument:argument];
|
||||
}
|
||||
|
||||
@protocol CustomRetainMethods
|
||||
@required
|
||||
-(id)returnRetained:(id)obj __attribute__((ns_returns_retained));
|
||||
@@ -126,86 +108,6 @@ void useCustomRetainMethods(id<CustomRetainMethods> p) {
|
||||
[p returnRetainedBlock:retainedBlock]();
|
||||
}
|
||||
|
||||
NSObject* createNSObject() {
|
||||
return [NSObject new];
|
||||
}
|
||||
|
||||
@protocol ExceptionThrower
|
||||
-(void)throwException;
|
||||
@end;
|
||||
|
||||
@interface ExceptionThrowerManager : NSObject
|
||||
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower;
|
||||
@end;
|
||||
|
||||
@interface Blocks : NSObject
|
||||
+(BOOL)blockIsNull:(void (^)(void))block;
|
||||
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block;
|
||||
|
||||
@property (class) void (^nullBlock)(void);
|
||||
@property (class) void (^notNullBlock)(void);
|
||||
@end;
|
||||
|
||||
@interface TestVarargs : NSObject
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...;
|
||||
@property NSString* formatted;
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ...;
|
||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||
@end;
|
||||
|
||||
@interface TestVarargs (TestVarargsExtension)
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||
@end;
|
||||
|
||||
@interface TestVarargsSubclass : TestVarargs
|
||||
// Test clashes:
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args;
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||
@end;
|
||||
|
||||
@interface TestOverrideInit : NSObject
|
||||
-(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER;
|
||||
+(instancetype)createWithValue:(int)value;
|
||||
@end;
|
||||
|
||||
@interface MultipleInheritanceClashBase : NSObject
|
||||
@property (nonnull) MultipleInheritanceClashBase* delegate;
|
||||
@end;
|
||||
|
||||
@protocol MultipleInheritanceClash
|
||||
@optional
|
||||
@property (nullable) id<MultipleInheritanceClash> delegate;
|
||||
@end;
|
||||
|
||||
@interface MultipleInheritanceClash1 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
||||
@end;
|
||||
|
||||
@interface MultipleInheritanceClash2 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
||||
@property MultipleInheritanceClashBase* delegate;
|
||||
@end;
|
||||
|
||||
@interface TestClashingWithAny1 : NSObject
|
||||
-(NSString*)toString;
|
||||
-(NSString*)toString_;
|
||||
-(int)hashCode;
|
||||
-(BOOL)equals:(id _Nullable)other;
|
||||
@end;
|
||||
|
||||
@interface TestClashingWithAny2 : NSObject
|
||||
-(void)toString;
|
||||
-(void)hashCode;
|
||||
-(void)equals:(int)p; // May clash.
|
||||
@end;
|
||||
|
||||
@interface TestClashingWithAny3 : NSObject
|
||||
// Not clashing actually.
|
||||
-(NSString*)toString:(int)p;
|
||||
-(int)hashCode:(int)p;
|
||||
-(BOOL)equals;
|
||||
@end;
|
||||
|
||||
id getPrinterProtocolRaw() {
|
||||
return @protocol(Printer);
|
||||
}
|
||||
@@ -213,76 +115,3 @@ id getPrinterProtocolRaw() {
|
||||
Protocol* getPrinterProtocol() {
|
||||
return @protocol(Printer);
|
||||
}
|
||||
|
||||
@interface TestInitWithCustomSelector : NSObject
|
||||
-(instancetype)initCustom;
|
||||
@property BOOL custom;
|
||||
|
||||
+(instancetype _Nonnull)createCustom;
|
||||
@end;
|
||||
|
||||
@interface TestAllocNoRetain : NSObject
|
||||
@property BOOL ok;
|
||||
@end;
|
||||
|
||||
@interface CustomString : NSString
|
||||
- (instancetype)initWithValue:(int)value;
|
||||
@property int value;
|
||||
@end;
|
||||
|
||||
CustomString* _Nonnull createCustomString(int value) {
|
||||
return [[CustomString alloc] initWithValue:value];
|
||||
}
|
||||
|
||||
int getCustomStringValue(CustomString* str) {
|
||||
return str.value;
|
||||
}
|
||||
|
||||
extern BOOL customStringDeallocated;
|
||||
|
||||
@interface TestConstructorReturnsNull : NSObject
|
||||
- (instancetype)init;
|
||||
@end;
|
||||
|
||||
@interface TestCallableReferences : NSObject
|
||||
@property int value;
|
||||
- (int)instanceMethod;
|
||||
+ (int)classMethod:(int)first :(int)second;
|
||||
@end;
|
||||
|
||||
// [KT-36067] cinterop tool fails when there is a structure member named Companion
|
||||
|
||||
struct EnumFieldMangleStruct {
|
||||
enum {Companion, Any} smth;
|
||||
};
|
||||
struct EnumFieldMangleStruct enumMangledStruct = { Any };
|
||||
|
||||
struct MyStruct {
|
||||
int Companion; // simple clash
|
||||
int _Companion;
|
||||
int $_Companion;
|
||||
int super;
|
||||
};
|
||||
|
||||
struct MyStruct myStruct = {11, 12, 13, 14};
|
||||
|
||||
@protocol Proto
|
||||
@property int Companion; // clash on implementing
|
||||
@end;
|
||||
|
||||
@interface FooMangled : NSObject<Proto>
|
||||
//- (void) CompanionS; // mangleSimple does not support this: it may clash after mangling
|
||||
@end;
|
||||
|
||||
|
||||
@class DeallocListener;
|
||||
|
||||
@interface DeallocExecutor : NSObject
|
||||
@property DeallocListener* deallocListener;
|
||||
@end;
|
||||
|
||||
@interface DeallocListener : NSObject
|
||||
@property (weak) DeallocExecutor* deallocExecutor;
|
||||
@property BOOL deallocated;
|
||||
-(BOOL)deallocExecutorIsNil;
|
||||
@end;
|
||||
|
||||
@@ -16,27 +16,11 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
fun run() {
|
||||
testConversions()
|
||||
// TODO: migrate remaining tests to interop/objc/tests/
|
||||
testTypeOps()
|
||||
testWeakRefs()
|
||||
testExceptions()
|
||||
testBlocks()
|
||||
testCustomRetain()
|
||||
testVarargs()
|
||||
testOverrideInit()
|
||||
testMultipleInheritanceClash()
|
||||
testClashingWithAny()
|
||||
testInitWithCustomSelector()
|
||||
testAllocNoRetain()
|
||||
testNSOutputStreamToMemoryConstructor()
|
||||
testExportObjCClass()
|
||||
testCustomString()
|
||||
testLocalizedStrings()
|
||||
testConstructorReturnsNull()
|
||||
testCallableReferences()
|
||||
testMangling()
|
||||
testSharing()
|
||||
testObjCWeakRef()
|
||||
|
||||
assertEquals(2, ForwardDeclaredEnum.TWO.value)
|
||||
|
||||
@@ -197,80 +181,6 @@ fun testTypeOps() {
|
||||
assertFails { MutablePairImpl(1, 2).asAny() as Foo }
|
||||
}
|
||||
|
||||
fun testConversions() {
|
||||
testMethodsOfAny(emptyList<Nothing>(), NSArray())
|
||||
testMethodsOfAny(listOf(1, "foo"), nsArrayOf(1, "foo"))
|
||||
testMethodsOfAny(42, NSNumber.numberWithInt(42), 17)
|
||||
testMethodsOfAny(true, NSNumber.numberWithBool(true), false)
|
||||
}
|
||||
|
||||
fun testMethodsOfAny(kotlinObject: Any, equalNsObject: NSObject, otherObject: Any = Any()) {
|
||||
assertEquals(kotlinObject.hashCode(), equalNsObject.hashCode())
|
||||
assertEquals(kotlinObject.toString(), equalNsObject.toString())
|
||||
assertEquals(kotlinObject, equalNsObject)
|
||||
assertEquals(equalNsObject, kotlinObject)
|
||||
assertNotEquals(equalNsObject, otherObject)
|
||||
}
|
||||
|
||||
fun testWeakRefs() {
|
||||
testWeakReference({ createNSObject()!! })
|
||||
|
||||
createAndAbandonWeakRef(NSObject())
|
||||
|
||||
testWeakReference({ NSArray.arrayWithArray(listOf(42)) as NSArray })
|
||||
}
|
||||
|
||||
fun testWeakReference(block: () -> NSObject) {
|
||||
val ref = autoreleasepool {
|
||||
createAndTestWeakReference(block)
|
||||
}
|
||||
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertNull(ref.get())
|
||||
}
|
||||
|
||||
fun createAndTestWeakReference(block: () -> NSObject): WeakReference<NSObject> {
|
||||
val ref = createWeakReference(block)
|
||||
assertNotNull(ref.get())
|
||||
assertEquals(ref.get()!!.hash(), ref.get()!!.hash())
|
||||
return ref
|
||||
}
|
||||
|
||||
fun createWeakReference(block: () -> NSObject) = WeakReference(block())
|
||||
|
||||
fun createAndAbandonWeakRef(obj: NSObject) {
|
||||
WeakReference(obj)
|
||||
}
|
||||
|
||||
fun testExceptions() {
|
||||
assertFailsWith<MyException> {
|
||||
ExceptionThrowerManager.throwExceptionWith(object : NSObject(), ExceptionThrowerProtocol {
|
||||
override fun throwException() {
|
||||
throw MyException()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun testBlocks() {
|
||||
assertTrue(Blocks.blockIsNull(null))
|
||||
assertFalse(Blocks.blockIsNull({}))
|
||||
|
||||
assertEquals(null, Blocks.nullBlock)
|
||||
assertNotEquals(null, Blocks.notNullBlock)
|
||||
|
||||
assertEquals(10, Blocks.same({ a, b, c, d -> a + b + c + d })!!(1, 2, 3, 4))
|
||||
|
||||
assertEquals(222, callProvidedBlock(object : NSObject(), BlockProviderProtocol {
|
||||
override fun block(): (Int) -> Int = { it * 2 }
|
||||
}, 111))
|
||||
|
||||
assertEquals(322, callPlusOneBlock(object : NSObject(), BlockConsumerProtocol {
|
||||
override fun callBlock(block: ((Int) -> Int)?, argument: Int) = block!!(argument)
|
||||
}, 321))
|
||||
}
|
||||
|
||||
private lateinit var retainedMustNotBeDeallocated: MustNotBeDeallocated
|
||||
|
||||
fun testCustomRetain() {
|
||||
@@ -299,156 +209,6 @@ fun testCustomRetain() {
|
||||
assertFalse(unexpectedDeallocation)
|
||||
}
|
||||
|
||||
fun testVarargs() {
|
||||
assertEquals(
|
||||
"a b -1",
|
||||
TestVarargs.testVarargsWithFormat(
|
||||
"%@ %s %d",
|
||||
"a" as NSString, "b".cstr, (-1).toByte()
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"2 3 9223372036854775807",
|
||||
TestVarargs(
|
||||
"%d %d %lld",
|
||||
2.toShort(), 3, Long.MAX_VALUE
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"0.1 0.2 1 0",
|
||||
TestVarargs.create(
|
||||
"%.1f %.1lf %d %d",
|
||||
0.1.toFloat(), 0.2, true, false
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"1 2 3",
|
||||
TestVarargs(
|
||||
format = "%d %d %d",
|
||||
args = *arrayOf(1, 2, 3)
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"4 5 6",
|
||||
TestVarargs(
|
||||
args = *arrayOf(4, *arrayOf(5, 6)),
|
||||
format = "%d %d %d"
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"7",
|
||||
TestVarargsSubclass.stringWithFormat(
|
||||
"%d",
|
||||
7
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun testOverrideInit() {
|
||||
assertEquals(42, (TestOverrideInitImpl.createWithValue(42) as TestOverrideInitImpl).value)
|
||||
}
|
||||
|
||||
class TestOverrideInitImpl @OverrideInit constructor(val value: Int) : TestOverrideInit(value) {
|
||||
companion object : TestOverrideInitMeta()
|
||||
}
|
||||
|
||||
private class MyException : Throwable()
|
||||
|
||||
fun testMultipleInheritanceClash() {
|
||||
val clash1 = MultipleInheritanceClash1()
|
||||
val clash2 = MultipleInheritanceClash2()
|
||||
|
||||
clash1.delegate = clash1
|
||||
assertEquals(clash1, clash1.delegate)
|
||||
clash1.setDelegate(clash2)
|
||||
assertEquals(clash2, clash1.delegate())
|
||||
|
||||
clash2.delegate = clash1
|
||||
assertEquals(clash1, clash2.delegate)
|
||||
clash2.setDelegate(clash2)
|
||||
assertEquals(clash2, clash2.delegate())
|
||||
}
|
||||
|
||||
fun testClashingWithAny() {
|
||||
assertEquals("description", TestClashingWithAny1().toString())
|
||||
assertEquals("toString", TestClashingWithAny1().toString_())
|
||||
assertEquals("toString_", TestClashingWithAny1().toString__())
|
||||
assertEquals(1, TestClashingWithAny1().hashCode())
|
||||
assertEquals(31, TestClashingWithAny1().hashCode_())
|
||||
assertFalse(TestClashingWithAny1().equals(TestClashingWithAny1()))
|
||||
assertTrue(TestClashingWithAny1().equals_(TestClashingWithAny1()))
|
||||
|
||||
assertEquals("description", TestClashingWithAny2().toString())
|
||||
assertEquals(Unit, TestClashingWithAny2().toString_())
|
||||
assertEquals(2, TestClashingWithAny2().hashCode())
|
||||
assertEquals(Unit, TestClashingWithAny2().hashCode_())
|
||||
assertFalse(TestClashingWithAny2().equals(TestClashingWithAny2()))
|
||||
assertEquals(Unit, TestClashingWithAny2().equals_(42))
|
||||
|
||||
assertEquals("description", TestClashingWithAny3().toString())
|
||||
assertEquals("toString:11", TestClashingWithAny3().toString(11))
|
||||
assertEquals(3, TestClashingWithAny3().hashCode())
|
||||
assertEquals(4, TestClashingWithAny3().hashCode(3))
|
||||
assertFalse(TestClashingWithAny3().equals(TestClashingWithAny3()))
|
||||
assertTrue(TestClashingWithAny3().equals())
|
||||
}
|
||||
|
||||
fun testInitWithCustomSelector() {
|
||||
assertFalse(TestInitWithCustomSelector().custom)
|
||||
assertTrue(TestInitWithCustomSelector(custom = Unit).custom)
|
||||
|
||||
val customSubclass: TestInitWithCustomSelector = TestInitWithCustomSelectorSubclass.createCustom()
|
||||
assertTrue(customSubclass is TestInitWithCustomSelectorSubclass)
|
||||
assertTrue(customSubclass.custom)
|
||||
|
||||
// Test side effect:
|
||||
var ok = false
|
||||
assertTrue(TestInitWithCustomSelector(run { ok = true }).custom)
|
||||
assertTrue(ok)
|
||||
}
|
||||
|
||||
private class TestInitWithCustomSelectorSubclass : TestInitWithCustomSelector {
|
||||
@OverrideInit constructor(custom: Unit) : super(custom) {
|
||||
assertSame(Unit, custom)
|
||||
}
|
||||
|
||||
companion object : TestInitWithCustomSelectorMeta()
|
||||
}
|
||||
|
||||
fun testAllocNoRetain() {
|
||||
// Ensure that calling Kotlin constructor generated for Objective-C initializer doesn't result in
|
||||
// redundant retain-release sequence for `alloc` result, since it may provoke specific bugs to reproduce, e.g.
|
||||
// the one found in [[NSOutputStream alloc] initToMemory] sequence where initToMemory deallocates its receiver
|
||||
// forcibly when replacing it with other object: (to be compiled with ARC enabled)
|
||||
/*
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void* mem;
|
||||
NSOutputStream* allocated = nil;
|
||||
|
||||
int main() {
|
||||
allocated = [NSOutputStream alloc];
|
||||
NSOutputStream* initialized = [allocated initToMemory];
|
||||
mem = calloc(1, 0x10); // To corrupt the 'allocated' object header.
|
||||
allocated = nil; // Crashes here in objc_release.
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
assertTrue(TestAllocNoRetain().ok)
|
||||
}
|
||||
|
||||
fun testNSOutputStreamToMemoryConstructor() {
|
||||
val stream: Any = NSOutputStream(toMemory = Unit)
|
||||
assertTrue(stream is NSOutputStream)
|
||||
}
|
||||
|
||||
private const val TestExportObjCClass1Name = "TestExportObjCClass"
|
||||
@ExportObjCClass(TestExportObjCClass1Name) class TestExportObjCClass1 : NSObject()
|
||||
|
||||
@@ -466,21 +226,6 @@ fun testExportObjCClass() {
|
||||
xor (TestExportObjCClass4().objCClassName == TestExportObjCClass34Name))
|
||||
}
|
||||
|
||||
fun testCustomString() {
|
||||
assertFalse(customStringDeallocated)
|
||||
|
||||
fun test() = autoreleasepool {
|
||||
val str: String = createCustomString(321)
|
||||
assertEquals("321", str)
|
||||
assertEquals("CustomString", str.objCClassName)
|
||||
assertEquals(321, getCustomStringValue(str))
|
||||
}
|
||||
|
||||
test()
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertTrue(customStringDeallocated)
|
||||
}
|
||||
|
||||
fun testLocalizedStrings() {
|
||||
val key = "screen_main_plural_string"
|
||||
val localizedString = NSBundle.mainBundle.localizedStringForKey(key, value = "", table = "Localizable")
|
||||
@@ -488,140 +233,7 @@ fun testLocalizedStrings() {
|
||||
assertEquals("Plural: 5 apples", string)
|
||||
}
|
||||
|
||||
fun testConstructorReturnsNull() {
|
||||
assertFailsWith<NullPointerException>() {
|
||||
TestConstructorReturnsNull()
|
||||
}
|
||||
}
|
||||
|
||||
fun testCallableReferences() {
|
||||
val createTestCallableReferences = ::TestCallableReferences
|
||||
assertEquals("<init>", createTestCallableReferences.name)
|
||||
val testCallableReferences: Any = createTestCallableReferences()
|
||||
assertTrue(testCallableReferences is TestCallableReferences)
|
||||
|
||||
val valueRef: kotlin.reflect.KMutableProperty0<Int> = testCallableReferences::value
|
||||
assertEquals("value", valueRef.name)
|
||||
assertEquals(0, valueRef())
|
||||
valueRef.set(42)
|
||||
assertEquals(42, valueRef())
|
||||
|
||||
val classMethodRef = (TestCallableReferences)::classMethod
|
||||
assertEquals("classMethod", classMethodRef.name)
|
||||
assertEquals(3, classMethodRef(1, 2))
|
||||
|
||||
val instanceMethodRef = TestCallableReferences::instanceMethod
|
||||
assertEquals("instanceMethod", instanceMethodRef.name)
|
||||
assertEquals(42, instanceMethodRef(testCallableReferences))
|
||||
|
||||
val boundInstanceMethodRef = testCallableReferences::instanceMethod
|
||||
assertEquals("instanceMethod", boundInstanceMethodRef.name)
|
||||
assertEquals(42, boundInstanceMethodRef())
|
||||
}
|
||||
|
||||
fun testMangling() {
|
||||
assertEquals(11, myStruct.`Companion$`)
|
||||
assertEquals(12, myStruct._Companion)
|
||||
assertEquals(13, myStruct.`$_Companion`)
|
||||
assertEquals(14, myStruct.`super`)
|
||||
|
||||
val objc = FooMangled()
|
||||
objc.`Companion$` = 99
|
||||
assertEquals(99, objc.Companion())
|
||||
assertEquals(99, objc.`Companion$`)
|
||||
|
||||
enumMangledStruct.smth = Companion
|
||||
assertEquals(Companion, enumMangledStruct.smth)
|
||||
}
|
||||
|
||||
private class NSObjectImpl : NSObject() {
|
||||
var x = 111
|
||||
}
|
||||
|
||||
fun testSharing() = withWorker {
|
||||
val obj = NSObjectImpl()
|
||||
val array = nsArrayOf(obj)
|
||||
|
||||
assertFalse(obj.isFrozen)
|
||||
|
||||
runInWorker {
|
||||
assertFailsWith<IncorrectDereferenceException> {
|
||||
array.objectAtIndex(0)
|
||||
}
|
||||
}
|
||||
|
||||
obj.x = 222
|
||||
obj.freeze()
|
||||
assertTrue(obj.isFrozen)
|
||||
|
||||
runInWorker {
|
||||
val obj1 = array.objectAtIndex(0) as NSObjectImpl
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
obj1.x = 333
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(222, obj.x)
|
||||
|
||||
// TODO: test [obj release] etc.
|
||||
}
|
||||
|
||||
fun testObjCWeakRef() {
|
||||
val deallocListener = DeallocListener()
|
||||
assertFalse(deallocListener.deallocated)
|
||||
|
||||
testObjCWeakRef0(deallocListener)
|
||||
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertTrue(deallocListener.deallocated)
|
||||
assertTrue(deallocListener.deallocExecutorIsNil())
|
||||
}
|
||||
|
||||
fun testObjCWeakRef0(deallocListener: DeallocListener) = withWorker {
|
||||
assertTrue(deallocListener.deallocExecutorIsNil())
|
||||
|
||||
val obj = object : DeallocExecutor() {}
|
||||
deallocListener.deallocExecutor = obj
|
||||
obj.deallocListener = deallocListener
|
||||
|
||||
assertFalse(deallocListener.deallocExecutorIsNil())
|
||||
|
||||
// TODO: can't actually test, Obj-C runtime doesn't expect _tryRetain throwing an exception.
|
||||
// runInWorker {
|
||||
// assertFailsWith<IncorrectDereferenceException> {
|
||||
// deallocListener.deallocExecutorIsNil()
|
||||
// }
|
||||
// }
|
||||
|
||||
obj.freeze()
|
||||
|
||||
runInWorker {
|
||||
// [deallocListener.deallocExecutorIsNil()] calls deallocExecutor getter, which retains [obj] and either
|
||||
// puts it to autoreleasepool or releases it immediately (Obj-C ARC optimization).
|
||||
// Wrap the call to autoreleasepool to ensure [obj] will be released:
|
||||
autoreleasepool {
|
||||
assertFalse(deallocListener.deallocExecutorIsNil())
|
||||
}
|
||||
// Process release of Kotlin reference to [obj] in any case:
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Worker.runInWorker(block: () -> Unit) {
|
||||
block.freeze()
|
||||
val future = this.execute(TransferMode.SAFE, { block }) {
|
||||
it()
|
||||
}
|
||||
future.result // Throws on failure.
|
||||
}
|
||||
|
||||
private val Any.objCClassName: String
|
||||
get() = object_getClassName(this)!!.toKString()
|
||||
|
||||
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
|
||||
elements.forEach {
|
||||
this.addObject(it as ObjCObject)
|
||||
}
|
||||
}
|
||||
|
||||
fun Any?.asAny(): Any? = this
|
||||
|
||||
@@ -95,263 +95,3 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
|
||||
return block;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation ExceptionThrowerManager
|
||||
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower {
|
||||
[thrower throwException];
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation Blocks
|
||||
+(BOOL)blockIsNull:(void (^)(void))block {
|
||||
return block == nil;
|
||||
}
|
||||
|
||||
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block {
|
||||
return block;
|
||||
}
|
||||
|
||||
+(void (^)(void)) nullBlock {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(void (^)(void)) notNullBlock {
|
||||
return ^{};
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation TestVarargs
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ... {
|
||||
self = [super init];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
self.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... {
|
||||
TestVarargs* result = [[self alloc] init];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
result.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ... {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation TestVarargsSubclass
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestOverrideInit
|
||||
-(instancetype)initWithValue:(int)value {
|
||||
return self = [super init];
|
||||
}
|
||||
|
||||
+(instancetype)createWithValue:(int)value {
|
||||
return [[self alloc] initWithValue:value];
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation MultipleInheritanceClashBase
|
||||
@end;
|
||||
|
||||
@implementation MultipleInheritanceClash1
|
||||
@end;
|
||||
|
||||
@implementation MultipleInheritanceClash2
|
||||
@end;
|
||||
|
||||
@implementation TestClashingWithAny1
|
||||
-(NSString*)description {
|
||||
return @"description";
|
||||
}
|
||||
|
||||
-(NSString*)toString {
|
||||
return @"toString";
|
||||
}
|
||||
|
||||
-(NSString*)toString_ {
|
||||
return @"toString_";
|
||||
}
|
||||
|
||||
-(NSUInteger)hash {
|
||||
return 1;
|
||||
}
|
||||
|
||||
-(int)hashCode {
|
||||
return 31;
|
||||
}
|
||||
|
||||
-(BOOL)equals:(id _Nullable)other {
|
||||
return YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestClashingWithAny2
|
||||
-(NSString*)description {
|
||||
return @"description";
|
||||
}
|
||||
|
||||
-(void)toString {
|
||||
}
|
||||
|
||||
-(NSUInteger)hash {
|
||||
return 2;
|
||||
}
|
||||
|
||||
-(void)hashCode {
|
||||
}
|
||||
|
||||
-(void)equals:(int)p {
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestClashingWithAny3
|
||||
-(NSString*)description {
|
||||
return @"description";
|
||||
}
|
||||
|
||||
-(NSString*)toString:(int)p {
|
||||
return [NSString stringWithFormat:@"%s:%d", "toString", p];
|
||||
}
|
||||
|
||||
-(NSUInteger)hash {
|
||||
return 3;
|
||||
}
|
||||
|
||||
-(int)hashCode:(int)p {
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
-(BOOL)equals {
|
||||
return YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestInitWithCustomSelector
|
||||
|
||||
-(instancetype)initCustom {
|
||||
if (self = [super init]) {
|
||||
self.custom = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.custom = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+(instancetype)createCustom {
|
||||
return [[self alloc] initCustom];
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation TestAllocNoRetain
|
||||
-(instancetype)init {
|
||||
__weak id weakSelf = self;
|
||||
self = [TestAllocNoRetain alloc];
|
||||
if (self = [super init]) {
|
||||
// Ensure that original self value was deallocated:
|
||||
self.ok = (weakSelf == nil);
|
||||
// So it's RC was 1, which means there wasn't redundant retain applied to it.
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end;
|
||||
|
||||
BOOL customStringDeallocated = NO;
|
||||
|
||||
@implementation CustomString {
|
||||
NSString* delegate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithValue:(int)value {
|
||||
if (self = [super init]) {
|
||||
self->delegate = @(value).description;
|
||||
self.value = value;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unichar)characterAtIndex:(NSUInteger)index {
|
||||
return [self->delegate characterAtIndex:index];
|
||||
}
|
||||
|
||||
- (NSUInteger)length {
|
||||
return self->delegate.length;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
customStringDeallocated = YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestConstructorReturnsNull
|
||||
- (instancetype)init {
|
||||
return nil;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestCallableReferences
|
||||
- (int)instanceMethod {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
+ (int)classMethod:(int)first :(int)second {
|
||||
return first + second;
|
||||
}
|
||||
@end;
|
||||
|
||||
|
||||
// [KT-36067] mangling
|
||||
@implementation FooMangled : NSObject
|
||||
@synthesize Companion;
|
||||
@end;
|
||||
|
||||
|
||||
@implementation DeallocExecutor
|
||||
-(void)dealloc {
|
||||
self.deallocListener.deallocated = YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation DeallocListener
|
||||
-(BOOL)deallocExecutorIsNil {
|
||||
return self.deallocExecutor == nil;
|
||||
}
|
||||
@end;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface TestAllocNoRetain : NSObject
|
||||
@property BOOL ok;
|
||||
@end;
|
||||
@@ -0,0 +1,27 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testAllocNoRetain() {
|
||||
// Ensure that calling Kotlin constructor generated for Objective-C initializer doesn't result in
|
||||
// redundant retain-release sequence for `alloc` result, since it may provoke specific bugs to reproduce, e.g.
|
||||
// the one found in [[NSOutputStream alloc] initToMemory] sequence where initToMemory deallocates its receiver
|
||||
// forcibly when replacing it with other object: (to be compiled with ARC enabled)
|
||||
/*
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void* mem;
|
||||
NSOutputStream* allocated = nil;
|
||||
|
||||
int main() {
|
||||
allocated = [NSOutputStream alloc];
|
||||
NSOutputStream* initialized = [allocated initToMemory];
|
||||
mem = calloc(1, 0x10); // To corrupt the 'allocated' object header.
|
||||
allocated = nil; // Crashes here in objc_release.
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
assertTrue(TestAllocNoRetain().ok)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#import "allocNoRetain.h"
|
||||
|
||||
@implementation TestAllocNoRetain
|
||||
-(instancetype)init {
|
||||
__weak id weakSelf = self;
|
||||
self = [TestAllocNoRetain alloc];
|
||||
if (self = [super init]) {
|
||||
// Ensure that original self value was deallocated:
|
||||
self.ok = (weakSelf == nil);
|
||||
// So it's RC was 1, which means there wasn't redundant retain applied to it.
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,27 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@protocol BlockProvider
|
||||
@required
|
||||
-(int (^)(int)) block;
|
||||
@end;
|
||||
|
||||
int callProvidedBlock(id<BlockProvider> blockProvider, int argument) {
|
||||
return [blockProvider block](argument);
|
||||
}
|
||||
|
||||
@protocol BlockConsumer
|
||||
@required
|
||||
-(int)callBlock:(int (^)(int))block argument:(int)argument;
|
||||
@end;
|
||||
|
||||
int callPlusOneBlock(id<BlockConsumer> blockConsumer, int argument) {
|
||||
return [blockConsumer callBlock:^int(int p) { return p + 1; } argument:argument];
|
||||
}
|
||||
|
||||
@interface Blocks : NSObject
|
||||
+(BOOL)blockIsNull:(void (^)(void))block;
|
||||
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block;
|
||||
|
||||
@property (class) void (^nullBlock)(void);
|
||||
@property (class) void (^notNullBlock)(void);
|
||||
@end;
|
||||
@@ -0,0 +1,21 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testBlocks() {
|
||||
assertTrue(Blocks.blockIsNull(null))
|
||||
assertFalse(Blocks.blockIsNull({}))
|
||||
|
||||
assertEquals(null, Blocks.nullBlock)
|
||||
assertNotEquals(null, Blocks.notNullBlock)
|
||||
|
||||
assertEquals(10, Blocks.same({ a, b, c, d -> a + b + c + d })!!(1, 2, 3, 4))
|
||||
|
||||
assertEquals(222, callProvidedBlock(object : NSObject(), BlockProviderProtocol {
|
||||
override fun block(): (Int) -> Int = { it * 2 }
|
||||
}, 111))
|
||||
|
||||
assertEquals(322, callPlusOneBlock(object : NSObject(), BlockConsumerProtocol {
|
||||
override fun callBlock(block: ((Int) -> Int)?, argument: Int) = block!!(argument)
|
||||
}, 321))
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#import "blocks.h"
|
||||
|
||||
@implementation Blocks
|
||||
+(BOOL)blockIsNull:(void (^)(void))block {
|
||||
return block == nil;
|
||||
}
|
||||
|
||||
+(int (^)(int, int, int, int))same:(int (^)(int, int, int, int))block {
|
||||
return block;
|
||||
}
|
||||
|
||||
+(void (^)(void)) nullBlock {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(void (^)(void)) notNullBlock {
|
||||
return ^{};
|
||||
}
|
||||
|
||||
@end;
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface TestCallableReferences : NSObject
|
||||
@property int value;
|
||||
- (int)instanceMethod;
|
||||
+ (int)classMethod:(int)first :(int)second;
|
||||
@end;
|
||||
@@ -0,0 +1,28 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testCallableReferences() {
|
||||
val createTestCallableReferences = ::TestCallableReferences
|
||||
assertEquals("<init>", createTestCallableReferences.name)
|
||||
val testCallableReferences: Any = createTestCallableReferences()
|
||||
assertTrue(testCallableReferences is TestCallableReferences)
|
||||
|
||||
val valueRef: kotlin.reflect.KMutableProperty0<Int> = testCallableReferences::value
|
||||
assertEquals("value", valueRef.name)
|
||||
assertEquals(0, valueRef())
|
||||
valueRef.set(42)
|
||||
assertEquals(42, valueRef())
|
||||
|
||||
val classMethodRef = (TestCallableReferences)::classMethod
|
||||
assertEquals("classMethod", classMethodRef.name)
|
||||
assertEquals(3, classMethodRef(1, 2))
|
||||
|
||||
val instanceMethodRef = TestCallableReferences::instanceMethod
|
||||
assertEquals("instanceMethod", instanceMethodRef.name)
|
||||
assertEquals(42, instanceMethodRef(testCallableReferences))
|
||||
|
||||
val boundInstanceMethodRef = testCallableReferences::instanceMethod
|
||||
assertEquals("instanceMethod", boundInstanceMethodRef.name)
|
||||
assertEquals(42, boundInstanceMethodRef())
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#import "callableReferences.h"
|
||||
|
||||
@implementation TestCallableReferences
|
||||
- (int)instanceMethod {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
+ (int)classMethod:(int)first :(int)second {
|
||||
return first + second;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,22 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@interface TestClashingWithAny1 : NSObject
|
||||
-(NSString*)toString;
|
||||
-(NSString*)toString_;
|
||||
-(int)hashCode;
|
||||
-(BOOL)equals:(id _Nullable)other;
|
||||
@end;
|
||||
|
||||
@interface TestClashingWithAny2 : NSObject
|
||||
-(void)toString;
|
||||
-(void)hashCode;
|
||||
-(void)equals:(int)p; // May clash.
|
||||
@end;
|
||||
|
||||
@interface TestClashingWithAny3 : NSObject
|
||||
// Not clashing actually.
|
||||
-(NSString*)toString:(int)p;
|
||||
-(int)hashCode:(int)p;
|
||||
-(BOOL)equals;
|
||||
@end;
|
||||
@@ -0,0 +1,27 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testClashingWithAny() {
|
||||
assertEquals("description", TestClashingWithAny1().toString())
|
||||
assertEquals("toString", TestClashingWithAny1().toString_())
|
||||
assertEquals("toString_", TestClashingWithAny1().toString__())
|
||||
assertEquals(1, TestClashingWithAny1().hashCode())
|
||||
assertEquals(31, TestClashingWithAny1().hashCode_())
|
||||
assertFalse(TestClashingWithAny1().equals(TestClashingWithAny1()))
|
||||
assertTrue(TestClashingWithAny1().equals_(TestClashingWithAny1()))
|
||||
|
||||
assertEquals("description", TestClashingWithAny2().toString())
|
||||
assertEquals(Unit, TestClashingWithAny2().toString_())
|
||||
assertEquals(2, TestClashingWithAny2().hashCode())
|
||||
assertEquals(Unit, TestClashingWithAny2().hashCode_())
|
||||
assertFalse(TestClashingWithAny2().equals(TestClashingWithAny2()))
|
||||
assertEquals(Unit, TestClashingWithAny2().equals_(42))
|
||||
|
||||
assertEquals("description", TestClashingWithAny3().toString())
|
||||
assertEquals("toString:11", TestClashingWithAny3().toString(11))
|
||||
assertEquals(3, TestClashingWithAny3().hashCode())
|
||||
assertEquals(4, TestClashingWithAny3().hashCode(3))
|
||||
assertFalse(TestClashingWithAny3().equals(TestClashingWithAny3()))
|
||||
assertTrue(TestClashingWithAny3().equals())
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#import "clashingWithAny.h"
|
||||
|
||||
@implementation TestClashingWithAny1
|
||||
-(NSString*)description {
|
||||
return @"description";
|
||||
}
|
||||
|
||||
-(NSString*)toString {
|
||||
return @"toString";
|
||||
}
|
||||
|
||||
-(NSString*)toString_ {
|
||||
return @"toString_";
|
||||
}
|
||||
|
||||
-(NSUInteger)hash {
|
||||
return 1;
|
||||
}
|
||||
|
||||
-(int)hashCode {
|
||||
return 31;
|
||||
}
|
||||
|
||||
-(BOOL)equals:(id _Nullable)other {
|
||||
return YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestClashingWithAny2
|
||||
-(NSString*)description {
|
||||
return @"description";
|
||||
}
|
||||
|
||||
-(void)toString {
|
||||
}
|
||||
|
||||
-(NSUInteger)hash {
|
||||
return 2;
|
||||
}
|
||||
|
||||
-(void)hashCode {
|
||||
}
|
||||
|
||||
-(void)equals:(int)p {
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestClashingWithAny3
|
||||
-(NSString*)description {
|
||||
return @"description";
|
||||
}
|
||||
|
||||
-(NSString*)toString:(int)p {
|
||||
return [NSString stringWithFormat:@"%s:%d", "toString", p];
|
||||
}
|
||||
|
||||
-(NSUInteger)hash {
|
||||
return 3;
|
||||
}
|
||||
|
||||
-(int)hashCode:(int)p {
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
-(BOOL)equals {
|
||||
return YES;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface TestConstructorReturnsNull : NSObject
|
||||
- (instancetype)init;
|
||||
@end;
|
||||
@@ -0,0 +1,9 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testConstructorReturnsNull() {
|
||||
assertFailsWith<NullPointerException>() {
|
||||
TestConstructorReturnsNull()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "constructorReturnsNull.h"
|
||||
|
||||
@implementation TestConstructorReturnsNull
|
||||
- (instancetype)init {
|
||||
return nil;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,18 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testConversions() {
|
||||
testMethodsOfAny(emptyList<Nothing>(), NSArray())
|
||||
testMethodsOfAny(listOf(1, "foo"), nsArrayOf(1, "foo"))
|
||||
testMethodsOfAny(42, NSNumber.numberWithInt(42), 17)
|
||||
testMethodsOfAny(true, NSNumber.numberWithBool(true), false)
|
||||
}
|
||||
|
||||
private fun testMethodsOfAny(kotlinObject: Any, equalNsObject: NSObject, otherObject: Any = Any()) {
|
||||
assertEquals(kotlinObject.hashCode(), equalNsObject.hashCode())
|
||||
assertEquals(kotlinObject.toString(), equalNsObject.toString())
|
||||
assertEquals(kotlinObject, equalNsObject)
|
||||
assertEquals(equalNsObject, kotlinObject)
|
||||
assertNotEquals(equalNsObject, otherObject)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
@interface CustomString : NSString
|
||||
- (instancetype)initWithValue:(int)value;
|
||||
@property int value;
|
||||
@end;
|
||||
|
||||
CustomString* _Nonnull createCustomString(int value) {
|
||||
return [[CustomString alloc] initWithValue:value];
|
||||
}
|
||||
|
||||
int getCustomStringValue(CustomString* str) {
|
||||
return str.value;
|
||||
}
|
||||
|
||||
extern BOOL customStringDeallocated;
|
||||
@@ -0,0 +1,21 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testCustomString() {
|
||||
assertFalse(customStringDeallocated)
|
||||
|
||||
fun test() = autoreleasepool {
|
||||
val str: String = createCustomString(321)
|
||||
assertEquals("321", str)
|
||||
assertEquals("CustomString", str.objCClassName)
|
||||
assertEquals(321, getCustomStringValue(str))
|
||||
}
|
||||
|
||||
test()
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertTrue(customStringDeallocated)
|
||||
}
|
||||
|
||||
private val Any.objCClassName: String
|
||||
get() = object_getClassName(this)!!.toKString()
|
||||
@@ -0,0 +1,32 @@
|
||||
#import "customString.h"
|
||||
|
||||
BOOL customStringDeallocated = NO;
|
||||
|
||||
@implementation CustomString {
|
||||
NSString* delegate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithValue:(int)value {
|
||||
if (self = [super init]) {
|
||||
self->delegate = @(value).description;
|
||||
self.value = value;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unichar)characterAtIndex:(NSUInteger)index {
|
||||
return [self->delegate characterAtIndex:index];
|
||||
}
|
||||
|
||||
- (NSUInteger)length {
|
||||
return self->delegate.length;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
customStringDeallocated = YES;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@protocol ExceptionThrower
|
||||
-(void)throwException;
|
||||
@end;
|
||||
|
||||
@interface ExceptionThrowerManager : NSObject
|
||||
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower;
|
||||
@end;
|
||||
@@ -0,0 +1,15 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testExceptions() {
|
||||
assertFailsWith<MyException> {
|
||||
ExceptionThrowerManager.throwExceptionWith(object : NSObject(), ExceptionThrowerProtocol {
|
||||
override fun throwException() {
|
||||
throw MyException()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class MyException : Throwable()
|
||||
@@ -0,0 +1,7 @@
|
||||
#import "exceptions.h"
|
||||
|
||||
@implementation ExceptionThrowerManager
|
||||
+(void)throwExceptionWith:(id<ExceptionThrower>)thrower {
|
||||
[thrower throwException];
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface TestInitWithCustomSelector : NSObject
|
||||
-(instancetype)initCustom;
|
||||
@property BOOL custom;
|
||||
|
||||
+(instancetype _Nonnull)createCustom;
|
||||
@end;
|
||||
@@ -0,0 +1,25 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testInitWithCustomSelector() {
|
||||
assertFalse(TestInitWithCustomSelector().custom)
|
||||
assertTrue(TestInitWithCustomSelector(custom = Unit).custom)
|
||||
|
||||
val customSubclass: TestInitWithCustomSelector = TestInitWithCustomSelectorSubclass.createCustom()
|
||||
assertTrue(customSubclass is TestInitWithCustomSelectorSubclass)
|
||||
assertTrue(customSubclass.custom)
|
||||
|
||||
// Test side effect:
|
||||
var ok = false
|
||||
assertTrue(TestInitWithCustomSelector(run { ok = true }).custom)
|
||||
assertTrue(ok)
|
||||
}
|
||||
|
||||
private class TestInitWithCustomSelectorSubclass : TestInitWithCustomSelector {
|
||||
@OverrideInit constructor(custom: Unit) : super(custom) {
|
||||
assertSame(Unit, custom)
|
||||
}
|
||||
|
||||
companion object : TestInitWithCustomSelectorMeta()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#import "initWithCustomSelector.h"
|
||||
|
||||
@implementation TestInitWithCustomSelector
|
||||
|
||||
-(instancetype)initCustom {
|
||||
if (self = [super init]) {
|
||||
self.custom = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.custom = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+(instancetype)createCustom {
|
||||
return [[self alloc] initCustom];
|
||||
}
|
||||
|
||||
@end;
|
||||
@@ -0,0 +1,11 @@
|
||||
import kotlin.system.exitProcess
|
||||
import kotlinx.cinterop.autoreleasepool
|
||||
import kotlin.native.internal.test.testLauncherEntryPoint
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
autoreleasepool {
|
||||
val exitCode = testLauncherEntryPoint(args)
|
||||
// Note: this test runner checks for memory leaks after successful execution, unlike standard one.
|
||||
if (exitCode != 0) exitProcess(exitCode)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
// [KT-36067] cinterop tool fails when there is a structure member named Companion
|
||||
|
||||
struct EnumFieldMangleStruct {
|
||||
enum {Companion, Any} smth;
|
||||
};
|
||||
struct EnumFieldMangleStruct enumMangledStruct = { Any };
|
||||
|
||||
struct MyStruct {
|
||||
int Companion; // simple clash
|
||||
int _Companion;
|
||||
int $_Companion;
|
||||
int super;
|
||||
};
|
||||
|
||||
struct MyStruct myStruct = {11, 12, 13, 14};
|
||||
|
||||
@protocol Proto
|
||||
@property int Companion; // clash on implementing
|
||||
@end;
|
||||
|
||||
@interface FooMangled : NSObject<Proto>
|
||||
//- (void) CompanionS; // mangleSimple does not support this: it may clash after mangling
|
||||
@end;
|
||||
@@ -0,0 +1,18 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testMangling() {
|
||||
assertEquals(11, myStruct.`Companion$`)
|
||||
assertEquals(12, myStruct._Companion)
|
||||
assertEquals(13, myStruct.`$_Companion`)
|
||||
assertEquals(14, myStruct.`super`)
|
||||
|
||||
val objc = FooMangled()
|
||||
objc.`Companion$` = 99
|
||||
assertEquals(99, objc.Companion())
|
||||
assertEquals(99, objc.`Companion$`)
|
||||
|
||||
enumMangledStruct.smth = Companion
|
||||
assertEquals(Companion, enumMangledStruct.smth)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#import "mangling.h"
|
||||
|
||||
// [KT-36067] mangling
|
||||
@implementation FooMangled : NSObject
|
||||
@synthesize Companion;
|
||||
@end;
|
||||
@@ -0,0 +1,17 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface MultipleInheritanceClashBase : NSObject
|
||||
@property (nonnull) MultipleInheritanceClashBase* delegate;
|
||||
@end;
|
||||
|
||||
@protocol MultipleInheritanceClash
|
||||
@optional
|
||||
@property (nullable) id<MultipleInheritanceClash> delegate;
|
||||
@end;
|
||||
|
||||
@interface MultipleInheritanceClash1 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
||||
@end;
|
||||
|
||||
@interface MultipleInheritanceClash2 : MultipleInheritanceClashBase <MultipleInheritanceClash>
|
||||
@property MultipleInheritanceClashBase* delegate;
|
||||
@end;
|
||||
@@ -0,0 +1,18 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testMultipleInheritanceClash() {
|
||||
val clash1 = MultipleInheritanceClash1()
|
||||
val clash2 = MultipleInheritanceClash2()
|
||||
|
||||
clash1.delegate = clash1
|
||||
assertEquals(clash1, clash1.delegate)
|
||||
clash1.setDelegate(clash2)
|
||||
assertEquals(clash2, clash1.delegate())
|
||||
|
||||
clash2.delegate = clash1
|
||||
assertEquals(clash1, clash2.delegate)
|
||||
clash2.setDelegate(clash2)
|
||||
assertEquals(clash2, clash2.delegate())
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "multipleInheritanceClash.h"
|
||||
|
||||
@implementation MultipleInheritanceClashBase
|
||||
@end;
|
||||
|
||||
@implementation MultipleInheritanceClash1
|
||||
@end;
|
||||
|
||||
@implementation MultipleInheritanceClash2
|
||||
@end;
|
||||
@@ -0,0 +1,8 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testNSOutputStreamToMemoryConstructor() {
|
||||
val stream: Any = NSOutputStream(toMemory = Unit)
|
||||
assertTrue(stream is NSOutputStream)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@class DeallocListener;
|
||||
|
||||
@interface DeallocExecutor : NSObject
|
||||
@property DeallocListener* deallocListener;
|
||||
@end;
|
||||
|
||||
@interface DeallocListener : NSObject
|
||||
@property (weak) DeallocExecutor* deallocExecutor;
|
||||
@property BOOL deallocated;
|
||||
-(BOOL)deallocExecutorIsNil;
|
||||
@end;
|
||||
@@ -0,0 +1,45 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testObjCWeakRef() {
|
||||
val deallocListener = DeallocListener()
|
||||
assertFalse(deallocListener.deallocated)
|
||||
|
||||
testObjCWeakRef0(deallocListener)
|
||||
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertTrue(deallocListener.deallocated)
|
||||
assertTrue(deallocListener.deallocExecutorIsNil())
|
||||
}
|
||||
|
||||
private fun testObjCWeakRef0(deallocListener: DeallocListener) = withWorker {
|
||||
assertTrue(deallocListener.deallocExecutorIsNil())
|
||||
|
||||
val obj = object : DeallocExecutor() {}
|
||||
deallocListener.deallocExecutor = obj
|
||||
obj.deallocListener = deallocListener
|
||||
|
||||
assertFalse(deallocListener.deallocExecutorIsNil())
|
||||
|
||||
// TODO: can't actually test, Obj-C runtime doesn't expect _tryRetain throwing an exception.
|
||||
// runInWorker {
|
||||
// assertFailsWith<IncorrectDereferenceException> {
|
||||
// deallocListener.deallocExecutorIsNil()
|
||||
// }
|
||||
// }
|
||||
|
||||
obj.freeze()
|
||||
|
||||
runInWorker {
|
||||
// [deallocListener.deallocExecutorIsNil()] calls deallocExecutor getter, which retains [obj] and either
|
||||
// puts it to autoreleasepool or releases it immediately (Obj-C ARC optimization).
|
||||
// Wrap the call to autoreleasepool to ensure [obj] will be released:
|
||||
autoreleasepool {
|
||||
assertFalse(deallocListener.deallocExecutorIsNil())
|
||||
}
|
||||
// Process release of Kotlin reference to [obj] in any case:
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#import "objcWeakRefs.h"
|
||||
|
||||
@implementation DeallocExecutor
|
||||
-(void)dealloc {
|
||||
self.deallocListener.deallocated = YES;
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation DeallocListener
|
||||
-(BOOL)deallocExecutorIsNil {
|
||||
return self.deallocExecutor == nil;
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,6 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface TestOverrideInit : NSObject
|
||||
-(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER;
|
||||
+(instancetype)createWithValue:(int)value;
|
||||
@end;
|
||||
@@ -0,0 +1,11 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testOverrideInit() {
|
||||
assertEquals(42, (TestOverrideInitImpl.createWithValue(42) as TestOverrideInitImpl).value)
|
||||
}
|
||||
|
||||
private class TestOverrideInitImpl @OverrideInit constructor(val value: Int) : TestOverrideInit(value) {
|
||||
companion object : TestOverrideInitMeta()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#import "OverrideInit.h"
|
||||
|
||||
@implementation TestOverrideInit
|
||||
-(instancetype)initWithValue:(int)value {
|
||||
return self = [super init];
|
||||
}
|
||||
|
||||
+(instancetype)createWithValue:(int)value {
|
||||
return [[self alloc] initWithValue:value];
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,36 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
private class NSObjectImpl : NSObject() {
|
||||
var x = 111
|
||||
}
|
||||
|
||||
@Test fun testSharing() = withWorker {
|
||||
val obj = NSObjectImpl()
|
||||
val array = nsArrayOf(obj)
|
||||
|
||||
assertFalse(obj.isFrozen)
|
||||
|
||||
runInWorker {
|
||||
assertFailsWith<IncorrectDereferenceException> {
|
||||
array.objectAtIndex(0)
|
||||
}
|
||||
}
|
||||
|
||||
obj.x = 222
|
||||
obj.freeze()
|
||||
assertTrue(obj.isFrozen)
|
||||
|
||||
runInWorker {
|
||||
val obj1 = array.objectAtIndex(0) as NSObjectImpl
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
obj1.x = 333
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(222, obj.x)
|
||||
|
||||
// TODO: test [obj release] etc.
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.concurrent.*
|
||||
import objcTests.*
|
||||
|
||||
fun Worker.runInWorker(block: () -> Unit) {
|
||||
block.freeze()
|
||||
val future = this.execute(TransferMode.SAFE, { block }) {
|
||||
it()
|
||||
}
|
||||
future.result // Throws on failure.
|
||||
}
|
||||
|
||||
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
|
||||
elements.forEach {
|
||||
this.addObject(it as ObjCObject)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
@interface TestVarargs : NSObject
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...;
|
||||
@property NSString* formatted;
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ...;
|
||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||
@end;
|
||||
|
||||
@interface TestVarargs (TestVarargsExtension)
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||
@end;
|
||||
|
||||
@interface TestVarargsSubclass : TestVarargs
|
||||
// Test clashes:
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args;
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||
@end;
|
||||
@@ -0,0 +1,53 @@
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testVarargs() {
|
||||
assertEquals(
|
||||
"a b -1",
|
||||
TestVarargs.testVarargsWithFormat(
|
||||
"%@ %s %d",
|
||||
"a" as NSString, "b".cstr, (-1).toByte()
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"2 3 9223372036854775807",
|
||||
TestVarargs(
|
||||
"%d %d %lld",
|
||||
2.toShort(), 3, Long.MAX_VALUE
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"0.1 0.2 1 0",
|
||||
TestVarargs.create(
|
||||
"%.1f %.1lf %d %d",
|
||||
0.1.toFloat(), 0.2, true, false
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"1 2 3",
|
||||
TestVarargs(
|
||||
format = "%d %d %d",
|
||||
args = *arrayOf(1, 2, 3)
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"4 5 6",
|
||||
TestVarargs(
|
||||
args = *arrayOf(4, *arrayOf(5, 6)),
|
||||
format = "%d %d %d"
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"7",
|
||||
TestVarargsSubclass.stringWithFormat(
|
||||
"%d",
|
||||
7
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#import <stdlib.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import "varargs.h"
|
||||
|
||||
@implementation TestVarargs
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ... {
|
||||
self = [super init];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
self.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... {
|
||||
TestVarargs* result = [[self alloc] init];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
result.formatted = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ... {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation TestVarargsSubclass
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
@end;
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
NSObject* createNSObject() {
|
||||
return [NSObject new];
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import kotlin.native.ref.*
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.test.*
|
||||
import objcTests.*
|
||||
|
||||
@Test fun testWeakRefs() {
|
||||
testWeakReference({ createNSObject()!! })
|
||||
|
||||
createAndAbandonWeakRef(NSObject())
|
||||
|
||||
testWeakReference({ NSArray.arrayWithArray(listOf(42)) as NSArray })
|
||||
}
|
||||
|
||||
private fun testWeakReference(block: () -> NSObject) {
|
||||
val ref = autoreleasepool {
|
||||
createAndTestWeakReference(block)
|
||||
}
|
||||
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertNull(ref.get())
|
||||
}
|
||||
|
||||
private fun createAndTestWeakReference(block: () -> NSObject): WeakReference<NSObject> {
|
||||
val ref = createWeakReference(block)
|
||||
assertNotNull(ref.get())
|
||||
assertEquals(ref.get()!!.hash(), ref.get()!!.hash())
|
||||
return ref
|
||||
}
|
||||
|
||||
private fun createWeakReference(block: () -> NSObject) = WeakReference(block())
|
||||
|
||||
private fun createAndAbandonWeakRef(obj: NSObject) {
|
||||
WeakReference(obj)
|
||||
}
|
||||
Reference in New Issue
Block a user