mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 13:07:02 +00:00
1294 lines
51 KiB
Plaintext
1294 lines
51 KiB
Plaintext
|
|
@import <Foundation/CPArray.j>
|
|
@import <AppKit/CPArrayController.j>
|
|
@import <AppKit/CPTextField.j>
|
|
|
|
@class Department
|
|
@class Employee
|
|
|
|
@implementation CPArrayControllerTest : OJTestCase
|
|
{
|
|
CPArrayController _arrayController @accessors(property=arrayController);
|
|
CPArray _contentArray @accessors(property=contentArray);
|
|
|
|
CPArray observations;
|
|
int aCount @accessors;
|
|
|
|
CPArray nameValues @accessors;
|
|
CPArray depValues @accessors;
|
|
}
|
|
|
|
- (CPArray)makeTestArray
|
|
{
|
|
return [[Employee employeeWithName:@"Francisco" department:[Department departmentWithName:@"Cappuccino"]],
|
|
[Employee employeeWithName:@"Ross" department:[Department departmentWithName:@"Cappuccino"]],
|
|
[Employee employeeWithName:@"Tom" department:[Department departmentWithName:@"CommonJS"]]];
|
|
}
|
|
|
|
- (CPArray)makeTestArrayMany
|
|
{
|
|
var a = [[Employee employeeWithName:@"Francisco" department:[Department departmentWithName:@"Cappuccino"]],
|
|
[Employee employeeWithName:@"Ross" department:[Department departmentWithName:@"Cappuccino"]],
|
|
[Employee employeeWithName:@"Tom" department:[Department departmentWithName:@"CommonJS"]]];
|
|
|
|
var i = 10000;
|
|
|
|
while (i--)
|
|
{
|
|
[a addObject:[Employee employeeWithName:@"Martin" + i department:[Department departmentWithName:@"Magic Lightning"]]];
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
- (void)initControllerWithSimpleArray
|
|
{
|
|
// Copy the array to allow the original to be reused.
|
|
_arrayController = [[CPArrayController alloc] initWithContent:[[self contentArray] copy]];
|
|
}
|
|
|
|
- (void)initControllerWithContentBinding
|
|
{
|
|
_arrayController = [CPArrayController new];
|
|
// The itemsArray keypath is implemented through KVC methods pointing to _contentArray.
|
|
[_arrayController bind:@"contentArray" toObject:self withKeyPath:@"itemsArray" options:nil];
|
|
}
|
|
|
|
- (void)setUp
|
|
{
|
|
// This will init the global var CPApp which are used internally in the AppKit
|
|
[[CPApplication alloc] init];
|
|
|
|
_contentArray = [self makeTestArray];
|
|
[self initControllerWithSimpleArray]
|
|
}
|
|
|
|
- (void)testInitWithContent
|
|
{
|
|
[self assert:[self contentArray] equals:[[self arrayController] contentArray]];
|
|
[self assert:[_CPObservableArray class] equals:[[[self arrayController] arrangedObjects] class] message:"arranged objects should be observable"];
|
|
}
|
|
|
|
- (void)testInitWithoutContent
|
|
{
|
|
_arrayController = [[CPArrayController alloc] init];
|
|
[self assert:[] equals:[[self arrayController] contentArray]];
|
|
}
|
|
|
|
- (void)testSetContent
|
|
{
|
|
var otherContent = [@"5", @"6"];
|
|
[[self arrayController] setContent:otherContent];
|
|
|
|
// This has not been decided on yet. See Issue #795.
|
|
// [self assertFalse:otherContent === [[self arrayController] contentArray] message:@"array controller should copy it's content"];
|
|
[self assert:otherContent equals:[[self arrayController] contentArray]];
|
|
[self assert:[_CPObservableArray class] equals:[[[self arrayController] arrangedObjects] class]];
|
|
}
|
|
|
|
- (void)testInsertObjectAtArrangedObjectIndex_SimpleArray
|
|
{
|
|
[self initControllerWithSimpleArray];
|
|
[self _testInsertObjectAtArrangedObjectIndex];
|
|
}
|
|
|
|
- (void)testInsertObjectAtArrangedObjectIndex_ContentBinding
|
|
{
|
|
[self initControllerWithContentBinding];
|
|
[self _testInsertObjectAtArrangedObjectIndex];
|
|
}
|
|
|
|
- (void)_testInsertObjectAtArrangedObjectIndex
|
|
{
|
|
var object = [Employee employeeWithName:@"Klaas Pieter" department:[Department departmentWithName:@"Theming"]],
|
|
arrayController = [self arrayController];
|
|
|
|
[arrayController setSortDescriptors:[[CPSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]];
|
|
[arrayController insertObject:object atArrangedObjectIndex:1];
|
|
|
|
[self assert:object equals:[[arrayController arrangedObjects] objectAtIndex:1]];
|
|
[self assertTrue:[[arrayController content] containsObject:object] message:@"object should be inserted into content"];
|
|
}
|
|
|
|
- (void)testAddObjectUpdatesArrangedObjectsWithoutSortDescriptors
|
|
{
|
|
var arrayController = [[CPArrayController alloc] init];
|
|
|
|
[arrayController addObject:@"content"];
|
|
[self assert:[@"content"] equals:[arrayController content]];
|
|
[self assert:[@"content"] equals:[arrayController arrangedObjects]];
|
|
}
|
|
|
|
- (void)testInsertObjectUpdatesArrangedObjectsWithoutSortDescriptors
|
|
{
|
|
var arrayController = [[CPArrayController alloc] init];
|
|
|
|
[arrayController insertObject:@"content" atArrangedObjectIndex:0];
|
|
[self assert:[@"content"] equals:[arrayController content]];
|
|
[self assert:[@"content"] equals:[arrayController arrangedObjects]];
|
|
}
|
|
|
|
- (void)testSelectPrevious
|
|
{
|
|
var arrayController = [self arrayController];
|
|
|
|
// Selection index: 1
|
|
[arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
|
|
[self assertTrue:[arrayController canSelectPrevious] message:@"index > 0; canSelectPrevious should return YES"]
|
|
|
|
[arrayController selectPrevious:self];
|
|
[self assert:[CPIndexSet indexSetWithIndex:0] equals:[arrayController selectionIndexes]];
|
|
|
|
// Selection index: 0
|
|
[self assertFalse:[arrayController canSelectPrevious] message:@"index <= 0; canSelectPrevious should return NO"];
|
|
|
|
[arrayController selectPrevious:self];
|
|
[self assert:[CPIndexSet indexSetWithIndex:0] equals:[arrayController selectionIndexes]];
|
|
}
|
|
|
|
- (void)testSelectNext
|
|
{
|
|
var arrayController = [self arrayController],
|
|
count = [[arrayController arrangedObjects] count];
|
|
|
|
// Selection index: count - 2
|
|
[arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndex:count - 2]];
|
|
[self assertTrue:[arrayController canSelectNext] message:@"index < (count - 1); canSelectNext should return YES"];
|
|
|
|
[arrayController selectNext:self];
|
|
[self assert:[CPIndexSet indexSetWithIndex:count - 1] equals:[arrayController selectionIndexes]];
|
|
|
|
// Selection index: count - 1
|
|
[self assertFalse:[arrayController canSelectNext] message:@"index >= (count - 1) canSelectNext should return NO"];
|
|
|
|
[arrayController selectNext:self];
|
|
[self assert:[CPIndexSet indexSetWithIndex:count - 1] equals:[arrayController selectionIndexes]];
|
|
}
|
|
|
|
|
|
/*!
|
|
Verify that the arranged objects ordering is correct versus the content array when objects are added with addObject and no sort descriptors are set.
|
|
|
|
(setContent: based version.)
|
|
*/
|
|
- (void)testAddObject_SimpleArray
|
|
{
|
|
_contentArray = [];
|
|
// Don't use a copy, use direct access for this particular test.
|
|
_arrayController = [[CPArrayController alloc] initWithContent:_contentArray];
|
|
[self _testAddObject];
|
|
}
|
|
|
|
/*!
|
|
Verify that the arranged objects ordering is correct versus the content array when objects are added with addObject and no sort descriptors are set.
|
|
|
|
(content binding based version.)
|
|
*/
|
|
- (void)testAddObject_ContentBinding
|
|
{
|
|
_contentArray = [];
|
|
[self initControllerWithContentBinding];
|
|
[self _testAddObject];
|
|
}
|
|
|
|
- (void)_testAddObject
|
|
{
|
|
[_arrayController addObject:[CPNumber numberWithInt:1]];
|
|
[_arrayController addObject:[CPNumber numberWithInt:2]];
|
|
|
|
[self assert:[CPNumber numberWithInt:1] equals:_contentArray[0]];
|
|
[self assert:[CPNumber numberWithInt:2] equals:_contentArray[1]];
|
|
|
|
[self assert:2 equals:[[_arrayController arrangedObjects] count]];
|
|
[self assert:[CPNumber numberWithInt:1] equals:[_arrayController arrangedObjects][0] message:"arranged objects should be in the correct order"];
|
|
[self assert:[CPNumber numberWithInt:2] equals:[_arrayController arrangedObjects][1] message:"arranged objects should be in the correct order"];
|
|
|
|
[_arrayController rearrangeObjects];
|
|
|
|
[self assert:[CPNumber numberWithInt:1] equals:[_arrayController arrangedObjects][0]];
|
|
[self assert:[CPNumber numberWithInt:2] equals:[_arrayController arrangedObjects][1]];
|
|
}
|
|
|
|
- (void)testAdd
|
|
{
|
|
_contentArray = [];
|
|
_arrayController = [[CPArrayController alloc] initWithContent:_contentArray];
|
|
[self _testAdd];
|
|
}
|
|
|
|
- (void)_testAdd
|
|
{
|
|
[_arrayController setObjectClass:[Employee class]];
|
|
[self assert:[[_arrayController contentArray] count] equals:0];
|
|
[_arrayController add:nil];
|
|
[self assert:[[_arrayController contentArray] count] equals:1];
|
|
[self assert:[[_arrayController arrangedObjects][0] class] equals:[Employee class]];
|
|
|
|
// switch it up so that we can tell where this one is.
|
|
[_arrayController setObjectClass:[Department class]];
|
|
[_arrayController add:nil];
|
|
[self assert:[[[_arrayController arrangedObjects] lastObject] class] equals:[Department class]];
|
|
}
|
|
|
|
- (void)testInsert
|
|
{
|
|
_contentArray = [];
|
|
_arrayController = [[CPArrayController alloc] initWithContent:_contentArray];
|
|
[self _testInsert];
|
|
}
|
|
|
|
- (void)_testInsert
|
|
{
|
|
[_arrayController setObjectClass:[Employee class]];
|
|
[_arrayController add:nil];
|
|
[_arrayController add:nil];
|
|
[_arrayController add:nil];
|
|
|
|
[_arrayController setObjectClass:[Department class]];
|
|
[_arrayController setSelectionIndex:1];
|
|
[_arrayController insert:nil];
|
|
[self assert:[[[_arrayController arrangedObjects] objectAtIndex:1] class] equals:[Department class]];
|
|
}
|
|
|
|
- (void)_initTestRemoveObjects_SimpleArray
|
|
{
|
|
var array = [[CPMutableDictionary dictionaryWithObject:0 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:1 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:2 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:3 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:4 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:5 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:6 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:7 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:8 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:9 forKey:@"number"]];
|
|
_arrayController = [[CPArrayController alloc] initWithContent:[array mutableCopy]];
|
|
}
|
|
|
|
- (void)_initTestRemoveObjects_ContentBinding
|
|
{
|
|
_contentArray = [[CPMutableDictionary dictionaryWithObject:0 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:1 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:2 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:3 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:4 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:5 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:6 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:7 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:8 forKey:@"number"],
|
|
[CPMutableDictionary dictionaryWithObject:9 forKey:@"number"]];
|
|
_arrayController = [CPArrayController new];
|
|
[_arrayController bind:@"contentArray" toObject:self withKeyPath:@"itemsArray" options:nil];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_OneSelectedObject];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_OneSelectedObject];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_OneSelectedObject
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:NO];
|
|
[_arrayController setPreservesSelection:NO];
|
|
[_arrayController setSelectionIndex:5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:5];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === CPNotFound];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_OneSelectedObject_AvoidingEmptySelection];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_OneSelectedObject_AvoidingEmptySelection];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_OneSelectedObject_AvoidingEmptySelection
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:YES];
|
|
[_arrayController setPreservesSelection:NO];
|
|
[_arrayController setSelectionIndex:5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:5];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4 message:"" + [_arrayController selectionIndex]];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 7];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 3];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 3];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[self assertTrue:[_arrayController selectionIndex] === 0];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 3];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[self assertTrue:[_arrayController selectionIndex] === CPNotFound];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_PreservesSelection_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_OneSelectedObject_PreservesSelection];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_PreservesSelection_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_OneSelectedObject_PreservesSelection];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_OneSelectedObject_PreservesSelection
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:NO];
|
|
[_arrayController setPreservesSelection:YES];
|
|
[_arrayController setSelectionIndex:5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:5];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === CPNotFound];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_PreservesSelection_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_PreservesSelection];
|
|
}
|
|
|
|
- (void)testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_PreservesSelection_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_PreservesSelection];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_OneSelectedObject_AvoidingEmptySelection_PreservesSelection
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:YES];
|
|
[_arrayController setPreservesSelection:YES];
|
|
[_arrayController setSelectionIndex:5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:5];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 5];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 4 message:"" + [_arrayController selectionIndex]];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 7];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:4];
|
|
[self assertTrue:[_arrayController selectionIndex] === 3];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 3];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[self assertTrue:[_arrayController selectionIndex] === 0];
|
|
[self assertTrue:[[[_arrayController selectedObjects] lastObject] objectForKey:@"number"] === 3];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
[self assertTrue:[_arrayController selectionIndex] === CPNotFound];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_MultipleSelectedObjects];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_MultipleSelectedObjects];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_MultipleSelectedObjects
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:NO];
|
|
[_arrayController setPreservesSelection:NO];
|
|
var indexes = [CPMutableIndexSet indexSet];
|
|
[indexes addIndex:2];
|
|
[indexes addIndex:4];
|
|
[indexes addIndex:6];
|
|
[indexes addIndex:8];
|
|
[_arrayController setSelectionIndexes:indexes];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
var indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:1];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[indexes1 addIndex:7];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[2, 4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:1];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:4];
|
|
[indexes1 addIndex:6];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:3];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
var indexes2 = [CPMutableIndexSet indexSet];
|
|
[indexes2 addIndex:2];
|
|
[indexes2 addIndex:3];
|
|
[indexes2 addIndex:5];
|
|
[_arrayController removeObjectsAtArrangedObjectIndexes:indexes2];
|
|
[self assertTrue:[[_arrayController selectionIndexes] lastIndex] === CPNotFound];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:YES];
|
|
[_arrayController setPreservesSelection:NO];
|
|
var indexes = [CPMutableIndexSet indexSet];
|
|
[indexes addIndex:2];
|
|
[indexes addIndex:4];
|
|
[indexes addIndex:6];
|
|
[indexes addIndex:8];
|
|
[_arrayController setSelectionIndexes:indexes];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
var indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:1];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[indexes1 addIndex:7];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[2, 4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:1];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:4];
|
|
[indexes1 addIndex:6];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:3];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
var indexes2 = [CPMutableIndexSet indexSet];
|
|
[indexes2 addIndex:2];
|
|
[indexes2 addIndex:3];
|
|
[indexes2 addIndex:5];
|
|
[_arrayController removeObjectsAtArrangedObjectIndexes:indexes2];
|
|
[self assertTrue:[[_arrayController selectionIndexes] lastIndex] === 2];
|
|
[self assert:[7] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_PreservesSelection_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_MultipleSelectedObjects_PreservesSelection];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_PreservesSelection_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_MultipleSelectedObjects_PreservesSelection];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_MultipleSelectedObjects_PreservesSelection
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:NO];
|
|
[_arrayController setPreservesSelection:YES];
|
|
var indexes = [CPMutableIndexSet indexSet];
|
|
[indexes addIndex:2];
|
|
[indexes addIndex:4];
|
|
[indexes addIndex:6];
|
|
[indexes addIndex:8];
|
|
[_arrayController setSelectionIndexes:indexes];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
var indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:1];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[indexes1 addIndex:7];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[2, 4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:1];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:4];
|
|
[indexes1 addIndex:6];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:3];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
var indexes2 = [CPMutableIndexSet indexSet];
|
|
[indexes2 addIndex:2];
|
|
[indexes2 addIndex:3];
|
|
[indexes2 addIndex:5];
|
|
[_arrayController removeObjectsAtArrangedObjectIndexes:indexes2];
|
|
[self assertTrue:[[_arrayController selectionIndexes] lastIndex] === CPNotFound];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_PreservesSelection_SimpleArray
|
|
{
|
|
[self _initTestRemoveObjects_SimpleArray];
|
|
[self _testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_PreservesSelection];
|
|
}
|
|
|
|
- (void)testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_PreservesSelection_ContentBinding
|
|
{
|
|
[self _initTestRemoveObjects_ContentBinding];
|
|
[self _testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_PreservesSelection];
|
|
}
|
|
|
|
- (void)_testRemoveObjects_MultipleSelectedObjects_AvoidingEmptySelection_PreservesSelection
|
|
{
|
|
[_arrayController setAvoidsEmptySelection:YES];
|
|
[_arrayController setPreservesSelection:YES];
|
|
var indexes = [CPMutableIndexSet indexSet];
|
|
[indexes addIndex:2];
|
|
[indexes addIndex:4];
|
|
[indexes addIndex:6];
|
|
[indexes addIndex:8];
|
|
[_arrayController setSelectionIndexes:indexes];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:0];
|
|
var indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:1];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[indexes1 addIndex:7];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[2, 4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:1];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:4];
|
|
[indexes1 addIndex:6];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
[_arrayController removeObjectAtArrangedObjectIndex:3];
|
|
indexes1 = [CPMutableIndexSet indexSet];
|
|
[indexes1 addIndex:2];
|
|
[indexes1 addIndex:3];
|
|
[indexes1 addIndex:5];
|
|
[self assert:indexes1 equals:[_arrayController selectionIndexes]];
|
|
[self assert:[4, 6, 8] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
|
|
var indexes2 = [CPMutableIndexSet indexSet];
|
|
[indexes2 addIndex:2];
|
|
[indexes2 addIndex:3];
|
|
[indexes2 addIndex:5];
|
|
[_arrayController removeObjectsAtArrangedObjectIndexes:indexes2];
|
|
[self assertTrue:[[_arrayController selectionIndexes] lastIndex] === 2];
|
|
[self assert:[7] equals:[[_arrayController selectedObjects] valueForKey:@"number"]];
|
|
}
|
|
|
|
- (void)testRemoveObjectsWithoutSelection
|
|
{
|
|
var arrayController = [self arrayController],
|
|
objectsToRemove = [[[self arrayController] arrangedObjects] objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 2)]];
|
|
|
|
// without any selection
|
|
[arrayController setSelectedObjects:[]];
|
|
|
|
// we should be able to remove arbitrary sets of objects
|
|
[arrayController removeObjects:objectsToRemove];
|
|
|
|
for (var i = 0; i < [objectsToRemove count]; i++)
|
|
[self assertFalse:[[arrayController arrangedObjects] containsObject:[objectsToRemove objectAtIndex:i]] message:@"remove objects should no longer appear in arrangedObjects"];
|
|
}
|
|
|
|
- (void)testRemoveObject
|
|
{
|
|
var arrayController = [self arrayController],
|
|
objectToRemove = [[arrayController arrangedObjects] objectAtIndex:0];
|
|
|
|
// without any selection
|
|
[arrayController setSelectedObjects:[]];
|
|
|
|
// we should be able to remove arbitrary objects
|
|
[arrayController removeObject:objectToRemove];
|
|
|
|
[self assertFalse:[[arrayController arrangedObjects] containsObject:objectToRemove] message:@"removed objects should no longer appear in arrangedObjects"];
|
|
}
|
|
|
|
- (void)testRemove_
|
|
{
|
|
var arrayController = [self arrayController],
|
|
objectToRemove = [[arrayController arrangedObjects] objectAtIndex:0];
|
|
|
|
[arrayController setSelectedObjects:[objectToRemove]];
|
|
[arrayController remove:nil];
|
|
|
|
[self assertFalse:[[arrayController arrangedObjects] containsObject:objectToRemove] message:@"removed objects should no longer appear in arrangedObjects"];
|
|
}
|
|
|
|
- (void)testRemoveObjectsAtArrangedObjectIndexes_
|
|
{
|
|
var arrayController = [self arrayController];
|
|
|
|
[arrayController removeObjectsAtArrangedObjectIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 2)]];
|
|
|
|
[self assert:1 equals:[[arrayController arrangedObjects] count] message:@"objects should be removed"];
|
|
}
|
|
|
|
- (void)testRemoveObjectsAtArrangedObjectIndexes_whenObservingCount
|
|
{
|
|
var arrayController = [self arrayController];
|
|
|
|
[arrayController addObserver:self forKeyPath:@"arrangedObjects.name" options:0 context:nil];
|
|
[self bind:@"aCount" toObject:arrayController withKeyPath:@"arrangedObjects.@count" options:nil];
|
|
|
|
// This crashed in a previous version of Cappuccino due to an error in _CPObservableArray's removeObjectAtIndex.
|
|
[arrayController removeObjectsAtArrangedObjectIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(1, 2)]];
|
|
|
|
[self assert:1 equals:[[arrayController arrangedObjects] count] message:@"objects should be removed"];
|
|
[self assert:aCount equals:[[arrayController arrangedObjects] count] message:@"count should be updated"];
|
|
}
|
|
|
|
- (void)testSelectionWhenObjectsDisappear
|
|
{
|
|
// If the selected object disappears during a rearrange, the selection
|
|
// should update appropriately, even if preserve selection is off.
|
|
var arrayController = [self arrayController];
|
|
[arrayController setPreservesSelection:NO];
|
|
|
|
// Use a copy to make sure our original remains pristine.
|
|
[arrayController setSelectionIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 3)]];
|
|
|
|
var newContent = [[self contentArray] copy];
|
|
[newContent removeObjectAtIndex:2];
|
|
|
|
[arrayController setContent:newContent];
|
|
|
|
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)] equals:[arrayController selectionIndexes] message:@"last object cannot be selected"];
|
|
}
|
|
|
|
- (void)testSelectingEmptyIndexesExplicitlyWithAvoidsEmptySelection
|
|
{
|
|
var arrayController = [self arrayController];
|
|
[arrayController setAvoidsEmptySelection:YES];
|
|
|
|
[arrayController setSelectionIndex:0];
|
|
[arrayController setSelectionIndexes:[CPIndexSet indexSet]];
|
|
[self assertTrue:([[arrayController selectionIndexes] count] == 0) message:@"Selection should be empty when unselecting explicitly, even with avoidsEmptySelection"];
|
|
}
|
|
|
|
- (void)testSelectingEmptyObjectsExplicitlyWithAvoidsEmptySelection
|
|
{
|
|
var arrayController = [self arrayController];
|
|
[arrayController setAvoidsEmptySelection:YES];
|
|
|
|
[arrayController setSelectionIndex:0];
|
|
[arrayController setSelectedObjects:[CPArray array]];
|
|
[self assertTrue:([[arrayController selectionIndexes] count] == 0) message:@"Selection should be empty when unselecting explicitly, even with avoidsEmptySelection"];
|
|
}
|
|
|
|
- (void)testContentBinding
|
|
{
|
|
[[self arrayController] bind:@"contentArray" toObject:self withKeyPath:@"contentArray" options:nil];
|
|
|
|
[self assert:[[self arrayController] contentArray] equals:[self contentArray]];
|
|
|
|
[[self mutableArrayValueForKey:@"contentArray"] addObject:@"4"];
|
|
[self assert:[self contentArray] equals:[[self arrayController] contentArray]
|
|
message:@"object 4 was added; contentArray should reflect this"];
|
|
|
|
[[self arrayController] insertObject:@"2" atArrangedObjectIndex:1];
|
|
[self assert:[[self arrayController] contentArray] equals:[self contentArray]];
|
|
}
|
|
|
|
- (CPArray)setupObservationFixture
|
|
{
|
|
// objj_msgSend_decorate(objj_backtrace_decorator);
|
|
|
|
var ac = [self arrayController];
|
|
[ac setSelectionIndex:0];
|
|
[ac setPreservesSelection:YES];
|
|
var newContent = [_contentArray copy];
|
|
[newContent removeObjectAtIndex:0];
|
|
|
|
[ac addObserver:self forKeyPath:"content" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
|
|
[ac addObserver:self forKeyPath:"selectionIndexes" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
|
|
[ac addObserver:self forKeyPath:"selectedObjects" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
|
|
[ac addObserver:self forKeyPath:"arrangedObjects" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
|
|
|
|
observations = [];
|
|
|
|
return newContent;
|
|
}
|
|
|
|
- (void)testObservationDuringSetContent
|
|
{
|
|
/*
|
|
There are two areas where CPArrayController can get into trouble with observation.
|
|
|
|
The more serious one is that the before values when observing selectedObjects
|
|
need to be correct and not affected by the new setContent: values.
|
|
|
|
Second, the array controller should not send out more than one of each notification
|
|
since repeated notifications for the same change can be a huge performance drain
|
|
from such a central piece of code.
|
|
*/
|
|
|
|
var ac = [self arrayController],
|
|
newContent = [self setupObservationFixture];
|
|
|
|
[ac setContent:newContent];
|
|
|
|
[observations sortUsingFunction:function(a, b) { return [a.keyPath compare:b.keyPath] } context:nil];
|
|
[self assert:4 equals:[observations count] message:"exactly 4 change notifications should be sent for new content"];
|
|
|
|
// for (var i=0; i<observations.length; i++)
|
|
// CPLog.error(observations[i].keyPath);
|
|
|
|
var observation = observations[0];
|
|
[self assert:"arrangedObjects" equals:observation.keyPath];
|
|
[self assert:_contentArray equals:observation.oldValue message:"old arranged content should be correct"];
|
|
[self assert:newContent equals:observation.newValue message:"new arranged content should be correct"];
|
|
|
|
observation = observations[1];
|
|
[self assert:"content" equals:observation.keyPath];
|
|
[self assert:_contentArray equals:observation.oldValue message:"old content should be correct"];
|
|
[self assert:newContent equals:observation.newValue message:"new content should be correct"];
|
|
|
|
observation = observations[2];
|
|
[self assert:"selectedObjects" equals:observation.keyPath];
|
|
[self assert:[_contentArray[0]] equals:observation.oldValue message:"old selected objects should be previous object at selected index"];
|
|
[self assert:[] equals:observation.newValue message:"new selected objects should be nothing"];
|
|
|
|
observation = observations[3];
|
|
[self assert:"selectionIndexes" equals:observation.keyPath];
|
|
[self assert:[CPIndexSet indexSetWithIndex:0] equals:observation.oldValue message:"old selected index should be 0"];
|
|
[self assert:[CPIndexSet indexSet] equals:observation.newValue message:"new selected index should be nothing"];
|
|
}
|
|
|
|
- (void)testObservationDuringRearrange
|
|
{
|
|
var ac = [self arrayController],
|
|
newContent = [self setupObservationFixture],
|
|
selection = [CPIndexSet indexSetWithIndex:0];
|
|
|
|
[selection addIndex:1];
|
|
// Francisco and Ross selected.
|
|
[ac setSelectionIndexes:selection];
|
|
|
|
observations = [];
|
|
[ac setFilterPredicate:[CPPredicate predicateWithFormat:@"(name != %@)", "Francisco"]];
|
|
|
|
[observations sortUsingFunction:function(a, b) { return [a.keyPath compare:b.keyPath] } context:nil];
|
|
//for (var i=0; i<observations.length; i++)
|
|
// CPLog.error(observations[i].keyPath);
|
|
[self assert:3 equals:[observations count] message:"exactly 3 change notifications should be sent for new filter"];
|
|
|
|
var observation = observations[0];
|
|
[self assert:"arrangedObjects" equals:observation.keyPath];
|
|
[self assert:_contentArray equals:observation.oldValue message:"old arranged content should be correct"];
|
|
[self assert:newContent equals:observation.newValue message:"new arranged content should be correct"];
|
|
|
|
observation = observations[1];
|
|
[self assert:"selectedObjects" equals:observation.keyPath];
|
|
[self assert:[_contentArray[0], _contentArray[1]] equals:observation.oldValue message:"old selected objects should be previous object at selected index"];
|
|
[self assert:[_contentArray[1]] equals:observation.newValue message:"new selected objects should be only non filtered ones"];
|
|
|
|
observation = observations[2];
|
|
[self assert:"selectionIndexes" equals:observation.keyPath];
|
|
[self assert:selection equals:observation.oldValue message:"old selected index should be 0 and 1"];
|
|
[self assert:[CPIndexSet indexSetWithIndex:0] equals:observation.newValue message:"new selected index should be 0"];
|
|
}
|
|
|
|
- (void)testObservationDuringSetSelectionIndexes
|
|
{
|
|
var arrayController = [self arrayController];
|
|
|
|
[self setupObservationFixture];
|
|
|
|
var newSelection = [CPIndexSet indexSetWithIndex:2];
|
|
[arrayController setSelectionIndexes:newSelection];
|
|
|
|
[self assertNotNull:[arrayController selection] message:@"a selection was made, selection proxy should be defined"];
|
|
[self assert:2 equals:[observations count] message:@"exactly 2 change notifications should be sent for new selection indexes"];
|
|
[self assert:newSelection equals:[arrayController selectionIndexes] message:@"selection was not set properly"];
|
|
}
|
|
|
|
- (void)testObservationDuringAddObject_
|
|
{
|
|
var arrayController = [self arrayController];
|
|
|
|
[arrayController addObserver:self forKeyPath:@"arrangedObjects" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
|
|
|
|
// Add something to clear.
|
|
[arrayController setFilterPredicate:[CPPredicate predicateWithFormat:@"(name != %@)", "Francisco"]];
|
|
observations = [];
|
|
var aPerson = [Employee employeeWithName:@"Alexander" department:[Department departmentWithName:@"Cosmic Path Finding"]];
|
|
|
|
[arrayController setClearsFilterPredicateOnInsertion:NO];
|
|
[self assert:0 equals:[observations count] message:@"no observations before addObject test"];
|
|
[arrayController addObject:aPerson];
|
|
[self assert:1 equals:[observations count] message:@"exactly 1 notification for addObject (clearsFilterPredicate NO)"];
|
|
|
|
observations = [];
|
|
|
|
// Even that this is on, adding an object should only result in one notification.
|
|
[arrayController setClearsFilterPredicateOnInsertion:YES];
|
|
|
|
[self assert:0 equals:[observations count] message:@"no observations before addObject test"];
|
|
[arrayController addObject:aPerson];
|
|
[self assert:1 equals:[observations count] message:@"exactly 1 notification for addObject (clearsFilterPredicate YES)"];
|
|
}
|
|
|
|
/*!
|
|
Test that if there is no filter predicate to clear, insertObject:atArrangedObjectIndex: with
|
|
clearsFilterPredicate YES does not send a false filterPredicate notification.
|
|
*/
|
|
- (void)testObservationDuringInsertObject_atArrangedIndex_
|
|
{
|
|
var arrayController = [self arrayController];
|
|
|
|
[arrayController addObserver:self forKeyPath:@"filterPredicate" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:nil];
|
|
|
|
// Add something to clear.
|
|
[arrayController setFilterPredicate:[CPPredicate predicateWithFormat:@"(name != %@)", "Francisco"]];
|
|
observations = [];
|
|
var aPerson = [Employee employeeWithName:@"Alexander" department:[Department departmentWithName:@"Cosmic Path Finding"]];
|
|
|
|
[arrayController setClearsFilterPredicateOnInsertion:YES];
|
|
[self assert:0 equals:[observations count] message:@"no observations before insertObject test"];
|
|
[arrayController insertObject:aPerson atArrangedObjectIndex:1];
|
|
[self assert:1 equals:[observations count] message:@"exactly 1 notification for insertObject (clearsFilterPredicate YES)"];
|
|
|
|
// Now that the filter is already cleared, we should not get notified that it clears again on the second insert.
|
|
[arrayController insertObject:aPerson atArrangedObjectIndex:1];
|
|
[self assert:1 equals:[observations count] message:@"exactly 1 notification for insertObject x 2 (clearsFilterPredicate YES)"];
|
|
}
|
|
|
|
/**
|
|
Replicate the situation found in the Bindings manual test where one array controller's
|
|
contents depend on the selection of another.
|
|
*/
|
|
- (void)testObservationBetweenBoundControllers
|
|
{
|
|
var companies = [Companies new],
|
|
companiesController = [CPArrayController new],
|
|
employeesController = [CPArrayController new];
|
|
|
|
[companiesController bind:@"contentArray" toObject:companies withKeyPath:@"items" options:nil];
|
|
[employeesController bind:@"contentArray" toObject:companiesController withKeyPath:@"selection.employees" options:nil];
|
|
|
|
[companiesController addObserver:self forKeyPath:@"selection" options:0 context:@"companies.selection"];
|
|
[companiesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"companies.selectionIndexes"];
|
|
[companiesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"companies.arrangedObjects"];
|
|
[employeesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"employees.selectionIndexes"];
|
|
[employeesController addObserver:self forKeyPath:@"selection" options:0 context:@"employees.selection"];
|
|
[employeesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"employees.arrangedObjects"];
|
|
|
|
observations = [];
|
|
|
|
[companiesController setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
|
|
|
|
//for (var i = 0; i < [observations count]; i++)
|
|
// CPLog.error(observations[i].context);
|
|
|
|
// There should be exactly one observation of each kind, and they should be in the right
|
|
// order.
|
|
[self assert:5 equals:[observations count]];
|
|
[self assert:@"companies.selectionIndexes" equals:observations[0].context];
|
|
[self assert:@"employees.arrangedObjects" equals:observations[1].context];
|
|
[self assert:@"employees.selectionIndexes" equals:observations[2].context];
|
|
[self assert:@"employees.selection" equals:observations[3].context];
|
|
// This observation registers after the employees update because we are later in the
|
|
// observation queue than the binding.
|
|
[self assert:@"companies.selection" equals:observations[4].context];
|
|
}
|
|
|
|
- (void)testCompoundKeyPaths
|
|
{
|
|
var departmentNameField = [[CPTextField alloc] init];
|
|
[departmentNameField bind:@"value" toObject:[self arrayController] withKeyPath:@"selection.department.name" options:nil];
|
|
|
|
// This should be 'No Selection'
|
|
[self assert:@"" equals:[departmentNameField stringValue]];
|
|
|
|
[[self arrayController] setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
|
|
[self assert:@"Cappuccino" equals:[departmentNameField stringValue]];
|
|
|
|
[[self arrayController] setSelectionIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
|
|
[self assert:@"Cappuccino" equals:[departmentNameField stringValue] message:@"key path values should be equal"];
|
|
|
|
[[self arrayController] setValue:@"280North" forKeyPath:@"selection.department.name"];
|
|
[self assert:@"280North" equals:[departmentNameField stringValue]];
|
|
|
|
[[self arrayController] setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
|
|
|
|
[[self arrayController] valueForKeyPath:@"selection.department.name"];
|
|
[[self arrayController] valueForKeyPath:@"selection.department.building"];
|
|
[[self arrayController] valueForKeyPath:@"selection.department"];
|
|
|
|
var employee = [[[self arrayController] selectedObjects] lastObject],
|
|
department = [Department departmentWithName:@"Meh"];
|
|
|
|
[department setBuilding:@"Building 1"];
|
|
[employee setDepartment:department];
|
|
|
|
[self assert:@"Meh" equals:[departmentNameField stringValue]];
|
|
[self assert:department equals:[[self arrayController] valueForKeyPath:@"selection.department"]];
|
|
[self assert:@"Building 1" equals:[[self arrayController] valueForKeyPath:@"selection.department.building"]];
|
|
}
|
|
|
|
- (void)testArrangedObjectsNotEmptyAfterSetContentWhenClearsFilterOnInsertionIsTrue
|
|
{
|
|
var arrayController = [[CPArrayController alloc] init];
|
|
[arrayController setFilterPredicate:nil];
|
|
[arrayController setClearsFilterPredicateOnInsertion:YES];
|
|
[arrayController setContent:[CPArray arrayWithObject:@"a"]];
|
|
|
|
[self assertTrue:[[arrayController arrangedObjects] count] > 0];
|
|
}
|
|
|
|
- (void)testArrangedObjectsWhenSetContentAndClearsFilterOnInsertionIsTrue
|
|
{
|
|
var arrayController = [[CPArrayController alloc] init];
|
|
[arrayController setClearsFilterPredicateOnInsertion:YES];
|
|
[arrayController setFilterPredicate:[CPPredicate predicateWithValue:NO]];
|
|
[arrayController setContent:[CPArray arrayWithObject:@"a"]];
|
|
|
|
[self assertTrue:[[arrayController arrangedObjects] count] == 0];
|
|
}
|
|
|
|
- (void)testArrangedObjectsWhenBindingAndClearsFilterOnInsertionIsTrue
|
|
{
|
|
var arrayController = [[CPArrayController alloc] init];
|
|
[arrayController setClearsFilterPredicateOnInsertion:YES];
|
|
[arrayController setFilterPredicate:[CPPredicate predicateWithValue:NO]];
|
|
[arrayController bind:@"contentArray" toObject:self withKeyPath:@"_contentArray" options:nil];
|
|
|
|
[self assertTrue:[[arrayController arrangedObjects] count] == 0];
|
|
}
|
|
|
|
- (void)testArrangedObjectsWithPredicateFilteringAfterContentArrayBinding
|
|
{
|
|
_contentArray = [self makeTestArray];
|
|
|
|
var arrayController = [[CPArrayController alloc] init];
|
|
[arrayController bind:@"contentArray" toObject:self withKeyPath:@"_contentArray" options:nil];
|
|
[arrayController setFilterPredicate:[CPPredicate predicateWithFormat:@"department.name BEGINSWITH 'Capp'"]];
|
|
|
|
var arrangedCount = [[arrayController arrangedObjects] count];
|
|
[self assertTrue:(arrangedCount == 2) message:@"Count should be 2 and is " + arrangedCount];
|
|
}
|
|
|
|
/**
|
|
In a table with arranged contents like [1, 1, 2, 1], selecting the second '1' and removing it
|
|
should result in [1, 2, 1] - not [2]. E.g. we don't use removeObject:1 but only remove the
|
|
actually selected instance.
|
|
*/
|
|
- (void)testRemove_OneOfMultipleEqualObjects
|
|
{
|
|
var ac = [CPArrayController new],
|
|
contentArray = [1, 1, 2, 1];
|
|
[ac setContent:contentArray];
|
|
[self assert:[1, 1, 2, 1] equals:[ac arrangedObjects]];
|
|
[ac setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
|
|
[ac remove:nil];
|
|
[self assert:[1, 2, 1] equals:[ac arrangedObjects] message:"only one copy of 1 removed + the right copy should be removed"];
|
|
}
|
|
|
|
- (void)testSetOutOfBoundsSelectedIndexes
|
|
{
|
|
var ac = [CPArrayController new],
|
|
contentArray = [1, 2, 3],
|
|
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 4)];
|
|
|
|
[ac setContent:contentArray];
|
|
[ac setSelectionIndexes:indexes];
|
|
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 3)] equals:[ac selectionIndexes]];
|
|
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 4)] equals:indexes];
|
|
}
|
|
|
|
- (void)testBindToNoSelectionMarker
|
|
{
|
|
var arrayController1 = [CPArrayController new],
|
|
arrayController2 = [CPArrayController new];
|
|
|
|
[arrayController2 setContent:@{ @"x": [1, 2, 3] }];
|
|
|
|
[arrayController1 bind:@"contentArray" toObject:arrayController2 withKeyPath:@"selection.x" options:nil];
|
|
// This used to cause a bug where the _CPKVCArray wrapping the selection proxy tried to call 'count' on
|
|
// CPNoSelectionMarker while attempting to copy itself.
|
|
[arrayController2 setSelectionIndexes:[CPIndexSet indexSet]];
|
|
|
|
[self assert:[] equals:[arrayController1 arrangedObjects] message:"arranged objects of an empty selection should be empty"];
|
|
|
|
// Make sure the regular case works.
|
|
[arrayController2 setSelectionIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 1)]];
|
|
[self assert:[1, 2, 3] equals:[arrayController1 arrangedObjects] message:"normal selection"];
|
|
}
|
|
|
|
/*!
|
|
Test that bindings are not generation any change dictionary with Old or New values as a observer
|
|
*/
|
|
- (void)testCreationOfOldAndNeChangeDictionaryinObserverProtocol
|
|
{
|
|
[self initControllerWithContentBinding];
|
|
|
|
var arrayController = [self arrayController];
|
|
|
|
[self bind:@"nameValues" toObject:arrayController withKeyPath:@"arrangedObjects.name" options:nil];
|
|
|
|
countGetName = 0;
|
|
|
|
[self setItemsArray:[]];
|
|
[self assert:0 equals:countGetName];
|
|
|
|
countGetName = 0;
|
|
|
|
[self setItemsArray:[self makeTestArray]];
|
|
[self assert:3 equals:countGetName message:@"3 items in array and one '.name' bindings should make 3 accesses to the name property not " + countGetName + " times"];
|
|
}
|
|
|
|
/*!
|
|
Test the speed of set an big array when the old was an empty.
|
|
Also test the speed when an empty array is set and the old is an big
|
|
*/
|
|
- (void)testPerformance
|
|
{
|
|
[self initControllerWithContentBinding];
|
|
|
|
var arrayController = [self arrayController];
|
|
[self bind:@"nameValues" toObject:arrayController withKeyPath:@"arrangedObjects.name" options:nil];
|
|
[self bind:@"depValues" toObject:arrayController withKeyPath:@"arrangedObjects.department" options:nil];
|
|
|
|
[self setItemsArray:[]];
|
|
var bigArray = [self makeTestArrayMany];
|
|
|
|
var d = new Date();
|
|
[self setItemsArray:bigArray];
|
|
var dd = new Date();
|
|
[self setItemsArray:[]];
|
|
var ddd = new Date();
|
|
|
|
CPLog.warn("\n" + [self className] + " Performance tests");
|
|
CPLog.warn("EmptyArray -> BigArray (" + [bigArray count] + "): " + (dd - d) + "ms.");
|
|
CPLog.warn("BigArray (" + [bigArray count] + ") -> EmptyArray: " + (ddd - dd) + "ms.");
|
|
}
|
|
|
|
- (void)observeValueForKeyPath:keyPath
|
|
ofObject:anActivity
|
|
change:change
|
|
context:context
|
|
{
|
|
// CPLog.warn("observeValueForKeyPath:" + keyPath);
|
|
// objj_backtrace_print(CPLog.error);
|
|
|
|
[observations addObject:{
|
|
keyPath: keyPath,
|
|
context: context,
|
|
oldValue: [change valueForKey:CPKeyValueChangeOldKey],
|
|
newValue: [change valueForKey:CPKeyValueChangeNewKey]
|
|
}];
|
|
}
|
|
|
|
/*!
|
|
The code below makes the key "itemsArray" KVC compliant so that it can be used for testing
|
|
array controllers bound to destinations with such setups.
|
|
*/
|
|
- (unsigned int)countOfItemsArray
|
|
{
|
|
return [_contentArray count];
|
|
}
|
|
|
|
- (id)objectInItemsArrayAtIndex:(CPUInteger)index
|
|
{
|
|
return [_contentArray objectAtIndex:index];
|
|
}
|
|
|
|
- (void)insertObject:(id)anObject inItemsArrayAtIndex:(CPUInteger)index
|
|
{
|
|
[_contentArray insertObject:anObject atIndex:index];
|
|
}
|
|
|
|
- (void)removeObjectFromItemsArrayAtIndex:(CPUInteger)index
|
|
{
|
|
[_contentArray removeObjectAtIndex:index];
|
|
}
|
|
|
|
- (void)replaceObjectInItemsArrayAtIndex:(CPUInteger)index withObject:(id)anObject
|
|
{
|
|
[_contentArray replaceObjectAtIndex:index withObject:anObject];
|
|
}
|
|
|
|
- (void)setItemsArray:(CPArray)newValue
|
|
{
|
|
[self setValue:newValue forKey:@"contentArray"];
|
|
}
|
|
|
|
- (CPArray)itemsArray
|
|
{
|
|
return [self valueForKey:@"contentArray"];
|
|
}
|
|
|
|
@end
|
|
|
|
var countGetName;
|
|
|
|
@implementation Employee : CPObject
|
|
{
|
|
CPString _name @accessors(property=name);
|
|
Department _department @accessors(property=department);
|
|
}
|
|
|
|
+ (id)employeeWithName:(CPString)theName department:(Department)theDepartment
|
|
{
|
|
return [[self alloc] initWithName:theName department:theDepartment];
|
|
}
|
|
|
|
- (id)initWithName:(CPString)theName department:(Department)theDepartment
|
|
{
|
|
if (self = [super init])
|
|
{
|
|
_name = theName;
|
|
_department = theDepartment;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (CPString)description
|
|
{
|
|
return [CPString stringWithFormat:@"<Employee %@>", [self name]];
|
|
}
|
|
|
|
- (CPString)name
|
|
{
|
|
countGetName++;
|
|
return _name;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation Department : CPObject
|
|
{
|
|
CPString _name @accessors(property=name);
|
|
CPString _building @accessors(property=building);
|
|
}
|
|
|
|
+ (id)departmentWithName:(CPString)theName
|
|
{
|
|
return [[self alloc] initWithName:theName];
|
|
}
|
|
|
|
- (id)initWithName:(CPString)theName
|
|
{
|
|
if (self = [super init])
|
|
{
|
|
_name = theName;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation Companies : CPObject
|
|
{
|
|
CPMutableArray items @accessors;
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
items = [CPMutableArray array];
|
|
[items addObject:@{
|
|
@"name": @"Spacely Sprockets",
|
|
@"employees": [CPArray arrayWithObjects:@"Tom", @"Dick", @"Harry"]
|
|
}];
|
|
[items addObject:@{
|
|
@"name": @"Cogswell Cogs",
|
|
@"employees": [CPArray arrayWithObjects:@"Jane", @"Mary", @"Vic"]
|
|
}];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
@end
|