Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests may randomly fail due to a bug in expectation fulfillment #419

Merged

Conversation

danthorpe
Copy link
Member

@danthorpe danthorpe commented Aug 25, 2016

If XCTestExpectations are not fulfilled on the main thread, waitForExpectationsWithTimeout may occasionally fail with the timeout.

Fix: Ensure that XCTestExpectation.fulfill() is always called on the main thread.

Some places this needs to be fixed:

  • OperationTests.addCompletionBlockToTestOperation()

In BasicTests.swift:

  • test__add_multiple_completion_blocks
  • test__block_operation_with_default_block_runs_completion_block_once
  • test__nsblockoperation_runs_completion_block_once
  • test__dependent_operations_always_run
  • test__delay_operation_completes_after_interval

In BlockConditionTests.swift:

  • test__operation_with_unsuccessful_block_condition_errors
  • test__operation_with_block_which_throws_condition_errors

In CloudCapabilitiesTests.swift:

  • test__request_permissions

In GroupOperationTests.swift:

  • test__group_operations_are_performed_in_order
  • test__adding_operation_to_running_group

In NegatedConditionTests.swift:

  • test__operation_with_successful_block_condition_fails

In RemoteNotificationConditionTests.swift:

  • test__condition_fails__when_registration_fails

In StressTests.swift:

  • test__completion_blocks
  • test__SR_192_OperationQueue_delegate_weak_var_thread_safety

@danthorpe danthorpe added this to the 3.1.2 milestone Aug 24, 2016
@danthorpe
Copy link
Member

danthorpe commented Aug 25, 2016

Build failures are in the stress tests:

[06:53:55]: ▸ StressTest
[06:53:58]: ▸ ✓ test__block_operation_cancel (2.250 seconds)
[06:54:05]: ▸ ✗ test__completion_blocks, Asynchronous wait failed: Exceeded timeout of 5 seconds, with unfulfilled expectations: "Interation: 9986", "Interation: 9987", "Interation: 9994", "Interation: 9988", "Interation: 9995", "Interation: 9989", "Interation: 9996", "Interation: 9998", "Interation: 9990", "Interation: 9991", "Interation: 9997", "Interation: 9993", "Interation: 9984", "Interation: 9999", "Interation: 9985", "Interation: 9992".
[06:54:09]: ▸ ✓ test__conditions (4.119 seconds)
[06:54:14]: ▸ ✓ test__conditions_will_finish_observer_operation_cancel_thread_safety (5.419 seconds)
[06:54:21]: ▸ ✓ test__conditions_with_single_dependency (7.099 seconds)
[06:54:26]: ▸ ✓ test__group_operation__does_not_finish_before_child_operations_are_finished (4.804 seconds)
[06:54:30]: ▸ ✓ test__group_operation_cancel (3.984 seconds)
[06:54:36]: ▸ ✓ test__group_operation_cancel_and_add_operation (5.423 seconds)
[06:54:40]: ▸ ✓ test__repeated_operation_cancel (3.946 seconds)
[06:54:46]: ▸ ✓ test__SR_192_OperationQueue_delegate_weak_var_thread_safety (6.002 seconds)
[06:54:46]: ▸ Stress_Tests.StressTest
[06:54:46]: ▸ test__completion_blocks, Asynchronous wait failed: Exceeded timeout of 5 seconds, with unfulfilled expectations: "Interation: 9986", "Interation: 9987", "Interation: 9994", "Interation: 9988", "Interation: 9995", "Interation: 9989", "Interation: 9996", "Interation: 9998", "Interation: 9990", "Interation: 9991", "Interation: 9997", "Interation: 9993", "Interation: 9984", "Interation: 9999", "Interation: 9985", "Interation: 9992".
[06:54:46]: ▸ /usr/local/var/buildkite-agent/builds/Hodor/blindingskies/procedurekit/Tests/Core/StressTests.swift:30
[06:54:46]: ▸ ```
[06:54:46]: ▸         }
[06:54:46]: ▸         waitForExpectationsWithTimeout(5, handler: nil)
[06:54:46]: ▸     }
[06:54:46]: ▸   ```
[06:54:46]: ▸      Executed 10 tests, with 1 failure (0 unexpected) in 50.137 (50.144) seconds
[06:54:46]: ▸

FYI - I've recently switched which machine swift/2.2 stuff runs on - now an older iMac, previously a MacPro. The batch sizes / timeouts might be a little bit ambitious now?

@swiftlyfalling
Copy link
Member Author

swiftlyfalling commented Aug 25, 2016

