Implement multi-file Obj-C interop tests (interop_objc_tests task)

Extract most of interop_objc_smoke tests
This commit is contained in:
Svyatoslav Scherbina
2020-04-08 15:24:44 +03:00
committed by SvyatoslavScherbina
parent f8e1409376
commit cd6840a817
51 changed files with 942 additions and 822 deletions
+34 -2
View File
@@ -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
-171
View File
@@ -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;
+1 -389
View File
@@ -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
-260
View File
@@ -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)
}