Группа отмены начала/конца в XCTest/приложении, вызывающая странное поведение

Испытательная установка:

// Create a clean context for this test.
NSManagedObjectContext *cleanContext = [MyContextManager sharedInstance] newMOC];

// Create a core data object.
MyCoreDataObject *myObject = [MyCoreDataObject insertNewObjectInMoc:cleanContext];
myObject.name = @"Test Name 1";

// Create an undo manager - set on the context.
cleanContext.undoManager = [NSUndoManager new];
// Turn off automatic grouping
cleanContext.undoManager.groupsByEvent = NO;
[cleanContext.undoManager beginUndoGrouping];

myObject.name = @"Test Name 2";

XCTAssertEqualObjects(myObject.name, @"Test Name 2", @"Setting name failed");

// End undo grouping
[cleanContext.undoManager endUndoGrouping];
// Undo the group that has just been created
[cleanContext.undoManager undoNestedGroup];

 XCTAssertEqualObjects(myObject.name, @"Test Name 1", @"Undo failed");

Я вижу, что у диспетчера отмен есть частный стек вызовов, к которому я могу получить доступ через [cleanContext.undoManager performSelector:@selector(_undoStack)]. При просмотре этого, хотя я отключил группировку по событиям, есть новые группы отмен, которые я не добавил.

Это причиняет много боли — такие ошибки, как NSUndoManager is in invalid state, случаются, хотя я правильно управляю начальной и конечной группами отмены.

Добавляет ли установка атрибутов в основной объект данных их автоматически независимо от настроек группировки по событиям?

Кому-нибудь удалось создать стабильную функцию отмены с помощью встроенного диспетчера отмены?


person teddy777    schedule 26.01.2015    source источник


Ответы (1)


Я не вижу весь ваш код, но если вы вызовете undo() в тестовом примере, он отменит все в вашем методе. (на самом деле полезно проверить)

Каждый цикл выполнения создает собственную группу отмены. Метод тестирования находится внутри этого цикла выполнения. Вызов undo() закроет эту группу, а затем все отменит.

Вы хотите вызвать undoNestedGroup(), если пытаетесь протестировать функцию отмены. (мой пример на Swift, но простой)

func testInsertUndo() {

  doc.undoManager?.beginUndoGrouping()
  let r1 = doc.createRegion("testRegion1")
  doc.undoManager?.endUndoGrouping()

  doc.undoManager?.beginUndoGrouping()
  doc.createRegion("testRegion2")
  doc.undoManager?.endUndoGrouping()

  // undo() will close surrounding run-loop undo group and then undo the whole thing
  doc.undoManager?.undoNestedGroup()
  XCTAssert(doc.regionCount == 1, "undo should remove last region \(doc.regionCount) != 1")

  for (index, r) in doc.regionEnumerator {
    XCTAssert(index == 0 && r === r1, "first region remains")
  }
}
person bshirley    schedule 13.01.2016
comment
Я позвонил undoNestedGroup, а не undo, поэтому не уверен, что причина в этом. Я видел странную группировку событий на undoStack. Этот вопрос довольно старый, я попробую использовать вашу быструю реализацию и посмотрю, смогу ли я заставить все работать. Несколько вопросов, что такое doc в вашем ответе? Это контекст управляемого объекта? Кроме того, как вы можете определить, когда конкретный цикл запуска начнется / закончится? - person teddy777; 15.01.2016
comment
doc — это настраиваемый подкласс NSDocument, в основном объект данных, управляемый в приложении в стиле документа — он сохраняет себя в формате файла (без основных данных) - person bshirley; 16.01.2016
comment
не уверен во внутренностях фреймворка XCTest, но они, вероятно, вызывают каждый тестовый пример из цикла выполнения, поэтому каждый метод будет в своей собственной группе undoManager. вы не можете это контролировать. в случае интерактивного пользовательского интерфейса вам гарантировано, что действия пользователя поступают оттуда, поэтому у вас есть новый вызов из цикла выполнения (и новая группировка undoManager) для любого действия пользователя. поскольку мы моделируем несколько действий пользователя в одном тестовом примере, мы должны действовать по-разному. - person bshirley; 16.01.2016