@danthorpe: Yep, that looks to be the issue. Will raise the affected timeouts. (I'm not sure that lowering batch sizes is a good idea, as some of the race conditions were consistently reproducible only with large batches.)

EDIT: Actually, it failed again, so I'm going to rework test__completion_blocks to use a dispatch_group.

@danthorpe
Copy link
Member

Hi @swiftlyfalling - will look to get you visibility on the build logs

[18:28:00]: ▸ StressTest
[18:28:08]: ▸ ✗ test__conditions_with_single_dependency, Asynchronous wait failed: Exceeded timeout of 3 seconds, with unfulfilled expectations: "Test: test__conditions_with_single_dependency(), D3293294-D65B-431E-94C0-8B490C63335D", "Test: test__conditions_with_single_dependency(), BBEB8955-7475-4F3F-A75F-C0D2D47BFDBC".
[18:28:08]: ▸ ✗ test__conditions_with_single_dependency, XCTAssertTrue failed -
[18:28:14]: ▸ ✓ test__group_operation__does_not_finish_before_child_operations_are_finished (5.634 seconds)
[18:28:19]: ▸ ✓ test__group_operation_cancel (4.527 seconds)
[18:28:25]: ▸ ✓ test__group_operation_cancel_and_add_operation (6.298 seconds)
[18:28:29]: ▸ ✓ test__repeated_operation_cancel (4.505 seconds)
[18:28:36]: ▸ ✓ test__SR_192_OperationQueue_delegate_weak_var_thread_safety (6.642 seconds)
[18:28:36]: ▸ Stress_Tests.StressTest
[18:28:36]: ▸ test__conditions_with_single_dependency, Asynchronous wait failed: Exceeded timeout of 3 seconds, with unfulfilled expectations: "Test: test__conditions_with_single_dependency(), D3293294-D65B-431E-94C0-8B490C63335D", "Test: test__conditions_with_single_dependency(), BBEB8955-7475-4F3F-A75F-C0D2D47BFDBC".
[18:28:36]: ▸ /usr/local/var/buildkite-agent/builds/Hodor/blindingskies/procedurekit/Tests/Core/OperationTests.swift:186
[18:28:36]: ▸ ```
[18:28:36]: ▸         queue.addOperation(operation)
[18:28:36]: ▸         waitForExpectationsWithTimeout(3, handler: nil)
[18:28:36]: ▸     }
[18:28:36]: ▸   ```
[18:28:36]: ▸ test__conditions_with_single_dependency, XCTAssertTrue failed - 
[18:28:36]: ▸ /usr/local/var/buildkite-agent/builds/Hodor/blindingskies/procedurekit/Tests/Core/StressTests.swift:58
[18:28:36]: ▸ ```
[18:28:36]: ▸         waitForOperation(operation)
[18:28:36]: ▸         XCTAssertTrue(operation.didExecute)
[18:28:36]: ▸     }
[18:28:36]: ▸   ```
[18:28:36]: ▸      Executed 6 tests, with 2 failures (0 unexpected) in 35.645 (35.651) seconds
[18:28:36]: ▸
[18:28:36]: ▸ All tests

@swiftlyfalling
Copy link
Member Author

@danthorpe: Much appreciated. Tuning the StressTest timeouts for the CI machine sight-unseen is problematic. 😁

@danthorpe
Copy link
Member

@swiftlyfalling yeah, at the moment I'm just waiting for some DNS propagation for Google apps.

@danthorpe
Copy link
Member

It's weird, it's currently failing with this....

Failing tests:
-[StressTest test__conditions_will_finish_observer_operation_cancel_thread_safety()]
** TEST FAILED **
[20:13:13]: Exit status: 65

But... the test suite doesn't actually include that test:

[20:12:01]: ▸ Test Suite Stress Tests.xctest started
[20:12:01]: ▸ StressTest
[20:12:04]: ▸ ✓ test__block_operation_cancel (2.506 seconds)
[20:12:05]: ▸ ✓ test__completion_blocks (1.521 seconds)
[20:12:10]: ▸ ✓ test__conditions (4.477 seconds)
[20:12:12]: ▸ 2016-08-25 20:12:12.609 xcodebuild[88986:3491648] [MT] IDETestOperationsObserverDebug: (EB8BC54B-D074-4119-B7DE-EAABE7A4C28A) Beginning test session EB8BC54B-D074-4119-B7DE-EAABE7A4C28A with Xcode 7D1014 on target <DVTLocalComputer: 0x7fd12e792040 (My Mac | x86_64)> (10.11.6 (15G31))
[20:12:12]: ▸ 2016-08-25 20:12:12.609 xcodebuild[88986:3491795]  IDETestOperationsObserverDebug: Writing diagnostic log for test session to:
[20:12:12]: ▸ /usr/local/var/buildkite-agent/builds/Hodor/blindingskies/procedurekit/.ci/xcodebuild-data/Logs/Test/69CCB48B-A9B9-423E-8A52-535B2878D63F/Session-2016-08-25_20:12:12-mSCMyq.log
[20:12:12]: ▸ Selected tests
[20:12:12]: ▸ Test Suite Stress Tests.xctest started
[20:12:12]: ▸ StressTest
[20:12:20]: ▸ ✓ test__conditions_with_single_dependency (7.806 seconds)
[20:12:25]: ▸ ✓ test__group_operation__does_not_finish_before_child_operations_are_finished (5.449 seconds)
[20:12:30]: ▸ ✓ test__group_operation_cancel (4.567 seconds)
[20:12:36]: ▸ ✓ test__group_operation_cancel_and_add_operation (6.182 seconds)
[20:12:41]: ▸ ✓ test__repeated_operation_cancel (4.548 seconds)
[20:12:48]: ▸ ✓ test__SR_192_OperationQueue_delegate_weak_var_thread_safety (6.882 seconds)
[20:12:48]: ▸      Executed 6 tests, with 0 failures (0 unexpected) in 35.434 (35.438) seconds

@swiftlyfalling
Copy link
Member Author

Something is going on on that build machine (maybe a corrupt DerivedData folder?). There are 10 tests in StressTests.swift.

dispatch_async(Queue.Main.queue, {
guard let expectation = expectation else { print("Test: \(#function): Finished expectation after timeout"); return }
expectation.fulfill()
})
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all quite good, I think ideally all of these expectation fulfill() should be done in OperationTests using the waitForOperation() API.

It can be revisited when we move things across to ProcedureKit and Swift 3.0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In many cases, agreed.

In cases like the one above, which is testing completion blocks, I think it may unfortunately need to remain in the completion block, due to the difference between completion blocks and DidFinishObservers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, yes. There are some special cases. 👍

@swiftlyfalling swiftlyfalling merged commit ca8b318 into swift/2.2 Aug 25, 2016
swiftlyfalling added a commit that referenced this pull request Aug 25, 2016
#424)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.
@swiftlyfalling swiftlyfalling deleted the swift/2_2/OPR-419_test_expectation_fulfillment_fixes branch August 25, 2016 22:19
danthorpe added a commit that referenced this pull request Aug 29, 2016
* Will Cancel Observer (#293)

* [OPR-293]: Refactors Start & Cancellation observers

* [OPR-293]: Go back to original group cancellation policy

* [OPR-293]: Adds errors to will cancel observer

* [OPR-293]: Avoids overriding cancel

Instead adds a will cancel observer.

* [OPR-293]: Corrects mistake where the queue was not suspended

!!

* [OPR-293]: Makes cancel a final method

This prevents Operation subclasses from overriding cancel. Instead, they should add an observer as appropriate.

WillCancelObserver <- block is executed directly before the operation will change its cancellation state.

DidCancelObserver <- block is executed directly after the operation did change its cancellation state.

Typically, subclasses will want to observe WillCancel to update their own internal state. But consumers will want to observe DidCancel to respond to operations being cancelled.

* [OPR-293]: Adds to new operations to property

Also makes the queue private.

* [OPR-293]: Cancels all operations in the queue.

This will ensure that the finishing operation is also cancelled.

* [OPR-293]: Adds will/did cancel API

* [OPR-293]: Fixes some issues with GroupOperation

One of the issues with adding the operations to the queue in the initializer, is that observers will not get notified.

So, we have to add the initial operations in `execute`. But, if we append to operations in addOperations, that means, we need to filter out any instances which have already been added.

For example, a subclass may well implement execute to first addOperation(myOperation); super.execute() which would cause a crash if we tried adding myOperation twice.

* [OPR-293]: Renames will/didFinish

This is to be consistent with operationWill/operationDidCancel

* Conditions as Operations (#286)

* [OPR-278]: Adds protocols for OperationType

* [OPR-278]: Creates ConditionOperation

With internal types for wrapping OperationCondition, and evaluating groups.

* [OPR-278]: Initial work removing ready override.

* [OPR-278]: Enables previously failing test

* [OPR-278]: Adds missing files to Extension project

* [OPR-278]: Enables previously failing test in iOS scheme too

* [OPR-278]: Increases the batch size.

Also removes the logging.

* [OPR-278]: Fixes scheduling of dependencies.

* [OPR-278]: Further fixes to dependency scheduling.

* [OPR-278]: Removes unnecessary protocols

* [OPR-278]: Removes dependency between evaluator and conditons

This can introduce a deadlock with mutual exclusivity.

* [OPR-278]: Fixes incorrect test with profiler.

* [OPR-278]: Enables all tests on OS X

* [OPR-278]: Enables all tests on iOS

* [OPR-278]: Prevents a retain cycle in ConditionOperation

* [OPR-278]: Adds failing test to clarify mutual exclusivity

* [OPR-278]: Sets mutual exclusion around operation

This was a mistake - it was getting set on the condition.

* [OPR-278]: Fixes mutual exclusivity tests

* [OPR-278]: Sorts out mutual conditions with dependencies

Also, operations which have dependencies, and mutually exclusive conditions which have dependencies.

* [OPR-278]: Clean up the code a bit

* [OPR-278]: Includes indirect dependencies

* [OPR-278]: Starts adding Stress Tests

This is a new test bundle which gets tested as part of the OS X test suite.

* [OPR-278]: Renames ConditionOperation to Condition

It *is* an operation, but I want it to be treated like a Condition with it's own evaluate API

* [OPR-278]: Sets up tests

* [OPR-278]: Renames to mutuallyExclusive

* [OPR-278]: Fixes target reference for Condition.swift

* [OPR-278]: Adds ComposedCondition

This is useful (and uses automatic result injection) for easily getting the result of a previous condition.

* [OPR-278]: Refactors BlockCondition

* [OPR-278]: Refactors NegatedCondition

* [OPR-278]: Initial work on updating framework conditions

- [x] BlockCondition
- [x] TrueCondition
- [x] FalseCondition
- [x] ComposedCondition
- [x] SilentCondition
- [x] NegatedCondition
- [x] MutuallyExclusive
- [x] NoFailedDependenciesCondition

* [OPR-278]: Corrects a mistake with removeDependencies

We we actually just adding?!

* [OPR-278]: Mixes the composed direct dependencies

* [OPR-278]: Updates UserNotificationCondition

to subclass Condition

* [OPR-278]: Fixes some tests for iOS

* [OPR-278]: Refactors AuthorizedFor

* [OPR-278]: Refactors UserConfirmationCondition

* [OPR-278]: Refactors ReachabilityCondition

* [OPR-278]: Refactors AddressBookCondition

* [OPR-278]: Refactors ContactsCondition

* [OPR-278]: Refactors RemoteNotificationCondition

* [OPR-278]: Adds @available annotation.

Still not really sure how to accuractly mark API as deprecated inside a framework.

* [OPR-278]: Adds some tests for conditions

* [OPR-278]: Refactors condition error type

* [OPR-278]: Refactors True False Condition

* [OPR-278]: Cleans up Condition tests

* [OPR-278]: Fixes tests

* Fixes an incorrect Fix-It hint. (#302)

Fixes #299.

* Fixes notice severity logs (#303)

This was done during development and accidentally committed & pushed.

Fixes #300

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* CLLocationAuthorizationStatus bug (#306)

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* [OPR-306]: Adds test coverage for location capability

* Improved support for CloudKit Errors (#304)

* [OPR-290]: Adds Error associated type on CKOperationType

At the moment, this is just fulfilled by NSError, however it will allow us to offer specialized error responses depending on the operation.

* [OPR-290]: Updates test cloud kit operation.

* [OPR-290]: Adds missing file to Extension project

* [OPR-290]: Moves CloudKit sources into a folder

* [OPR-290]: Adds errors for Modify Zones & Records

* [OPR-290]: Adds custom error types for all CloudKit Operations

* [OPR-290]: Fixes error handling

Now every CKOperation has it's own bespoke error type.

* [OPR-290]: Tidies up AssociatedErrorType

* [OPR-290]: Fixes issues with AssociatedErrorType

* [OPR-290]: Adds error recovery to BatchedCloudKitOperation

* [OPR-290]: Exposes properties on OPRCKOperation

These are needed to configure operations inside error handlers.

* Removes unnecessary let statement (#310)

* Supports entersReaderIfAvailable configuration in WebpageOperation. (#312)

It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Refactors WebpageOperation to subclass ComposedOperation  (#315)

* Extended WebpageOperation with the ability to configure entersReaderIfAvailable at initialization.
It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Fixed a bug in WebpageOperation that that resulted in a retain cycle.

* - Refactored WebpageOperation as ComposedOperation to fix a retain cycle.
- Added Documentation.

* Fixed some typos in WebpageOperationTests.

* Implemented OpenInSafariOperation (#317)

* Implemented OpenInSafariOperation

* Made the displayControllerFrom non optional.

* Refactored OpenInSafariOperation.

* Fixes a bug removing dependencies on Condition (#309)

The issue is that ComposedCondition must remove the dependency on its composed condition too.

* Improves cancellation checks in main() (#319)

* [OPR-287]: Invokes observers in main

main() is called from start, and we already check that the operation is not cancelled. Therefore the check at the start of main is redundant. However, we don't check after the observers have been executed that the operation is cancelled.

* [OPR-287]: SwiftLint fixes.

* Exposes LogSeverity enum to Objective-C (#324)

* Remove Reachability from CloudKitOperation (#327)

* Ignore DS_Store

* Rip out reachability from CloudKitOperation

* [OPR-327]: Adds trailing closure syntax

* [OPR-327]: Adds TimeoutObserver

* [OPR-327]: Adds tests for timeout observer

* Override NSOperationQueue's mainQueue() (#330)

* Override NSOperationQueue's mainQueue() to return the main queue as an OperationQueue

* set maxConcurrentOperationCount to 1

* Adds missing functionality of UIAlertController to AlertOperation (#334)

* Added missing functionality of UIAlertController to AlertOperation.

* Annotated new functionality as public.

* Added test cases for new functionality.

* Made alert property of AlertOperation private.

* SwiftLint corrections.

* Added a test for preferredAction and fixed a but related to it.

* Made UserIntent enum objc accessbile (#341)

* Fixed network observer flickering (#338)

Added tests for multiple operations having network observer

* Supports error recovery in GroupOperation (#326)

* [OPR-326]: Adds Errors subtype in Operation

* [OPR-326]: Refactors GroupOperation willFinish API

* Revert "[OPR-326]: Adds Errors subtype in Operation"

This reverts commit 759f2ef.

* [OPR-326]: WIP on group operation errors

* [OPR-326]: Fixes issues with retry operation tests

This is still not quite right.

* [OPR-326]: Allows RetryOperation to fully recover from errors

* [OPR-326]: Fixes issue with tests

* [OPR-326]: Tweaks ProfilerTest case

* [OPR-326]: Refactors ComposedOperation to just subclass GroupOperation

* [OPR-326]: Changes behaviour when a group produces an operation

* [OPR-326]: Logs when a GroupOperation successfully recovers from errors

* [OPR-326]: Logs when the next operation will be added

* [OPR-326]: Adds an immediate WaitStrategy

* [OPR-326]: Adds the operation to the log when attempting recovery

* [OPR-326]: Removes unneeded handling of child errors

* [OPR-326]: Allows the WaitStrategy to be defined at init

* [OPR-326]: Changes to support ComposedOperation

* [OPR-326]: Speeds up CloudKit tests with .Immediate wait strategy

* [OPR-326]: WIP on GroupOperation errors

* [OPR-326]: Fixes a bug where operation would not retry

In the case where there is a default handler, but no custom one set for the same error.

* [OPR-326]: Renames internal methods to be more Swifty

* [OPR-326]: Fixes errors - whoops.

* Always create CLLocationManager on the main queue (#321)

* [OPR-321]: Creates CLLocationManager on the main thread

Also makes it thread safe.

* [OPR-321]: Refactors capability to use guard & early exit.

* [OPR-321]: Creates CLLocationManager lazily on the main thread

Refactors UserLocationOperation to not use generic at the same time

* [OPR-321]: Fixes deadlock in creating CLLocationManager

* [OPR-321]: Refactors location operations for early exit

* [OPR-321]: Removes use of NSThread to check for main queue

* [OPR-321]: Fixes a mistake with the merge

* RepeatedOperation allows for configuration block to be reset by the payload (#348)

* [OPR-332]: Adds support for RetryOperation to provide a configuration block

* [OPR-332]: Removes commented out code

* [OPR-332]: Adds test to check that configure block is replaced

* Turns on Whole Module Optimization (#350)

Fixes #349

* Exposes the UIAlert for customization. (#351)

This also fixes a bug where it wasn't possible to set a barButtonItem on an ActionSheet while presenting as a popover.

* Fixes OperationQueue.delegate crash (SR-192) (#353)

* Add failing test

test__SR_192_OperationQueue_delegate_weak_var_thread_safety:
Tests race condition (and crash) involving OperationQueue’s weak
delegate var.

* Fix for OperationQueue.delegate crash (SR-192)

The underlying error is in Swift < 3.0, with concurrent reads of weak
properties.
See: https://bugs.swift.org/browse/SR-192

This change wraps the public delegate property with an NSLock. It also
sets it to nil first-thing in deinit to safely get rid of it.

* Simplistic support for CKLimitExceeded error handling (#294)

* [OPR-294]: Initial work adding built in support for CKLimitExceeded

* [OPR-294]: Rewrite with support for nil response

* [OPR-294]: Adds support for adding error handlers in bulk

* [OPR-294]: Fixes some comments

* [OPR-294]: Moves error handling to CloudKitError

* [OPR-294]: Adds BatchProcessErrorType

This is only needed for CKMarkNotificationsOperation

* [OPR-294]: Renames error protocols

* [OPR-294]: Adds support for batch processing

* [OPR-294]: Adds & Refactors for tests

* [OPR-294]: Minor tweaks

* [OPR-294]: Further tweaks

* [OPR-294]: Fixes error in bisect

* [OPR-294]: Adds API to replace the configure block outright

* [OPR-294]: Fixes failing tests

* [OPR-294]: Refactors stress tests condition

* Adds TaskOperation (#355)

* [OPR-313]: Adds TaskOperation

* [OPR-313]: Adds more test coverage

* [OPR-313]: Adds header documentation

* Vastly improves thread safety in Operations (#358)

* FIX: GroupOperation.operations contains the initial operations twice after beginning to execute.

Cause:
`GroupOperation.execute()` calls
`GroupOperation.addOperations(operations)`,
which then re-adds the operations back to the operations array.

Solution:
Added a private **`_addOperations()`** method to `GroupOperation` with
an additional flag to override this behavior - used in
`GroupOperation.execute()`.

`addOperations()` now calls this private implementation method.

* Operation.cancel(), Operation.finish() thread-safety

- `Operation.cancel()` now acquires the stateLock.
- `Operation.finish()` now acquires the stateLock.
- `stateLock` is now NSRecursiveLock().
- var `Operation.cancelled` is thread-safe.
- var `Operation.errors` is thread-safe.
- Explicitly send KVO notifications on NSOperation keyPaths
at the right times, avoiding lots of unnecessary notifications.
- Removed “State” KVO notifications. (Previously used to
trigger NSOperation isExecuting/isFinished notifications,
which are now triggered explicitly.)
- Added missing operationWillFinish() call to finish().
- operationDidFinish() now happens after the state has been
set to .Finished.
- `Operation.cancelWithError(errors)` now asserts if it’s
called while the Operation is finishing or finished.

* Operation.main() thread-safety, Operation.disableAutomaticFinishing

- `Operation.main()` acquires the stateLock when appropriate.
- `Operation.main()` manually sends the isExecuting NSOperation KVO
notifications when setting executing state.
- Added ability to disable Operation’s automatic calling of finish(),
with `init(disableAutomaticFinishing: Bool)`. (Defaults to existing
behavior - i.e. false.)

* GroupOperation improvements (finish and thread-safety)

- GroupOperation should only finish() after its children have finished.
- `GroupOperation._addOperations()` acquires the stateLock.
- `GroupOperation._addOperations()` cancels new child operations if the
GroupOperation is cancelled.

* RepeatedOperation.addNextOperation() must check cancelled

- RepeatedOperation.addNextOperation() now checks cancelled status.

* Added new cancellation-related tests

* Added new GroupOperation tests

* Added Operation KVO notification tests (for NSOperation keyPaths)

* Fixed spurious commit

* Handle more possible cases in Operation.main()

- Check that the Operation is not already executing.
- Check that the Operation has not been finished by a WillExecute
observer (either directly or as a side-effect of something else). Added
a test case that demonstrates this possibility.

* Add missing super.init() calls

* More GroupOperation cancelling fixes

- Changed the internal WillCancelObserver to a DidCancelObserver to
ensure that the cancelled state has been set prior to cancelling child
operations. (This ensures that side-effects of propagating cancellation
to child operations - like RepeatedOperation attempting to add the next
operation - can safely check the cancelled status.)

* GroupOperation should not call queue.cancelAllOperations()

Calling queue.cancelAllOperations() also cancels the
finishingOperation, which skips properly waiting until all other child
operations are finished.
Fixed test.

* Added willProduceOperation method to OperationQueueDelegate, fix GroupOperation handling of child-produced operations

Informational method only.
This enables GroupOperation to add child-produced operations to its
internal operations array, and thus cancel them when the GroupOperation
is cancelled.
Added tests.

* GroupOperation should ignore queue delegate calls from other queues

Added missing guard at the top of the queue delegate methods in
GroupOperation.
Added test to illustrate simple failure case this change covers.

* Refactor lock usage in Operation.finish() and .cancel()

- Refactor lock usage to hold the stateLock only when necessary or
manipulating internal state, and *not* when sending KVO notifications
or notifying observers.
- Improvements and fixes to KVO Notification Tests.

* Further refactor lock usage in Operation (.execute())

- Refactor lock usage in `Operation.execute()` to hold the stateLock
only when necessary or manipulating internal state, and *not* when
sending KVO notifications or notifying observers.

* Add a number of new StressTests that previously failed/crashed

New tests have support for a `batches` parameter.

* Initial fix for Logging-related crashes (thread-safety issues)

- `Operation._log` must not be a lazy var.
From the Swift book:
> If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
For more: https://bugs.swift.org/browse/SR-1042
- `Operation.log` must be thread-safe.
Initial fix involves capturing a copy of the logging context (settings
+ operationName) at the time of access of `Operation.log`, returning
that captured context as a LoggerType, and surrounding access of .log
with a Read/Write lock.
- LogManager properties must be thread-safe.

* Refactor lock usage in GroupOperation

- var `GroupOperation.operations` is now thread-safe.
- GroupOperation now utilizes a private CanFinishOperation to safely
transition to executing the finishingOperation. (See code & comments.)
- GroupOperation now has its own lock that is used sparingly to protect
finishing state.
- `GroupOperation._addOperation()` now utilizes the groupFinishLock.

* Minor tweaks

- `Operation.stateLock` should remain private.

* SwiftLint clean-up

* Refactor lock usage in GroupOperation

- Refactor lock usage to hold the groupFinishLock only when necessary
or manipulating internal state, and *not* when sending KVO
notifications, notifying observers, or changing additionalOperations
state (which might send KVO notifications or notify observers).

* Clean-up

- Remove asserts from `Operation.cancelWithErrors()`. Operation
cancellation methods should be safe to call at any time without
asserting.
- Other minor tweaks.

* Clean-up

- Moved NSOperationKeyPaths to extension NSOperation.KeyPath enum.

* Clean-up

* Clean-up

- Remove unneeded `self.`

* Clean-up

* Clarify docs & re-formatting

* Add new inits to Queue (NSQualityOfService and qos_class_t)

Per code review.

* Clean-up of GroupOperation

* Make Operation.cancel() final (#359)

Per discussion in:
#293
#358 (comment)

* Supports composing simple types for Result injection (#362)

* [OPR-362]: Linting whitespace changes

* [OPR-362]: Adds support for composing “executors” inside a final Operation type

This is a convenience class to aid adoption of _Operations_ without requiring types to subclass Operation. Instead, classes only need to conform to Executor, and then can use the Execute class, which support cancellation, and result injection.

* Supports async executors (#363)

* Fixes a finished log message (#365)

The "Operation Did finish" log was using the opposite logic for error logging

* Group's internal queue is non-public; exposes appropriate properties (#361)

* Make GroupOperation's internal queue non-public, and expose appropriate properties on GroupOperation

- Make GroupOperation.queue ~private~internal. (So it can be tested.)
- Expose appropriate run-time editable properties of the
GroupOperation’s queue as properties on GroupOperation.

Specifically:
- public var maxConcurrentOperationCount: Int
- public var suspended: Bool
- public var qualityOfService: NSQualityOfService

- Add tests.

* [OPR-361]: Support underlying queue in Group

* GatedOperation cancels composed operation when gate is closed (#377)

* [OPR-377]: Adds early exit with guard

* [OPR-377]: Refactors GatedOperation execute

* [OPR-377]: Fixes typo

* Result Injection protocol improvements (#378)

* [OPR-378]: Updates result injection APIs

* [OPR-378]: Adds unit tests

* [OPR-378]: Removes unnecessary weak self capture

* Fixes dependency gets added twice if it's direct & indirect (#379)

* [OPR-379]: Adds a failing unit test

* [OPR-379]: Makes indirect dependencies unique set

* Removes TaskOperation from non-macOS platforms (#382)

* Removes AlertPresentation mutual exclusion (#383)

* Adds IgnoredCondition (#385)

* Filters out indirect dependencies which have already been added (#386)

* Fixes issues with ignored condition (#390)

This means that conditions which are ignored mean that the attached operation does not execute, but also does not finish with an error. It’s a nicer use case than GatedOperation. This works by checking the OperationResult for .Ignored and then only cancel() instead of cancelWithError().

Note that if the operation also has .Failed(errors) condition result, in addition to .Ignored then the operation will still not run but this time with errors (as expected).

Note that if the operation also has .Satisfied condition result, in addition to .Ignored then the operation will not run, but without errors.

* NoFailedDependenciesCondition supports ignoring cancelled operations (#397)

Fixes #396. 

* [OPR-396]: NoFailedDependenciesCondition support ignoring cancels

* [OPR-396]: Refactors initializer

* [OPR-396]: Renames the condition

* Ability to add configuration to the next (retried) CloudKitOperation (#395)

* Adds CloudKitOperation.setPrepareForRetryHandler()

* Adds BatchedCloudKitOperation.setPrepareForNextOperationHandler()

* Clean-up

* Updated new APIs, added documentation

-Simpler, cleaner, better APIs.
-Added documentation and examples for the new APIs.

* Add documentation for BatchedCloudKitOperation.

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* [3.2.0]: Updates the CHANGELOG

* [3.2.0]: Sets the version to 3.2.0

* [3.2.0]: Updates the CHANGELOG
danthorpe added a commit that referenced this pull request Aug 29, 2016
* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)


* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* 3.2.0 (#431)

* Will Cancel Observer (#293)

* [OPR-293]: Refactors Start & Cancellation observers

* [OPR-293]: Go back to original group cancellation policy

* [OPR-293]: Adds errors to will cancel observer

* [OPR-293]: Avoids overriding cancel

Instead adds a will cancel observer.

* [OPR-293]: Corrects mistake where the queue was not suspended

!!

* [OPR-293]: Makes cancel a final method

This prevents Operation subclasses from overriding cancel. Instead, they should add an observer as appropriate.

WillCancelObserver <- block is executed directly before the operation will change its cancellation state.

DidCancelObserver <- block is executed directly after the operation did change its cancellation state.

Typically, subclasses will want to observe WillCancel to update their own internal state. But consumers will want to observe DidCancel to respond to operations being cancelled.

* [OPR-293]: Adds to new operations to property

Also makes the queue private.

* [OPR-293]: Cancels all operations in the queue.

This will ensure that the finishing operation is also cancelled.

* [OPR-293]: Adds will/did cancel API

* [OPR-293]: Fixes some issues with GroupOperation

One of the issues with adding the operations to the queue in the initializer, is that observers will not get notified.

So, we have to add the initial operations in `execute`. But, if we append to operations in addOperations, that means, we need to filter out any instances which have already been added.

For example, a subclass may well implement execute to first addOperation(myOperation); super.execute() which would cause a crash if we tried adding myOperation twice.

* [OPR-293]: Renames will/didFinish

This is to be consistent with operationWill/operationDidCancel

* Conditions as Operations (#286)

* [OPR-278]: Adds protocols for OperationType

* [OPR-278]: Creates ConditionOperation

With internal types for wrapping OperationCondition, and evaluating groups.

* [OPR-278]: Initial work removing ready override.

* [OPR-278]: Enables previously failing test

* [OPR-278]: Adds missing files to Extension project

* [OPR-278]: Enables previously failing test in iOS scheme too

* [OPR-278]: Increases the batch size.

Also removes the logging.

* [OPR-278]: Fixes scheduling of dependencies.

* [OPR-278]: Further fixes to dependency scheduling.

* [OPR-278]: Removes unnecessary protocols

* [OPR-278]: Removes dependency between evaluator and conditons

This can introduce a deadlock with mutual exclusivity.

* [OPR-278]: Fixes incorrect test with profiler.

* [OPR-278]: Enables all tests on OS X

* [OPR-278]: Enables all tests on iOS

* [OPR-278]: Prevents a retain cycle in ConditionOperation

* [OPR-278]: Adds failing test to clarify mutual exclusivity

* [OPR-278]: Sets mutual exclusion around operation

This was a mistake - it was getting set on the condition.

* [OPR-278]: Fixes mutual exclusivity tests

* [OPR-278]: Sorts out mutual conditions with dependencies

Also, operations which have dependencies, and mutually exclusive conditions which have dependencies.

* [OPR-278]: Clean up the code a bit

* [OPR-278]: Includes indirect dependencies

* [OPR-278]: Starts adding Stress Tests

This is a new test bundle which gets tested as part of the OS X test suite.

* [OPR-278]: Renames ConditionOperation to Condition

It *is* an operation, but I want it to be treated like a Condition with it's own evaluate API

* [OPR-278]: Sets up tests

* [OPR-278]: Renames to mutuallyExclusive

* [OPR-278]: Fixes target reference for Condition.swift

* [OPR-278]: Adds ComposedCondition

This is useful (and uses automatic result injection) for easily getting the result of a previous condition.

* [OPR-278]: Refactors BlockCondition

* [OPR-278]: Refactors NegatedCondition

* [OPR-278]: Initial work on updating framework conditions

- [x] BlockCondition
- [x] TrueCondition
- [x] FalseCondition
- [x] ComposedCondition
- [x] SilentCondition
- [x] NegatedCondition
- [x] MutuallyExclusive
- [x] NoFailedDependenciesCondition

* [OPR-278]: Corrects a mistake with removeDependencies

We we actually just adding?!

* [OPR-278]: Mixes the composed direct dependencies

* [OPR-278]: Updates UserNotificationCondition

to subclass Condition

* [OPR-278]: Fixes some tests for iOS

* [OPR-278]: Refactors AuthorizedFor

* [OPR-278]: Refactors UserConfirmationCondition

* [OPR-278]: Refactors ReachabilityCondition

* [OPR-278]: Refactors AddressBookCondition

* [OPR-278]: Refactors ContactsCondition

* [OPR-278]: Refactors RemoteNotificationCondition

* [OPR-278]: Adds @available annotation.

Still not really sure how to accuractly mark API as deprecated inside a framework.

* [OPR-278]: Adds some tests for conditions

* [OPR-278]: Refactors condition error type

* [OPR-278]: Refactors True False Condition

* [OPR-278]: Cleans up Condition tests

* [OPR-278]: Fixes tests

* Fixes an incorrect Fix-It hint. (#302)

Fixes #299.

* Fixes notice severity logs (#303)

This was done during development and accidentally committed & pushed.

Fixes #300

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* CLLocationAuthorizationStatus bug (#306)

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* [OPR-306]: Adds test coverage for location capability

* Improved support for CloudKit Errors (#304)

* [OPR-290]: Adds Error associated type on CKOperationType

At the moment, this is just fulfilled by NSError, however it will allow us to offer specialized error responses depending on the operation.

* [OPR-290]: Updates test cloud kit operation.

* [OPR-290]: Adds missing file to Extension project

* [OPR-290]: Moves CloudKit sources into a folder

* [OPR-290]: Adds errors for Modify Zones & Records

* [OPR-290]: Adds custom error types for all CloudKit Operations

* [OPR-290]: Fixes error handling

Now every CKOperation has it's own bespoke error type.

* [OPR-290]: Tidies up AssociatedErrorType

* [OPR-290]: Fixes issues with AssociatedErrorType

* [OPR-290]: Adds error recovery to BatchedCloudKitOperation

* [OPR-290]: Exposes properties on OPRCKOperation

These are needed to configure operations inside error handlers.

* Removes unnecessary let statement (#310)

* Supports entersReaderIfAvailable configuration in WebpageOperation. (#312)

It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Refactors WebpageOperation to subclass ComposedOperation  (#315)

* Extended WebpageOperation with the ability to configure entersReaderIfAvailable at initialization.
It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Fixed a bug in WebpageOperation that that resulted in a retain cycle.

* - Refactored WebpageOperation as ComposedOperation to fix a retain cycle.
- Added Documentation.

* Fixed some typos in WebpageOperationTests.

* Implemented OpenInSafariOperation (#317)

* Implemented OpenInSafariOperation

* Made the displayControllerFrom non optional.

* Refactored OpenInSafariOperation.

* Fixes a bug removing dependencies on Condition (#309)

The issue is that ComposedCondition must remove the dependency on its composed condition too.

* Improves cancellation checks in main() (#319)

* [OPR-287]: Invokes observers in main

main() is called from start, and we already check that the operation is not cancelled. Therefore the check at the start of main is redundant. However, we don't check after the observers have been executed that the operation is cancelled.

* [OPR-287]: SwiftLint fixes.

* Exposes LogSeverity enum to Objective-C (#324)

* Remove Reachability from CloudKitOperation (#327)

* Ignore DS_Store

* Rip out reachability from CloudKitOperation

* [OPR-327]: Adds trailing closure syntax

* [OPR-327]: Adds TimeoutObserver

* [OPR-327]: Adds tests for timeout observer

* Override NSOperationQueue's mainQueue() (#330)

* Override NSOperationQueue's mainQueue() to return the main queue as an OperationQueue

* set maxConcurrentOperationCount to 1

* Adds missing functionality of UIAlertController to AlertOperation (#334)

* Added missing functionality of UIAlertController to AlertOperation.

* Annotated new functionality as public.

* Added test cases for new functionality.

* Made alert property of AlertOperation private.

* SwiftLint corrections.

* Added a test for preferredAction and fixed a but related to it.

* Made UserIntent enum objc accessbile (#341)

* Fixed network observer flickering (#338)

Added tests for multiple operations having network observer

* Supports error recovery in GroupOperation (#326)

* [OPR-326]: Adds Errors subtype in Operation

* [OPR-326]: Refactors GroupOperation willFinish API

* Revert "[OPR-326]: Adds Errors subtype in Operation"

This reverts commit 759f2ef.

* [OPR-326]: WIP on group operation errors

* [OPR-326]: Fixes issues with retry operation tests

This is still not quite right.

* [OPR-326]: Allows RetryOperation to fully recover from errors

* [OPR-326]: Fixes issue with tests

* [OPR-326]: Tweaks ProfilerTest case

* [OPR-326]: Refactors ComposedOperation to just subclass GroupOperation

* [OPR-326]: Changes behaviour when a group produces an operation

* [OPR-326]: Logs when a GroupOperation successfully recovers from errors

* [OPR-326]: Logs when the next operation will be added

* [OPR-326]: Adds an immediate WaitStrategy

* [OPR-326]: Adds the operation to the log when attempting recovery

* [OPR-326]: Removes unneeded handling of child errors

* [OPR-326]: Allows the WaitStrategy to be defined at init

* [OPR-326]: Changes to support ComposedOperation

* [OPR-326]: Speeds up CloudKit tests with .Immediate wait strategy

* [OPR-326]: WIP on GroupOperation errors

* [OPR-326]: Fixes a bug where operation would not retry

In the case where there is a default handler, but no custom one set for the same error.

* [OPR-326]: Renames internal methods to be more Swifty

* [OPR-326]: Fixes errors - whoops.

* Always create CLLocationManager on the main queue (#321)

* [OPR-321]: Creates CLLocationManager on the main thread

Also makes it thread safe.

* [OPR-321]: Refactors capability to use guard & early exit.

* [OPR-321]: Creates CLLocationManager lazily on the main thread

Refactors UserLocationOperation to not use generic at the same time

* [OPR-321]: Fixes deadlock in creating CLLocationManager

* [OPR-321]: Refactors location operations for early exit

* [OPR-321]: Removes use of NSThread to check for main queue

* [OPR-321]: Fixes a mistake with the merge

* RepeatedOperation allows for configuration block to be reset by the payload (#348)

* [OPR-332]: Adds support for RetryOperation to provide a configuration block

* [OPR-332]: Removes commented out code

* [OPR-332]: Adds test to check that configure block is replaced

* Turns on Whole Module Optimization (#350)

Fixes #349

* Exposes the UIAlert for customization. (#351)

This also fixes a bug where it wasn't possible to set a barButtonItem on an ActionSheet while presenting as a popover.

* Fixes OperationQueue.delegate crash (SR-192) (#353)

* Add failing test

test__SR_192_OperationQueue_delegate_weak_var_thread_safety:
Tests race condition (and crash) involving OperationQueue’s weak
delegate var.

* Fix for OperationQueue.delegate crash (SR-192)

The underlying error is in Swift < 3.0, with concurrent reads of weak
properties.
See: https://bugs.swift.org/browse/SR-192

This change wraps the public delegate property with an NSLock. It also
sets it to nil first-thing in deinit to safely get rid of it.

* Simplistic support for CKLimitExceeded error handling (#294)

* [OPR-294]: Initial work adding built in support for CKLimitExceeded

* [OPR-294]: Rewrite with support for nil response

* [OPR-294]: Adds support for adding error handlers in bulk

* [OPR-294]: Fixes some comments

* [OPR-294]: Moves error handling to CloudKitError

* [OPR-294]: Adds BatchProcessErrorType

This is only needed for CKMarkNotificationsOperation

* [OPR-294]: Renames error protocols

* [OPR-294]: Adds support for batch processing

* [OPR-294]: Adds & Refactors for tests

* [OPR-294]: Minor tweaks

* [OPR-294]: Further tweaks

* [OPR-294]: Fixes error in bisect

* [OPR-294]: Adds API to replace the configure block outright

* [OPR-294]: Fixes failing tests

* [OPR-294]: Refactors stress tests condition

* Adds TaskOperation (#355)

* [OPR-313]: Adds TaskOperation

* [OPR-313]: Adds more test coverage

* [OPR-313]: Adds header documentation

* Vastly improves thread safety in Operations (#358)

* FIX: GroupOperation.operations contains the initial operations twice after beginning to execute.

Cause:
`GroupOperation.execute()` calls
`GroupOperation.addOperations(operations)`,
which then re-adds the operations back to the operations array.

Solution:
Added a private **`_addOperations()`** method to `GroupOperation` with
an additional flag to override this behavior - used in
`GroupOperation.execute()`.

`addOperations()` now calls this private implementation method.

* Operation.cancel(), Operation.finish() thread-safety

- `Operation.cancel()` now acquires the stateLock.
- `Operation.finish()` now acquires the stateLock.
- `stateLock` is now NSRecursiveLock().
- var `Operation.cancelled` is thread-safe.
- var `Operation.errors` is thread-safe.
- Explicitly send KVO notifications on NSOperation keyPaths
at the right times, avoiding lots of unnecessary notifications.
- Removed “State” KVO notifications. (Previously used to
trigger NSOperation isExecuting/isFinished notifications,
which are now triggered explicitly.)
- Added missing operationWillFinish() call to finish().
- operationDidFinish() now happens after the state has been
set to .Finished.
- `Operation.cancelWithError(errors)` now asserts if it’s
called while the Operation is finishing or finished.

* Operation.main() thread-safety, Operation.disableAutomaticFinishing

- `Operation.main()` acquires the stateLock when appropriate.
- `Operation.main()` manually sends the isExecuting NSOperation KVO
notifications when setting executing state.
- Added ability to disable Operation’s automatic calling of finish(),
with `init(disableAutomaticFinishing: Bool)`. (Defaults to existing
behavior - i.e. false.)

* GroupOperation improvements (finish and thread-safety)

- GroupOperation should only finish() after its children have finished.
- `GroupOperation._addOperations()` acquires the stateLock.
- `GroupOperation._addOperations()` cancels new child operations if the
GroupOperation is cancelled.

* RepeatedOperation.addNextOperation() must check cancelled

- RepeatedOperation.addNextOperation() now checks cancelled status.

* Added new cancellation-related tests

* Added new GroupOperation tests

* Added Operation KVO notification tests (for NSOperation keyPaths)

* Fixed spurious commit

* Handle more possible cases in Operation.main()

- Check that the Operation is not already executing.
- Check that the Operation has not been finished by a WillExecute
observer (either directly or as a side-effect of something else). Added
a test case that demonstrates this possibility.

* Add missing super.init() calls

* More GroupOperation cancelling fixes

- Changed the internal WillCancelObserver to a DidCancelObserver to
ensure that the cancelled state has been set prior to cancelling child
operations. (This ensures that side-effects of propagating cancellation
to child operations - like RepeatedOperation attempting to add the next
operation - can safely check the cancelled status.)

* GroupOperation should not call queue.cancelAllOperations()

Calling queue.cancelAllOperations() also cancels the
finishingOperation, which skips properly waiting until all other child
operations are finished.
Fixed test.

* Added willProduceOperation method to OperationQueueDelegate, fix GroupOperation handling of child-produced operations

Informational method only.
This enables GroupOperation to add child-produced operations to its
internal operations array, and thus cancel them when the GroupOperation
is cancelled.
Added tests.

* GroupOperation should ignore queue delegate calls from other queues

Added missing guard at the top of the queue delegate methods in
GroupOperation.
Added test to illustrate simple failure case this change covers.

* Refactor lock usage in Operation.finish() and .cancel()

- Refactor lock usage to hold the stateLock only when necessary or
manipulating internal state, and *not* when sending KVO notifications
or notifying observers.
- Improvements and fixes to KVO Notification Tests.

* Further refactor lock usage in Operation (.execute())

- Refactor lock usage in `Operation.execute()` to hold the stateLock
only when necessary or manipulating internal state, and *not* when
sending KVO notifications or notifying observers.

* Add a number of new StressTests that previously failed/crashed

New tests have support for a `batches` parameter.

* Initial fix for Logging-related crashes (thread-safety issues)

- `Operation._log` must not be a lazy var.
From the Swift book:
> If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
For more: https://bugs.swift.org/browse/SR-1042
- `Operation.log` must be thread-safe.
Initial fix involves capturing a copy of the logging context (settings
+ operationName) at the time of access of `Operation.log`, returning
that captured context as a LoggerType, and surrounding access of .log
with a Read/Write lock.
- LogManager properties must be thread-safe.

* Refactor lock usage in GroupOperation

- var `GroupOperation.operations` is now thread-safe.
- GroupOperation now utilizes a private CanFinishOperation to safely
transition to executing the finishingOperation. (See code & comments.)
- GroupOperation now has its own lock that is used sparingly to protect
finishing state.
- `GroupOperation._addOperation()` now utilizes the groupFinishLock.

* Minor tweaks

- `Operation.stateLock` should remain private.

* SwiftLint clean-up

* Refactor lock usage in GroupOperation

- Refactor lock usage to hold the groupFinishLock only when necessary
or manipulating internal state, and *not* when sending KVO
notifications, notifying observers, or changing additionalOperations
state (which might send KVO notifications or notify observers).

* Clean-up

- Remove asserts from `Operation.cancelWithErrors()`. Operation
cancellation methods should be safe to call at any time without
asserting.
- Other minor tweaks.

* Clean-up

- Moved NSOperationKeyPaths to extension NSOperation.KeyPath enum.

* Clean-up

* Clean-up

- Remove unneeded `self.`

* Clean-up

* Clarify docs & re-formatting

* Add new inits to Queue (NSQualityOfService and qos_class_t)

Per code review.

* Clean-up of GroupOperation

* Make Operation.cancel() final (#359)

Per discussion in:
#293
#358 (comment)

* Supports composing simple types for Result injection (#362)

* [OPR-362]: Linting whitespace changes

* [OPR-362]: Adds support for composing “executors” inside a final Operation type

This is a convenience class to aid adoption of _Operations_ without requiring types to subclass Operation. Instead, classes only need to conform to Executor, and then can use the Execute class, which support cancellation, and result injection.

* Supports async executors (#363)

* Fixes a finished log message (#365)

The "Operation Did finish" log was using the opposite logic for error logging

* Group's internal queue is non-public; exposes appropriate properties (#361)

* Make GroupOperation's internal queue non-public, and expose appropriate properties on GroupOperation

- Make GroupOperation.queue ~private~internal. (So it can be tested.)
- Expose appropriate run-time editable properties of the
GroupOperation’s queue as properties on GroupOperation.

Specifically:
- public var maxConcurrentOperationCount: Int
- public var suspended: Bool
- public var qualityOfService: NSQualityOfService

- Add tests.

* [OPR-361]: Support underlying queue in Group

* GatedOperation cancels composed operation when gate is closed (#377)

* [OPR-377]: Adds early exit with guard

* [OPR-377]: Refactors GatedOperation execute

* [OPR-377]: Fixes typo

* Result Injection protocol improvements (#378)

* [OPR-378]: Updates result injection APIs

* [OPR-378]: Adds unit tests

* [OPR-378]: Removes unnecessary weak self capture

* Fixes dependency gets added twice if it's direct & indirect (#379)

* [OPR-379]: Adds a failing unit test

* [OPR-379]: Makes indirect dependencies unique set

* Removes TaskOperation from non-macOS platforms (#382)

* Removes AlertPresentation mutual exclusion (#383)

* Adds IgnoredCondition (#385)

* Filters out indirect dependencies which have already been added (#386)

* Fixes issues with ignored condition (#390)

This means that conditions which are ignored mean that the attached operation does not execute, but also does not finish with an error. It’s a nicer use case than GatedOperation. This works by checking the OperationResult for .Ignored and then only cancel() instead of cancelWithError().

Note that if the operation also has .Failed(errors) condition result, in addition to .Ignored then the operation will still not run but this time with errors (as expected).

Note that if the operation also has .Satisfied condition result, in addition to .Ignored then the operation will not run, but without errors.

* NoFailedDependenciesCondition supports ignoring cancelled operations (#397)

Fixes #396. 

* [OPR-396]: NoFailedDependenciesCondition support ignoring cancels

* [OPR-396]: Refactors initializer

* [OPR-396]: Renames the condition

* Ability to add configuration to the next (retried) CloudKitOperation (#395)

* Adds CloudKitOperation.setPrepareForRetryHandler()

* Adds BatchedCloudKitOperation.setPrepareForNextOperationHandler()

* Clean-up

* Updated new APIs, added documentation

-Simpler, cleaner, better APIs.
-Added documentation and examples for the new APIs.

* Add documentation for BatchedCloudKitOperation.

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* [3.2.0]: Updates the CHANGELOG

* [3.2.0]: Sets the version to 3.2.0

* [3.2.0]: Updates the CHANGELOG

* [OPR-440]: Fixes iOS test target
danthorpe pushed a commit that referenced this pull request Sep 8, 2016
#424)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.
danthorpe added a commit that referenced this pull request Sep 8, 2016
* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)


* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* 3.2.0 (#431)

* Will Cancel Observer (#293)

* [OPR-293]: Refactors Start & Cancellation observers

* [OPR-293]: Go back to original group cancellation policy

* [OPR-293]: Adds errors to will cancel observer

* [OPR-293]: Avoids overriding cancel

Instead adds a will cancel observer.

* [OPR-293]: Corrects mistake where the queue was not suspended

!!

* [OPR-293]: Makes cancel a final method

This prevents Operation subclasses from overriding cancel. Instead, they should add an observer as appropriate.

WillCancelObserver <- block is executed directly before the operation will change its cancellation state.

DidCancelObserver <- block is executed directly after the operation did change its cancellation state.

Typically, subclasses will want to observe WillCancel to update their own internal state. But consumers will want to observe DidCancel to respond to operations being cancelled.

* [OPR-293]: Adds to new operations to property

Also makes the queue private.

* [OPR-293]: Cancels all operations in the queue.

This will ensure that the finishing operation is also cancelled.

* [OPR-293]: Adds will/did cancel API

* [OPR-293]: Fixes some issues with GroupOperation

One of the issues with adding the operations to the queue in the initializer, is that observers will not get notified.

So, we have to add the initial operations in `execute`. But, if we append to operations in addOperations, that means, we need to filter out any instances which have already been added.

For example, a subclass may well implement execute to first addOperation(myOperation); super.execute() which would cause a crash if we tried adding myOperation twice.

* [OPR-293]: Renames will/didFinish

This is to be consistent with operationWill/operationDidCancel

* Conditions as Operations (#286)

* [OPR-278]: Adds protocols for OperationType

* [OPR-278]: Creates ConditionOperation

With internal types for wrapping OperationCondition, and evaluating groups.

* [OPR-278]: Initial work removing ready override.

* [OPR-278]: Enables previously failing test

* [OPR-278]: Adds missing files to Extension project

* [OPR-278]: Enables previously failing test in iOS scheme too

* [OPR-278]: Increases the batch size.

Also removes the logging.

* [OPR-278]: Fixes scheduling of dependencies.

* [OPR-278]: Further fixes to dependency scheduling.

* [OPR-278]: Removes unnecessary protocols

* [OPR-278]: Removes dependency between evaluator and conditons

This can introduce a deadlock with mutual exclusivity.

* [OPR-278]: Fixes incorrect test with profiler.

* [OPR-278]: Enables all tests on OS X

* [OPR-278]: Enables all tests on iOS

* [OPR-278]: Prevents a retain cycle in ConditionOperation

* [OPR-278]: Adds failing test to clarify mutual exclusivity

* [OPR-278]: Sets mutual exclusion around operation

This was a mistake - it was getting set on the condition.

* [OPR-278]: Fixes mutual exclusivity tests

* [OPR-278]: Sorts out mutual conditions with dependencies

Also, operations which have dependencies, and mutually exclusive conditions which have dependencies.

* [OPR-278]: Clean up the code a bit

* [OPR-278]: Includes indirect dependencies

* [OPR-278]: Starts adding Stress Tests

This is a new test bundle which gets tested as part of the OS X test suite.

* [OPR-278]: Renames ConditionOperation to Condition

It *is* an operation, but I want it to be treated like a Condition with it's own evaluate API

* [OPR-278]: Sets up tests

* [OPR-278]: Renames to mutuallyExclusive

* [OPR-278]: Fixes target reference for Condition.swift

* [OPR-278]: Adds ComposedCondition

This is useful (and uses automatic result injection) for easily getting the result of a previous condition.

* [OPR-278]: Refactors BlockCondition

* [OPR-278]: Refactors NegatedCondition

* [OPR-278]: Initial work on updating framework conditions

- [x] BlockCondition
- [x] TrueCondition
- [x] FalseCondition
- [x] ComposedCondition
- [x] SilentCondition
- [x] NegatedCondition
- [x] MutuallyExclusive
- [x] NoFailedDependenciesCondition

* [OPR-278]: Corrects a mistake with removeDependencies

We we actually just adding?!

* [OPR-278]: Mixes the composed direct dependencies

* [OPR-278]: Updates UserNotificationCondition

to subclass Condition

* [OPR-278]: Fixes some tests for iOS

* [OPR-278]: Refactors AuthorizedFor

* [OPR-278]: Refactors UserConfirmationCondition

* [OPR-278]: Refactors ReachabilityCondition

* [OPR-278]: Refactors AddressBookCondition

* [OPR-278]: Refactors ContactsCondition

* [OPR-278]: Refactors RemoteNotificationCondition

* [OPR-278]: Adds @available annotation.

Still not really sure how to accuractly mark API as deprecated inside a framework.

* [OPR-278]: Adds some tests for conditions

* [OPR-278]: Refactors condition error type

* [OPR-278]: Refactors True False Condition

* [OPR-278]: Cleans up Condition tests

* [OPR-278]: Fixes tests

* Fixes an incorrect Fix-It hint. (#302)

Fixes #299.

* Fixes notice severity logs (#303)

This was done during development and accidentally committed & pushed.

Fixes #300

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* CLLocationAuthorizationStatus bug (#306)

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* [OPR-306]: Adds test coverage for location capability

* Improved support for CloudKit Errors (#304)

* [OPR-290]: Adds Error associated type on CKOperationType

At the moment, this is just fulfilled by NSError, however it will allow us to offer specialized error responses depending on the operation.

* [OPR-290]: Updates test cloud kit operation.

* [OPR-290]: Adds missing file to Extension project

* [OPR-290]: Moves CloudKit sources into a folder

* [OPR-290]: Adds errors for Modify Zones & Records

* [OPR-290]: Adds custom error types for all CloudKit Operations

* [OPR-290]: Fixes error handling

Now every CKOperation has it's own bespoke error type.

* [OPR-290]: Tidies up AssociatedErrorType

* [OPR-290]: Fixes issues with AssociatedErrorType

* [OPR-290]: Adds error recovery to BatchedCloudKitOperation

* [OPR-290]: Exposes properties on OPRCKOperation

These are needed to configure operations inside error handlers.

* Removes unnecessary let statement (#310)

* Supports entersReaderIfAvailable configuration in WebpageOperation. (#312)

It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Refactors WebpageOperation to subclass ComposedOperation  (#315)

* Extended WebpageOperation with the ability to configure entersReaderIfAvailable at initialization.
It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Fixed a bug in WebpageOperation that that resulted in a retain cycle.

* - Refactored WebpageOperation as ComposedOperation to fix a retain cycle.
- Added Documentation.

* Fixed some typos in WebpageOperationTests.

* Implemented OpenInSafariOperation (#317)

* Implemented OpenInSafariOperation

* Made the displayControllerFrom non optional.

* Refactored OpenInSafariOperation.

* Fixes a bug removing dependencies on Condition (#309)

The issue is that ComposedCondition must remove the dependency on its composed condition too.

* Improves cancellation checks in main() (#319)

* [OPR-287]: Invokes observers in main

main() is called from start, and we already check that the operation is not cancelled. Therefore the check at the start of main is redundant. However, we don't check after the observers have been executed that the operation is cancelled.

* [OPR-287]: SwiftLint fixes.

* Exposes LogSeverity enum to Objective-C (#324)

* Remove Reachability from CloudKitOperation (#327)

* Ignore DS_Store

* Rip out reachability from CloudKitOperation

* [OPR-327]: Adds trailing closure syntax

* [OPR-327]: Adds TimeoutObserver

* [OPR-327]: Adds tests for timeout observer

* Override NSOperationQueue's mainQueue() (#330)

* Override NSOperationQueue's mainQueue() to return the main queue as an OperationQueue

* set maxConcurrentOperationCount to 1

* Adds missing functionality of UIAlertController to AlertOperation (#334)

* Added missing functionality of UIAlertController to AlertOperation.

* Annotated new functionality as public.

* Added test cases for new functionality.

* Made alert property of AlertOperation private.

* SwiftLint corrections.

* Added a test for preferredAction and fixed a but related to it.

* Made UserIntent enum objc accessbile (#341)

* Fixed network observer flickering (#338)

Added tests for multiple operations having network observer

* Supports error recovery in GroupOperation (#326)

* [OPR-326]: Adds Errors subtype in Operation

* [OPR-326]: Refactors GroupOperation willFinish API

* Revert "[OPR-326]: Adds Errors subtype in Operation"

This reverts commit 759f2ef.

* [OPR-326]: WIP on group operation errors

* [OPR-326]: Fixes issues with retry operation tests

This is still not quite right.

* [OPR-326]: Allows RetryOperation to fully recover from errors

* [OPR-326]: Fixes issue with tests

* [OPR-326]: Tweaks ProfilerTest case

* [OPR-326]: Refactors ComposedOperation to just subclass GroupOperation

* [OPR-326]: Changes behaviour when a group produces an operation

* [OPR-326]: Logs when a GroupOperation successfully recovers from errors

* [OPR-326]: Logs when the next operation will be added

* [OPR-326]: Adds an immediate WaitStrategy

* [OPR-326]: Adds the operation to the log when attempting recovery

* [OPR-326]: Removes unneeded handling of child errors

* [OPR-326]: Allows the WaitStrategy to be defined at init

* [OPR-326]: Changes to support ComposedOperation

* [OPR-326]: Speeds up CloudKit tests with .Immediate wait strategy

* [OPR-326]: WIP on GroupOperation errors

* [OPR-326]: Fixes a bug where operation would not retry

In the case where there is a default handler, but no custom one set for the same error.

* [OPR-326]: Renames internal methods to be more Swifty

* [OPR-326]: Fixes errors - whoops.

* Always create CLLocationManager on the main queue (#321)

* [OPR-321]: Creates CLLocationManager on the main thread

Also makes it thread safe.

* [OPR-321]: Refactors capability to use guard & early exit.

* [OPR-321]: Creates CLLocationManager lazily on the main thread

Refactors UserLocationOperation to not use generic at the same time

* [OPR-321]: Fixes deadlock in creating CLLocationManager

* [OPR-321]: Refactors location operations for early exit

* [OPR-321]: Removes use of NSThread to check for main queue

* [OPR-321]: Fixes a mistake with the merge

* RepeatedOperation allows for configuration block to be reset by the payload (#348)

* [OPR-332]: Adds support for RetryOperation to provide a configuration block

* [OPR-332]: Removes commented out code

* [OPR-332]: Adds test to check that configure block is replaced

* Turns on Whole Module Optimization (#350)

Fixes #349

* Exposes the UIAlert for customization. (#351)

This also fixes a bug where it wasn't possible to set a barButtonItem on an ActionSheet while presenting as a popover.

* Fixes OperationQueue.delegate crash (SR-192) (#353)

* Add failing test

test__SR_192_OperationQueue_delegate_weak_var_thread_safety:
Tests race condition (and crash) involving OperationQueue’s weak
delegate var.

* Fix for OperationQueue.delegate crash (SR-192)

The underlying error is in Swift < 3.0, with concurrent reads of weak
properties.
See: https://bugs.swift.org/browse/SR-192

This change wraps the public delegate property with an NSLock. It also
sets it to nil first-thing in deinit to safely get rid of it.

* Simplistic support for CKLimitExceeded error handling (#294)

* [OPR-294]: Initial work adding built in support for CKLimitExceeded

* [OPR-294]: Rewrite with support for nil response

* [OPR-294]: Adds support for adding error handlers in bulk

* [OPR-294]: Fixes some comments

* [OPR-294]: Moves error handling to CloudKitError

* [OPR-294]: Adds BatchProcessErrorType

This is only needed for CKMarkNotificationsOperation

* [OPR-294]: Renames error protocols

* [OPR-294]: Adds support for batch processing

* [OPR-294]: Adds & Refactors for tests

* [OPR-294]: Minor tweaks

* [OPR-294]: Further tweaks

* [OPR-294]: Fixes error in bisect

* [OPR-294]: Adds API to replace the configure block outright

* [OPR-294]: Fixes failing tests

* [OPR-294]: Refactors stress tests condition

* Adds TaskOperation (#355)

* [OPR-313]: Adds TaskOperation

* [OPR-313]: Adds more test coverage

* [OPR-313]: Adds header documentation

* Vastly improves thread safety in Operations (#358)

* FIX: GroupOperation.operations contains the initial operations twice after beginning to execute.

Cause:
`GroupOperation.execute()` calls
`GroupOperation.addOperations(operations)`,
which then re-adds the operations back to the operations array.

Solution:
Added a private **`_addOperations()`** method to `GroupOperation` with
an additional flag to override this behavior - used in
`GroupOperation.execute()`.

`addOperations()` now calls this private implementation method.

* Operation.cancel(), Operation.finish() thread-safety

- `Operation.cancel()` now acquires the stateLock.
- `Operation.finish()` now acquires the stateLock.
- `stateLock` is now NSRecursiveLock().
- var `Operation.cancelled` is thread-safe.
- var `Operation.errors` is thread-safe.
- Explicitly send KVO notifications on NSOperation keyPaths
at the right times, avoiding lots of unnecessary notifications.
- Removed “State” KVO notifications. (Previously used to
trigger NSOperation isExecuting/isFinished notifications,
which are now triggered explicitly.)
- Added missing operationWillFinish() call to finish().
- operationDidFinish() now happens after the state has been
set to .Finished.
- `Operation.cancelWithError(errors)` now asserts if it’s
called while the Operation is finishing or finished.

* Operation.main() thread-safety, Operation.disableAutomaticFinishing

- `Operation.main()` acquires the stateLock when appropriate.
- `Operation.main()` manually sends the isExecuting NSOperation KVO
notifications when setting executing state.
- Added ability to disable Operation’s automatic calling of finish(),
with `init(disableAutomaticFinishing: Bool)`. (Defaults to existing
behavior - i.e. false.)

* GroupOperation improvements (finish and thread-safety)

- GroupOperation should only finish() after its children have finished.
- `GroupOperation._addOperations()` acquires the stateLock.
- `GroupOperation._addOperations()` cancels new child operations if the
GroupOperation is cancelled.

* RepeatedOperation.addNextOperation() must check cancelled

- RepeatedOperation.addNextOperation() now checks cancelled status.

* Added new cancellation-related tests

* Added new GroupOperation tests

* Added Operation KVO notification tests (for NSOperation keyPaths)

* Fixed spurious commit

* Handle more possible cases in Operation.main()

- Check that the Operation is not already executing.
- Check that the Operation has not been finished by a WillExecute
observer (either directly or as a side-effect of something else). Added
a test case that demonstrates this possibility.

* Add missing super.init() calls

* More GroupOperation cancelling fixes

- Changed the internal WillCancelObserver to a DidCancelObserver to
ensure that the cancelled state has been set prior to cancelling child
operations. (This ensures that side-effects of propagating cancellation
to child operations - like RepeatedOperation attempting to add the next
operation - can safely check the cancelled status.)

* GroupOperation should not call queue.cancelAllOperations()

Calling queue.cancelAllOperations() also cancels the
finishingOperation, which skips properly waiting until all other child
operations are finished.
Fixed test.

* Added willProduceOperation method to OperationQueueDelegate, fix GroupOperation handling of child-produced operations

Informational method only.
This enables GroupOperation to add child-produced operations to its
internal operations array, and thus cancel them when the GroupOperation
is cancelled.
Added tests.

* GroupOperation should ignore queue delegate calls from other queues

Added missing guard at the top of the queue delegate methods in
GroupOperation.
Added test to illustrate simple failure case this change covers.

* Refactor lock usage in Operation.finish() and .cancel()

- Refactor lock usage to hold the stateLock only when necessary or
manipulating internal state, and *not* when sending KVO notifications
or notifying observers.
- Improvements and fixes to KVO Notification Tests.

* Further refactor lock usage in Operation (.execute())

- Refactor lock usage in `Operation.execute()` to hold the stateLock
only when necessary or manipulating internal state, and *not* when
sending KVO notifications or notifying observers.

* Add a number of new StressTests that previously failed/crashed

New tests have support for a `batches` parameter.

* Initial fix for Logging-related crashes (thread-safety issues)

- `Operation._log` must not be a lazy var.
From the Swift book:
> If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
For more: https://bugs.swift.org/browse/SR-1042
- `Operation.log` must be thread-safe.
Initial fix involves capturing a copy of the logging context (settings
+ operationName) at the time of access of `Operation.log`, returning
that captured context as a LoggerType, and surrounding access of .log
with a Read/Write lock.
- LogManager properties must be thread-safe.

* Refactor lock usage in GroupOperation

- var `GroupOperation.operations` is now thread-safe.
- GroupOperation now utilizes a private CanFinishOperation to safely
transition to executing the finishingOperation. (See code & comments.)
- GroupOperation now has its own lock that is used sparingly to protect
finishing state.
- `GroupOperation._addOperation()` now utilizes the groupFinishLock.

* Minor tweaks

- `Operation.stateLock` should remain private.

* SwiftLint clean-up

* Refactor lock usage in GroupOperation

- Refactor lock usage to hold the groupFinishLock only when necessary
or manipulating internal state, and *not* when sending KVO
notifications, notifying observers, or changing additionalOperations
state (which might send KVO notifications or notify observers).

* Clean-up

- Remove asserts from `Operation.cancelWithErrors()`. Operation
cancellation methods should be safe to call at any time without
asserting.
- Other minor tweaks.

* Clean-up

- Moved NSOperationKeyPaths to extension NSOperation.KeyPath enum.

* Clean-up

* Clean-up

- Remove unneeded `self.`

* Clean-up

* Clarify docs & re-formatting

* Add new inits to Queue (NSQualityOfService and qos_class_t)

Per code review.

* Clean-up of GroupOperation

* Make Operation.cancel() final (#359)

Per discussion in:
#293
#358 (comment)

* Supports composing simple types for Result injection (#362)

* [OPR-362]: Linting whitespace changes

* [OPR-362]: Adds support for composing “executors” inside a final Operation type

This is a convenience class to aid adoption of _Operations_ without requiring types to subclass Operation. Instead, classes only need to conform to Executor, and then can use the Execute class, which support cancellation, and result injection.

* Supports async executors (#363)

* Fixes a finished log message (#365)

The "Operation Did finish" log was using the opposite logic for error logging

* Group's internal queue is non-public; exposes appropriate properties (#361)

* Make GroupOperation's internal queue non-public, and expose appropriate properties on GroupOperation

- Make GroupOperation.queue ~private~internal. (So it can be tested.)
- Expose appropriate run-time editable properties of the
GroupOperation’s queue as properties on GroupOperation.

Specifically:
- public var maxConcurrentOperationCount: Int
- public var suspended: Bool
- public var qualityOfService: NSQualityOfService

- Add tests.

* [OPR-361]: Support underlying queue in Group

* GatedOperation cancels composed operation when gate is closed (#377)

* [OPR-377]: Adds early exit with guard

* [OPR-377]: Refactors GatedOperation execute

* [OPR-377]: Fixes typo

* Result Injection protocol improvements (#378)

* [OPR-378]: Updates result injection APIs

* [OPR-378]: Adds unit tests

* [OPR-378]: Removes unnecessary weak self capture

* Fixes dependency gets added twice if it's direct & indirect (#379)

* [OPR-379]: Adds a failing unit test

* [OPR-379]: Makes indirect dependencies unique set

* Removes TaskOperation from non-macOS platforms (#382)

* Removes AlertPresentation mutual exclusion (#383)

* Adds IgnoredCondition (#385)

* Filters out indirect dependencies which have already been added (#386)

* Fixes issues with ignored condition (#390)

This means that conditions which are ignored mean that the attached operation does not execute, but also does not finish with an error. It’s a nicer use case than GatedOperation. This works by checking the OperationResult for .Ignored and then only cancel() instead of cancelWithError().

Note that if the operation also has .Failed(errors) condition result, in addition to .Ignored then the operation will still not run but this time with errors (as expected).

Note that if the operation also has .Satisfied condition result, in addition to .Ignored then the operation will not run, but without errors.

* NoFailedDependenciesCondition supports ignoring cancelled operations (#397)

Fixes #396. 

* [OPR-396]: NoFailedDependenciesCondition support ignoring cancels

* [OPR-396]: Refactors initializer

* [OPR-396]: Renames the condition

* Ability to add configuration to the next (retried) CloudKitOperation (#395)

* Adds CloudKitOperation.setPrepareForRetryHandler()

* Adds BatchedCloudKitOperation.setPrepareForNextOperationHandler()

* Clean-up

* Updated new APIs, added documentation

-Simpler, cleaner, better APIs.
-Added documentation and examples for the new APIs.

* Add documentation for BatchedCloudKitOperation.

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* [3.2.0]: Updates the CHANGELOG

* [3.2.0]: Sets the version to 3.2.0

* [3.2.0]: Updates the CHANGELOG

* [OPR-440]: Fixes iOS test target
danthorpe pushed a commit that referenced this pull request Sep 8, 2016
#424)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.
danthorpe added a commit that referenced this pull request Sep 8, 2016
* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)


* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* 3.2.0 (#431)

* Will Cancel Observer (#293)

* [OPR-293]: Refactors Start & Cancellation observers

* [OPR-293]: Go back to original group cancellation policy

* [OPR-293]: Adds errors to will cancel observer

* [OPR-293]: Avoids overriding cancel

Instead adds a will cancel observer.

* [OPR-293]: Corrects mistake where the queue was not suspended

!!

* [OPR-293]: Makes cancel a final method

This prevents Operation subclasses from overriding cancel. Instead, they should add an observer as appropriate.

WillCancelObserver <- block is executed directly before the operation will change its cancellation state.

DidCancelObserver <- block is executed directly after the operation did change its cancellation state.

Typically, subclasses will want to observe WillCancel to update their own internal state. But consumers will want to observe DidCancel to respond to operations being cancelled.

* [OPR-293]: Adds to new operations to property

Also makes the queue private.

* [OPR-293]: Cancels all operations in the queue.

This will ensure that the finishing operation is also cancelled.

* [OPR-293]: Adds will/did cancel API

* [OPR-293]: Fixes some issues with GroupOperation

One of the issues with adding the operations to the queue in the initializer, is that observers will not get notified.

So, we have to add the initial operations in `execute`. But, if we append to operations in addOperations, that means, we need to filter out any instances which have already been added.

For example, a subclass may well implement execute to first addOperation(myOperation); super.execute() which would cause a crash if we tried adding myOperation twice.

* [OPR-293]: Renames will/didFinish

This is to be consistent with operationWill/operationDidCancel

* Conditions as Operations (#286)

* [OPR-278]: Adds protocols for OperationType

* [OPR-278]: Creates ConditionOperation

With internal types for wrapping OperationCondition, and evaluating groups.

* [OPR-278]: Initial work removing ready override.

* [OPR-278]: Enables previously failing test

* [OPR-278]: Adds missing files to Extension project

* [OPR-278]: Enables previously failing test in iOS scheme too

* [OPR-278]: Increases the batch size.

Also removes the logging.

* [OPR-278]: Fixes scheduling of dependencies.

* [OPR-278]: Further fixes to dependency scheduling.

* [OPR-278]: Removes unnecessary protocols

* [OPR-278]: Removes dependency between evaluator and conditons

This can introduce a deadlock with mutual exclusivity.

* [OPR-278]: Fixes incorrect test with profiler.

* [OPR-278]: Enables all tests on OS X

* [OPR-278]: Enables all tests on iOS

* [OPR-278]: Prevents a retain cycle in ConditionOperation

* [OPR-278]: Adds failing test to clarify mutual exclusivity

* [OPR-278]: Sets mutual exclusion around operation

This was a mistake - it was getting set on the condition.

* [OPR-278]: Fixes mutual exclusivity tests

* [OPR-278]: Sorts out mutual conditions with dependencies

Also, operations which have dependencies, and mutually exclusive conditions which have dependencies.

* [OPR-278]: Clean up the code a bit

* [OPR-278]: Includes indirect dependencies

* [OPR-278]: Starts adding Stress Tests

This is a new test bundle which gets tested as part of the OS X test suite.

* [OPR-278]: Renames ConditionOperation to Condition

It *is* an operation, but I want it to be treated like a Condition with it's own evaluate API

* [OPR-278]: Sets up tests

* [OPR-278]: Renames to mutuallyExclusive

* [OPR-278]: Fixes target reference for Condition.swift

* [OPR-278]: Adds ComposedCondition

This is useful (and uses automatic result injection) for easily getting the result of a previous condition.

* [OPR-278]: Refactors BlockCondition

* [OPR-278]: Refactors NegatedCondition

* [OPR-278]: Initial work on updating framework conditions

- [x] BlockCondition
- [x] TrueCondition
- [x] FalseCondition
- [x] ComposedCondition
- [x] SilentCondition
- [x] NegatedCondition
- [x] MutuallyExclusive
- [x] NoFailedDependenciesCondition

* [OPR-278]: Corrects a mistake with removeDependencies

We we actually just adding?!

* [OPR-278]: Mixes the composed direct dependencies

* [OPR-278]: Updates UserNotificationCondition

to subclass Condition

* [OPR-278]: Fixes some tests for iOS

* [OPR-278]: Refactors AuthorizedFor

* [OPR-278]: Refactors UserConfirmationCondition

* [OPR-278]: Refactors ReachabilityCondition

* [OPR-278]: Refactors AddressBookCondition

* [OPR-278]: Refactors ContactsCondition

* [OPR-278]: Refactors RemoteNotificationCondition

* [OPR-278]: Adds @available annotation.

Still not really sure how to accuractly mark API as deprecated inside a framework.

* [OPR-278]: Adds some tests for conditions

* [OPR-278]: Refactors condition error type

* [OPR-278]: Refactors True False Condition

* [OPR-278]: Cleans up Condition tests

* [OPR-278]: Fixes tests

* Fixes an incorrect Fix-It hint. (#302)

Fixes #299.

* Fixes notice severity logs (#303)

This was done during development and accidentally committed & pushed.

Fixes #300

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* CLLocationAuthorizationStatus bug (#306)

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* [OPR-306]: Adds test coverage for location capability

* Improved support for CloudKit Errors (#304)

* [OPR-290]: Adds Error associated type on CKOperationType

At the moment, this is just fulfilled by NSError, however it will allow us to offer specialized error responses depending on the operation.

* [OPR-290]: Updates test cloud kit operation.

* [OPR-290]: Adds missing file to Extension project

* [OPR-290]: Moves CloudKit sources into a folder

* [OPR-290]: Adds errors for Modify Zones & Records

* [OPR-290]: Adds custom error types for all CloudKit Operations

* [OPR-290]: Fixes error handling

Now every CKOperation has it's own bespoke error type.

* [OPR-290]: Tidies up AssociatedErrorType

* [OPR-290]: Fixes issues with AssociatedErrorType

* [OPR-290]: Adds error recovery to BatchedCloudKitOperation

* [OPR-290]: Exposes properties on OPRCKOperation

These are needed to configure operations inside error handlers.

* Removes unnecessary let statement (#310)

* Supports entersReaderIfAvailable configuration in WebpageOperation. (#312)

It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Refactors WebpageOperation to subclass ComposedOperation  (#315)

* Extended WebpageOperation with the ability to configure entersReaderIfAvailable at initialization.
It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Fixed a bug in WebpageOperation that that resulted in a retain cycle.

* - Refactored WebpageOperation as ComposedOperation to fix a retain cycle.
- Added Documentation.

* Fixed some typos in WebpageOperationTests.

* Implemented OpenInSafariOperation (#317)

* Implemented OpenInSafariOperation

* Made the displayControllerFrom non optional.

* Refactored OpenInSafariOperation.

* Fixes a bug removing dependencies on Condition (#309)

The issue is that ComposedCondition must remove the dependency on its composed condition too.

* Improves cancellation checks in main() (#319)

* [OPR-287]: Invokes observers in main

main() is called from start, and we already check that the operation is not cancelled. Therefore the check at the start of main is redundant. However, we don't check after the observers have been executed that the operation is cancelled.

* [OPR-287]: SwiftLint fixes.

* Exposes LogSeverity enum to Objective-C (#324)

* Remove Reachability from CloudKitOperation (#327)

* Ignore DS_Store

* Rip out reachability from CloudKitOperation

* [OPR-327]: Adds trailing closure syntax

* [OPR-327]: Adds TimeoutObserver

* [OPR-327]: Adds tests for timeout observer

* Override NSOperationQueue's mainQueue() (#330)

* Override NSOperationQueue's mainQueue() to return the main queue as an OperationQueue

* set maxConcurrentOperationCount to 1

* Adds missing functionality of UIAlertController to AlertOperation (#334)

* Added missing functionality of UIAlertController to AlertOperation.

* Annotated new functionality as public.

* Added test cases for new functionality.

* Made alert property of AlertOperation private.

* SwiftLint corrections.

* Added a test for preferredAction and fixed a but related to it.

* Made UserIntent enum objc accessbile (#341)

* Fixed network observer flickering (#338)

Added tests for multiple operations having network observer

* Supports error recovery in GroupOperation (#326)

* [OPR-326]: Adds Errors subtype in Operation

* [OPR-326]: Refactors GroupOperation willFinish API

* Revert "[OPR-326]: Adds Errors subtype in Operation"

This reverts commit 759f2ef.

* [OPR-326]: WIP on group operation errors

* [OPR-326]: Fixes issues with retry operation tests

This is still not quite right.

* [OPR-326]: Allows RetryOperation to fully recover from errors

* [OPR-326]: Fixes issue with tests

* [OPR-326]: Tweaks ProfilerTest case

* [OPR-326]: Refactors ComposedOperation to just subclass GroupOperation

* [OPR-326]: Changes behaviour when a group produces an operation

* [OPR-326]: Logs when a GroupOperation successfully recovers from errors

* [OPR-326]: Logs when the next operation will be added

* [OPR-326]: Adds an immediate WaitStrategy

* [OPR-326]: Adds the operation to the log when attempting recovery

* [OPR-326]: Removes unneeded handling of child errors

* [OPR-326]: Allows the WaitStrategy to be defined at init

* [OPR-326]: Changes to support ComposedOperation

* [OPR-326]: Speeds up CloudKit tests with .Immediate wait strategy

* [OPR-326]: WIP on GroupOperation errors

* [OPR-326]: Fixes a bug where operation would not retry

In the case where there is a default handler, but no custom one set for the same error.

* [OPR-326]: Renames internal methods to be more Swifty

* [OPR-326]: Fixes errors - whoops.

* Always create CLLocationManager on the main queue (#321)

* [OPR-321]: Creates CLLocationManager on the main thread

Also makes it thread safe.

* [OPR-321]: Refactors capability to use guard & early exit.

* [OPR-321]: Creates CLLocationManager lazily on the main thread

Refactors UserLocationOperation to not use generic at the same time

* [OPR-321]: Fixes deadlock in creating CLLocationManager

* [OPR-321]: Refactors location operations for early exit

* [OPR-321]: Removes use of NSThread to check for main queue

* [OPR-321]: Fixes a mistake with the merge

* RepeatedOperation allows for configuration block to be reset by the payload (#348)

* [OPR-332]: Adds support for RetryOperation to provide a configuration block

* [OPR-332]: Removes commented out code

* [OPR-332]: Adds test to check that configure block is replaced

* Turns on Whole Module Optimization (#350)

Fixes #349

* Exposes the UIAlert for customization. (#351)

This also fixes a bug where it wasn't possible to set a barButtonItem on an ActionSheet while presenting as a popover.

* Fixes OperationQueue.delegate crash (SR-192) (#353)

* Add failing test

test__SR_192_OperationQueue_delegate_weak_var_thread_safety:
Tests race condition (and crash) involving OperationQueue’s weak
delegate var.

* Fix for OperationQueue.delegate crash (SR-192)

The underlying error is in Swift < 3.0, with concurrent reads of weak
properties.
See: https://bugs.swift.org/browse/SR-192

This change wraps the public delegate property with an NSLock. It also
sets it to nil first-thing in deinit to safely get rid of it.

* Simplistic support for CKLimitExceeded error handling (#294)

* [OPR-294]: Initial work adding built in support for CKLimitExceeded

* [OPR-294]: Rewrite with support for nil response

* [OPR-294]: Adds support for adding error handlers in bulk

* [OPR-294]: Fixes some comments

* [OPR-294]: Moves error handling to CloudKitError

* [OPR-294]: Adds BatchProcessErrorType

This is only needed for CKMarkNotificationsOperation

* [OPR-294]: Renames error protocols

* [OPR-294]: Adds support for batch processing

* [OPR-294]: Adds & Refactors for tests

* [OPR-294]: Minor tweaks

* [OPR-294]: Further tweaks

* [OPR-294]: Fixes error in bisect

* [OPR-294]: Adds API to replace the configure block outright

* [OPR-294]: Fixes failing tests

* [OPR-294]: Refactors stress tests condition

* Adds TaskOperation (#355)

* [OPR-313]: Adds TaskOperation

* [OPR-313]: Adds more test coverage

* [OPR-313]: Adds header documentation

* Vastly improves thread safety in Operations (#358)

* FIX: GroupOperation.operations contains the initial operations twice after beginning to execute.

Cause:
`GroupOperation.execute()` calls
`GroupOperation.addOperations(operations)`,
which then re-adds the operations back to the operations array.

Solution:
Added a private **`_addOperations()`** method to `GroupOperation` with
an additional flag to override this behavior - used in
`GroupOperation.execute()`.

`addOperations()` now calls this private implementation method.

* Operation.cancel(), Operation.finish() thread-safety

- `Operation.cancel()` now acquires the stateLock.
- `Operation.finish()` now acquires the stateLock.
- `stateLock` is now NSRecursiveLock().
- var `Operation.cancelled` is thread-safe.
- var `Operation.errors` is thread-safe.
- Explicitly send KVO notifications on NSOperation keyPaths
at the right times, avoiding lots of unnecessary notifications.
- Removed “State” KVO notifications. (Previously used to
trigger NSOperation isExecuting/isFinished notifications,
which are now triggered explicitly.)
- Added missing operationWillFinish() call to finish().
- operationDidFinish() now happens after the state has been
set to .Finished.
- `Operation.cancelWithError(errors)` now asserts if it’s
called while the Operation is finishing or finished.

* Operation.main() thread-safety, Operation.disableAutomaticFinishing

- `Operation.main()` acquires the stateLock when appropriate.
- `Operation.main()` manually sends the isExecuting NSOperation KVO
notifications when setting executing state.
- Added ability to disable Operation’s automatic calling of finish(),
with `init(disableAutomaticFinishing: Bool)`. (Defaults to existing
behavior - i.e. false.)

* GroupOperation improvements (finish and thread-safety)

- GroupOperation should only finish() after its children have finished.
- `GroupOperation._addOperations()` acquires the stateLock.
- `GroupOperation._addOperations()` cancels new child operations if the
GroupOperation is cancelled.

* RepeatedOperation.addNextOperation() must check cancelled

- RepeatedOperation.addNextOperation() now checks cancelled status.

* Added new cancellation-related tests

* Added new GroupOperation tests

* Added Operation KVO notification tests (for NSOperation keyPaths)

* Fixed spurious commit

* Handle more possible cases in Operation.main()

- Check that the Operation is not already executing.
- Check that the Operation has not been finished by a WillExecute
observer (either directly or as a side-effect of something else). Added
a test case that demonstrates this possibility.

* Add missing super.init() calls

* More GroupOperation cancelling fixes

- Changed the internal WillCancelObserver to a DidCancelObserver to
ensure that the cancelled state has been set prior to cancelling child
operations. (This ensures that side-effects of propagating cancellation
to child operations - like RepeatedOperation attempting to add the next
operation - can safely check the cancelled status.)

* GroupOperation should not call queue.cancelAllOperations()

Calling queue.cancelAllOperations() also cancels the
finishingOperation, which skips properly waiting until all other child
operations are finished.
Fixed test.

* Added willProduceOperation method to OperationQueueDelegate, fix GroupOperation handling of child-produced operations

Informational method only.
This enables GroupOperation to add child-produced operations to its
internal operations array, and thus cancel them when the GroupOperation
is cancelled.
Added tests.

* GroupOperation should ignore queue delegate calls from other queues

Added missing guard at the top of the queue delegate methods in
GroupOperation.
Added test to illustrate simple failure case this change covers.

* Refactor lock usage in Operation.finish() and .cancel()

- Refactor lock usage to hold the stateLock only when necessary or
manipulating internal state, and *not* when sending KVO notifications
or notifying observers.
- Improvements and fixes to KVO Notification Tests.

* Further refactor lock usage in Operation (.execute())

- Refactor lock usage in `Operation.execute()` to hold the stateLock
only when necessary or manipulating internal state, and *not* when
sending KVO notifications or notifying observers.

* Add a number of new StressTests that previously failed/crashed

New tests have support for a `batches` parameter.

* Initial fix for Logging-related crashes (thread-safety issues)

- `Operation._log` must not be a lazy var.
From the Swift book:
> If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
For more: https://bugs.swift.org/browse/SR-1042
- `Operation.log` must be thread-safe.
Initial fix involves capturing a copy of the logging context (settings
+ operationName) at the time of access of `Operation.log`, returning
that captured context as a LoggerType, and surrounding access of .log
with a Read/Write lock.
- LogManager properties must be thread-safe.

* Refactor lock usage in GroupOperation

- var `GroupOperation.operations` is now thread-safe.
- GroupOperation now utilizes a private CanFinishOperation to safely
transition to executing the finishingOperation. (See code & comments.)
- GroupOperation now has its own lock that is used sparingly to protect
finishing state.
- `GroupOperation._addOperation()` now utilizes the groupFinishLock.

* Minor tweaks

- `Operation.stateLock` should remain private.

* SwiftLint clean-up

* Refactor lock usage in GroupOperation

- Refactor lock usage to hold the groupFinishLock only when necessary
or manipulating internal state, and *not* when sending KVO
notifications, notifying observers, or changing additionalOperations
state (which might send KVO notifications or notify observers).

* Clean-up

- Remove asserts from `Operation.cancelWithErrors()`. Operation
cancellation methods should be safe to call at any time without
asserting.
- Other minor tweaks.

* Clean-up

- Moved NSOperationKeyPaths to extension NSOperation.KeyPath enum.

* Clean-up

* Clean-up

- Remove unneeded `self.`

* Clean-up

* Clarify docs & re-formatting

* Add new inits to Queue (NSQualityOfService and qos_class_t)

Per code review.

* Clean-up of GroupOperation

* Make Operation.cancel() final (#359)

Per discussion in:
#293
#358 (comment)

* Supports composing simple types for Result injection (#362)

* [OPR-362]: Linting whitespace changes

* [OPR-362]: Adds support for composing “executors” inside a final Operation type

This is a convenience class to aid adoption of _Operations_ without requiring types to subclass Operation. Instead, classes only need to conform to Executor, and then can use the Execute class, which support cancellation, and result injection.

* Supports async executors (#363)

* Fixes a finished log message (#365)

The "Operation Did finish" log was using the opposite logic for error logging

* Group's internal queue is non-public; exposes appropriate properties (#361)

* Make GroupOperation's internal queue non-public, and expose appropriate properties on GroupOperation

- Make GroupOperation.queue ~private~internal. (So it can be tested.)
- Expose appropriate run-time editable properties of the
GroupOperation’s queue as properties on GroupOperation.

Specifically:
- public var maxConcurrentOperationCount: Int
- public var suspended: Bool
- public var qualityOfService: NSQualityOfService

- Add tests.

* [OPR-361]: Support underlying queue in Group

* GatedOperation cancels composed operation when gate is closed (#377)

* [OPR-377]: Adds early exit with guard

* [OPR-377]: Refactors GatedOperation execute

* [OPR-377]: Fixes typo

* Result Injection protocol improvements (#378)

* [OPR-378]: Updates result injection APIs

* [OPR-378]: Adds unit tests

* [OPR-378]: Removes unnecessary weak self capture

* Fixes dependency gets added twice if it's direct & indirect (#379)

* [OPR-379]: Adds a failing unit test

* [OPR-379]: Makes indirect dependencies unique set

* Removes TaskOperation from non-macOS platforms (#382)

* Removes AlertPresentation mutual exclusion (#383)

* Adds IgnoredCondition (#385)

* Filters out indirect dependencies which have already been added (#386)

* Fixes issues with ignored condition (#390)

This means that conditions which are ignored mean that the attached operation does not execute, but also does not finish with an error. It’s a nicer use case than GatedOperation. This works by checking the OperationResult for .Ignored and then only cancel() instead of cancelWithError().

Note that if the operation also has .Failed(errors) condition result, in addition to .Ignored then the operation will still not run but this time with errors (as expected).

Note that if the operation also has .Satisfied condition result, in addition to .Ignored then the operation will not run, but without errors.

* NoFailedDependenciesCondition supports ignoring cancelled operations (#397)

Fixes #396. 

* [OPR-396]: NoFailedDependenciesCondition support ignoring cancels

* [OPR-396]: Refactors initializer

* [OPR-396]: Renames the condition

* Ability to add configuration to the next (retried) CloudKitOperation (#395)

* Adds CloudKitOperation.setPrepareForRetryHandler()

* Adds BatchedCloudKitOperation.setPrepareForNextOperationHandler()

* Clean-up

* Updated new APIs, added documentation

-Simpler, cleaner, better APIs.
-Added documentation and examples for the new APIs.

* Add documentation for BatchedCloudKitOperation.

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* [3.2.0]: Updates the CHANGELOG

* [3.2.0]: Sets the version to 3.2.0

* [3.2.0]: Updates the CHANGELOG

* [OPR-440]: Fixes iOS test target
danthorpe added a commit that referenced this pull request Sep 14, 2016
* Will Cancel Observer (#293)

* [OPR-293]: Refactors Start & Cancellation observers

* [OPR-293]: Go back to original group cancellation policy

* [OPR-293]: Adds errors to will cancel observer

* [OPR-293]: Avoids overriding cancel

Instead adds a will cancel observer.

* [OPR-293]: Corrects mistake where the queue was not suspended

!!

* [OPR-293]: Makes cancel a final method

This prevents Operation subclasses from overriding cancel. Instead, they should add an observer as appropriate.

WillCancelObserver <- block is executed directly before the operation will change its cancellation state.

DidCancelObserver <- block is executed directly after the operation did change its cancellation state.

Typically, subclasses will want to observe WillCancel to update their own internal state. But consumers will want to observe DidCancel to respond to operations being cancelled.

* [OPR-293]: Adds to new operations to property

Also makes the queue private.

* [OPR-293]: Cancels all operations in the queue.

This will ensure that the finishing operation is also cancelled.

* [OPR-293]: Adds will/did cancel API

* [OPR-293]: Fixes some issues with GroupOperation

One of the issues with adding the operations to the queue in the initializer, is that observers will not get notified.

So, we have to add the initial operations in `execute`. But, if we append to operations in addOperations, that means, we need to filter out any instances which have already been added.

For example, a subclass may well implement execute to first addOperation(myOperation); super.execute() which would cause a crash if we tried adding myOperation twice.

* [OPR-293]: Renames will/didFinish

This is to be consistent with operationWill/operationDidCancel

* Conditions as Operations (#286)

* [OPR-278]: Adds protocols for OperationType

* [OPR-278]: Creates ConditionOperation

With internal types for wrapping OperationCondition, and evaluating groups.

* [OPR-278]: Initial work removing ready override.

* [OPR-278]: Enables previously failing test

* [OPR-278]: Adds missing files to Extension project

* [OPR-278]: Enables previously failing test in iOS scheme too

* [OPR-278]: Increases the batch size.

Also removes the logging.

* [OPR-278]: Fixes scheduling of dependencies.

* [OPR-278]: Further fixes to dependency scheduling.

* [OPR-278]: Removes unnecessary protocols

* [OPR-278]: Removes dependency between evaluator and conditons

This can introduce a deadlock with mutual exclusivity.

* [OPR-278]: Fixes incorrect test with profiler.

* [OPR-278]: Enables all tests on OS X

* [OPR-278]: Enables all tests on iOS

* [OPR-278]: Prevents a retain cycle in ConditionOperation

* [OPR-278]: Adds failing test to clarify mutual exclusivity

* [OPR-278]: Sets mutual exclusion around operation

This was a mistake - it was getting set on the condition.

* [OPR-278]: Fixes mutual exclusivity tests

* [OPR-278]: Sorts out mutual conditions with dependencies

Also, operations which have dependencies, and mutually exclusive conditions which have dependencies.

* [OPR-278]: Clean up the code a bit

* [OPR-278]: Includes indirect dependencies

* [OPR-278]: Starts adding Stress Tests

This is a new test bundle which gets tested as part of the OS X test suite.

* [OPR-278]: Renames ConditionOperation to Condition

It *is* an operation, but I want it to be treated like a Condition with it's own evaluate API

* [OPR-278]: Sets up tests

* [OPR-278]: Renames to mutuallyExclusive

* [OPR-278]: Fixes target reference for Condition.swift

* [OPR-278]: Adds ComposedCondition

This is useful (and uses automatic result injection) for easily getting the result of a previous condition.

* [OPR-278]: Refactors BlockCondition

* [OPR-278]: Refactors NegatedCondition

* [OPR-278]: Initial work on updating framework conditions

- [x] BlockCondition
- [x] TrueCondition
- [x] FalseCondition
- [x] ComposedCondition
- [x] SilentCondition
- [x] NegatedCondition
- [x] MutuallyExclusive
- [x] NoFailedDependenciesCondition

* [OPR-278]: Corrects a mistake with removeDependencies

We we actually just adding?!

* [OPR-278]: Mixes the composed direct dependencies

* [OPR-278]: Updates UserNotificationCondition

to subclass Condition

* [OPR-278]: Fixes some tests for iOS

* [OPR-278]: Refactors AuthorizedFor

* [OPR-278]: Refactors UserConfirmationCondition

* [OPR-278]: Refactors ReachabilityCondition

* [OPR-278]: Refactors AddressBookCondition

* [OPR-278]: Refactors ContactsCondition

* [OPR-278]: Refactors RemoteNotificationCondition

* [OPR-278]: Adds @available annotation.

Still not really sure how to accuractly mark API as deprecated inside a framework.

* [OPR-278]: Adds some tests for conditions

* [OPR-278]: Refactors condition error type

* [OPR-278]: Refactors True False Condition

* [OPR-278]: Cleans up Condition tests

* [OPR-278]: Fixes tests

* Fixes an incorrect Fix-It hint. (#302)

Fixes #299.

* Fixes notice severity logs (#303)

This was done during development and accidentally committed & pushed.

Fixes #300

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* CLLocationAuthorizationStatus bug (#306)

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* [OPR-306]: Adds test coverage for location capability

* Improved support for CloudKit Errors (#304)

* [OPR-290]: Adds Error associated type on CKOperationType

At the moment, this is just fulfilled by NSError, however it will allow us to offer specialized error responses depending on the operation.

* [OPR-290]: Updates test cloud kit operation.

* [OPR-290]: Adds missing file to Extension project

* [OPR-290]: Moves CloudKit sources into a folder

* [OPR-290]: Adds errors for Modify Zones & Records

* [OPR-290]: Adds custom error types for all CloudKit Operations

* [OPR-290]: Fixes error handling

Now every CKOperation has it's own bespoke error type.

* [OPR-290]: Tidies up AssociatedErrorType

* [OPR-290]: Fixes issues with AssociatedErrorType

* [OPR-290]: Adds error recovery to BatchedCloudKitOperation

* [OPR-290]: Exposes properties on OPRCKOperation

These are needed to configure operations inside error handlers.

* Removes unnecessary let statement (#310)

* Supports entersReaderIfAvailable configuration in WebpageOperation. (#312)

It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Refactors WebpageOperation to subclass ComposedOperation  (#315)

* Extended WebpageOperation with the ability to configure entersReaderIfAvailable at initialization.
It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Fixed a bug in WebpageOperation that that resulted in a retain cycle.

* - Refactored WebpageOperation as ComposedOperation to fix a retain cycle.
- Added Documentation.

* Fixed some typos in WebpageOperationTests.

* Implemented OpenInSafariOperation (#317)

* Implemented OpenInSafariOperation

* Made the displayControllerFrom non optional.

* Refactored OpenInSafariOperation.

* Fixes a bug removing dependencies on Condition (#309)

The issue is that ComposedCondition must remove the dependency on its composed condition too.

* Improves cancellation checks in main() (#319)

* [OPR-287]: Invokes observers in main

main() is called from start, and we already check that the operation is not cancelled. Therefore the check at the start of main is redundant. However, we don't check after the observers have been executed that the operation is cancelled.

* [OPR-287]: SwiftLint fixes.

* Exposes LogSeverity enum to Objective-C (#324)

* Remove Reachability from CloudKitOperation (#327)

* Ignore DS_Store

* Rip out reachability from CloudKitOperation

* [OPR-327]: Adds trailing closure syntax

* [OPR-327]: Adds TimeoutObserver

* [OPR-327]: Adds tests for timeout observer

* Override NSOperationQueue's mainQueue() (#330)

* Override NSOperationQueue's mainQueue() to return the main queue as an OperationQueue

* set maxConcurrentOperationCount to 1

* Adds missing functionality of UIAlertController to AlertOperation (#334)

* Added missing functionality of UIAlertController to AlertOperation.

* Annotated new functionality as public.

* Added test cases for new functionality.

* Made alert property of AlertOperation private.

* SwiftLint corrections.

* Added a test for preferredAction and fixed a but related to it.

* Made UserIntent enum objc accessbile (#341)

* Fixed network observer flickering (#338)

Added tests for multiple operations having network observer

* Supports error recovery in GroupOperation (#326)

* [OPR-326]: Adds Errors subtype in Operation

* [OPR-326]: Refactors GroupOperation willFinish API

* Revert "[OPR-326]: Adds Errors subtype in Operation"

This reverts commit 759f2ef3643cafbbda3544185234b8adeec474ef.

* [OPR-326]: WIP on group operation errors

* [OPR-326]: Fixes issues with retry operation tests

This is still not quite right.

* [OPR-326]: Allows RetryOperation to fully recover from errors

* [OPR-326]: Fixes issue with tests

* [OPR-326]: Tweaks ProfilerTest case

* [OPR-326]: Refactors ComposedOperation to just subclass GroupOperation

* [OPR-326]: Changes behaviour when a group produces an operation

* [OPR-326]: Logs when a GroupOperation successfully recovers from errors

* [OPR-326]: Logs when the next operation will be added

* [OPR-326]: Adds an immediate WaitStrategy

* [OPR-326]: Adds the operation to the log when attempting recovery

* [OPR-326]: Removes unneeded handling of child errors

* [OPR-326]: Allows the WaitStrategy to be defined at init

* [OPR-326]: Changes to support ComposedOperation

* [OPR-326]: Speeds up CloudKit tests with .Immediate wait strategy

* [OPR-326]: WIP on GroupOperation errors

* [OPR-326]: Fixes a bug where operation would not retry

In the case where there is a default handler, but no custom one set for the same error.

* [OPR-326]: Renames internal methods to be more Swifty

* [OPR-326]: Fixes errors - whoops.

* Always create CLLocationManager on the main queue (#321)

* [OPR-321]: Creates CLLocationManager on the main thread

Also makes it thread safe.

* [OPR-321]: Refactors capability to use guard & early exit.

* [OPR-321]: Creates CLLocationManager lazily on the main thread

Refactors UserLocationOperation to not use generic at the same time

* [OPR-321]: Fixes deadlock in creating CLLocationManager

* [OPR-321]: Refactors location operations for early exit

* [OPR-321]: Removes use of NSThread to check for main queue

* [OPR-321]: Fixes a mistake with the merge

* RepeatedOperation allows for configuration block to be reset by the payload (#348)

* [OPR-332]: Adds support for RetryOperation to provide a configuration block

* [OPR-332]: Removes commented out code

* [OPR-332]: Adds test to check that configure block is replaced

* Turns on Whole Module Optimization (#350)

Fixes #349

* Exposes the UIAlert for customization. (#351)

This also fixes a bug where it wasn't possible to set a barButtonItem on an ActionSheet while presenting as a popover.

* Fixes OperationQueue.delegate crash (SR-192) (#353)

* Add failing test

test__SR_192_OperationQueue_delegate_weak_var_thread_safety:
Tests race condition (and crash) involving OperationQueue’s weak
delegate var.

* Fix for OperationQueue.delegate crash (SR-192)

The underlying error is in Swift < 3.0, with concurrent reads of weak
properties.
See: https://bugs.swift.org/browse/SR-192

This change wraps the public delegate property with an NSLock. It also
sets it to nil first-thing in deinit to safely get rid of it.

* Simplistic support for CKLimitExceeded error handling (#294)

* [OPR-294]: Initial work adding built in support for CKLimitExceeded

* [OPR-294]: Rewrite with support for nil response

* [OPR-294]: Adds support for adding error handlers in bulk

* [OPR-294]: Fixes some comments

* [OPR-294]: Moves error handling to CloudKitError

* [OPR-294]: Adds BatchProcessErrorType

This is only needed for CKMarkNotificationsOperation

* [OPR-294]: Renames error protocols

* [OPR-294]: Adds support for batch processing

* [OPR-294]: Adds & Refactors for tests

* [OPR-294]: Minor tweaks

* [OPR-294]: Further tweaks

* [OPR-294]: Fixes error in bisect

* [OPR-294]: Adds API to replace the configure block outright

* [OPR-294]: Fixes failing tests

* [OPR-294]: Refactors stress tests condition

* Adds TaskOperation (#355)

* [OPR-313]: Adds TaskOperation

* [OPR-313]: Adds more test coverage

* [OPR-313]: Adds header documentation

* Vastly improves thread safety in Operations (#358)

* FIX: GroupOperation.operations contains the initial operations twice after beginning to execute.

Cause:
`GroupOperation.execute()` calls
`GroupOperation.addOperations(operations)`,
which then re-adds the operations back to the operations array.

Solution:
Added a private **`_addOperations()`** method to `GroupOperation` with
an additional flag to override this behavior - used in
`GroupOperation.execute()`.

`addOperations()` now calls this private implementation method.

* Operation.cancel(), Operation.finish() thread-safety

- `Operation.cancel()` now acquires the stateLock.
- `Operation.finish()` now acquires the stateLock.
- `stateLock` is now NSRecursiveLock().
- var `Operation.cancelled` is thread-safe.
- var `Operation.errors` is thread-safe.
- Explicitly send KVO notifications on NSOperation keyPaths
at the right times, avoiding lots of unnecessary notifications.
- Removed “State” KVO notifications. (Previously used to
trigger NSOperation isExecuting/isFinished notifications,
which are now triggered explicitly.)
- Added missing operationWillFinish() call to finish().
- operationDidFinish() now happens after the state has been
set to .Finished.
- `Operation.cancelWithError(errors)` now asserts if it’s
called while the Operation is finishing or finished.

* Operation.main() thread-safety, Operation.disableAutomaticFinishing

- `Operation.main()` acquires the stateLock when appropriate.
- `Operation.main()` manually sends the isExecuting NSOperation KVO
notifications when setting executing state.
- Added ability to disable Operation’s automatic calling of finish(),
with `init(disableAutomaticFinishing: Bool)`. (Defaults to existing
behavior - i.e. false.)

* GroupOperation improvements (finish and thread-safety)

- GroupOperation should only finish() after its children have finished.
- `GroupOperation._addOperations()` acquires the stateLock.
- `GroupOperation._addOperations()` cancels new child operations if the
GroupOperation is cancelled.

* RepeatedOperation.addNextOperation() must check cancelled

- RepeatedOperation.addNextOperation() now checks cancelled status.

* Added new cancellation-related tests

* Added new GroupOperation tests

* Added Operation KVO notification tests (for NSOperation keyPaths)

* Fixed spurious commit

* Handle more possible cases in Operation.main()

- Check that the Operation is not already executing.
- Check that the Operation has not been finished by a WillExecute
observer (either directly or as a side-effect of something else). Added
a test case that demonstrates this possibility.

* Add missing super.init() calls

* More GroupOperation cancelling fixes

- Changed the internal WillCancelObserver to a DidCancelObserver to
ensure that the cancelled state has been set prior to cancelling child
operations. (This ensures that side-effects of propagating cancellation
to child operations - like RepeatedOperation attempting to add the next
operation - can safely check the cancelled status.)

* GroupOperation should not call queue.cancelAllOperations()

Calling queue.cancelAllOperations() also cancels the
finishingOperation, which skips properly waiting until all other child
operations are finished.
Fixed test.

* Added willProduceOperation method to OperationQueueDelegate, fix GroupOperation handling of child-produced operations

Informational method only.
This enables GroupOperation to add child-produced operations to its
internal operations array, and thus cancel them when the GroupOperation
is cancelled.
Added tests.

* GroupOperation should ignore queue delegate calls from other queues

Added missing guard at the top of the queue delegate methods in
GroupOperation.
Added test to illustrate simple failure case this change covers.

* Refactor lock usage in Operation.finish() and .cancel()

- Refactor lock usage to hold the stateLock only when necessary or
manipulating internal state, and *not* when sending KVO notifications
or notifying observers.
- Improvements and fixes to KVO Notification Tests.

* Further refactor lock usage in Operation (.execute())

- Refactor lock usage in `Operation.execute()` to hold the stateLock
only when necessary or manipulating internal state, and *not* when
sending KVO notifications or notifying observers.

* Add a number of new StressTests that previously failed/crashed

New tests have support for a `batches` parameter.

* Initial fix for Logging-related crashes (thread-safety issues)

- `Operation._log` must not be a lazy var.
From the Swift book:
> If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
For more: https://bugs.swift.org/browse/SR-1042
- `Operation.log` must be thread-safe.
Initial fix involves capturing a copy of the logging context (settings
+ operationName) at the time of access of `Operation.log`, returning
that captured context as a LoggerType, and surrounding access of .log
with a Read/Write lock.
- LogManager properties must be thread-safe.

* Refactor lock usage in GroupOperation

- var `GroupOperation.operations` is now thread-safe.
- GroupOperation now utilizes a private CanFinishOperation to safely
transition to executing the finishingOperation. (See code & comments.)
- GroupOperation now has its own lock that is used sparingly to protect
finishing state.
- `GroupOperation._addOperation()` now utilizes the groupFinishLock.

* Minor tweaks

- `Operation.stateLock` should remain private.

* SwiftLint clean-up

* Refactor lock usage in GroupOperation

- Refactor lock usage to hold the groupFinishLock only when necessary
or manipulating internal state, and *not* when sending KVO
notifications, notifying observers, or changing additionalOperations
state (which might send KVO notifications or notify observers).

* Clean-up

- Remove asserts from `Operation.cancelWithErrors()`. Operation
cancellation methods should be safe to call at any time without
asserting.
- Other minor tweaks.

* Clean-up

- Moved NSOperationKeyPaths to extension NSOperation.KeyPath enum.

* Clean-up

* Clean-up

- Remove unneeded `self.`

* Clean-up

* Clarify docs & re-formatting

* Add new inits to Queue (NSQualityOfService and qos_class_t)

Per code review.

* Clean-up of GroupOperation

* Make Operation.cancel() final (#359)

Per discussion in:
https://github.com/danthorpe/Operations/pull/293
https://github.com/danthorpe/Operations/pull/358#discussion_r70167086

* Supports composing simple types for Result injection (#362)

* [OPR-362]: Linting whitespace changes

* [OPR-362]: Adds support for composing “executors” inside a final Operation type

This is a convenience class to aid adoption of _Operations_ without requiring types to subclass Operation. Instead, classes only need to conform to Executor, and then can use the Execute class, which support cancellation, and result injection.

* Supports async executors (#363)

* Fixes a finished log message (#365)

The "Operation Did finish" log was using the opposite logic for error logging

* Group's internal queue is non-public; exposes appropriate properties (#361)

* Make GroupOperation's internal queue non-public, and expose appropriate properties on GroupOperation

- Make GroupOperation.queue ~private~internal. (So it can be tested.)
- Expose appropriate run-time editable properties of the
GroupOperation’s queue as properties on GroupOperation.

Specifically:
- public var maxConcurrentOperationCount: Int
- public var suspended: Bool
- public var qualityOfService: NSQualityOfService

- Add tests.

* [OPR-361]: Support underlying queue in Group

* GatedOperation cancels composed operation when gate is closed (#377)

* [OPR-377]: Adds early exit with guard

* [OPR-377]: Refactors GatedOperation execute

* [OPR-377]: Fixes typo

* Result Injection protocol improvements (#378)

* [OPR-378]: Updates result injection APIs

* [OPR-378]: Adds unit tests

* [OPR-378]: Removes unnecessary weak self capture

* Fixes dependency gets added twice if it's direct & indirect (#379)

* [OPR-379]: Adds a failing unit test

* [OPR-379]: Makes indirect dependencies unique set

* Removes TaskOperation from non-macOS platforms (#382)

* Removes AlertPresentation mutual exclusion (#383)

* Adds IgnoredCondition (#385)

* Filters out indirect dependencies which have already been added (#386)

* Fixes issues with ignored condition (#390)

This means that conditions which are ignored mean that the attached operation does not execute, but also does not finish with an error. It’s a nicer use case than GatedOperation. This works by checking the OperationResult for .Ignored and then only cancel() instead of cancelWithError().

Note that if the operation also has .Failed(errors) condition result, in addition to .Ignored then the operation will still not run but this time with errors (as expected).

Note that if the operation also has .Satisfied condition result, in addition to .Ignored then the operation will not run, but without errors.

* NoFailedDependenciesCondition supports ignoring cancelled operations (#397)

Fixes #396. 

* [OPR-396]: NoFailedDependenciesCondition support ignoring cancels

* [OPR-396]: Refactors initializer

* [OPR-396]: Renames the condition

* Ability to add configuration to the next (retried) CloudKitOperation (#395)

* Adds CloudKitOperation.setPrepareForRetryHandler()

* Adds BatchedCloudKitOperation.setPrepareForNextOperationHandler()

* Clean-up

* Updated new APIs, added documentation

-Simpler, cleaner, better APIs.
-Added documentation and examples for the new APIs.

* Add documentation for BatchedCloudKitOperation.

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* Resolving Unmanaged<CFErrorRef> warning (#452)

CFErrorRef is a typealias for CFError that is a struct. Unmanaged is for objects only.wq

* FIX: macOS deployment target should be 10.10 (Swift 2.2) (#453) (#454)

* Separate out extra features from podspec (#456)

* [OPR-456]: Removes Calendar, Location, Passbook, Photos etc from standard framework

* [OPR-456]: Reorders subspecs a bit

* [OPR-456]: Sorts out Extension compatible project

* [OPR-456]: Removes Slather step

* [OPR-456]: Fixes podspec error

* [swift/2.3]: Updates to Swift 2.3

* [swift/2.3]: Fixes issue with selecting agent with swift version.

* [swift/2.3]: Sets the developer directory

* [swift/2.3]: Sets the Swift version

* [swift/2.3]: Gets SWIFT_VERSION set correctly at tests.

Also had to prefix Dispatch. to a bunch of dispatch_async calls.

* [swift/2.3]: Disables StressTests

* [swift/2.3]: Removes slather

* [swift/2.3]: Removes Slather properly

* [swift/2.3]: Updates project to latest warnings

* [swift/2.3]: Updates for Swift 2.3

* [swift/2.3]: Updates .gitignore

* Supports new CloudKit operations in iOS 10 / macOS 10.12 (#406)

* Add new CloudKit associatedtypes, and missing/new CKOperation parameters.

* Add CKFetchAllChanges protocol

* Add support for CKAcceptSharesOperation

* Add support for CKDiscoverAllUserIdentitiesOperation

* Add support for CKDiscoverUserIdentitiesOperation

* Add support for CKFetchDatabaseChangesOperation

* Add support for CKFetchRecordZoneChangesOperation

* Add support for CKFetchShareMetadataOperation

* Add support for CKFetchShareParticipantsOperation

* Add test operations for the new CKOperations to CloudKitOperationTests

* Add tests

* Add tests

* Add tests

* Add tests

* Add availability markers for deprecated CKOperations

* Fix watchOS support for CloudKitInterface.swift

* Disable swiftlint file_length warning for CloudKitInterface.swift

* Remove unneeded associated Error type declaration from the specific protocols

* CKDiscoverAllUserIdentitiesOperation is unavailable on tvOS

* Make public some properties that should be public

* Integrating recent changes from swift/2.2 branch into swift/2.3 branch (#424)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Updates swift/2.3 with 3.2.0 release (#440)

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* 3.1.1 (#411)


* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* [3.1.1]: Adds CHANGELOG

* [3.1.1]: Updates version

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift version explicitly

* [OPR-427]: Puts the coveralls environment vars back

* [OPR-427]: Updates README to point to swift/2.2 coverage badge

* [OPR-427]: Fixes coverage badge

* Removes offending OpenInSafariOperationTests (#434)

Fixes #432

* Replaces some instances of String interpolation (#435)

* 3.2.0 (#431)

* Will Cancel Observer (#293)

* [OPR-293]: Refactors Start & Cancellation observers

* [OPR-293]: Go back to original group cancellation policy

* [OPR-293]: Adds errors to will cancel observer

* [OPR-293]: Avoids overriding cancel

Instead adds a will cancel observer.

* [OPR-293]: Corrects mistake where the queue was not suspended

!!

* [OPR-293]: Makes cancel a final method

This prevents Operation subclasses from overriding cancel. Instead, they should add an observer as appropriate.

WillCancelObserver <- block is executed directly before the operation will change its cancellation state.

DidCancelObserver <- block is executed directly after the operation did change its cancellation state.

Typically, subclasses will want to observe WillCancel to update their own internal state. But consumers will want to observe DidCancel to respond to operations being cancelled.

* [OPR-293]: Adds to new operations to property

Also makes the queue private.

* [OPR-293]: Cancels all operations in the queue.

This will ensure that the finishing operation is also cancelled.

* [OPR-293]: Adds will/did cancel API

* [OPR-293]: Fixes some issues with GroupOperation

One of the issues with adding the operations to the queue in the initializer, is that observers will not get notified.

So, we have to add the initial operations in `execute`. But, if we append to operations in addOperations, that means, we need to filter out any instances which have already been added.

For example, a subclass may well implement execute to first addOperation(myOperation); super.execute() which would cause a crash if we tried adding myOperation twice.

* [OPR-293]: Renames will/didFinish

This is to be consistent with operationWill/operationDidCancel

* Conditions as Operations (#286)

* [OPR-278]: Adds protocols for OperationType

* [OPR-278]: Creates ConditionOperation

With internal types for wrapping OperationCondition, and evaluating groups.

* [OPR-278]: Initial work removing ready override.

* [OPR-278]: Enables previously failing test

* [OPR-278]: Adds missing files to Extension project

* [OPR-278]: Enables previously failing test in iOS scheme too

* [OPR-278]: Increases the batch size.

Also removes the logging.

* [OPR-278]: Fixes scheduling of dependencies.

* [OPR-278]: Further fixes to dependency scheduling.

* [OPR-278]: Removes unnecessary protocols

* [OPR-278]: Removes dependency between evaluator and conditons

This can introduce a deadlock with mutual exclusivity.

* [OPR-278]: Fixes incorrect test with profiler.

* [OPR-278]: Enables all tests on OS X

* [OPR-278]: Enables all tests on iOS

* [OPR-278]: Prevents a retain cycle in ConditionOperation

* [OPR-278]: Adds failing test to clarify mutual exclusivity

* [OPR-278]: Sets mutual exclusion around operation

This was a mistake - it was getting set on the condition.

* [OPR-278]: Fixes mutual exclusivity tests

* [OPR-278]: Sorts out mutual conditions with dependencies

Also, operations which have dependencies, and mutually exclusive conditions which have dependencies.

* [OPR-278]: Clean up the code a bit

* [OPR-278]: Includes indirect dependencies

* [OPR-278]: Starts adding Stress Tests

This is a new test bundle which gets tested as part of the OS X test suite.

* [OPR-278]: Renames ConditionOperation to Condition

It *is* an operation, but I want it to be treated like a Condition with it's own evaluate API

* [OPR-278]: Sets up tests

* [OPR-278]: Renames to mutuallyExclusive

* [OPR-278]: Fixes target reference for Condition.swift

* [OPR-278]: Adds ComposedCondition

This is useful (and uses automatic result injection) for easily getting the result of a previous condition.

* [OPR-278]: Refactors BlockCondition

* [OPR-278]: Refactors NegatedCondition

* [OPR-278]: Initial work on updating framework conditions

- [x] BlockCondition
- [x] TrueCondition
- [x] FalseCondition
- [x] ComposedCondition
- [x] SilentCondition
- [x] NegatedCondition
- [x] MutuallyExclusive
- [x] NoFailedDependenciesCondition

* [OPR-278]: Corrects a mistake with removeDependencies

We we actually just adding?!

* [OPR-278]: Mixes the composed direct dependencies

* [OPR-278]: Updates UserNotificationCondition

to subclass Condition

* [OPR-278]: Fixes some tests for iOS

* [OPR-278]: Refactors AuthorizedFor

* [OPR-278]: Refactors UserConfirmationCondition

* [OPR-278]: Refactors ReachabilityCondition

* [OPR-278]: Refactors AddressBookCondition

* [OPR-278]: Refactors ContactsCondition

* [OPR-278]: Refactors RemoteNotificationCondition

* [OPR-278]: Adds @available annotation.

Still not really sure how to accuractly mark API as deprecated inside a framework.

* [OPR-278]: Adds some tests for conditions

* [OPR-278]: Refactors condition error type

* [OPR-278]: Refactors True False Condition

* [OPR-278]: Cleans up Condition tests

* [OPR-278]: Fixes tests

* Fixes an incorrect Fix-It hint. (#302)

Fixes #299.

* Fixes notice severity logs (#303)

This was done during development and accidentally committed & pushed.

Fixes #300

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* CLLocationAuthorizationStatus bug (#306)

* Fixes bug: CLLocationManager prematurely responds with 'NotDetermined' auth status when asking for permission (#305)

* [OPR-306]: Adds test coverage for location capability

* Improved support for CloudKit Errors (#304)

* [OPR-290]: Adds Error associated type on CKOperationType

At the moment, this is just fulfilled by NSError, however it will allow us to offer specialized error responses depending on the operation.

* [OPR-290]: Updates test cloud kit operation.

* [OPR-290]: Adds missing file to Extension project

* [OPR-290]: Moves CloudKit sources into a folder

* [OPR-290]: Adds errors for Modify Zones & Records

* [OPR-290]: Adds custom error types for all CloudKit Operations

* [OPR-290]: Fixes error handling

Now every CKOperation has it's own bespoke error type.

* [OPR-290]: Tidies up AssociatedErrorType

* [OPR-290]: Fixes issues with AssociatedErrorType

* [OPR-290]: Adds error recovery to BatchedCloudKitOperation

* [OPR-290]: Exposes properties on OPRCKOperation

These are needed to configure operations inside error handlers.

* Removes unnecessary let statement (#310)

* Supports entersReaderIfAvailable configuration in WebpageOperation. (#312)

It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Refactors WebpageOperation to subclass ComposedOperation  (#315)

* Extended WebpageOperation with the ability to configure entersReaderIfAvailable at initialization.
It is possible now to show an SFSafariViewController with entersReaderIfAvailable set to false.

* Fixed a bug in WebpageOperation that that resulted in a retain cycle.

* - Refactored WebpageOperation as ComposedOperation to fix a retain cycle.
- Added Documentation.

* Fixed some typos in WebpageOperationTests.

* Implemented OpenInSafariOperation (#317)

* Implemented OpenInSafariOperation

* Made the displayControllerFrom non optional.

* Refactored OpenInSafariOperation.

* Fixes a bug removing dependencies on Condition (#309)

The issue is that ComposedCondition must remove the dependency on its composed condition too.

* Improves cancellation checks in main() (#319)

* [OPR-287]: Invokes observers in main

main() is called from start, and we already check that the operation is not cancelled. Therefore the check at the start of main is redundant. However, we don't check after the observers have been executed that the operation is cancelled.

* [OPR-287]: SwiftLint fixes.

* Exposes LogSeverity enum to Objective-C (#324)

* Remove Reachability from CloudKitOperation (#327)

* Ignore DS_Store

* Rip out reachability from CloudKitOperation

* [OPR-327]: Adds trailing closure syntax

* [OPR-327]: Adds TimeoutObserver

* [OPR-327]: Adds tests for timeout observer

* Override NSOperationQueue's mainQueue() (#330)

* Override NSOperationQueue's mainQueue() to return the main queue as an OperationQueue

* set maxConcurrentOperationCount to 1

* Adds missing functionality of UIAlertController to AlertOperation (#334)

* Added missing functionality of UIAlertController to AlertOperation.

* Annotated new functionality as public.

* Added test cases for new functionality.

* Made alert property of AlertOperation private.

* SwiftLint corrections.

* Added a test for preferredAction and fixed a but related to it.

* Made UserIntent enum objc accessbile (#341)

* Fixed network observer flickering (#338)

Added tests for multiple operations having network observer

* Supports error recovery in GroupOperation (#326)

* [OPR-326]: Adds Errors subtype in Operation

* [OPR-326]: Refactors GroupOperation willFinish API

* Revert "[OPR-326]: Adds Errors subtype in Operation"

This reverts commit 759f2ef3643cafbbda3544185234b8adeec474ef.

* [OPR-326]: WIP on group operation errors

* [OPR-326]: Fixes issues with retry operation tests

This is still not quite right.

* [OPR-326]: Allows RetryOperation to fully recover from errors

* [OPR-326]: Fixes issue with tests

* [OPR-326]: Tweaks ProfilerTest case

* [OPR-326]: Refactors ComposedOperation to just subclass GroupOperation

* [OPR-326]: Changes behaviour when a group produces an operation

* [OPR-326]: Logs when a GroupOperation successfully recovers from errors

* [OPR-326]: Logs when the next operation will be added

* [OPR-326]: Adds an immediate WaitStrategy

* [OPR-326]: Adds the operation to the log when attempting recovery

* [OPR-326]: Removes unneeded handling of child errors

* [OPR-326]: Allows the WaitStrategy to be defined at init

* [OPR-326]: Changes to support ComposedOperation

* [OPR-326]: Speeds up CloudKit tests with .Immediate wait strategy

* [OPR-326]: WIP on GroupOperation errors

* [OPR-326]: Fixes a bug where operation would not retry

In the case where there is a default handler, but no custom one set for the same error.

* [OPR-326]: Renames internal methods to be more Swifty

* [OPR-326]: Fixes errors - whoops.

* Always create CLLocationManager on the main queue (#321)

* [OPR-321]: Creates CLLocationManager on the main thread

Also makes it thread safe.

* [OPR-321]: Refactors capability to use guard & early exit.

* [OPR-321]: Creates CLLocationManager lazily on the main thread

Refactors UserLocationOperation to not use generic at the same time

* [OPR-321]: Fixes deadlock in creating CLLocationManager

* [OPR-321]: Refactors location operations for early exit

* [OPR-321]: Removes use of NSThread to check for main queue

* [OPR-321]: Fixes a mistake with the merge

* RepeatedOperation allows for configuration block to be reset by the payload (#348)

* [OPR-332]: Adds support for RetryOperation to provide a configuration block

* [OPR-332]: Removes commented out code

* [OPR-332]: Adds test to check that configure block is replaced

* Turns on Whole Module Optimization (#350)

Fixes #349

* Exposes the UIAlert for customization. (#351)

This also fixes a bug where it wasn't possible to set a barButtonItem on an ActionSheet while presenting as a popover.

* Fixes OperationQueue.delegate crash (SR-192) (#353)

* Add failing test

test__SR_192_OperationQueue_delegate_weak_var_thread_safety:
Tests race condition (and crash) involving OperationQueue’s weak
delegate var.

* Fix for OperationQueue.delegate crash (SR-192)

The underlying error is in Swift < 3.0, with concurrent reads of weak
properties.
See: https://bugs.swift.org/browse/SR-192

This change wraps the public delegate property with an NSLock. It also
sets it to nil first-thing in deinit to safely get rid of it.

* Simplistic support for CKLimitExceeded error handling (#294)

* [OPR-294]: Initial work adding built in support for CKLimitExceeded

* [OPR-294]: Rewrite with support for nil response

* [OPR-294]: Adds support for adding error handlers in bulk

* [OPR-294]: Fixes some comments

* [OPR-294]: Moves error handling to CloudKitError

* [OPR-294]: Adds BatchProcessErrorType

This is only needed for CKMarkNotificationsOperation

* [OPR-294]: Renames error protocols

* [OPR-294]: Adds support for batch processing

* [OPR-294]: Adds & Refactors for tests

* [OPR-294]: Minor tweaks

* [OPR-294]: Further tweaks

* [OPR-294]: Fixes error in bisect

* [OPR-294]: Adds API to replace the configure block outright

* [OPR-294]: Fixes failing tests

* [OPR-294]: Refactors stress tests condition

* Adds TaskOperation (#355)

* [OPR-313]: Adds TaskOperation

* [OPR-313]: Adds more test coverage

* [OPR-313]: Adds header documentation

* Vastly improves thread safety in Operations (#358)

* FIX: GroupOperation.operations contains the initial operations twice after beginning to execute.

Cause:
`GroupOperation.execute()` calls
`GroupOperation.addOperations(operations)`,
which then re-adds the operations back to the operations array.

Solution:
Added a private **`_addOperations()`** method to `GroupOperation` with
an additional flag to override this behavior - used in
`GroupOperation.execute()`.

`addOperations()` now calls this private implementation method.

* Operation.cancel(), Operation.finish() thread-safety

- `Operation.cancel()` now acquires the stateLock.
- `Operation.finish()` now acquires the stateLock.
- `stateLock` is now NSRecursiveLock().
- var `Operation.cancelled` is thread-safe.
- var `Operation.errors` is thread-safe.
- Explicitly send KVO notifications on NSOperation keyPaths
at the right times, avoiding lots of unnecessary notifications.
- Removed “State” KVO notifications. (Previously used to
trigger NSOperation isExecuting/isFinished notifications,
which are now triggered explicitly.)
- Added missing operationWillFinish() call to finish().
- operationDidFinish() now happens after the state has been
set to .Finished.
- `Operation.cancelWithError(errors)` now asserts if it’s
called while the Operation is finishing or finished.

* Operation.main() thread-safety, Operation.disableAutomaticFinishing

- `Operation.main()` acquires the stateLock when appropriate.
- `Operation.main()` manually sends the isExecuting NSOperation KVO
notifications when setting executing state.
- Added ability to disable Operation’s automatic calling of finish(),
with `init(disableAutomaticFinishing: Bool)`. (Defaults to existing
behavior - i.e. false.)

* GroupOperation improvements (finish and thread-safety)

- GroupOperation should only finish() after its children have finished.
- `GroupOperation._addOperations()` acquires the stateLock.
- `GroupOperation._addOperations()` cancels new child operations if the
GroupOperation is cancelled.

* RepeatedOperation.addNextOperation() must check cancelled

- RepeatedOperation.addNextOperation() now checks cancelled status.

* Added new cancellation-related tests

* Added new GroupOperation tests

* Added Operation KVO notification tests (for NSOperation keyPaths)

* Fixed spurious commit

* Handle more possible cases in Operation.main()

- Check that the Operation is not already executing.
- Check that the Operation has not been finished by a WillExecute
observer (either directly or as a side-effect of something else). Added
a test case that demonstrates this possibility.

* Add missing super.init() calls

* More GroupOperation cancelling fixes

- Changed the internal WillCancelObserver to a DidCancelObserver to
ensure that the cancelled state has been set prior to cancelling child
operations. (This ensures that side-effects of propagating cancellation
to child operations - like RepeatedOperation attempting to add the next
operation - can safely check the cancelled status.)

* GroupOperation should not call queue.cancelAllOperations()

Calling queue.cancelAllOperations() also cancels the
finishingOperation, which skips properly waiting until all other child
operations are finished.
Fixed test.

* Added willProduceOperation method to OperationQueueDelegate, fix GroupOperation handling of child-produced operations

Informational method only.
This enables GroupOperation to add child-produced operations to its
internal operations array, and thus cancel them when the GroupOperation
is cancelled.
Added tests.

* GroupOperation should ignore queue delegate calls from other queues

Added missing guard at the top of the queue delegate methods in
GroupOperation.
Added test to illustrate simple failure case this change covers.

* Refactor lock usage in Operation.finish() and .cancel()

- Refactor lock usage to hold the stateLock only when necessary or
manipulating internal state, and *not* when sending KVO notifications
or notifying observers.
- Improvements and fixes to KVO Notification Tests.

* Further refactor lock usage in Operation (.execute())

- Refactor lock usage in `Operation.execute()` to hold the stateLock
only when necessary or manipulating internal state, and *not* when
sending KVO notifications or notifying observers.

* Add a number of new StressTests that previously failed/crashed

New tests have support for a `batches` parameter.

* Initial fix for Logging-related crashes (thread-safety issues)

- `Operation._log` must not be a lazy var.
From the Swift book:
> If a property marked with the lazy modifier is accessed by multiple
threads simultaneously and the property has not yet been initialized,
there is no guarantee that the property will be initialized only once.
For more: https://bugs.swift.org/browse/SR-1042
- `Operation.log` must be thread-safe.
Initial fix involves capturing a copy of the logging context (settings
+ operationName) at the time of access of `Operation.log`, returning
that captured context as a LoggerType, and surrounding access of .log
with a Read/Write lock.
- LogManager properties must be thread-safe.

* Refactor lock usage in GroupOperation

- var `GroupOperation.operations` is now thread-safe.
- GroupOperation now utilizes a private CanFinishOperation to safely
transition to executing the finishingOperation. (See code & comments.)
- GroupOperation now has its own lock that is used sparingly to protect
finishing state.
- `GroupOperation._addOperation()` now utilizes the groupFinishLock.

* Minor tweaks

- `Operation.stateLock` should remain private.

* SwiftLint clean-up

* Refactor lock usage in GroupOperation

- Refactor lock usage to hold the groupFinishLock only when necessary
or manipulating internal state, and *not* when sending KVO
notifications, notifying observers, or changing additionalOperations
state (which might send KVO notifications or notify observers).

* Clean-up

- Remove asserts from `Operation.cancelWithErrors()`. Operation
cancellation methods should be safe to call at any time without
asserting.
- Other minor tweaks.

* Clean-up

- Moved NSOperationKeyPaths to extension NSOperation.KeyPath enum.

* Clean-up

* Clean-up

- Remove unneeded `self.`

* Clean-up

* Clarify docs & re-formatting

* Add new inits to Queue (NSQualityOfService and qos_class_t)

Per code review.

* Clean-up of GroupOperation

* Make Operation.cancel() final (#359)

Per discussion in:
https://github.com/danthorpe/Operations/pull/293
https://github.com/danthorpe/Operations/pull/358#discussion_r70167086

* Supports composing simple types for Result injection (#362)

* [OPR-362]: Linting whitespace changes

* [OPR-362]: Adds support for composing “executors” inside a final Operation type

This is a convenience class to aid adoption of _Operations_ without requiring types to subclass Operation. Instead, classes only need to conform to Executor, and then can use the Execute class, which support cancellation, and result injection.

* Supports async executors (#363)

* Fixes a finished log message (#365)

The "Operation Did finish" log was using the opposite logic for error logging

* Group's internal queue is non-public; exposes appropriate properties (#361)

* Make GroupOperation's internal queue non-public, and expose appropriate properties on GroupOperation

- Make GroupOperation.queue ~private~internal. (So it can be tested.)
- Expose appropriate run-time editable properties of the
GroupOperation’s queue as properties on GroupOperation.

Specifically:
- public var maxConcurrentOperationCount: Int
- public var suspended: Bool
- public var qualityOfService: NSQualityOfService

- Add tests.

* [OPR-361]: Support underlying queue in Group

* GatedOperation cancels composed operation when gate is closed (#377)

* [OPR-377]: Adds early exit with guard

* [OPR-377]: Refactors GatedOperation execute

* [OPR-377]: Fixes typo

* Result Injection protocol improvements (#378)

* [OPR-378]: Updates result injection APIs

* [OPR-378]: Adds unit tests

* [OPR-378]: Removes unnecessary weak self capture

* Fixes dependency gets added twice if it's direct & indirect (#379)

* [OPR-379]: Adds a failing unit test

* [OPR-379]: Makes indirect dependencies unique set

* Removes TaskOperation from non-macOS platforms (#382)

* Removes AlertPresentation mutual exclusion (#383)

* Adds IgnoredCondition (#385)

* Filters out indirect dependencies which have already been added (#386)

* Fixes issues with ignored condition (#390)

This means that conditions which are ignored mean that the attached operation does not execute, but also does not finish with an error. It’s a nicer use case than GatedOperation. This works by checking the OperationResult for .Ignored and then only cancel() instead of cancelWithError().

Note that if the operation also has .Failed(errors) condition result, in addition to .Ignored then the operation will still not run but this time with errors (as expected).

Note that if the operation also has .Satisfied condition result, in addition to .Ignored then the operation will not run, but without errors.

* NoFailedDependenciesCondition supports ignoring cancelled operations (#397)

Fixes #396. 

* [OPR-396]: NoFailedDependenciesCondition support ignoring cancels

* [OPR-396]: Refactors initializer

* [OPR-396]: Renames the condition

* Ability to add configuration to the next (retried) CloudKitOperation (#395)

* Adds CloudKitOperation.setPrepareForRetryHandler()

* Adds BatchedCloudKitOperation.setPrepareForNextOperationHandler()

* Clean-up

* Updated new APIs, added documentation

-Simpler, cleaner, better APIs.
-Added documentation and examples for the new APIs.

* Add documentation for BatchedCloudKitOperation.

* Unschedules dispatch queue when stopping notifier (#360) (#410)

* Makes Operation's conditions property public accessible (#412)

* Fixes crash when accessing deallocated self (#416) (#417)

* fix: Crash when self was destroyed, issue #416

* ConditionOperation "executed before operation set" assertion fix (#420)

Fixes #415. 

* [OPR-415]: Replace the assertionFailure if Condition.operation == nil with a log statement

Previously, if a ConditionOperation’s `operation` var was nil when it executed, the following assertionFailure occurred:
`assertionFailure("ConditionOperation executed before operation set.”)`

However, this could also occur if the weak `operation` var was set, but the operation cancelled/finished/went away before the ConditionOperation was executed.

Thus the `assertionFailure` has been replaced with a log statement.

* [OPR-415]: Add a new StressTest case that covers some ConditionOperation race conditions and thread safety

* Fix for NetworkObserver Timer not properly handling cancellation (#344) (#421)

The value of `isCancelled` was being captured by the `dispatch_after` block at `init`, and thus the check always passed and the Timer's handler was always invoked (even if `Timer.cancel()` was called).
This was resolved by making `Timer` a reference type.

* Flickering / robustness fixes for NetworkObserver / NetworkIndicatorController (#422)

Previously, NetworkIndicatorController would not ensure that any existing Timer was cancelled when another observed (network) operation started prior to the Timer firing.

This resulted in potential flickering of the network activity indicator (or inaccurate state, where it disappeared too quickly) in some cases.

* Tests may randomly fail due to a bug in expectation fulfillment (#419)

Fixed a number of places where `XCTestExpectation.fulfill()` was not being called on the main thread:

* Main-thread expectation fulfillment in OperationTests.addCompletionBlockToTestOperation()

* Main-thread expectation fulfillment in BasicTests.swift

* Main-thread expectation fulfillment in StressTests.swift

* Increase StressTest timeout for CI

* Recode `test__completion_blocks` StressTest to use a dispatch_group instead of an expectation for every TestOperation.

* Main-thread expectation fulfillment in BlockConditionTests.swift

* Main-thread expectation fulfillment in CloudCapabilitiesTests.swift

* Main-thread expectation fulfillment in GroupOperationTests.swift

* Main-thread expectation fulfillment in NegatedConditionTests.swift

* Main-thread expectation fulfillment in RemoteNotificationConditionTests.swift

* Adjust StressTest to try to fix CI

`test__completion_blocks` used a `BlockOperation { }`, which (currently) defaults to dispatching the block to the main queue. Changing this to a `BlockOperation(block:)`, which uses the queue on which the BlockOperation is executed.

* Enhance internal testing `waitForOperation` methods to support an optional timeout parameter

* Increase timeouts for some StressTests to fix CI

* Fix `Operation.cancelWithErrors()` thread-safety (#423)

* Fix `Operation.cancelWithErrors()` thread-safety

The potential existed for `Operation.cancelWithErrors()` to result in an assert due to an illegal state transition from `.Pending` => `.Finishing` while `cancelled == false`.

This was fixed by ensuring that the additional cancellation errors are appended to the `_internalErrors` array at the same time that `_cancelled` is set to `true` (i.e. within the same acquisition of the lock).

* Added StressTest of `Operation.cancelWithErrors()`

* Rewrite `test__dependent_operations_always_run` to use a dispatch_group instead of an expectation for every BlockOperation

This speeds up the test, and resolves possible main-thread-stall issues running the test.

* Improve CI pipeline for Swift 2.2 (#427)

* [OPR-427]: Adds iOS-Simulator metadata to pipeline

* [OPR-427]: Refactors pipeline to set Swift …
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants