mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-24 17:18:54 +00:00
The TextEditor work list recorded redo as unreliable. Retesting proved that undo and redo work correctly. Supporting investigation disclosed an edge case in CPUndoManager: when undo ran in the same run loop cycle as the change it reverted, it closed the open event grouping itself, but the run loop's scheduled close still ran and raised CPInternalInconsistencyException because no grouping was open. * Foundation/CPUndoManager.j: Cancel the run loop's scheduled close when undo closes the event grouping itself. * Tests/Foundation/CPUndoManagerTest.j: Add a test that undoes a change in the same run loop cycle and then redoes it, and correct the doIt selector registered by the undoIt helper. * Tests/Manual/TextEditor/WORKLIST.md: Remove the redo entry recorded in error, and record undo and redo as tested.
104 lines
3.3 KiB
Plaintext
104 lines
3.3 KiB
Plaintext
|
|
@import <Foundation/CPUndoManager.j>
|
|
|
|
@implementation CPUndoManagerTest : OJTestCase
|
|
{
|
|
CPArray receivedNotifications;
|
|
CPUndoManager undoManager;
|
|
BOOL itIsDone;
|
|
}
|
|
|
|
- (void)setUp
|
|
{
|
|
undoManager = [[CPUndoManager alloc] init];
|
|
itIsDone = NO;
|
|
receivedNotifications = [CPMutableArray array];
|
|
}
|
|
|
|
- (void)testUndoMenuTitleForUndoActionName
|
|
{
|
|
[self assert:[undoManager undoMenuTitleForUndoActionName:undefined] equals:@"Undo"];
|
|
[self assert:[undoManager undoMenuTitleForUndoActionName:nil] equals:@"Undo"];
|
|
[self assert:[undoManager undoMenuTitleForUndoActionName:""] equals:@"Undo"];
|
|
|
|
[self assert:[undoManager undoMenuTitleForUndoActionName:0] equals:@"Undo 0"];
|
|
[self assert:[undoManager undoMenuTitleForUndoActionName:"0"] equals:@"Undo 0"];
|
|
|
|
[self assert:[undoManager undoMenuTitleForUndoActionName:"STRING"] equals:@"Undo STRING"];
|
|
}
|
|
|
|
- (void)testRedoMenuTitleForUndoActionName
|
|
{
|
|
[self assert:[undoManager redoMenuTitleForUndoActionName:undefined] equals:@"Redo"];
|
|
[self assert:[undoManager redoMenuTitleForUndoActionName:nil] equals:@"Redo"];
|
|
[self assert:[undoManager redoMenuTitleForUndoActionName:""] equals:@"Redo"];
|
|
|
|
[self assert:[undoManager redoMenuTitleForUndoActionName:0] equals:@"Redo 0"];
|
|
[self assert:[undoManager redoMenuTitleForUndoActionName:"0"] equals:@"Redo 0"];
|
|
|
|
[self assert:[undoManager redoMenuTitleForUndoActionName:"STRING"] equals:@"Redo STRING"];
|
|
}
|
|
|
|
- (void)testNotifications
|
|
{
|
|
|
|
[[CPNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(receiveNotification:)
|
|
name:CPUndoManagerDidCloseUndoGroupNotification
|
|
object:undoManager];
|
|
|
|
[self doIt];
|
|
|
|
// The default run loop undo grouping won't be closed until the next run loop cycle.
|
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
|
|
[self assert:CPUndoManagerDidCloseUndoGroupNotification equals:[receivedNotifications[0] name]];
|
|
|
|
[[CPNotificationCenter defaultCenter] removeObserver:self];
|
|
}
|
|
|
|
- (void)testUndoInSameRunLoopCycleAsChange
|
|
{
|
|
[self doIt];
|
|
|
|
// The change opened an event grouping that the run loop is scheduled to close. Undo closes it first.
|
|
[undoManager undo];
|
|
|
|
[self assertFalse:itIsDone message:@"undo should revert the change"];
|
|
|
|
// The scheduled close must not run against the grouping that undo already closed.
|
|
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
|
|
|
[self assert:0 equals:[undoManager groupingLevel]];
|
|
[self assertTrue:[undoManager canRedo] message:@"the undone change should be available to redo"];
|
|
|
|
[undoManager redo];
|
|
|
|
[self assertTrue:itIsDone message:@"redo should reapply the change"];
|
|
}
|
|
|
|
- (void)receiveNotification:(CPNotification)aNotification
|
|
{
|
|
[receivedNotifications addObject:aNotification];
|
|
}
|
|
|
|
- (void)doIt
|
|
{
|
|
[undoManager registerUndoWithTarget:self
|
|
selector:@selector(undoIt)
|
|
object:nil];
|
|
|
|
itIsDone = YES;
|
|
}
|
|
|
|
- (void)undoIt
|
|
{
|
|
[undoManager registerUndoWithTarget:self
|
|
selector:@selector(doIt)
|
|
object:nil];
|
|
|
|
itIsDone = NO;
|
|
}
|
|
|
|
@end
|