Skip to content

Commit

Permalink
Fix bugs related to Subdocument and Blob
Browse files Browse the repository at this point in the history
* Fixed Subdocument bug when replacing with date value in document.properties.
* Fixed CBLBlob detection in CBLDocument.
* Added more unit tests.

#1626
  • Loading branch information
pasin committed Mar 3, 2017
1 parent 2e345bc commit 2315953
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 16 deletions.
21 changes: 15 additions & 6 deletions Objective-C/CBLDocument.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#import "CBLJSON.h"
#import "CBLSharedKeys.hh"
#import "CBLStringBytes.h"
#import "CBLSubdocument.h"


NSString* const kCBLDocumentChangeNotification = @"CBLDocumentChangeNotification";
Expand Down Expand Up @@ -241,8 +242,10 @@ - (void) setC4Doc: (nullable C4Document*)doc {
// The next three functions search recursively for a property "_cbltype":"blob".

static bool objectContainsBlob(__unsafe_unretained id value) {
if ([value isKindOfClass: [NSDictionary class]])
return dictContainsBlob(value);
if ([value isKindOfClass: [CBLBlob class]])
return true;
else if ([value isKindOfClass: [CBLSubdocument class]])
return subdocContainsBlob(value);
else if ([value isKindOfClass: [NSArray class]])
return arrayContainsBlob(value);
else
Expand All @@ -256,9 +259,15 @@ static bool arrayContainsBlob(__unsafe_unretained NSArray* array) {
return false;
}

static bool dictContainsBlob(__unsafe_unretained NSDictionary* dict) {
if ([dict[@"_cbltype"] isEqual: @"blob"])
return true;
static bool subdocContainsBlob(__unsafe_unretained CBLSubdocument* subdoc) {
__block bool containsBlob = false;
[subdoc.properties enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
*stop = containsBlob = objectContainsBlob(value);
}];
return containsBlob;
}

static bool containsBlob(__unsafe_unretained NSDictionary* dict) {
__block bool containsBlob = false;
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
*stop = containsBlob = objectContainsBlob(value);
Expand All @@ -283,7 +292,7 @@ - (BOOL) saveInto: (C4Document **)outDoc
};
if (deletion)
put.revFlags = kRevDeleted;
if (dictContainsBlob(propertiesToSave))
if (containsBlob(propertiesToSave))
put.revFlags |= kRevHasAttachments;
if (propertiesToSave.count > 0) {
// Encode properties to Fleece data:
Expand Down
16 changes: 8 additions & 8 deletions Objective-C/CBLProperties.mm
Original file line number Diff line number Diff line change
Expand Up @@ -457,18 +457,18 @@ - (id) convertValue: (nullable id)value oldValue: (nullable id)oldValue forKey:
if ($equal(value, oldValue))
return value; // nothing to convert

if ([value isKindOfClass: [NSDate class]])
return [CBLJSON JSONObjectWithDate: value];
else if ([value isKindOfClass: [CBLSubdocument class]])
if ([value isKindOfClass: [CBLSubdocument class]])
return [self convertSubdoc: value oldValue: oldValue forKey: key];
else if ([value isKindOfClass: [NSDictionary class]])
return [self convertDict: value oldValue: oldValue forKey: key];
else if ([value isKindOfClass: [NSArray class]])
return [self convertArray: value oldVale: oldValue forKey: key];
else {
[self invalidateIfSubdocument: oldValue];
return value;
}
else if ([value isKindOfClass: [NSDate class]])
value = [CBLJSON JSONObjectWithDate: value];

[self invalidateIfSubdocument: oldValue];

return value;
}


Expand Down Expand Up @@ -529,7 +529,7 @@ - (id) convertArray: (NSArray*)array oldVale: (nullable id)oldValue forKey: (NSS
id nValue = array[i];
id oValue = [oldArray count] > i ? oldArray[i] : nil;

// FIXME: Array can be nested, using a simple arraySet is not enough
// FIXME: Array can be nested, using a simple arraySet is not enough.
if ([oValue isKindOfClass: [CBLSubdocument class]] && [arraySet containsObject: oValue]) {
// Prevent the subdocument to be invalidated so the subdocument can be reordered:
oValue = nil;
Expand Down
104 changes: 103 additions & 1 deletion Objective-C/Tests/SubdocumentTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ - (void) testSetDocumentPropertiesNil {
}


- (void) testReplaceSubdocumentWithNonDict {
- (void) testReplaceWithNonDict {
CBLSubdocument* address = [CBLSubdocument subdocument];
address[@"street"] = @"1 Star Way.";
AssertEqualObjects(address[@"street"], @"1 Star Way.");
Expand All @@ -413,6 +413,108 @@ - (void) testReplaceSubdocumentWithNonDict {
}


- (void) testReplaceWithNewSubdocument {
CBLSubdocument* address = [CBLSubdocument subdocument];
address[@"street"] = @"1 Star Way.";
AssertEqualObjects(address[@"street"], @"1 Star Way.");
AssertEqualObjects(address.properties, (@{@"street": @"1 Star Way."}));

doc[@"address"] = address;
AssertEqualObjects(doc[@"address"], address);
AssertEqualObjects(address.document, doc);

CBLSubdocument* nuAddress = [CBLSubdocument subdocument];
address[@"street"] = @"123 Space Dr.";
doc[@"address"] = nuAddress;

AssertEqualObjects(doc[@"address"], nuAddress);
AssertNil(address.document);
AssertNil(address.properties);
}


- (void) testReplaceWithNewDocProperties {
doc.properties = @{ @"name": @"Jason",
@"address": @{
@"street": @"1 Star Way.",
@"phones": @{@"mobile": @"650-123-4567"}
},
@"work": @{@"company": @"Couchbase"},
@"subscription": @{@"type": @"silver"},
@"expiration": @{@"date": @"2017-03-03T07:13:46.536Z"},
@"references": @[@{@"name": @"Scott"}, @{@"name": @"Sam"}]
};

CBLSubdocument* address = doc[@"address"];
CBLSubdocument* phones = address[@"phones"];
AssertNotNil(address);
AssertNotNil(phones);

CBLSubdocument* work = doc[@"work"];
AssertNotNil(work);

CBLSubdocument* subscription = doc[@"subscription"];
AssertNotNil(subscription);

CBLSubdocument* expiration = doc[@"expiration"];
AssertNotNil(expiration);

NSArray* references = doc[@"references"];
AssertEqual([references count], 2u);
CBLSubdocument* r1 = references[0];
CBLSubdocument* r2 = references[1];
AssertNotNil(r1);
AssertNotNil(r2);

CBLSubdocument* nuSubscription = [CBLSubdocument subdocument];
nuSubscription[@"type"] = @"platinum";

NSDate* date = [NSDate date];
doc.properties = @{ @"name": @"Jason",
@"address": @"1 Star Way.",
@"work": @{ @"company": @"Couchbase", @"position": @"Engineer" },
@"subscription": nuSubscription,
@"expiration": date,
@"references": @[@{@"name": @"Smith"}]
};

AssertEqualObjects(doc[@"address"], @"1 Star Way.");
AssertNil(address.document);
AssertNil(address.parent);
AssertNil(address.properties);

AssertNil(phones.document);
AssertNil(phones.parent);
AssertNil(phones.properties);

AssertEqual(doc[@"work"], work);
AssertEqualObjects(work[@"company"], @"Couchbase");
AssertEqualObjects(work[@"position"], @"Engineer");

AssertEqualObjects(doc[@"subscription"], nuSubscription);
AssertEqualObjects(nuSubscription[@"type"], @"platinum");
AssertNil(subscription.document);
AssertNil(subscription.parent);
AssertNil(subscription.properties);

AssertEqualObjects([CBLJSON JSONObjectWithDate: [doc dateForKey: @"expiration"]],
[CBLJSON JSONObjectWithDate: date]);
AssertNil(expiration.document);
AssertNil(expiration.parent);
AssertNil(expiration.properties);

references = doc[@"references"];
AssertEqual([references count], 1u);
AssertEqualObjects(references[0], r1);
AssertEqualObjects(r1.document, doc);
AssertEqualObjects(r1.parent, doc);
AssertEqualObjects(r1[@"name"], @"Smith");
AssertNil(r2.document);
AssertNil(r2.parent);
AssertNil(r2.properties);
}


- (void) testDeleteDocument {
doc.properties = @{ @"name": @"Jason",
@"address": @{
Expand Down
78 changes: 77 additions & 1 deletion Swift/Tests/SubdocumentTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ class SubdocumentTest: CBLTestCase {
XCTAssertNil(r2.property("name"))
}

func testReplaceSubdocumentWithNonDict() {
func testReplaceWithNonDict() {
let address = Subdocument()
address["street"] = "1 Star Way."

Expand All @@ -349,6 +349,82 @@ class SubdocumentTest: CBLTestCase {
XCTAssertNil(address.properties)
}

func testReplaceWithNewSubdocument() {
let address = Subdocument()
address["street"] = "1 Star Way."

doc["address"] = address
XCTAssert(doc.property("address") as? Subdocument === address)
XCTAssert(address.document === doc)

let nuAddress = Subdocument()
nuAddress["street"] = "123 Space Dr."
doc["address"] = nuAddress

XCTAssert(doc.property("address") as? Subdocument === nuAddress)
XCTAssertNil(address.document)
XCTAssertNil(address.properties)
}

func testReplaceWithNewDocProperties() {
doc.properties = ["name": "Jason",
"address": [
"street": "1 Star Way.",
"phones":["mobile": "650-123-4567"]],
"work": ["company": "Couchbase"],
"subscription": ["type": "silver"],
"expiration": ["date": "2017-03-03T07:13:46.536Z"],
"references": [["name": "Scott"], ["name": "Sam"]]]

let address: Subdocument = doc["address"]!
let phones: Subdocument = address["phones"]!
let work: Subdocument = doc["work"]!
let subscription: Subdocument = doc["subscription"]!
let expiration: Subdocument = doc["expiration"]!

var references: [Any] = doc["references"]!
let r1 = references[0] as! Subdocument
let r2 = references[1] as! Subdocument

let nuSubscription = Subdocument()
nuSubscription["type"] = "platinum"

let date = Date()
doc.properties = ["name": "Jason",
"address": "1 Star Way.",
"work": ["company": "Couchbase", "position": "Engineer"],
"subscription": nuSubscription,
"expiration": date,
"references": [["name": "Smith"]]]

XCTAssertEqual(doc["address"], "1 Star Way.")
XCTAssertNil(address.document)
XCTAssertNil(address.properties)

XCTAssertNil(phones.document)
XCTAssertNil(phones.properties)

XCTAssert(doc.property("work") as? Subdocument === work)
XCTAssertEqual(work["company"], "Couchbase")
XCTAssertEqual(work["position"], "Engineer")

XCTAssert(doc.property("subscription") as? Subdocument === nuSubscription)
XCTAssertNil(subscription.document)
XCTAssertNil(subscription.properties)

XCTAssertNotNil(doc.property("expiration"))
XCTAssertNil(doc.property("expiration") as? Subdocument)
XCTAssertNil(expiration.document)
XCTAssertNil(expiration.properties)

references = doc["references"]!
XCTAssertEqual(references.count, 1)
XCTAssert(references[0] as! Subdocument === r1)
XCTAssertEqual(r1["name"], "Smith")
XCTAssertNil(r2.document)
XCTAssertNil(r2.properties)
}

func testDeleteDocument() throws {
doc.properties = ["name": "Jason",
"address": [
Expand Down

0 comments on commit 2315953

Please sign in to comment.