[ffigen] Test assign/retain/copy properties (#390)

* Test autorelease pools and more copying cases

* Test assign/retain/copy properties
diff --git a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart
index 74302f9..63ed706 100644
--- a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart
+++ b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart
@@ -132,6 +132,98 @@
       calloc.free(counter);
     });
 
+    assignPropertiesInnerInner(Pointer<Int32> counter, ArcTestObject outerObj) {
+      final assignObj = ArcTestObject.newWithCounter_(lib, counter);
+      expect(counter.value, 2);
+      outerObj.assignedProperty = assignObj;
+      expect(counter.value, 2);
+      expect(assignObj, outerObj.assignedProperty);
+      // To test that outerObj isn't holding a reference to assignObj, we let
+      // assignObj go out of scope, but keep outerObj in scope. This is
+      // dangerous because outerObj now has a dangling reference, so don't
+      // access that reference.
+    }
+
+    assignPropertiesInner(Pointer<Int32> counter) {
+      final outerObj = ArcTestObject.newWithCounter_(lib, counter);
+      expect(counter.value, 1);
+      assignPropertiesInnerInner(counter, outerObj);
+      doGC();
+      // assignObj has been cleaned up.
+      expect(counter.value, 1);
+    }
+
+    test('assign properties ref count correctly', () {
+      final counter = calloc<Int32>();
+      counter.value = 0;
+      assignPropertiesInner(counter);
+      doGC();
+      expect(counter.value, 0);
+      calloc.free(counter);
+    });
+
+    retainPropertiesInnerInner(Pointer<Int32> counter, ArcTestObject outerObj) {
+      final retainObj = ArcTestObject.newWithCounter_(lib, counter);
+      expect(counter.value, 2);
+      outerObj.retainedProperty = retainObj;
+      expect(counter.value, 2);
+      expect(retainObj, outerObj.retainedProperty);
+    }
+
+    retainPropertiesInner(Pointer<Int32> counter) {
+      final outerObj = ArcTestObject.newWithCounter_(lib, counter);
+      expect(counter.value, 1);
+      retainPropertiesInnerInner(counter, outerObj);
+      doGC();
+      // retainObj is still around, because outerObj retains a reference to it.
+      expect(counter.value, 2);
+    }
+
+    test('retain properties ref count correctly', () {
+      final counter = calloc<Int32>();
+      counter.value = 0;
+      // The getters of retain properties retain+autorelease the value. So we
+      // need an autorelease pool.
+      final pool = lib.createAutoreleasePool();
+      retainPropertiesInner(counter);
+      doGC();
+      expect(counter.value, 1);
+      lib.destroyAutoreleasePool(pool);
+      expect(counter.value, 0);
+      calloc.free(counter);
+    });
+
+    copyPropertiesInner(Pointer<Int32> counter) {
+      final outerObj = ArcTestObject.newWithCounter_(lib, counter);
+      expect(counter.value, 1);
+
+      final copyObj = ArcTestObject.newWithCounter_(lib, counter);
+      expect(counter.value, 2);
+      outerObj.copiedProperty = copyObj;
+      // Copy properties make a copy of the object, so now we have 3 objects.
+      expect(counter.value, 3);
+      expect(copyObj, isNot(outerObj.copiedProperty));
+
+      final anotherCopy = outerObj.copiedProperty;
+      // The getter doesn't copy the object.
+      expect(counter.value, 3);
+      expect(anotherCopy, outerObj.copiedProperty);
+    }
+
+    test('copy properties ref count correctly', () {
+      final counter = calloc<Int32>();
+      counter.value = 0;
+      // The getters of copy properties retain+autorelease the value. So we need
+      // an autorelease pool.
+      final pool = lib.createAutoreleasePool();
+      copyPropertiesInner(counter);
+      doGC();
+      expect(counter.value, 1);
+      lib.destroyAutoreleasePool(pool);
+      expect(counter.value, 0);
+      calloc.free(counter);
+    });
+
     castFromPointerInnerReleaseAndRetain(int address) {
       final fromCast = RefCounted.castFromPointer(
           lib, Pointer<ObjCObject>.fromAddress(address),
diff --git a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m
index f4a4fc9..d249389 100644
--- a/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m
+++ b/pkgs/ffigen/test/native_objc_test/automated_ref_count_test.m
@@ -21,6 +21,10 @@
 - (id)copyWithZone:(NSZone*) zone;
 - (ArcTestObject*)returnsRetained NS_RETURNS_RETAINED;
 
+@property (assign) ArcTestObject* assignedProperty;
+@property (retain) ArcTestObject* retainedProperty;
+@property (copy) ArcTestObject* copiedProperty;
+
 @end
 
 @interface RefCounted : NSObject
@@ -58,6 +62,8 @@
 
 - (void)dealloc {
   --*counter;
+  [_retainedProperty release];
+  [_copiedProperty release];
   [super dealloc];
 }