Marino Faggiana 6 жил өмнө
parent
commit
ebb2e5ea9a
100 өөрчлөгдсөн 13618 нэмэгдсэн , 3 устгасан
  1. 1 0
      Cartfile
  2. 1 0
      Cartfile.resolved
  3. 6 0
      Carthage/Checkouts/realm-cocoa/.dir-locals.el
  4. 2 0
      Carthage/Checkouts/realm-cocoa/.gitattributes
  5. 113 0
      Carthage/Checkouts/realm-cocoa/.gitignore
  6. 4 0
      Carthage/Checkouts/realm-cocoa/.gitmodules
  7. 246 0
      Carthage/Checkouts/realm-cocoa/.jenkins.yml
  8. 34 0
      Carthage/Checkouts/realm-cocoa/.swiftlint.yml
  9. 39 0
      Carthage/Checkouts/realm-cocoa/.travis.yml
  10. 62 3
      Carthage/Checkouts/realm-cocoa/CHANGELOG.md
  11. 64 0
      Carthage/Checkouts/realm-cocoa/CONTRIBUTING.md
  12. 61 0
      Carthage/Checkouts/realm-cocoa/Configuration/Base.xcconfig
  13. 12 0
      Carthage/Checkouts/realm-cocoa/Configuration/Debug.xcconfig
  14. 11 0
      Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm iOS static.xcconfig
  15. 37 0
      Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm.xcconfig
  16. 6 0
      Carthage/Checkouts/realm-cocoa/Configuration/Realm/Tests iOS static.xcconfig
  17. 22 0
      Carthage/Checkouts/realm-cocoa/Configuration/Realm/Tests.xcconfig
  18. 12 0
      Carthage/Checkouts/realm-cocoa/Configuration/RealmSwift/RealmSwift.xcconfig
  19. 18 0
      Carthage/Checkouts/realm-cocoa/Configuration/RealmSwift/Tests.xcconfig
  20. 7 0
      Carthage/Checkouts/realm-cocoa/Configuration/Release.xcconfig
  21. 21 0
      Carthage/Checkouts/realm-cocoa/Configuration/TestHost.xcconfig
  22. 15 0
      Carthage/Checkouts/realm-cocoa/Configuration/object-server-config.yml
  23. 280 0
      Carthage/Checkouts/realm-cocoa/Jenkinsfile.releasability
  24. 0 0
      Carthage/Checkouts/realm-cocoa/LICENSE
  25. 113 0
      Carthage/Checkouts/realm-cocoa/Realm.podspec
  26. 0 0
      Carthage/Checkouts/realm-cocoa/Realm/NSError+RLMSync.h
  27. 43 0
      Carthage/Checkouts/realm-cocoa/Realm/NSError+RLMSync.m
  28. 20 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/Object-Server-Tests-Bridging-Header.h
  29. 22 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/ObjectServerTests-Info.plist
  30. 58 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMAncillaryObjectServerTests.m
  31. 2038 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm
  32. 1196 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMPermissionsAPITests.m
  33. 622 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMPermissionsTests.mm
  34. 25 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.h
  35. 44 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.m
  36. 25 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncSessionRefreshHandle+ObjectServerTests.h
  37. 66 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncSessionRefreshHandle+ObjectServerTests.m
  38. 145 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.h
  39. 529 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.mm
  40. 34 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncUser+ObjectServerTests.h
  41. 76 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncUser+ObjectServerTests.mm
  42. 26 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMTestUtils.h
  43. 53 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMTestUtils.m
  44. 627 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftObjectServerTests.swift
  45. 306 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftPermissionsAPITests.swift
  46. 253 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftPermissionsTests.swift
  47. 168 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftSyncTestCase.swift
  48. 27 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/ca-key.pem
  49. 19 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/ca.pem
  50. 27 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-cert-key.pem
  51. 19 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-cert.pem
  52. 19 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-other-cert.pem
  53. BIN
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-other.cer
  54. BIN
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost.cer
  55. 19 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/not-localhost-cert.pem
  56. BIN
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/not-localhost.cer
  57. 80 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/test-ros-server.js
  58. 1 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.dockerignore
  59. 32 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitignore
  60. 3 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitmodules
  61. 67 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CodeCoverage.cmake
  62. 124 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake
  63. 425 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake
  64. 38 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/Sanitizers.cmake
  65. 1703 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/android.toolchain.cmake
  66. 43 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt
  67. 23 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile
  68. 189 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile
  69. 269 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/LICENSE
  70. 4 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list
  71. 11 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/.gitattributes
  72. 29 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/.gitignore
  73. 268 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/.travis.yml
  74. 284 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/CMakeLists.txt
  75. 46 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/CODE_OF_CONDUCT.md
  76. 23 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/LICENSE.txt
  77. 61 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/appveyor.yml
  78. BIN
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/catch-hand-icon.png
  79. BIN
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/catch-icon-tiny.png
  80. BIN
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/catch-logo-small.png
  81. 19 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/conanfile.py
  82. 185 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/contrib/ParseAndAddCatchTests.cmake
  83. 141 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/assertions.md
  84. 143 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/build-systems.md
  85. 293 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/command-line.md
  86. 16 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/commercial-users.md
  87. 140 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/configuration.md
  88. 60 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/contributing.md
  89. 73 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/event-listeners.md
  90. 131 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/limitations.md
  91. 82 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/logging.md
  92. 104 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/matchers.md
  93. 86 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/opensource-users.md
  94. 72 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/own-main.md
  95. 365 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/release-notes.md
  96. 63 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/release-process.md
  97. 45 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/reporters.md
  98. 64 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/slow-compiles.md
  99. 88 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/test-cases-and-sections.md
  100. 32 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/test-fixtures.md

+ 1 - 0
Cartfile

@@ -8,3 +8,4 @@ github "ealeksandrov/EAIntroView"
 github "calimarkus/JDStatusBarNotification"
 github "ChangbaDevs/KTVHTTPCache" ~> 1.1.7
 github "jdg/MBProgressHUD" ~> 1.1.0
+github "realm/realm-cocoa"

+ 1 - 0
Cartfile.resolved

@@ -7,5 +7,6 @@ github "ealeksandrov/EAIntroView" "2.12.0"
 github "ealeksandrov/EARestrictedScrollView" "1.1.0"
 github "jdg/MBProgressHUD" "1.1.0"
 github "kishikawakatsumi/UICKeyChainStore" "v2.1.2"
+github "realm/realm-cocoa" "v3.11.1"
 github "sgr-ksmt/PDFGenerator" "2.1"
 github "tilltue/TLPhotoPicker" "1.7.7"

+ 6 - 0
Carthage/Checkouts/realm-cocoa/.dir-locals.el

@@ -0,0 +1,6 @@
+;; Project specific Emacs settings
+((nil . ((c-basic-offset . 4)
+         (indent-tabs-mode . nil)
+         (c-file-style . "ellemtel")
+         (c-file-offsets . ((innamespace . 0)))
+         (show-trailing-whitespace . t))))

+ 2 - 0
Carthage/Checkouts/realm-cocoa/.gitattributes

@@ -0,0 +1,2 @@
+CHANGELOG.md merge=union
+*.mm linguist-language=Objective-C

+ 113 - 0
Carthage/Checkouts/realm-cocoa/.gitignore

@@ -0,0 +1,113 @@
+*~
+.DS_Store
+
+# Merge files
+*.orig
+
+# Binaries
+*.dylib
+*.a
+*.o
+*.d
+*.libdeps
+*.zip
+*.realm
+*.realm.lock
+
+# core
+core
+core-*
+
+# sync
+sync*
+
+# sh build.sh config
+/Realm/config.mk
+
+# sh build.sh test
+build/
+
+# sh build.sh build-iphone
+/iphone-lib/include
+
+# sh build.sh ios-framework
+# sh build.sh package-examples
+*/Realm.framework
+Realm.framework
+
+# sh build.sh build-cocoa
+bin
+
+# sh build.sh cocoapods-setup
+/include
+
+# sh build.sh docs
+/docs/objc_output
+/docs/swift_output
+/Realm/RLMPlatform.h
+
+# XCode
+*.bak
+xcuserdata/
+project.xcworkspace
+*.xccheckout
+DerivedData
+
+# AppCode
+.idea/
+*.iml
+# backup and crash files
+*.swp
+
+# xcpretty
+build.log
+
+# ruby
+*.gem
+*.rbc
+/.config
+/coverage/
+/InstalledFiles
+/pkg/
+/spec/reports/
+/test/tmp/
+/test/version_tmp/
+/tmp/
+
+## Specific to RubyMotion:
+.dat*
+.repl_history
+build/
+
+## Documentation cache and generated files:
+/.yardoc/
+/_yardoc/
+/doc/
+/rdoc/
+
+## Environment normalisation:
+/.bundle/
+/lib/bundler/man/
+
+# for a library or gem, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# Gemfile.lock
+# .ruby-version
+# .ruby-gemset
+
+# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
+.rvmrc
+
+## Carthage
+# Cartfiles are ignored because they're generated on demand in the installation examples
+Cartfile
+Carthage
+
+## Swift Version
+SwiftVersion.swift
+
+examples/ios/objc/Draw/Constants.h
+
+## Sync testing
+test-ros-instance
+Realm/ObjectServerTests/node_modules

+ 4 - 0
Carthage/Checkouts/realm-cocoa/.gitmodules

@@ -0,0 +1,4 @@
+[submodule "Realm/ObjectStore"]
+	path = Realm/ObjectStore
+	url = https://github.com/realm/realm-object-store.git
+	branch = master

+ 246 - 0
Carthage/Checkouts/realm-cocoa/.jenkins.yml

@@ -0,0 +1,246 @@
+# Yaml Axis Plugin
+# https://wiki.jenkins-ci.org/display/JENKINS/Yaml+Axis+Plugin
+
+xcode_version:
+  - 9.2
+  - 9.3
+  - 9.4
+  - 10.0
+target:
+  - osx
+  - docs
+  - ios-static
+  - ios-dynamic
+  - ios-swift
+  - osx-swift
+  - watchos
+  - cocoapods
+  - swiftlint
+  - tvos
+  - osx-encryption
+  - osx-object-server
+
+  - ios-device-objc-ios8
+  # - ios-device-swift-ios8
+  - ios-device-objc-ios10
+  # - ios-device-swift-ios10
+  - tvos-device
+configuration:
+  - Debug
+  - Release
+
+# Combinations have to be excluded in a way that's hard to read.
+# This table shows which jobs will run:
+
+# +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+# | Configuration Matrix | osx | docs | ios-static | ios-dynamic | ios-swift | osx-swift | watchos | cocoapods | swiftlint | tvos | osx-encryption | osx-object-server | ios-device-objc-ios8 | ios-device-swift-ios8 | ios-device-objc-ios10 | ios-device-swift-ios10 | tvos-device |
+# | -------------------- | --- | ---- | ---------- | ----------- | --------- | --------- | ------- | --------- | --------- | ---- | -------------- | ----------------- | -------------------- | --------------------- | --------------------- | ---------------------- | ----------- |
+# | 9.2   | Debug        | X   |      | X          |             |           |           |         |           |           |      |                |                   |                      |                       |                       |                        |             |
+# | 9.2   | Release      | X   |      | X          | X           | X         | X         | X       | X         |           | X    | X              | X                 | X                    |                       | X                     |                        |             |
+# | -------------------- | --- | ---- | ---------- | ----------- | --------- | --------- | ------- | --------- | --------- | ---- | -------------- | ----------------- | -------------------- | --------------------- | --------------------- | ---------------------- | ----------- |
+# | 9.3   | Debug        | X   |      |            |             |           |           |         |           |           |      |                |                   |                      |                       |                       |                        |             |
+# | 9.3   | Release      | X   |      | X          | X           | X         | X         | X       | X         |           | X    |                |                   |                      |                       |                       |                        |             |
+# | -------------------- | --- | ---- | ---------- | ----------- | --------- | --------- | ------- | --------- | --------- | ---- | -------------- | ----------------- | -------------------- | --------------------- | --------------------- | ---------------------- | ----------- |
+# | 9.4   | Debug        | X   |      |            |             |           |           |         |           |           |      |                |                   |                      |                       |                       |                        |             |
+# | 9.4   | Release      | X   |      | X          | X           | X         | X         | X       | X         |           | X    |                |                   |                      |                       |                       |                        |             |
+# | -------------------- | --- | ---- | ---------- | ----------- | --------- | --------- | ------- | --------- | --------- | ---- | -------------- | ----------------- | -------------------- | --------------------- | --------------------- | ---------------------- | ----------- |
+# | 10.0  | Debug        | X   |      |            | X           | X         | X         | X       |           |           | X    |                |                   |                      |                       |                       |                        |             |
+# | 10.0  | Release      | X   | X    | X          | X           | X         | X         | X       | X         | X         | X    | X              | X                 |                      |                       | X                     |                        | X           |
+# +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+exclude:
+  ################
+  # docs
+  ################
+  # Just run on 10.0 Release
+  - xcode_version: 9.2
+    target: docs
+  - xcode_version: 9.3
+    target: docs
+  - xcode_version: 9.4
+    target: docs
+  - target: docs
+    configuration: Debug
+
+  ################
+  # ios-static
+  ################
+  # Skip on 9.3/9.4/10.0 Debug
+  - xcode_version: 9.3
+    target: ios-static
+    configuration: Debug
+  - xcode_version: 9.4
+    target: ios-static
+    configuration: Debug
+  - xcode_version: 10.0
+    target: ios-static
+    configuration: Debug
+
+  ################
+  # ios-dynamic
+  ################
+  # Skip on 9.2/9.3/0.2 Debug
+  - xcode_version: 9.2
+    target: ios-dynamic
+    configuration: Debug
+  - xcode_version: 9.3
+    target: ios-dynamic
+    configuration: Debug
+  - xcode_version: 9.4
+    target: ios-dynamic
+    configuration: Debug
+
+  ################
+  # ios-swift
+  ################
+  # Skip 9.2/9.3/9.4 Debug
+  - xcode_version: 9.2
+    target: ios-swift
+    configuration: Debug
+  - xcode_version: 9.3
+    target: ios-swift
+    configuration: Debug
+  - xcode_version: 9.4
+    target: ios-swift
+    configuration: Debug
+
+  ################
+  # osx-swift
+  ################
+  # Skip 9.2/9.3/9.4 Debug
+  - xcode_version: 9.2
+    target: osx-swift
+    configuration: Debug
+  - xcode_version: 9.3
+    target: osx-swift
+    configuration: Debug
+  - xcode_version: 9.4
+    target: osx-swift
+    configuration: Debug
+
+  ################
+  # watchos
+  ################
+  # Skip 9.2/9.3/9.4 Debug
+  - xcode_version: 9.2
+    target: watchos
+    configuration: Debug
+  - xcode_version: 9.3
+    target: watchos
+    configuration: Debug
+  - xcode_version: 9.4
+    target: watchos
+    configuration: Debug
+
+  ################
+  # cocoapods
+  ################
+  # Skip Debug
+  - target: cocoapods
+    configuration: Debug
+
+  ################
+  # swiftlint
+  ################
+  # Just run on 10.0 Release
+  - xcode_version: 9.2
+    target: swiftlint
+  - xcode_version: 9.3
+    target: swiftlint
+  - xcode_version: 9.4
+    target: swiftlint
+  - target: swiftlint
+    configuration: Debug
+
+  ################
+  # tvos
+  ################
+  # Skip 9.2/9.3/9.4 Debug
+  - xcode_version: 9.2
+    target: tvos
+    configuration: Debug
+  - xcode_version: 9.3
+    target: tvos
+    configuration: Debug
+  - xcode_version: 9.4
+    target: tvos
+    configuration: Debug
+
+  ################
+  # osx-encryption
+  ################
+  # Just run on 9.2/10.0 Release
+  - xcode_version: 9.3
+    target: osx-encryption
+  - xcode_version: 9.4
+    target: osx-encryption
+  - target: osx-encryption
+    configuration: Debug
+
+  ################
+  # osx-object-server
+  ################
+  # Just run on 9.2/10.0 Release
+  - xcode_version: 9.3
+    target: osx-object-server
+  - xcode_version: 9.4
+    target: osx-object-server
+  - target: osx-object-server
+    configuration: Debug
+
+  ################
+  # ios-device-objc-ios8
+  ################
+  # Just run on 9.2/9.4 Release
+  - xcode_version: 9.3
+    target: ios-device-objc-ios8
+  - xcode_version: 10.0
+    target: ios-device-objc-ios8
+  - target: ios-device-objc-ios8
+    configuration: Debug
+
+  ################
+  # ios-device-swift-ios8
+  ################
+  # Just run on 9.2/10.0 Release
+  - xcode_version: 9.3
+    target: ios-device-swift-ios8
+  - xcode_version: 9.4
+    target: ios-device-swift-ios8
+  - target: ios-device-swift-ios8
+    configuration: Debug
+
+  ################
+  # ios-device-objc-ios10
+  ################
+  # Just run on 9.2/10.0 Release
+  - xcode_version: 9.3
+    target: ios-device-objc-ios10
+  - xcode_version: 9.4
+    target: ios-device-objc-ios10
+  - target: ios-device-objc-ios10
+    configuration: Debug
+
+  ################
+  # ios-device-swift-ios10
+  ################
+  # Just run on 9.2/10.0 Release
+  - xcode_version: 9.3
+    target: ios-device-swift-ios10
+  - xcode_version: 9.4
+    target: ios-device-swift-ios10
+  - target: ios-device-swift-ios10
+    configuration: Debug
+
+  ################
+  # tvos-device
+  ################
+  # Just run on 10.0 Release
+  - xcode_version: 9.2
+    target: tvos-device
+  - xcode_version: 9.3
+    target: tvos-device
+  - xcode_version: 9.4
+    target: tvos-device
+  - target: tvos-device
+    configuration: Debug

+ 34 - 0
Carthage/Checkouts/realm-cocoa/.swiftlint.yml

@@ -0,0 +1,34 @@
+included:
+  - Realm/ObjectServerTests
+  - RealmSwift
+  - Realm/Swift
+identifier_name:
+  min_length: # not possible to disable this partial rule, so set it to zero
+    warning: 0
+    error: 0
+  excluded:
+    - _nilValue()
+    - _nsError
+    - _nsErrorDomain
+    - _observe(_:)
+    - _realmColumnNames()
+    - _realmObjectName()
+    - _rlmArray()
+    - id
+    - pk
+    - to
+disabled_rules:
+  - block_based_kvo
+  - file_length
+  - force_cast
+  - force_try
+  - function_body_length
+  - line_length
+  - nesting
+  - syntactic_sugar
+  - todo
+  - type_body_length
+  - vertical_whitespace
+  # swiftlint complains about superfluous disable commands when the violation
+  # occurs in an inactive #if and doesn't support conditionally disabling it
+  - cyclomatic_complexity

+ 39 - 0
Carthage/Checkouts/realm-cocoa/.travis.yml

@@ -0,0 +1,39 @@
+language: objective-c
+osx_image: xcode9.3
+branches:
+  only: master
+script: placeholder # workaround for https://github.com/travis-ci/travis-ci/issues/4681
+matrix:
+  include:
+    - script: ./build.sh verify-swiftlint
+      env: JOB=verify-swiftlint
+      before_install: brew update; brew outdated swiftlint || brew upgrade swiftlint
+    - script: ./build.sh verify-docs
+      env: JOB=verify-docs
+      before_install: gem install jazzy
+    - script: ./build.sh verify-osx-swift
+      env: JOB=verify-osx-swift
+    - script: ./build.sh test-tvos
+      env: JOB=test-tvos
+    - script: ./build.sh verify-osx
+      env: JOB=verify-osx
+    # These jobs are disabled pending work to fix their configuration when running
+    # on the Travis machines, without also breaking our internal CI system.
+    # - script: ./build.sh test-ios-swift
+    #   env: JOB=test-ios-swift
+    # - script: ./build.sh test-ios-static
+    #   env: JOB=test-ios-static
+
+    ############################################################################
+    # These jobs pass but are disabled because they occasionally make Travis
+    # exceed its maximum of 50 minutes in aggregate for matrix builds.
+    ############################################################################
+
+    # - script: ./build.sh verify-ios-dynamic
+    #   env: JOB=verify-ios-dynamic
+    # - script: ./build.sh verify-osx-encryption
+    #   env: JOB=verify-osx-encryption
+  exclude:
+    - script: placeholder # workaround for https://github.com/travis-ci/travis-ci/issues/4681
+notifications:
+  email: false

+ 62 - 3
Libraries external/Realm/Realm.framework/CHANGELOG.md → Carthage/Checkouts/realm-cocoa/CHANGELOG.md

@@ -1,3 +1,62 @@
+3.11.1 Release notes (2018-10-19)
+=============================================================
+
+### Fixed
+
+* Fix `SyncUser.requestEmailConfirmation` not triggering the email confirmation
+  flow on ROS. (PR [#5953](https://github.com/realm/realm-cocoa/pull/5953), since 3.5.0)
+* Add some missing validation in the getters and setters of properties on
+  managed Realm objects, which would sometimes result in an application
+  crashing with a segfault rather than the appropriate exception being thrown
+  when trying to write to an object which has been deleted.
+  (PR [#5952](https://github.com/realm/realm-cocoa/pull/5952), since 2.8.0)
+
+### Compatibility
+
+* File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.11.0 or later.
+
+3.11.0 Release notes (2018-10-04)
+=============================================================
+
+### Enhancements
+* Reduce memory usage when integrating synchronized changes sent by ROS.
+* Devices will now report download progress for read-only Realms, allowing the
+  server to compact Realms more aggressively and reducing the amount of
+  server-side storage space required.
+
+### Fixed
+* Fix a crash when adding an object with a non-`@objc` `String?` property which
+  has not been explicitly ignored to a Realm on watchOS 5 (and possibly other
+  platforms when building with Xcode 10).
+  (Issue: [5929](https://github.com/realm/realm-cocoa/issues/5929)).
+* Fix some merge algorithm bugs which could result in `BadChangesetError`
+  being thrown when integrating changes sent by the server.
+
+### Compatibility
+* File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
+* **NOTE!!!
+  You will need to upgrade your Realm Object Server to at least version 3.11.0
+  or use [Realm Cloud](https://cloud.realm.io).
+  If you try to connect to a ROS v3.10.x or previous, you will see an error
+  like `Wrong protocol version in Sync HTTP request, client protocol version = 25,
+  server protocol version = 24`.**
+
+### Internal
+* Update to Sync 3.12.2.
+
+3.10.0 Release notes (2018-09-19)
+=============================================================
+
+Prebuilt binaries are now built for Xcode 9.2, 9.3, 9.4 and 10.0.
+
+Older versions of Xcode are still supported when building from source, but you
+should be migrating to at least Xcode 9.2 as soon as possible.
+
+### Enhancements
+
+* Add support for Watch Series 4 by adding an arm64_32 slice to the library.
+
 3.9.0 Release notes (2018-09-10)
 =============================================================
 
@@ -121,7 +180,7 @@ details.
 ### Deprecations
 
 * `+[RLMSyncConfiguration initWithUser] has been deprecated in favor of `-[RLMSyncUser configurationWithURL:url].
-* `+[RLMSyncConfiguration automaticConfiguration] has been deprecated in favor of `-[RLMSyncUser configuration]. 
+* `+[RLMSyncConfiguration automaticConfiguration] has been deprecated in favor of `-[RLMSyncUser configuration].
 * `+[RLMSyncConfiguration automaticConfigurationForUser] has been deprecated in favor of `-[RLMSyncUser configuration].
 * `-[RLMSyncConfiguration isPartial] has been deprecated in favor of `-[RLMSyncConfiguration fullSynchronization]`.
 
@@ -2100,7 +2159,7 @@ Prebuilt frameworks are now built with Xcode 7.3.
   `RLMRealm`/`Realm` instances.
 * Fail with `RLMErrorFileNotFound` instead of the more generic `RLMErrorFileAccess`,
   if no file was found when a realm was opened as read-only or if the directory part
-  of the specified path was not found when a copy should be written. 
+  of the specified path was not found when a copy should be written.
 * Greatly improve performance when deleting objects with one or more indexed
   properties.
 * Indexing `BOOL`/`Bool` and `NSDate` properties are now supported.
@@ -2135,7 +2194,7 @@ Prebuilt frameworks are now built with Xcode 7.3.
 
 * Support for tvOS.
 * Support for building Realm Swift from source when using Carthage.
-* The block parameter of `-[RLMRealm transactionWithBlock:]`/`Realm.write(_:)` is 
+* The block parameter of `-[RLMRealm transactionWithBlock:]`/`Realm.write(_:)` is
   now marked as `__attribute__((noescape))`/`@noescape`.
 * Many forms of queries with key paths on both sides of the comparison operator
   are now supported.

+ 64 - 0
Carthage/Checkouts/realm-cocoa/CONTRIBUTING.md

@@ -0,0 +1,64 @@
+# Contributing
+
+## Filing Issues
+
+Whether you find a bug, typo or an API call that could be clarified, please [file an issue](https://github.com/realm/realm-cocoa/issues) on our GitHub repository.
+
+When filing an issue, please provide as much of the following information as possible in order to help others fix it:
+
+1. **Goals**
+2. **Expected results**
+3. **Actual results**
+4. **Steps to reproduce**
+5. **Code sample that highlights the issue** (full Xcode projects that we can compile ourselves are ideal)
+6. **Version of Realm / Xcode / OSX**
+7. **Version of involved dependency manager (CocoaPods / Carthage)**
+
+If you'd like to send us sensitive sample code to help troubleshoot your issue, you can email <help@realm.io> directly.
+
+### Speeding things up :runner:
+
+You may just copy this little script below and run it directly in your project directory in **Terminal.app**. It will take of compiling a list of relevant data as described in points 6. and 7. in the list above. It copies the list directly to your pasteboard for your convenience, so you can attach it easily when filing a new issue without having to worry about formatting and we may help you faster because we don't have to ask for particular details of your local setup first.
+
+```shell
+echo "\`\`\`
+$(sw_vers)
+
+$(xcode-select -p)
+$(xcodebuild -version)
+
+$(which pod && pod --version)
+$(test -e Podfile.lock && cat Podfile.lock | sed -nE 's/^  - (Realm(Swift)? [^:]*):?/\1/p' || echo "(not in use here)")
+
+$(which bash && bash -version | head -n1)
+
+$(which carthage && carthage version)
+$(test -e Cartfile.resolved && cat Cartfile.resolved | grep --color=no realm || echo "(not in use here)")
+
+$(which git && git --version)
+\`\`\`" | tee /dev/tty | pbcopy
+```
+
+## Contributing Enhancements
+
+We love contributions to Realm! If you'd like to contribute code, documentation, or any other improvements, please [file a Pull Request](https://github.com/realm/realm-cocoa/pulls) on our GitHub repository. Make sure to accept our [CLA](#cla) and to follow our [style guide](https://github.com/realm/realm-cocoa/wiki/Objective-C-Style-Guide).
+
+### Commit Messages
+
+Although we don’t enforce a strict format for commit messages, we prefer that you follow the guidelines below, which are common among open source projects. Following these guidelines helps with the review process, searching commit logs and documentation of implementation details. At a high level, the contents of the commit message should convey the rationale of the change, without delving into much detail. For example, `setter names were not set right` leaves the reviewer wondering about which bits and why they weren’t “right”. In contrast, `[RLMProperty] Correctly capitalize setterName` conveys almost all there is to the change.
+
+Below are some guidelines about the format of the commit message itself:
+
+* Separate the commit message into a single-line title and a separate body that describes the change.
+* Make the title concise to be easily read within a commit log.
+* Make the body concise, while including the complete reasoning. Unless required to understand the change, additional code examples or other details should be left to the pull request.
+* If the commit fixes a bug, include the number of the issue in the message.
+* Use the first person present tense - for example "Fix …" instead of "Fixes …" or "Fixed …".
+* For text formatting and spelling, follow the same rules as documentation and in-code comments — for example, the use of capitalization and periods.
+* If the commit is a bug fix on top of another recently committed change, or a revert or reapply of a patch, include the Git revision number of the prior related commit, e.g. `Revert abcd3fg because it caused #1234`.
+
+### CLA
+
+Realm welcomes all contributions! The only requirement we have is that, like many other projects, we need to have a [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_License_Agreement) (CLA) in place before we can accept any external code. Our own CLA is a modified version of the Apache Software Foundation’s CLA.
+
+[Please submit your CLA electronically using our Google form](https://docs.google.com/forms/d/1bVp-Wp5nmNFz9Nx-ngTmYBVWVdwTyKj4T0WtfVm0Ozs/viewform?fbzx=4154977190905366979) so we can accept your submissions. The GitHub username you file there will need to match that of your Pull Requests. If you have any questions or cannot file the CLA electronically, you can email <help@realm.io>.

+ 61 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Base.xcconfig

@@ -0,0 +1,61 @@
+ALWAYS_SEARCH_USER_PATHS = NO;
+CLANG_CXX_LANGUAGE_STANDARD = c++14;
+CLANG_CXX_LIBRARY = libc++;
+CLANG_ENABLE_MODULES = YES;
+CLANG_ENABLE_OBJC_ARC = YES;
+CLANG_WARN_ASSIGN_ENUM = YES;
+CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+CLANG_WARN_BOOL_CONVERSION = YES;
+CLANG_WARN_COMMA = YES;
+CLANG_WARN_CONSTANT_CONVERSION = YES;
+CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+CLANG_WARN_EMPTY_BODY = YES;
+CLANG_WARN_ENUM_CONVERSION = YES;
+CLANG_WARN_INFINITE_RECURSION = YES;
+CLANG_WARN_INT_CONVERSION = YES;
+CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
+CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+CLANG_WARN_STRICT_PROTOTYPES = YES;
+CLANG_WARN_SUSPICIOUS_MOVE = YES;
+CLANG_WARN_UNREACHABLE_CODE = YES;
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+COMBINE_HIDPI_IMAGES = YES;
+ENABLE_STRICT_OBJC_MSGSEND = YES;
+GCC_C_LANGUAGE_STANDARD = gnu99;
+GCC_NO_COMMON_BLOCKS = YES;
+GCC_PRECOMPILE_PREFIX_HEADER = YES;
+GCC_PREFIX_HEADER = $(REALM_PREFIX_HEADER);
+GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
+GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+GCC_WARN_SIGN_COMPARE = YES;
+GCC_WARN_UNDECLARED_SELECTOR = YES;
+GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+GCC_WARN_UNKNOWN_PRAGMAS = YES;
+GCC_WARN_UNUSED_FUNCTION = YES;
+GCC_WARN_UNUSED_PARAMETER = YES;
+GCC_WARN_UNUSED_VARIABLE = YES;
+SWIFT_COMPILATION_MODE = wholemodule;
+SWIFT_OPTIMIZATION_LEVEL = -Owholemodule;
+WARNING_CFLAGS = -Wmismatched-tags -Wunused-private-field -Wpartial-availability;
+OTHER_CFLAGS = -fvisibility-inlines-hidden;
+
+OTHER_CPLUSPLUSFLAGS = $(inherited) -isystem core/include;
+HEADER_SEARCH_PATHS = $(inherited) Realm/ObjectStore/src;
+
+CODE_SIGN_IDENTITY[sdk=iphone*] = iPhone Developer;
+CODE_SIGNING_REQUIRED[sdk=macosx] = NO;
+
+MACOSX_DEPLOYMENT_TARGET = 10.9;
+IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+WATCHOS_DEPLOYMENT_TARGET = 2.0;
+TVOS_DEPLOYMENT_TARGET = 9.0;
+
+SWIFT_VERSION = 3.0;

+ 12 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Debug.xcconfig

@@ -0,0 +1,12 @@
+#include "Base.xcconfig"
+
+BITCODE_GENERATION_MODE = marker;
+COPY_PHASE_STRIP = NO;
+ENABLE_TESTABILITY = YES;
+GCC_OPTIMIZATION_LEVEL = 0;
+ONLY_ACTIVE_ARCH = YES;
+SWIFT_OPTIMIZATION_LEVEL = -Onone;
+
+GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 REALM_DEBUG REALM_HAVE_CONFIG REALM_ENABLE_SYNC __ASSERTMACROS__;
+
+REALM_LIBRARY_SUFFIX = -dbg;

+ 11 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm iOS static.xcconfig

@@ -0,0 +1,11 @@
+#include "Realm.xcconfig"
+
+SUPPORTED_PLATFORMS = iphoneos iphonesimulator;
+TARGETED_DEVICE_FAMILY = 1,2;
+IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+
+MACH_O_TYPE = staticlib;
+EXECUTABLE_NAME = Realm;
+
+CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static;
+CONFIGURATION_TEMP_DIR = $(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static;

+ 37 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm.xcconfig

@@ -0,0 +1,37 @@
+SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator;
+TARGETED_DEVICE_FAMILY = 1,2,3,4;
+
+INFOPLIST_FILE = Realm/Realm-Info.plist;
+MODULEMAP_FILE = $(SRCROOT)/Realm/Realm.modulemap;
+PRODUCT_BUNDLE_IDENTIFIER = io.Realm.${PRODUCT_NAME:rfc1034identifier};
+PRODUCT_NAME = Realm;
+SKIP_INSTALL = YES;
+
+DEFINES_MODULE = YES;
+DYLIB_COMPATIBILITY_VERSION = 1;
+DYLIB_CURRENT_VERSION = 1;
+DYLIB_INSTALL_NAME_BASE = @rpath;
+MACH_O_TYPE = mh_dylib;
+FRAMEWORK_VERSION = A;
+
+APPLICATION_EXTENSION_API_ONLY = YES;
+HEADER_SEARCH_PATHS = $(inherited) $(DERIVED_FILE_DIR);
+LIBRARY_SEARCH_PATHS = core;
+
+ENABLE_BITCODE[sdk=iphone*] = YES;
+ENABLE_BITCODE[sdk=watch*] = YES;
+ENABLE_BITCODE[sdk=appletv*] = YES;
+
+LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+
+OTHER_LDFLAGS[sdk=macosx*] = -lrealm$(REALM_LIBRARY_SUFFIX);
+OTHER_LIBTOOLFLAGS[sdk=macosx*] = -lrealm$(REALM_LIBRARY_SUFFIX);
+OTHER_LDFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);
+OTHER_LIBTOOLFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);
+OTHER_LDFLAGS[sdk=watch*] = -lrealm-watchos$(REALM_LIBRARY_SUFFIX);
+OTHER_LIBTOOLFLAGS[sdk=watch*] = -lrealm-watchos$(REALM_LIBRARY_SUFFIX);
+OTHER_LDFLAGS[sdk=appletv*] = -lrealm-tvos$(REALM_LIBRARY_SUFFIX);
+OTHER_LIBTOOLFLAGS[sdk=appletv*] = -lrealm-tvos$(REALM_LIBRARY_SUFFIX);

+ 6 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Realm/Tests iOS static.xcconfig

@@ -0,0 +1,6 @@
+#include "Tests.xcconfig"
+
+CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static;
+IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+
+TEST_HOST[sdk=iphone*] = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/TestHost.app/TestHost;

+ 22 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Realm/Tests.xcconfig

@@ -0,0 +1,22 @@
+SUPPORTED_PLATFORMS = macosx iphonesimulator iphoneos appletvos appletvsimulator;
+SKIP_INSTALL = YES;
+
+INFOPLIST_FILE = Realm/Tests/RealmTests-Info.plist;
+PRODUCT_BUNDLE_IDENTIFIER = io.Realm.${PRODUCT_NAME:rfc1034identifier};
+
+LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks;
+
+OTHER_CFLAGS = -fobjc-arc-exceptions;
+
+SWIFT_OBJC_BRIDGING_HEADER = Realm/Tests/Swift/Swift-Tests-Bridging-Header.h;
+SWIFT_OPTIMIZATION_LEVEL = -Onone;
+
+TEST_HOST[sdk=iphone*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost;
+TEST_HOST[sdk=appletv*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost;
+TEST_HOST[sdk=macosx*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/Contents/MacOS/TestHost;
+
+EXCLUDED_SOURCE_FILE_NAMES[sdk=iphone*] = InterprocessTests.m SwiftSchemaTests.swift;
+EXCLUDED_SOURCE_FILE_NAMES[sdk=appletv*] = EncryptionTests.mm InterprocessTests.m SwiftSchemaTests.swift;
+EXCLUDED_SOURCE_FILE_NAMES[sdk=watch*] = *;

+ 12 - 0
Carthage/Checkouts/realm-cocoa/Configuration/RealmSwift/RealmSwift.xcconfig

@@ -0,0 +1,12 @@
+SUPPORTED_PLATFORMS = macosx iphonesimulator iphoneos watchsimulator watchos appletvos appletvsimulator;
+APPLICATION_EXTENSION_API_ONLY = YES;
+SKIP_INSTALL = YES;
+
+DYLIB_COMPATIBILITY_VERSION = 1;
+DYLIB_CURRENT_VERSION = 1;
+DYLIB_INSTALL_NAME_BASE = @rpath;
+LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+DEFINES_MODULE = YES;
+INFOPLIST_FILE = RealmSwift/RealmSwift-Info.plist;
+PRODUCT_NAME = RealmSwift;
+PRODUCT_BUNDLE_IDENTIFIER = io.realm.RealmSwit;

+ 18 - 0
Carthage/Checkouts/realm-cocoa/Configuration/RealmSwift/Tests.xcconfig

@@ -0,0 +1,18 @@
+SUPPORTED_PLATFORMS = macosx iphonesimulator iphoneos appletvos appletvsimulator;
+SKIP_INSTALL = YES;
+INFOPLIST_FILE = RealmSwift/Tests/RealmSwiftTests-Info.plist;
+PRODUCT_NAME = $(TARGET_NAME);
+SWIFT_OBJC_BRIDGING_HEADER = RealmSwift/Tests/RealmSwiftTests-BridgingHeader.h
+SWIFT_OPTIMIZATION_LEVEL = -Onone;
+
+LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks;
+
+EXCLUDED_SOURCE_FILE_NAMES[sdk=iphone*] = build/osx/*;
+EXCLUDED_SOURCE_FILE_NAMES[sdk=appletv*] = build/osx/*;
+EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = build/ios/*;
+
+TEST_HOST[sdk=iphone*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost;
+TEST_HOST[sdk=appletv*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost;
+TEST_HOST[sdk=macosx*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/Contents/MacOS/TestHost;

+ 7 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Release.xcconfig

@@ -0,0 +1,7 @@
+#include "Base.xcconfig"
+
+BITCODE_GENERATION_MODE = bitcode;
+DEBUG_INFORMATION_FORMAT = dwarf-with-dsym;
+ENABLE_NS_ASSERTIONS = NO;
+GCC_PREPROCESSOR_DEFINITIONS = REALM_HAVE_CONFIG REALM_ENABLE_SYNC __ASSERTMACROS__;
+VALIDATE_PRODUCT = YES;

+ 21 - 0
Carthage/Checkouts/realm-cocoa/Configuration/TestHost.xcconfig

@@ -0,0 +1,21 @@
+SUPPORTED_PLATFORMS = macosx iphonesimulator iphoneos appletvos appletvsimulator;
+
+COPY_PHASE_STRIP = NO;
+LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks;
+INFOPLIST_FILE = Realm/Tests/TestHost/Info.plist;
+PRODUCT_NAME = $(TARGET_NAME);
+CLANG_MODULES_AUTOLINK = NO;
+ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+
+PRODUCT_BUNDLE_IDENTIFIER = io.realm.TestHost;
+
+OTHER_LDFLAGS[sdk=iphone*] = -framework UIKit;
+OTHER_LDFLAGS[sdk=appletv*] = -framework UIKit;
+OTHER_LDFLAGS[sdk=macosx*] = -framework Cocoa;
+
+PRINCIPAL_CLASS[sdk=iphone*] = UIApplication;
+PRINCIPAL_CLASS[sdk=appletv*] = UIApplication;
+PRINCIPAL_CLASS[sdk=macosx*] = NSApplication;
+
+IPHONEOS_DEPLOYMENT_TARGET = 8.0;

+ 15 - 0
Carthage/Checkouts/realm-cocoa/Configuration/object-server-config.yml

@@ -0,0 +1,15 @@
+storage:
+  root_path: 'root_dir'
+auth:
+  public_key_path: 'keys/token-signature.pub'
+  private_key_path: 'keys/token-signature.key'
+  sync_hosts:
+    - 'localhost:9080'
+  providers:
+    password:
+      iterations: 1
+proxy:
+  http:
+    listen_address: '::'
+enterprise:
+  skip_setup: true

+ 280 - 0
Carthage/Checkouts/realm-cocoa/Jenkinsfile.releasability

@@ -0,0 +1,280 @@
+swiftVersions = ['3.2.3', '3.3', '4.0.3', '4.1', '4.1.2', '4.2']
+platforms = ['osx', 'ios', 'watchos', 'tvos']
+platformNames = ['osx': 'macOS', 'ios': 'iOS', 'watchos': 'watchOS', 'tvos': 'tvOS']
+carthageXcodeVersion = '9.4'
+carthageSwiftVersion = '3.3.2'
+
+def installationTest(platform, test, language) {
+  return {
+    node('osx') {
+      deleteDir()
+      unstash 'source'
+
+      if (test == "dynamic" || test == "static") {
+        unstash "${language}-packaged"
+      }
+
+      sh """
+      archive=\$(echo \$PWD/realm-${language}-*.zip)
+      cd examples/installation
+
+      if [[ -f \$archive ]]; then
+        mv \$archive .
+        unzip realm-${language}-*zip
+        find . -name 'realm-${language}-*' -print0 | xargs -J% mv % realm-${language}-latest
+      fi
+
+      sed -i '' 's/swift-3.[0-9.]*/swift-${carthageSwiftVersion}/g' osx/swift/DynamicExample/DynamicExample.xcodeproj/project.pbxproj
+      sed -i '' 's/swift-3.[0-9.]*/swift-${carthageSwiftVersion}/g' ios/swift/DynamicExample/DynamicExample.xcodeproj/project.pbxproj
+      export REALM_XCODE_VERSION=${carthageXcodeVersion}
+      ./build.sh test-${platform}-${language}-${test}
+      """
+    }
+  }
+}
+
+def buildObjC(platform, outputDirectory=null) {
+  return {
+    node('osx') {
+      deleteDir()
+      unstash 'source'
+      sh "XCMODE=xcpretty ./build.sh package-${platform}"
+      dir(outputDirectory ?: "build/${platform}") {
+        stash includes: "realm-framework-${platform}.zip", name: "${platform}-objc"
+      }
+    }
+  }
+}
+
+
+def doBuild() {
+  stage('prepare') {
+    node('docker') {
+      deleteDir()
+
+      checkout(
+        [
+          $class           : 'GitSCM',
+          branches         : scm.branches,
+          gitTool          : 'native git',
+          extensions       : scm.extensions + [[$class: 'CleanCheckout'],
+                                               [$class: 'SubmoduleOption', parentCredentials: true]],
+          userRemoteConfigs: scm.userRemoteConfigs,
+        ]
+      )
+
+      stash includes: '**', name: 'source'
+    }
+  }
+
+  stage('build') {
+    def parallelBuilds = [
+      'Docs': {
+        node('osx') {
+          deleteDir()
+          unstash 'source'
+          sh """
+          export REALM_SWIFT_VERSION=${swiftVersions.last()}
+          ./scripts/reset-simulators.sh
+          ./build.sh docs
+          cd docs
+          zip -r objc-docs.zip objc_output
+          zip -r swift-docs.zip swift_output
+          """
+          dir('docs') {
+            archiveArtifacts artifacts: '*-docs.zip'
+          }
+        }
+      },
+
+      'Examples': {
+        node('osx') {
+          deleteDir()
+          unstash 'source'
+          sh 'XCMODE=xcpretty ./build.sh package-examples'
+          stash includes: 'realm-examples.zip', name: 'examples'
+        }
+      },
+
+      'macOS Obj-C': buildObjC('osx', 'build/DerivedData/Realm/Build/Products/Release'),
+      'iOS Obj-C': buildObjC('ios'),
+      'watchOS Obj-C': buildObjC('watchos'),
+      'tvOS Obj-C': buildObjC('tvos'),
+      'iOS Obj-C static': buildObjC('ios-static'),
+    ]
+
+    for (def p in platforms) {
+      def platform = p
+      def platformName = platformNames[platform]
+      parallelBuilds["${platformName} Carthage"] = {
+        node('osx') {
+          deleteDir()
+          unstash 'source'
+          sh """
+          export REALM_XCODE_VERSION=${carthageXcodeVersion}
+          . ./scripts/swift-version.sh
+          set_xcode_and_swift_versions
+
+          carthage build --no-skip-current --platform ${platform}
+          carthage archive --output Carthage-${platform}.framework.zip
+          """
+          stash includes: "Carthage-${platform}.framework.zip",
+                name: "${platform}-carthage"
+        }
+      }
+    }
+
+    for (def p in platforms) {
+      def platform = p
+      def platformName = platformNames[platform]
+      for (def v in swiftVersions) {
+        def swiftVersion = v
+        parallelBuilds["${platformName} Swift ${swiftVersion}"] = {
+          node('osx') {
+            deleteDir()
+            unstash 'source'
+            sh "XCMODE=xcpretty ./build.sh package-${platform}-swift-${swiftVersion}"
+            dir("build/${platform}") {
+              stash includes: "realm-swift-framework-${platform}-swift-${swiftVersion}.zip",
+                    name: "${platform}-swift-${swiftVersion}"
+            }
+          }
+        }
+      }
+    }
+
+    parallel parallelBuilds
+  }
+
+  stage('package') {
+    parallel (
+      "Obj-C": {
+        node('osx') {
+          deleteDir()
+
+          for (def platform in platforms) {
+            unstash "${platform}-objc"
+          }
+
+          unstash 'ios-static-objc'
+          unstash 'examples'
+          unstash 'source'
+
+          sh './build.sh package-release objc'
+          stash include: 'realm-objc-*.zip', name: 'objc-packaged'
+          archiveArtifacts artifacts: 'realm-objc-*.zip'
+        }
+      },
+      "Swift": {
+        node('osx') {
+          deleteDir()
+
+          for (def platform in platforms) {
+            for (def swiftVersion in swiftVersions) {
+              unstash "${platform}-swift-${swiftVersion}"
+            }
+          }
+
+          unstash 'examples'
+          unstash 'source'
+
+          sh './build.sh package-release swift'
+          sh 'rm realm-swift-framework-*.zip'
+          stash include: 'realm-swift-*.zip', name: 'swift-packaged'
+          archiveArtifacts artifacts: 'realm-swift-*.zip'
+        }
+      },
+      "Carthage": {
+        node('osx') {
+          deleteDir()
+
+          for (def platform in platforms) {
+            unstash "${platform}-carthage"
+          }
+
+          sh '''
+          for zip in Carthage-*.framework.zip; do
+            ditto -xk $zip merged/
+          done
+
+          ditto -ck merged/ Carthage.framework.zip
+          '''
+
+          archiveArtifacts artifacts: 'Carthage.framework.zip'
+        }
+      }
+    )
+  }
+
+  stage('test') {
+    def parallelBuilds = [
+      'Test Examples': {
+        node('osx') {
+          deleteDir()
+
+          // FIXME: Split Obj-C and Swift.
+          unstash 'objc-packaged'
+          unstash 'swift-packaged'
+
+          def sha = params.sha
+          sh """
+          curl -O https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/build.sh
+          mkdir -p scripts
+          curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/swift-version.sh -o scripts/swift-version.sh
+          curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/reset-simulators.sh -o scripts/reset-simulators.sh
+          curl https://raw.githubusercontent.com/realm/realm-cocoa/${sha}/scripts/reset-simulators.rb -o scripts/reset-simulators.rb
+          chmod +x scripts/reset-simulators.rb
+
+          XCMODE=xcpretty sh build.sh package-test-examples
+          """
+        }
+      },
+
+      'Test iOS static': {
+        node('osx') {
+          deleteDir()
+          unstash 'source'
+
+          sh 'XCMODE=xcpretty IS_RUNNING_PACKAGING=1 sh build.sh test-ios-static'
+        }
+      },
+
+      'Test macOS': {
+        node('osx') {
+          deleteDir()
+          unstash 'source'
+
+          sh 'XCMODE=xcpretty sh build.sh test-osx'
+        }
+      }
+    ]
+
+    for (def platform in ["osx", "ios"]) {
+      def platformName = platformNames[platform]
+      for (def test in ["dynamic", "cocoapods", "carthage"]) {
+        parallelBuilds["Installation - ${platformName} Obj-C ${test}"] = installationTest(platform, test, 'objc')
+      }
+    }
+
+    parallelBuilds["Installation - iOS Obj-C static"] = installationTest('ios', 'static', 'objc')
+    parallelBuilds["Installation - iOS Obj-C CocoaPods dynamic"] = installationTest('ios', 'cocoapods-dynamic', 'objc')
+
+    for (def platform in ["osx", "ios"]) {
+      def platformName = platformNames[platform]
+      for (def test in ["cocoapods", "carthage"]) {
+        parallelBuilds["Installation - ${platformName} Swift ${test}"] = installationTest(platform, test, 'swift')
+      }
+    }
+
+    parallel parallelBuilds
+  }
+}
+
+try {
+  doBuild()
+} catch (e) {
+  // If there was an exception thrown, the build failed
+  currentBuild.result = "FAILED"
+  throw e
+}
+

+ 0 - 0
Libraries external/Realm/Realm.framework/LICENSE → Carthage/Checkouts/realm-cocoa/LICENSE


+ 113 - 0
Carthage/Checkouts/realm-cocoa/Realm.podspec

@@ -0,0 +1,113 @@
+Pod::Spec.new do |s|
+  s.name                    = 'Realm'
+  version                   = `sh build.sh get-version`
+  s.version                 = version
+  s.summary                 = 'Realm is a modern data framework & database for iOS, macOS, tvOS & watchOS.'
+  s.description             = <<-DESC
+                              The Realm Mobile Database, for Objective-C. (If you want to use Realm from Swift, see the “RealmSwift” pod.)
+
+                              The Realm Mobile Database is a fast, easy-to-use replacement for Core Data & SQLite. Use it with the Realm Mobile Platform for realtime, automatic data sync. Works on iOS, macOS, tvOS & watchOS. Learn more and get help at https://realm.io.
+                              DESC
+  s.homepage                = "https://realm.io"
+  s.source                  = { :git => 'https://github.com/realm/realm-cocoa.git', :tag => "v#{s.version}", :submodules => true }
+  s.author                  = { 'Realm' => 'help@realm.io' }
+  s.library                 = 'c++', 'z'
+  s.requires_arc            = true
+  s.social_media_url        = 'https://twitter.com/realm'
+  has_versioned_docs        = !(version =~ /alpha|beta|rc/)
+  s.documentation_url       = "https://realm.io/docs/objc/#{has_versioned_docs ? s.version : 'latest'}"
+  s.license                 = { :type => 'Apache 2.0', :file => 'LICENSE' }
+
+  public_header_files       = 'include/**/RLMArray.h',
+                              'include/**/RLMCollection.h',
+                              'include/**/RLMConstants.h',
+                              'include/**/RLMListBase.h',
+                              'include/**/RLMMigration.h',
+                              'include/**/RLMObject.h',
+                              'include/**/RLMObjectBase.h',
+                              'include/**/RLMObjectSchema.h',
+                              'include/**/RLMOptionalBase.h',
+                              'include/**/RLMPlatform.h',
+                              'include/**/RLMProperty.h',
+                              'include/**/RLMRealm.h',
+                              'include/**/RLMRealm+Sync.h',
+                              'include/**/RLMRealmConfiguration+Sync.h',
+                              'include/**/RLMRealmConfiguration.h',
+                              'include/**/RLMResults.h',
+                              'include/**/RLMSchema.h',
+                              'include/**/RLMSyncConfiguration.h',
+                              'include/**/RLMSyncCredentials.h',
+                              'include/**/RLMSyncManager.h',
+                              'include/**/RLMSyncPermission.h',
+                              'include/**/RLMSyncSession.h',
+                              'include/**/RLMSyncSubscription.h',
+                              'include/**/RLMSyncUser.h',
+                              'include/**/RLMSyncUtil.h',
+                              'include/**/RLMThreadSafeReference.h',
+                              'include/**/NSError+RLMSync.h',
+                              'include/**/Realm.h',
+
+                              # Realm.Dynamic module
+                              'include/**/RLMRealm_Dynamic.h',
+                              'include/**/RLMObjectBase_Dynamic.h'
+
+                              # Realm.Private module
+  private_header_files      = 'include/**/RLMAccessor.h',
+                              'include/**/RLMArray_Private.h',
+                              'include/**/RLMCollection_Private.h',
+                              'include/**/RLMListBase.h',
+                              'include/**/RLMObjectBase_Private.h',
+                              'include/**/RLMObjectSchema_Private.h',
+                              'include/**/RLMObjectStore.h',
+                              'include/**/RLMObject_Private.h',
+                              'include/**/RLMOptionalBase.h',
+                              'include/**/RLMProperty_Private.h',
+                              'include/**/RLMRealmConfiguration_Private.h',
+                              'include/**/RLMRealm_Private.h',
+                              'include/**/RLMResults_Private.h',
+                              'include/**/RLMSchema_Private.h',
+                              'include/**/RLMSyncConfiguration_Private.h',
+                              'include/**/RLMSyncUtil_Private.h'
+
+  source_files              = 'Realm/*.{m,mm}',
+                              'Realm/ObjectStore/src/*.cpp',
+                              'Realm/ObjectStore/src/sync/*.cpp',
+                              'Realm/ObjectStore/src/sync/impl/*.cpp',
+                              'Realm/ObjectStore/src/sync/impl/apple/*.cpp',
+                              'Realm/ObjectStore/src/impl/*.cpp',
+                              'Realm/ObjectStore/src/impl/apple/*.cpp',
+                              'Realm/ObjectStore/src/util/*.cpp',
+                              'Realm/ObjectStore/src/util/apple/*.cpp'
+
+  s.frameworks              = 'Security'
+  s.module_map              = 'Realm/Realm.modulemap'
+  s.compiler_flags          = "-DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@\"#{s.version}\"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC"
+  s.prepare_command         = 'sh build.sh cocoapods-setup'
+  s.source_files            = source_files + private_header_files
+  s.private_header_files    = private_header_files
+  s.header_mappings_dir     = 'include'
+  s.pod_target_xcconfig     = { 'APPLICATION_EXTENSION_API_ONLY' => 'YES',
+                                'CLANG_CXX_LANGUAGE_STANDARD' => 'c++14',
+                                'CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF' => 'NO',
+                                'OTHER_CPLUSPLUSFLAGS' => '-isystem "${PODS_ROOT}/Realm/include/core" -fvisibility-inlines-hidden',
+                                'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Realm/include" "${PODS_ROOT}/Realm/include/Realm"',
+                              }
+  s.preserve_paths          = %w(build.sh include)
+
+  s.ios.deployment_target   = '8.0'
+  s.ios.vendored_library    = 'core/librealmcore-ios.a'
+
+  s.osx.deployment_target   = '10.9'
+  s.osx.vendored_library    = 'core/librealmcore-macosx.a'
+
+  s.watchos.deployment_target = '2.0'
+  s.watchos.vendored_library  = 'core/librealmcore-watchos.a'
+
+  s.tvos.deployment_target = '9.0'
+  s.tvos.vendored_library  = 'core/librealmcore-tvos.a'
+
+  s.subspec 'Headers' do |s|
+    s.source_files          = public_header_files
+    s.public_header_files   = public_header_files
+  end
+end

+ 0 - 0
Libraries external/Realm/Realm.framework/Headers/NSError+RLMSync.h → Carthage/Checkouts/realm-cocoa/Realm/NSError+RLMSync.h


+ 43 - 0
Carthage/Checkouts/realm-cocoa/Realm/NSError+RLMSync.m

@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "NSError+RLMSync.h"
+
+#import "RLMSyncUtil.h"
+
+@implementation NSError (RLMSync)
+
+- (RLMSyncErrorActionToken *)rlmSync_errorActionToken {
+    if (self.domain != RLMSyncErrorDomain) {
+        return nil;
+    }
+    if (self.code == RLMSyncErrorClientResetError
+        || self.code == RLMSyncErrorPermissionDeniedError) {
+        return (RLMSyncErrorActionToken *)self.userInfo[kRLMSyncErrorActionTokenKey];
+    }
+    return nil;
+}
+
+- (NSString *)rlmSync_clientResetBackedUpRealmPath {
+    if (self.domain == RLMSyncErrorDomain && self.code == RLMSyncErrorClientResetError) {
+        return self.userInfo[kRLMSyncPathOfRealmBackupCopyKey];
+    }
+    return nil;
+}
+
+@end

+ 20 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/Object-Server-Tests-Bridging-Header.h

@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncTestCase.h"
+#import "RLMSyncUser+ObjectServerTests.h"

+ 22 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/ObjectServerTests-Info.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 58 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMAncillaryObjectServerTests.m

@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import <XCTest/XCTest.h>
+
+#import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
+
+@interface RLMAncillaryObjectServerTests : XCTestCase
+@end
+
+@interface RLMSyncSessionRefreshHandle ()
++ (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate;
+@end
+
+@implementation RLMAncillaryObjectServerTests
+
+/// Ensure the `fireDateForTokenExpirationDate:nowDate:` method works properly.
+/// Rationale: we swizzle this method out for our end-to-end tests, so we need to verify the original works.
+- (void)testRefreshHandleDateComparison {
+    [RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:NO blockOnRefreshCompletion:nil];
+
+    // The method should return nil if the dates are equal in value.
+    NSDate *date = [NSDate date];
+    NSDate *nowDate = [NSDate dateWithTimeIntervalSince1970:date.timeIntervalSince1970];
+    XCTAssertNil([RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:date nowDate:nowDate]);
+
+    // The method should return nil if the expiration date is in the past.
+    date = [NSDate dateWithTimeIntervalSince1970:(date.timeIntervalSince1970 - 1)];
+    XCTAssertNil([RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:date nowDate:nowDate]);
+
+    // The method should return nil if the expiration date is not far enough forward in the future.
+    date = [NSDate dateWithTimeIntervalSince1970:(date.timeIntervalSince1970 + 1)];
+    XCTAssertNil([RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:date nowDate:nowDate]);
+
+    // The method should return an actual date if the expiration date is far enough forward in the future.
+    date = [NSDate dateWithTimeIntervalSince1970:(date.timeIntervalSince1970 + 100)];
+    NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:date nowDate:nowDate];
+    XCTAssertNotNil(fireDate);
+    XCTAssertGreaterThan(fireDate.timeIntervalSinceReferenceDate, nowDate.timeIntervalSinceReferenceDate);
+    XCTAssertLessThan(fireDate.timeIntervalSinceReferenceDate, date.timeIntervalSinceReferenceDate);
+}
+
+@end

+ 2038 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm

@@ -0,0 +1,2038 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncTestCase.h"
+#import "RLMTestUtils.h"
+#import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
+#import "RLMSyncUser+ObjectServerTests.h"
+#import "RLMSyncUtil_Private.h"
+#import "RLMRealm+Sync.h"
+#import "RLMRealmConfiguration_Private.h"
+#import "RLMRealmUtil.hpp"
+#import "RLMRealm_Dynamic.h"
+
+#pragma mark - Test objects
+
+@interface PartialSyncObjectA : RLMObject
+@property NSInteger number;
+@property NSString *string;
++ (instancetype)objectWithNumber:(NSInteger)number string:(NSString *)string;
+@end
+
+@interface PartialSyncObjectB : RLMObject
+@property NSInteger number;
+@property NSString *firstString;
+@property NSString *secondString;
++ (instancetype)objectWithNumber:(NSInteger)number firstString:(NSString *)first secondString:(NSString *)second;
+@end
+
+@implementation PartialSyncObjectA
++ (instancetype)objectWithNumber:(NSInteger)number string:(NSString *)string {
+    PartialSyncObjectA *object = [[PartialSyncObjectA alloc] init];
+    object.number = number;
+    object.string = string;
+    return object;
+}
+@end
+
+@implementation PartialSyncObjectB
++ (instancetype)objectWithNumber:(NSInteger)number firstString:(NSString *)first secondString:(NSString *)second {
+    PartialSyncObjectB *object = [[PartialSyncObjectB alloc] init];
+    object.number = number;
+    object.firstString = first;
+    object.secondString = second;
+    return object;
+}
+@end
+
+@interface RLMObjectServerTests : RLMSyncTestCase
+@end
+
+@implementation RLMObjectServerTests
+
+#pragma mark - Authentication and Tokens
+
+/// Valid username/password credentials should be able to log in a user. Using the same credentials should return the
+/// same user object.
+- (void)testUsernamePasswordAuthentication {
+    RLMSyncUser *firstUser = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                                    server:[RLMSyncTestCase authServerURL]];
+    RLMSyncUser *secondUser = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                             register:NO]
+                                                     server:[RLMSyncTestCase authServerURL]];
+    // Two users created with the same credential should resolve to the same actual user.
+    XCTAssertTrue([firstUser.identity isEqualToString:secondUser.identity]);
+    // Authentication server property should be properly set.
+    XCTAssertEqualObjects(firstUser.authenticationServer, [RLMSyncTestCase authServerURL]);
+    XCTAssertFalse(firstUser.isAdmin);
+}
+
+/// A valid admin token should be able to log in a user.
+- (void)testAdminTokenAuthentication {
+    RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithAccessToken:self.adminToken identity:@"test"];
+    XCTAssertNotNil(credentials);
+
+    RLMSyncUser *user = [self logInUserForCredentials:credentials server:[RLMObjectServerTests authServerURL]];
+    XCTAssertTrue(user.isAdmin);
+}
+
+/// An invalid username/password credential should not be able to log in a user and a corresponding error should be generated.
+- (void)testInvalidPasswordAuthentication {
+    [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd) register:YES]
+                          server:[RLMSyncTestCase authServerURL]];
+
+    RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithUsername:NSStringFromSelector(_cmd)
+                                                                         password:@"INVALID_PASSWORD"
+                                                                         register:NO];
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@""];
+    [RLMSyncUser logInWithCredentials:credentials
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
+        XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
+        XCTAssertNotNil(error.localizedDescription);
+
+        [expectation fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// A non-existsing user should not be able to log in and a corresponding error should be generated.
+- (void)testNonExistingUsernameAuthentication {
+    RLMSyncCredentials *credentials = [RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                       register:NO];
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@""];
+    [RLMSyncUser logInWithCredentials:credentials
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
+        XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
+        XCTAssertNotNil(error.localizedDescription);
+
+        [expectation fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// Registering a user with existing username should return corresponding error.
+- (void)testExistingUsernameRegistration {
+    RLMSyncCredentials *credentials = [RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                            register:YES];
+
+    [self logInUserForCredentials:credentials server:[RLMSyncTestCase authServerURL]];
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@""];
+    [RLMSyncUser logInWithCredentials:credentials
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                        onCompletion:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
+        XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
+        XCTAssertNotNil(error.localizedDescription);
+
+        [expectation fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// Errors reported in RLMSyncManager.errorHandler shouldn't contain sync error domain errors as underlying error
+- (void)testSyncErrorHandlerErrorDomain {
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    XCTAssertNotNil(user);
+
+    NSURL *realmURL = [NSURL URLWithString:@"realm://127.0.0.1:9080/THE_PATH_USER_DONT_HAVE_ACCESS_TO/test"];
+
+    RLMRealmConfiguration *c = [user configurationWithURL:realmURL fullSynchronization:true];
+
+    NSError *error = nil;
+    __attribute__((objc_precise_lifetime)) RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:&error];
+    XCTAssertNil(error);
+    XCTAssertTrue(realm.isEmpty);
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@""];
+    [RLMSyncManager sharedManager].errorHandler = ^(__unused NSError *error,
+                                                    __unused RLMSyncSession *session) {
+        XCTAssertTrue([error.domain isEqualToString:RLMSyncErrorDomain]);
+        XCTAssertFalse([[error.userInfo[kRLMSyncUnderlyingErrorKey] domain] isEqualToString:RLMSyncErrorDomain]);
+        [expectation fulfill];
+    };
+
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// The pre-emptive token refresh subsystem should function, and properly refresh the token.
+- (void)testPreemptiveTokenRefresh {
+    // Prepare the test.
+    __block NSInteger refreshCount = 0;
+    __block NSInteger errorCount = 0;
+    [RLMSyncManager sharedManager].errorHandler = ^(__unused NSError *error,
+                                                    __unused RLMSyncSession *session) {
+        errorCount++;
+    };
+
+    __block XCTestExpectation *ex;
+    [RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:YES
+                                        blockOnRefreshCompletion:^(BOOL success) {
+                                            XCTAssertTrue(success);
+                                            refreshCount++;
+                                            [ex fulfill];
+                                        }];
+    // Open the Realm.
+    NSURL *url = REALM_URL();
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:true]
+                                               server:[RLMObjectServerTests authServerURL]];
+    __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:user];
+    ex = [self expectationWithDescription:@"Timer fired"];
+    [self waitForExpectationsWithTimeout:10 handler:nil];
+    XCTAssertTrue(errorCount == 0);
+    XCTAssertTrue(refreshCount > 0);
+}
+
+#pragma mark - Users
+
+/// `[RLMSyncUser all]` should be updated once a user is logged in.
+- (void)testBasicUserPersistence {
+    XCTAssertNil([RLMSyncUser currentUser]);
+    XCTAssertEqual([[RLMSyncUser allUsers] count], 0U);
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    XCTAssertNotNil(user);
+    XCTAssertEqual([[RLMSyncUser allUsers] count], 1U);
+    XCTAssertEqualObjects([RLMSyncUser allUsers], @{user.identity: user});
+    XCTAssertEqualObjects([RLMSyncUser currentUser], user);
+
+    RLMSyncUser *user2 = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:[NSStringFromSelector(_cmd) stringByAppendingString:@"2"]
+                                                                                             register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    XCTAssertEqual([[RLMSyncUser allUsers] count], 2U);
+    NSDictionary *dict2 = @{user.identity: user, user2.identity: user2};
+    XCTAssertEqualObjects([RLMSyncUser allUsers], dict2);
+    RLMAssertThrowsWithReasonMatching([RLMSyncUser currentUser], @"currentUser cannot be called if more that one valid, logged-in user exists");
+}
+
+/// `[RLMSyncUser currentUser]` should become nil if the user is logged out.
+- (void)testCurrentUserLogout {
+    XCTAssertNil([RLMSyncUser currentUser]);
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    XCTAssertNotNil(user);
+    XCTAssertEqualObjects([RLMSyncUser currentUser], user);
+    [user logOut];
+    XCTAssertNil([RLMSyncUser currentUser]);
+}
+
+/// A sync user should return a session when asked for it based on the path.
+- (void)testUserGetSessionForValidURL {
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    NSURL *url = REALM_URL();
+    [self openRealmForURL:url user:user immediatelyBlock:^{
+        RLMSyncSession *session = [user sessionForURL:url];
+        XCTAssertNotNil(session);
+    }];
+    // Check session existence after binding.
+    RLMSyncSession *session = [user sessionForURL:url];
+    XCTAssertNotNil(session);
+}
+
+/// A sync user should return nil when asked for a URL that doesn't exist.
+- (void)testUserGetSessionForInvalidURL {
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMSyncSession *badSession = [user sessionForURL:[NSURL URLWithString:@"realm://127.0.0.1:9080/noSuchRealm"]];
+    XCTAssertNil(badSession);
+}
+
+/// A sync user should be able to successfully change their own password.
+- (void)testUserChangePassword {
+    NSString *userName = NSStringFromSelector(_cmd);
+    NSString *firstPassword = @"a";
+    NSString *secondPassword = @"b";
+    // Successfully create user, change its password, log out,
+    // then fail to change password again due to being logged out.
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:firstPassword
+                                                                       register:YES];
+        RLMSyncUser *user = [self logInUserForCredentials:creds
+                                                   server:[RLMObjectServerTests authServerURL]];
+        XCTestExpectation *ex = [self expectationWithDescription:@"change password callback invoked"];
+        [user changePassword:secondPassword completion:^(NSError * _Nullable error) {
+            XCTAssertNil(error);
+            [ex fulfill];
+        }];
+        [self waitForExpectationsWithTimeout:2.0 handler:nil];
+        [user logOut];
+        ex = [self expectationWithDescription:@"change password callback invoked"];
+        [user changePassword:@"fail" completion:^(NSError * _Nullable error) {
+            XCTAssertNotNil(error);
+            [ex fulfill];
+        }];
+        [self waitForExpectationsWithTimeout:2.0 handler:nil];
+    }
+    // Fail to log in with original password.
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:firstPassword
+                                                                       register:NO];
+
+        XCTestExpectation *ex = [self expectationWithDescription:@"login callback invoked"];
+        [RLMSyncUser logInWithCredentials:creds
+                            authServerURL:[RLMObjectServerTests authServerURL]
+                             onCompletion:^(RLMSyncUser *user, NSError *error) {
+            XCTAssertNil(user);
+            XCTAssertNotNil(error);
+            XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
+            XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
+            XCTAssertNotNil(error.localizedDescription);
+
+            [ex fulfill];
+        }];
+        [self waitForExpectationsWithTimeout:2.0 handler:nil];
+    }
+    // Successfully log in with new password.
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:secondPassword
+                                                                       register:NO];
+        RLMSyncUser *user = [self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]];
+        XCTAssertNotNil(user);
+        XCTAssertEqualObjects(RLMSyncUser.currentUser, user);
+        [user logOut];
+        XCTAssertNil(RLMSyncUser.currentUser);
+    }
+}
+
+/// A sync admin user should be able to successfully change another user's password.
+- (void)testOtherUserChangePassword {
+    // Create admin user.
+    NSURL *url = [RLMObjectServerTests authServerURL];
+    RLMSyncUser *adminUser = [self createAdminUserForURL:url username:[[NSUUID UUID] UUIDString]];
+
+    NSString *username = NSStringFromSelector(_cmd);
+    NSString *firstPassword = @"a";
+    NSString *secondPassword = @"b";
+    NSString *nonAdminUserID = nil;
+    // Successfully create user.
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:username
+                                                                       password:firstPassword
+                                                                       register:YES];
+        RLMSyncUser *user = [self logInUserForCredentials:creds server:url];
+        nonAdminUserID = user.identity;
+        [user logOut];
+    }
+    // Fail to change password from non-admin user.
+    {
+        NSString *username2 = [NSString stringWithFormat:@"%@_2", username];
+        RLMSyncCredentials *creds2 = [RLMSyncCredentials credentialsWithUsername:username2
+                                                                             password:@"a"
+                                                                             register:YES];
+        RLMSyncUser *user2 = [self logInUserForCredentials:creds2 server:url];
+        XCTestExpectation *ex = [self expectationWithDescription:@"change password callback invoked"];
+        [user2 changePassword:@"foobar" forUserID:nonAdminUserID completion:^(NSError *error) {
+            XCTAssertNotNil(error);
+            [ex fulfill];
+        }];
+        [self waitForExpectationsWithTimeout:2.0 handler:nil];
+    }
+    // Change password from admin user.
+    {
+        XCTestExpectation *ex = [self expectationWithDescription:@"change password callback invoked"];
+        [adminUser changePassword:secondPassword forUserID:nonAdminUserID completion:^(NSError *error) {
+            XCTAssertNil(error);
+            [ex fulfill];
+        }];
+        [self waitForExpectationsWithTimeout:2.0 handler:nil];
+    }
+    // Fail to log in with original password.
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:username
+                                                                       password:firstPassword
+                                                                       register:NO];
+
+        XCTestExpectation *ex = [self expectationWithDescription:@"login callback invoked"];
+        [RLMSyncUser logInWithCredentials:creds
+                            authServerURL:[RLMObjectServerTests authServerURL]
+                             onCompletion:^(RLMSyncUser *user, NSError *error) {
+            XCTAssertNil(user);
+            XCTAssertNotNil(error);
+            XCTAssertEqual(error.domain, RLMSyncAuthErrorDomain);
+            XCTAssertEqual(error.code, RLMSyncAuthErrorInvalidCredential);
+            XCTAssertNotNil(error.localizedDescription);
+
+            [ex fulfill];
+        }];
+        [self waitForExpectationsWithTimeout:2.0 handler:nil];
+    }
+    // Successfully log in with new password.
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:username
+                                                                       password:secondPassword
+                                                                       register:NO];
+        RLMSyncUser *user = [self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]];
+        XCTAssertNotNil(user);
+        [user logOut];
+    }
+}
+
+- (void)testRequestPasswordResetForRegisteredUser {
+    NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
+    RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:@"a" register:YES];
+    [[self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]] logOut];
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser requestPasswordResetForAuthServer:[RLMObjectServerTests authServerURL] userEmail:userName completion:^(NSError *error) {
+        XCTAssertNil(error);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    NSString *token = [self emailForAddress:userName];
+    XCTAssertNotNil(token);
+
+    // Use the password reset token
+    ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser completePasswordResetForAuthServer:[RLMObjectServerTests authServerURL] token:token password:@"new password"
+                                         completion:^(NSError *error) {
+                                             XCTAssertNil(error);
+                                             [ex fulfill];
+                                         }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Should now be able to log in with the new password
+    {
+        RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName
+                                                                       password:@"new password"
+                                                                       register:NO];
+        RLMSyncUser *user = [self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]];
+        XCTAssertNotNil(user);
+        [user logOut];
+    }
+
+    // Reusing the token should fail
+    ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser completePasswordResetForAuthServer:[RLMObjectServerTests authServerURL] token:token password:@"new password 2"
+                                         completion:^(NSError *error) {
+                                             XCTAssertNotNil(error);
+                                             [ex fulfill];
+                                         }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+- (void)testRequestPasswordResetForNonexistentUser {
+    NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser requestPasswordResetForAuthServer:[RLMObjectServerTests authServerURL] userEmail:userName completion:^(NSError *error) {
+        // Not an error even though the user doesn't exist
+        XCTAssertNil(error);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Should not have sent an email to the non-registered user
+    XCTAssertNil([self emailForAddress:userName]);
+}
+
+- (void)testRequestPasswordResetWithBadAuthURL {
+    NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
+    NSURL *badAuthUrl = [[RLMObjectServerTests authServerURL] URLByAppendingPathComponent:@"/bad"];
+    [RLMSyncUser requestPasswordResetForAuthServer:badAuthUrl userEmail:userName completion:^(NSError *error) {
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.userInfo[@"statusCode"], @404);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+- (void)testRequestConfirmEmailForRegisteredUser {
+    NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
+    RLMSyncCredentials *creds = [RLMSyncCredentials credentialsWithUsername:userName password:@"a" register:YES];
+    [[self logInUserForCredentials:creds server:[RLMObjectServerTests authServerURL]] logOut];
+
+    // This token is sent by ROS upon user registration
+    NSString *registrationToken = [self emailForAddress:userName];
+    XCTAssertNotNil(registrationToken);
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser requestEmailConfirmationForAuthServer:[RLMObjectServerTests authServerURL]
+                                             userEmail:userName completion:^(NSError *error) {
+                                                 XCTAssertNil(error);
+                                                 [ex fulfill];
+                                             }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // This token should have been created when requestEmailConfirmationForAuthServer was called
+    NSString *token = [self emailForAddress:userName];
+    XCTAssertNotNil(token);
+    XCTAssertNotEqual(token, registrationToken);
+
+    // Use the token
+    ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser confirmEmailForAuthServer:[RLMObjectServerTests authServerURL] token:token
+                                            completion:^(NSError *error) {
+                                                XCTAssertNil(error);
+                                                [ex fulfill];
+                                            }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Reusing the token should fail
+    ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser confirmEmailForAuthServer:[RLMObjectServerTests authServerURL] token:token
+                                            completion:^(NSError *error) {
+                                                XCTAssertNotNil(error);
+                                                [ex fulfill];
+                                            }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+- (void)testRequestConfirmEmailForNonexistentUser {
+    NSString *userName = [NSStringFromSelector(_cmd) stringByAppendingString:@"@example.com"];
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"callback invoked"];
+    [RLMSyncUser requestEmailConfirmationForAuthServer:[RLMObjectServerTests authServerURL]
+                                             userEmail:userName completion:^(NSError *error) {
+                                                 // Not an error even though the user doesn't exist
+                                                 XCTAssertNil(error);
+                                                 [ex fulfill];
+                                             }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Should not have sent an email to the non-registered user
+    XCTAssertNil([self emailForAddress:userName]);
+}
+
+/// A sync admin user should be able to retrieve information about other users.
+- (void)testRetrieveUserInfo {
+    NSString *nonAdminUsername = @"meela@realm.example.org";
+    NSString *adminUsername = @"jyaku";
+    NSString *pw = @"p";
+    NSURL *server = [RLMObjectServerTests authServerURL];
+
+    // Create a non-admin user.
+    RLMSyncCredentials *c1 = [RLMSyncCredentials credentialsWithUsername:nonAdminUsername password:pw register:YES];
+    RLMSyncUser *nonAdminUser = [self logInUserForCredentials:c1 server:server];
+
+    // Create an admin user.
+    __unused RLMSyncUser *adminUser = [self createAdminUserForURL:server username:adminUsername];
+
+    // Create another admin user.
+    RLMSyncUser *userDoingLookups = [self createAdminUserForURL:server username:[[NSUUID UUID] UUIDString]];
+
+    // Get the non-admin user's info.
+    XCTestExpectation *ex1 = [self expectationWithDescription:@"should be able to get info about non-admin user"];
+    [userDoingLookups retrieveInfoForUser:nonAdminUsername
+                         identityProvider:RLMIdentityProviderUsernamePassword
+                               completion:^(RLMSyncUserInfo *info, NSError *err) {
+                                   XCTAssertNil(err);
+                                   XCTAssertNotNil(info);
+                                   XCTAssertGreaterThan([info.accounts count], ((NSUInteger) 0));
+                                   RLMSyncUserAccountInfo *acctInfo = [info.accounts firstObject];
+                                   XCTAssertEqualObjects(acctInfo.providerUserIdentity, nonAdminUsername);
+                                   XCTAssertEqualObjects(acctInfo.provider, RLMIdentityProviderUsernamePassword);
+                                   XCTAssertFalse(info.isAdmin);
+                                   [ex1 fulfill];
+                               }];
+    [self waitForExpectationsWithTimeout:10 handler:nil];
+
+    // Get the admin user's info.
+    XCTestExpectation *ex2 = [self expectationWithDescription:@"should be able to get info about admin user"];
+    [userDoingLookups retrieveInfoForUser:adminUsername
+                         identityProvider:RLMIdentityProviderDebug
+                               completion:^(RLMSyncUserInfo *info, NSError *err) {
+                                   XCTAssertNil(err);
+                                   XCTAssertNotNil(info);
+                                   XCTAssertGreaterThan([info.accounts count], ((NSUInteger) 0));
+                                   RLMSyncUserAccountInfo *acctInfo = [info.accounts firstObject];
+                                   XCTAssertEqualObjects(acctInfo.providerUserIdentity, adminUsername);
+                                   XCTAssertEqualObjects(acctInfo.provider, RLMIdentityProviderDebug);
+                                   XCTAssertTrue(info.isAdmin);
+                                   [ex2 fulfill];
+                               }];
+    [self waitForExpectationsWithTimeout:10 handler:nil];
+
+    // Get invalid user's info.
+    XCTestExpectation *ex3 = [self expectationWithDescription:@"should fail for non-existent user"];
+    [userDoingLookups retrieveInfoForUser:@"invalid_user@realm.example.org"
+                         identityProvider:RLMIdentityProviderUsernamePassword
+                               completion:^(RLMSyncUserInfo *info, NSError *err) {
+                                   XCTAssertNotNil(err);
+                                   XCTAssertEqualObjects(err.domain, RLMSyncAuthErrorDomain);
+                                   XCTAssertEqual(err.code, RLMSyncAuthErrorUserDoesNotExist);
+                                   XCTAssertNil(info);
+                                   [ex3 fulfill];
+                               }];
+    [self waitForExpectationsWithTimeout:10 handler:nil];
+
+    // Get info using user without admin privileges.
+    XCTestExpectation *ex4 = [self expectationWithDescription:@"should fail for user without admin privileges"];
+    [nonAdminUser retrieveInfoForUser:adminUsername
+                     identityProvider:RLMIdentityProviderUsernamePassword
+                           completion:^(RLMSyncUserInfo *info, NSError *err) {
+                               XCTAssertNotNil(err);
+                               XCTAssertEqualObjects(err.domain, RLMSyncAuthErrorDomain);
+                               // FIXME: Shouldn't this be RLMSyncAuthErrorAccessDeniedOrInvalidPath?
+                               XCTAssertEqual(err.code, RLMSyncAuthErrorUserDoesNotExist);
+                               XCTAssertNil(info);
+                               [ex4 fulfill];
+                           }];
+    [self waitForExpectationsWithTimeout:10 handler:nil];
+}
+
+/// The login queue argument should be respected.
+- (void)testLoginQueueForSuccessfulLogin {
+    // Make global queue
+    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+
+    RLMSyncCredentials *c1 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
+                                                                password:@"p"
+                                                                register:YES];
+    XCTestExpectation *ex1 = [self expectationWithDescription:@"User logs in successfully on background queue"];
+    [RLMSyncUser logInWithCredentials:c1
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                              timeout:30.0
+                        callbackQueue:queue
+                         onCompletion:^(RLMSyncUser *user, __unused NSError *error) {
+                             XCTAssertNotNil(user);
+                             XCTAssertFalse([NSThread isMainThread]);
+                             [ex1 fulfill];
+                         }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    RLMSyncCredentials *c2 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
+                                                                password:@"p"
+                                                                register:YES];
+    XCTestExpectation *ex2 = [self expectationWithDescription:@"User logs in successfully on main queue"];
+    [RLMSyncUser logInWithCredentials:c2
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                              timeout:30.0
+                        callbackQueue:dispatch_get_main_queue()
+                         onCompletion:^(RLMSyncUser *user, __unused NSError *error) {
+                             XCTAssertNotNil(user);
+                             XCTAssertTrue([NSThread isMainThread]);
+                             [ex2 fulfill];
+                         }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// The login queue argument should be respected.
+- (void)testLoginQueueForFailedLogin {
+    // Make global queue
+    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+
+    RLMSyncCredentials *c1 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
+                                                                password:@"p"
+                                                                register:NO];
+    XCTestExpectation *ex1 = [self expectationWithDescription:@"Error returned on background queue"];
+    [RLMSyncUser logInWithCredentials:c1
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                              timeout:30.0
+                        callbackQueue:queue
+                         onCompletion:^(__unused RLMSyncUser *user, NSError *error) {
+                             XCTAssertNotNil(error);
+                             XCTAssertFalse([NSThread isMainThread]);
+                             [ex1 fulfill];
+                         }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    RLMSyncCredentials *c2 = [RLMSyncCredentials credentialsWithUsername:[[NSUUID UUID] UUIDString]
+                                                                password:@"p"
+                                                                register:NO];
+    XCTestExpectation *ex2 = [self expectationWithDescription:@"Error returned on main queue"];
+    [RLMSyncUser logInWithCredentials:c2
+                        authServerURL:[RLMObjectServerTests authServerURL]
+                              timeout:30.0
+                        callbackQueue:dispatch_get_main_queue()
+                         onCompletion:^(__unused RLMSyncUser *user, NSError *error) {
+                             XCTAssertNotNil(error);
+                             XCTAssertTrue([NSThread isMainThread]);
+                             [ex2 fulfill];
+                         }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+- (void)testUserExpirationCallback {
+    NSString *username = NSStringFromSelector(_cmd);
+    RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithUsername:username
+                                                                         password:@"a"
+                                                                         register:YES];
+    RLMSyncUser *user = [self logInUserForCredentials:credentials
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"callback should fire"];
+    // Set a callback on the user
+    __weak RLMSyncUser *weakUser = user;
+    user.errorHandler = ^(RLMSyncUser *u, NSError *error) {
+        XCTAssertEqualObjects(u.identity, weakUser.identity);
+        // Make sure we get the right error.
+        XCTAssertEqualObjects(error.domain, RLMSyncAuthErrorDomain);
+        XCTAssertEqual(error.code, RLMSyncAuthErrorAccessDeniedOrInvalidPath);
+        [ex fulfill];
+    };
+
+    // Screw up the token on the user using a debug API
+    [self manuallySetRefreshTokenForUser:user value:@"not_a_real_refresh_token"];
+
+    // Try to log in a Realm; this will cause our errorHandler block defined above to be fired.
+    __attribute__((objc_precise_lifetime)) RLMRealm *r = [self immediatelyOpenRealmForURL:REALM_URL() user:user];
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+    XCTAssertTrue(user.state == RLMSyncUserStateLoggedOut);
+}
+
+#pragma mark - Basic Sync
+
+/// It should be possible to successfully open a Realm configured for sync with an access token.
+- (void)testOpenRealmWithAdminToken {
+    // FIXME (tests): opening a Realm with the access token, then opening a Realm at the same virtual path
+    // with normal credentials, causes Realms to fail to bind with a "bad virtual path" error.
+    RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithAccessToken:self.adminToken identity:@"test"];
+    XCTAssertNotNil(credentials);
+    RLMSyncUser *user = [self logInUserForCredentials:credentials
+                                               server:[RLMObjectServerTests authServerURL]];
+    NSURL *url = [NSURL URLWithString:@"realm://127.0.0.1:9080/testSyncWithAdminToken"];
+    RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:YES];
+    NSError *error = nil;
+    RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:&error];
+    XCTAssertNil(error);
+    XCTAssertTrue(realm.isEmpty);
+}
+
+/// It should be possible to successfully open a Realm configured for sync with a normal user.
+- (void)testOpenRealmWithNormalCredentials {
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    NSURL *url = REALM_URL();
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+    XCTAssertTrue(realm.isEmpty);
+}
+
+/// If client B adds objects to a synced Realm, client A should see those objects.
+- (void)testAddObjects {
+    NSURL *url = REALM_URL();
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                              server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+    if (self.isParent) {
+        CHECK_COUNT(0, SyncObject, realm);
+        RLMRunChildAndWait();
+        [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@3]];
+    } else {
+        // Add objects.
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+/// If client B deletes objects from a synced Realm, client A should see the effects of that deletion.
+- (void)testDeleteObjects {
+    NSURL *url = REALM_URL();
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+    if (self.isParent) {
+        // Add objects.
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1", @"parent-2", @"parent-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+        RLMRunChildAndWait();
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(0, SyncObject, realm);
+    } else {
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+        [realm beginWriteTransaction];
+        [realm deleteAllObjects];
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(0, SyncObject, realm);
+    }
+}
+
+#pragma mark - Encryption
+
+/// If client B encrypts its synced Realm, client A should be able to access that Realm with a different encryption key.
+- (void)testEncryptedSyncedRealm {
+    NSURL *url = REALM_URL();
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    NSData *key = RLMGenerateKey();
+    RLMRealm *realm = [self openRealmForURL:url user:user encryptionKey:key
+                                 stopPolicy:RLMSyncStopPolicyAfterChangesUploaded immediatelyBlock:nil];
+    if (self.isParent) {
+        CHECK_COUNT(0, SyncObject, realm);
+        RLMRunChildAndWait();
+        [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@3]];
+    } else {
+        // Add objects.
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+/// If an encrypted synced Realm is re-opened with the wrong key, throw an exception.
+- (void)testEncryptedSyncedRealmWrongKey {
+    NSURL *url = REALM_URL();
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    if (self.isParent) {
+        NSString *path;
+        @autoreleasepool {
+            RLMRealm *realm = [self openRealmForURL:url user:user encryptionKey:RLMGenerateKey()
+                                         stopPolicy:RLMSyncStopPolicyImmediately immediatelyBlock:nil];
+            path = realm.configuration.pathOnDisk;
+            CHECK_COUNT(0, SyncObject, realm);
+            RLMRunChildAndWait();
+            [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@3]];
+        }
+        RLMRealmConfiguration *c = [RLMRealmConfiguration defaultConfiguration];
+        c.fileURL = [NSURL fileURLWithPath:path];
+        RLMAssertThrowsWithError([RLMRealm realmWithConfiguration:c error:nil],
+                                 @"Unable to open a realm at path",
+                                 RLMErrorFileAccess,
+                                 @"invalid mnemonic");
+        c.encryptionKey = RLMGenerateKey();
+        RLMAssertThrowsWithError([RLMRealm realmWithConfiguration:c error:nil],
+                                 @"Unable to open a realm at path",
+                                 RLMErrorFileAccess,
+                                 @"Realm file decryption failed");
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user encryptionKey:RLMGenerateKey()
+                                     stopPolicy:RLMSyncStopPolicyImmediately immediatelyBlock:nil];
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+#pragma mark - Multiple Realm Sync
+
+/// If a client opens multiple Realms, there should be one session object for each Realm that was opened.
+- (void)testMultipleRealmsSessions {
+    NSURL *urlA = CUSTOM_REALM_URL(@"a");
+    NSURL *urlB = CUSTOM_REALM_URL(@"b");
+    NSURL *urlC = CUSTOM_REALM_URL(@"c");
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    // Open three Realms.
+    __attribute__((objc_precise_lifetime)) RLMRealm *realmealmA = [self openRealmForURL:urlA user:user];
+    __attribute__((objc_precise_lifetime)) RLMRealm *realmealmB = [self openRealmForURL:urlB user:user];
+    __attribute__((objc_precise_lifetime)) RLMRealm *realmealmC = [self openRealmForURL:urlC user:user];
+    // Make sure there are three active sessions for the user.
+    XCTAssert(user.allSessions.count == 3, @"Expected 3 sessions, but didn't get 3 sessions");
+    XCTAssertNotNil([user sessionForURL:urlA], @"Expected to get a session for URL A");
+    XCTAssertNotNil([user sessionForURL:urlB], @"Expected to get a session for URL B");
+    XCTAssertNotNil([user sessionForURL:urlC], @"Expected to get a session for URL C");
+    XCTAssertTrue([user sessionForURL:urlA].state == RLMSyncSessionStateActive, @"Expected active session for URL A");
+    XCTAssertTrue([user sessionForURL:urlB].state == RLMSyncSessionStateActive, @"Expected active session for URL B");
+    XCTAssertTrue([user sessionForURL:urlC].state == RLMSyncSessionStateActive, @"Expected active session for URL C");
+}
+
+/// A client should be able to open multiple Realms and add objects to each of them.
+- (void)testMultipleRealmsAddObjects {
+    NSURL *urlA = CUSTOM_REALM_URL(@"a");
+    NSURL *urlB = CUSTOM_REALM_URL(@"b");
+    NSURL *urlC = CUSTOM_REALM_URL(@"c");
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realmA = [self openRealmForURL:urlA user:user];
+    RLMRealm *realmB = [self openRealmForURL:urlB user:user];
+    RLMRealm *realmC = [self openRealmForURL:urlC user:user];
+    if (self.isParent) {
+        [self waitForDownloadsForRealm:realmA];
+        [self waitForDownloadsForRealm:realmB];
+        [self waitForDownloadsForRealm:realmC];
+        CHECK_COUNT(0, SyncObject, realmA);
+        CHECK_COUNT(0, SyncObject, realmB);
+        CHECK_COUNT(0, SyncObject, realmC);
+        RLMRunChildAndWait();
+        [self waitForDownloadsForUser:user
+                               realms:@[realmA, realmB, realmC]
+                            realmURLs:@[urlA, urlB, urlC]
+                       expectedCounts:@[@3, @2, @5]];
+    } else {
+        // Add objects.
+        [self addSyncObjectsToRealm:realmA
+                       descriptions:@[@"child-A1", @"child-A2", @"child-A3"]];
+        [self addSyncObjectsToRealm:realmB
+                       descriptions:@[@"child-B1", @"child-B2"]];
+        [self addSyncObjectsToRealm:realmC
+                       descriptions:@[@"child-C1", @"child-C2", @"child-C3", @"child-C4", @"child-C5"]];
+        [self waitForUploadsForRealm:realmA];
+        [self waitForUploadsForRealm:realmB];
+        [self waitForUploadsForRealm:realmC];
+        CHECK_COUNT(3, SyncObject, realmA);
+        CHECK_COUNT(2, SyncObject, realmB);
+        CHECK_COUNT(5, SyncObject, realmC);
+    }
+}
+
+/// A client should be able to open multiple Realms and delete objects from each of them.
+- (void)testMultipleRealmsDeleteObjects {
+    NSURL *urlA = CUSTOM_REALM_URL(@"a");
+    NSURL *urlB = CUSTOM_REALM_URL(@"b");
+    NSURL *urlC = CUSTOM_REALM_URL(@"c");
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realmA = [self openRealmForURL:urlA user:user];
+    RLMRealm *realmB = [self openRealmForURL:urlB user:user];
+    RLMRealm *realmC = [self openRealmForURL:urlC user:user];
+    if (self.isParent) {
+        [self waitForDownloadsForRealm:realmA];
+        [self waitForDownloadsForRealm:realmB];
+        [self waitForDownloadsForRealm:realmC];
+        // Add objects.
+        [self addSyncObjectsToRealm:realmA
+                       descriptions:@[@"parent-A1", @"parent-A2", @"parent-A3", @"parent-A4"]];
+        [self addSyncObjectsToRealm:realmB
+                       descriptions:@[@"parent-B1", @"parent-B2", @"parent-B3", @"parent-B4", @"parent-B5"]];
+        [self addSyncObjectsToRealm:realmC
+                       descriptions:@[@"parent-C1", @"parent-C2"]];
+        [self waitForUploadsForRealm:realmA];
+        [self waitForUploadsForRealm:realmB];
+        [self waitForUploadsForRealm:realmC];
+        CHECK_COUNT(4, SyncObject, realmA);
+        CHECK_COUNT(5, SyncObject, realmB);
+        CHECK_COUNT(2, SyncObject, realmC);
+        RLMRunChildAndWait();
+        [self waitForDownloadsForUser:user
+                               realms:@[realmA, realmB, realmC]
+                            realmURLs:@[urlA, urlB, urlC]
+                       expectedCounts:@[@0, @0, @0]];
+    } else {
+        // Delete all the objects from the Realms.
+        [self waitForDownloadsForRealm:realmA];
+        [self waitForDownloadsForRealm:realmB];
+        [self waitForDownloadsForRealm:realmC];
+        CHECK_COUNT(4, SyncObject, realmA);
+        CHECK_COUNT(5, SyncObject, realmB);
+        CHECK_COUNT(2, SyncObject, realmC);
+        [realmA beginWriteTransaction];
+        [realmA deleteAllObjects];
+        [realmA commitWriteTransaction];
+        [realmB beginWriteTransaction];
+        [realmB deleteAllObjects];
+        [realmB commitWriteTransaction];
+        [realmC beginWriteTransaction];
+        [realmC deleteAllObjects];
+        [realmC commitWriteTransaction];
+        [self waitForUploadsForRealm:realmA];
+        [self waitForUploadsForRealm:realmB];
+        [self waitForUploadsForRealm:realmC];
+        CHECK_COUNT(0, SyncObject, realmA);
+        CHECK_COUNT(0, SyncObject, realmB);
+        CHECK_COUNT(0, SyncObject, realmC);
+    }
+}
+
+#pragma mark - Session Lifetime
+
+/// When a session opened by a Realm goes out of scope, it should stay alive long enough to finish any waiting uploads.
+- (void)testUploadChangesWhenRealmOutOfScope {
+    const NSInteger OBJECT_COUNT = 10000;
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    if (self.isParent) {
+        // Open the Realm in an autorelease pool so that it is destroyed as soon as possible.
+        @autoreleasepool {
+            RLMRealm *realm = [self openRealmForURL:url user:user];
+            [realm beginWriteTransaction];
+            for (NSInteger i=0; i<OBJECT_COUNT; i++) {
+                [realm addObject:[[SyncObject alloc] initWithValue:@[[NSString stringWithFormat:@"parent-%@", @(i+1)]]]];
+            }
+            [realm commitWriteTransaction];
+            CHECK_COUNT(OBJECT_COUNT, SyncObject, realm);
+        }
+        // Run the sub-test. (Give the upload a bit of time to start.)
+        // NOTE: This sleep should be fine because:
+        // - There is currently no API that allows asynchronous coordination for waiting for an upload to begin.
+        // - A delay longer than the specified one will not affect the outcome of the test.
+        sleep(2);
+        RLMRunChildAndWait();
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Wait for download to complete.
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(OBJECT_COUNT, SyncObject, realm);
+    }
+}
+
+#pragma mark - Logging Back In
+
+/// A Realm that was opened before a user logged out should be able to resume uploading if the user logs back in.
+- (void)testLogBackInSameRealmUpload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+
+    if (self.isParent) {
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
+        CHECK_COUNT(1, SyncObject, realm);
+        [self waitForUploadsForRealm:realm];
+        // Log out the user.
+        [user logOut];
+        // Log the user back in.
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-2", @"parent-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+        RLMRunChildAndWait();
+    } else {
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+/// A Realm that was opened before a user logged out should be able to resume downloading if the user logs back in.
+- (void)testLogBackInSameRealmDownload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+
+    if (self.isParent) {
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
+        CHECK_COUNT(1, SyncObject, realm);
+        [self waitForUploadsForRealm:realm];
+        // Log out the user.
+        [user logOut];
+        // Log the user back in.
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        RLMRunChildAndWait();
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    } else {
+        [self waitForDownloadsForRealm:realm];
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+/// A Realm that was opened while a user was logged out should be able to start uploading if the user logs back in.
+- (void)testLogBackInDeferredRealmUpload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    NSError *error = nil;
+    if (self.isParent) {
+        // Semaphore for knowing when the Realm is successfully opened for sync.
+        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+        RLMRealmConfiguration *config = [user configurationWithURL:url fullSynchronization:true];
+        [user logOut];
+        // Open a Realm after the user's been logged out.
+        [self primeSyncManagerWithSemaphore:sema];
+        RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
+        XCTAssertNil(error, @"Error when opening Realm: %@", error);
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
+        CHECK_COUNT(1, SyncObject, realm);
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        // Wait for the Realm's session to be bound.
+        WAIT_FOR_SEMAPHORE(sema, 30);
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-2", @"parent-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+        RLMRunChildAndWait();
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        XCTAssertNil(error, @"Error when opening Realm: %@", error);
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+/// A Realm that was opened while a user was logged out should be able to start downloading if the user logs back in.
+- (void)testLogBackInDeferredRealmDownload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    NSError *error = nil;
+    if (self.isParent) {
+        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+        RLMRunChildAndWait();
+        RLMRealmConfiguration *config = [user configurationWithURL:url fullSynchronization:true];
+        [user logOut];
+        // Open a Realm after the user's been logged out.
+        [self primeSyncManagerWithSemaphore:sema];
+        RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:&error];
+        XCTAssertNil(error, @"Error when opening Realm: %@", error);
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
+        CHECK_COUNT(1, SyncObject, realm);
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        // Wait for the Realm's session to be bound.
+        WAIT_FOR_SEMAPHORE(sema, 30);
+        [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@4]];
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        XCTAssertNil(error, @"Error when opening Realm: %@", error);
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(3, SyncObject, realm);
+    }
+}
+
+/// After logging back in, a Realm whose path has been opened for the first time should properly upload changes.
+- (void)testLogBackInOpenFirstTimePathUpload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    // Now run a basic multi-client test.
+    if (self.isParent) {
+        // Log out the user.
+        [user logOut];
+        // Log the user back in.
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                     server:[RLMObjectServerTests authServerURL]];
+        // Open the Realm (for the first time).
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(2, SyncObject, realm);
+        RLMRunChildAndWait();
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Add objects.
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(2, SyncObject, realm);
+    }
+}
+
+/// After logging back in, a Realm whose path has been opened for the first time should properly download changes.
+- (void)testLogBackInOpenFirstTimePathDownload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    // Now run a basic multi-client test.
+    if (self.isParent) {
+        // Log out the user.
+        [user logOut];
+        // Log the user back in.
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        // Open the Realm (for the first time).
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Run the sub-test.
+        RLMRunChildAndWait();
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(2, SyncObject, realm);
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Add objects.
+        [self waitForDownloadsForRealm:realm];
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(2, SyncObject, realm);
+    }
+}
+
+/// If a client logs in, connects, logs out, and logs back in, sync should properly upload changes for a new
+/// `RLMRealm` that is opened for the same path as a previously-opened Realm.
+- (void)testLogBackInReopenRealmUpload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    // Open the Realm
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+    if (self.isParent) {
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(1, SyncObject, realm);
+        // Log out the user.
+        [user logOut];
+        // Log the user back in.
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        // Open the Realm again.
+        realm = [self immediatelyOpenRealmForURL:url user:user];
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3", @"child-4"]];
+        CHECK_COUNT(5, SyncObject, realm);
+        [self waitForUploadsForRealm:realm];
+        RLMRunChildAndWait();
+    } else {
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(5, SyncObject, realm);
+    }
+}
+
+/// If a client logs in, connects, logs out, and logs back in, sync should properly download changes for a new
+/// `RLMRealm` that is opened for the same path as a previously-opened Realm.
+- (void)testLogBackInReopenRealmDownload {
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                              server:[RLMObjectServerTests authServerURL]];
+    // Open the Realm
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+    if (self.isParent) {
+        [self addSyncObjectsToRealm:realm descriptions:@[@"parent-1"]];
+        [self waitForUploadsForRealm:realm];
+        XCTAssert([SyncObject allObjectsInRealm:realm].count == 1, @"Expected 1 item");
+        // Log out the user.
+        [user logOut];
+        // Log the user back in.
+        user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                   register:NO]
+                                      server:[RLMObjectServerTests authServerURL]];
+        // Run the sub-test.
+        RLMRunChildAndWait();
+        // Open the Realm again and get the items.
+        realm = [self immediatelyOpenRealmForURL:url user:user];
+        [self waitForDownloadsForUser:user realms:@[realm] realmURLs:@[url] expectedCounts:@[@5]];
+    } else {
+        // Add objects.
+        [self waitForDownloadsForRealm:realm];
+        CHECK_COUNT(1, SyncObject, realm);
+        [self addSyncObjectsToRealm:realm descriptions:@[@"child-1", @"child-2", @"child-3", @"child-4"]];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(5, SyncObject, realm);
+    }
+}
+
+#pragma mark - Session suspend and resume
+
+- (void)testSuspendAndResume {
+    NSURL *urlA = CUSTOM_REALM_URL(@"a");
+    NSURL *urlB = CUSTOM_REALM_URL(@"b");
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    RLMRealm *realmA = [self openRealmForURL:urlA user:user];
+    RLMRealm *realmB = [self openRealmForURL:urlB user:user];
+    if (self.isParent) {
+        [self waitForDownloadsForRealm:realmA];
+        [self waitForDownloadsForRealm:realmB];
+        CHECK_COUNT(0, SyncObject, realmA);
+        CHECK_COUNT(0, SyncObject, realmB);
+
+        // Suspend the session for realm A and then add an object to each Realm
+        RLMSyncSession *sessionA = [RLMSyncSession sessionForRealm:realmA];
+        [sessionA suspend];
+
+        [self addSyncObjectsToRealm:realmA descriptions:@[@"child-A1"]];
+        [self addSyncObjectsToRealm:realmB descriptions:@[@"child-B1"]];
+        [self waitForUploadsForRealm:realmB];
+
+        RLMRunChildAndWait();
+
+        // A should still be 1 since it's suspended. If it wasn't suspended, it
+        // should have downloaded before B due to the ordering in the child.
+        [self waitForDownloadsForRealm:realmB];
+        CHECK_COUNT(1, SyncObject, realmA);
+        CHECK_COUNT(3, SyncObject, realmB);
+
+        // A should see the other two from the child after resuming
+        [sessionA resume];
+        [self waitForDownloadsForRealm:realmA];
+        CHECK_COUNT(3, SyncObject, realmA);
+    } else {
+        // Child shouldn't see the object in A
+        [self waitForDownloadsForRealm:realmA];
+        [self waitForDownloadsForRealm:realmB];
+        CHECK_COUNT(0, SyncObject, realmA);
+        CHECK_COUNT(1, SyncObject, realmB);
+
+        [self addSyncObjectsToRealm:realmA descriptions:@[@"child-A2", @"child-A3"]];
+        [self waitForUploadsForRealm:realmA];
+        [self addSyncObjectsToRealm:realmB descriptions:@[@"child-B2", @"child-B3"]];
+        [self waitForUploadsForRealm:realmB];
+        CHECK_COUNT(2, SyncObject, realmA);
+        CHECK_COUNT(3, SyncObject, realmB);
+    }
+}
+
+#pragma mark - Client reset
+
+/// Ensure that a client reset error is propagated up to the binding successfully.
+- (void)testClientReset {
+    NSURL *url = REALM_URL();
+    NSString *sessionName = NSStringFromSelector(_cmd);
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:sessionName
+                                                                                            register:true]
+                                               server:[RLMObjectServerTests authServerURL]];
+    // Open the Realm
+    __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:user];
+
+    __block NSError *theError = nil;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Waiting for error handler to be called..."];
+    [RLMSyncManager sharedManager].errorHandler = ^void(NSError *error, RLMSyncSession *session) {
+        // Make sure we're actually looking at the right session.
+        XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
+        theError = error;
+        [ex fulfill];
+    };
+    [user simulateClientResetErrorForSession:url];
+    [self waitForExpectationsWithTimeout:10 handler:nil];
+    XCTAssertNotNil(theError);
+    XCTAssertTrue(theError.code == RLMSyncErrorClientResetError);
+    NSString *pathValue = [theError rlmSync_clientResetBackedUpRealmPath];
+    XCTAssertNotNil(pathValue);
+    // Sanity check the recovery path.
+    NSString *recoveryPath = @"io.realm.object-server-recovered-realms/recovered_realm";
+    XCTAssertTrue([pathValue rangeOfString:recoveryPath].location != NSNotFound);
+    XCTAssertNotNil([theError rlmSync_errorActionToken]);
+}
+
+/// Test manually initiating client reset.
+- (void)testClientResetManualInitiation {
+    NSURL *url = REALM_URL();
+    NSString *sessionName = NSStringFromSelector(_cmd);
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:sessionName
+                                                                                            register:true]
+                                               server:[RLMObjectServerTests authServerURL]];
+    __block NSError *theError = nil;
+    @autoreleasepool {
+        __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:user];
+        XCTestExpectation *ex = [self expectationWithDescription:@"Waiting for error handler to be called..."];
+        [RLMSyncManager sharedManager].errorHandler = ^void(NSError *error, RLMSyncSession *session) {
+            // Make sure we're actually looking at the right session.
+            XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
+            theError = error;
+            [ex fulfill];
+        };
+        [user simulateClientResetErrorForSession:url];
+        [self waitForExpectationsWithTimeout:10 handler:nil];
+        XCTAssertNotNil(theError);
+    }
+    // At this point the Realm should be invalidated and client reset should be possible.
+    NSString *pathValue = [theError rlmSync_clientResetBackedUpRealmPath];
+    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:pathValue]);
+    [RLMSyncSession immediatelyHandleError:[theError rlmSync_errorActionToken]];
+    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:pathValue]);
+}
+
+#pragma mark - Progress Notifications
+
+- (void)testStreamingDownloadNotifier {
+    const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    __block NSInteger callCount = 0;
+    __block NSUInteger transferred = 0;
+    __block NSUInteger transferrable = 0;
+    // Open the Realm
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+    if (self.isParent) {
+        __block BOOL hasBeenFulfilled = NO;
+        // Register a notifier.
+        RLMSyncSession *session = [user sessionForURL:url];
+        XCTAssertNotNil(session);
+        XCTestExpectation *ex = [self expectationWithDescription:@"streaming-download-notifier"];
+        RLMProgressNotificationToken *token = [session addProgressNotificationForDirection:RLMSyncProgressDirectionDownload
+                                                                                      mode:RLMSyncProgressModeReportIndefinitely
+                                                                                     block:^(NSUInteger xfr, NSUInteger xfb) {
+            // Make sure the values are increasing, and update our stored copies.
+            XCTAssert(xfr >= transferred);
+            XCTAssert(xfb >= transferrable);
+            transferred = xfr;
+            transferrable = xfb;
+            callCount++;
+            if (transferrable > 0 && transferred >= transferrable && !hasBeenFulfilled) {
+                [ex fulfill];
+                hasBeenFulfilled = YES;
+            }
+        }];
+        // Wait for the child process to upload everything.
+        RLMRunChildAndWait();
+        [self waitForExpectationsWithTimeout:10.0 handler:nil];
+        [token invalidate];
+        // The notifier should have been called at least twice: once at the beginning and at least once
+        // to report progress.
+        XCTAssert(callCount > 1);
+        XCTAssert(transferred >= transferrable,
+                  @"Transferred (%@) needs to be greater than or equal to transferrable (%@)",
+                  @(transferred), @(transferrable));
+    } else {
+        // Write lots of data to the Realm, then wait for it to be uploaded.
+        [realm beginWriteTransaction];
+        for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
+            [realm addObject:[HugeSyncObject object]];
+        }
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+    }
+}
+
+- (void)testStreamingUploadNotifier {
+    const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    __block NSInteger callCount = 0;
+    __block NSUInteger transferred = 0;
+    __block NSUInteger transferrable = 0;
+    __block BOOL hasBeenFulfilled = NO;
+    // Open the Realm
+    RLMRealm *realm = [self openRealmForURL:url user:user];
+
+    // Register a notifier.
+    RLMSyncSession *session = [user sessionForURL:url];
+    XCTAssertNotNil(session);
+    XCTestExpectation *ex = [self expectationWithDescription:@"streaming-upload-expectation"];
+    RLMProgressNotificationToken *token = [session addProgressNotificationForDirection:RLMSyncProgressDirectionUpload
+                                                                                  mode:RLMSyncProgressModeReportIndefinitely
+                                                                                 block:^(NSUInteger xfr, NSUInteger xfb) {
+                                                                                     // Make sure the values are
+                                                                                     // increasing, and update our
+                                                                                     // stored copies.
+                                                                                     XCTAssert(xfr >= transferred);
+                                                                                     XCTAssert(xfb >= transferrable);
+                                                                                     transferred = xfr;
+                                                                                     transferrable = xfb;
+                                                                                     callCount++;
+                                                                                     if (transferred > 0
+                                                                                         && transferred >= transferrable
+                                                                                         && !hasBeenFulfilled) {
+                                                                                         [ex fulfill];
+                                                                                         hasBeenFulfilled = YES;
+                                                                                     }
+                                                                                 }];
+    // Upload lots of data
+    [realm beginWriteTransaction];
+    for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
+        [realm addObject:[HugeSyncObject object]];
+    }
+    [realm commitWriteTransaction];
+    // Wait for upload to begin and finish
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+    [token invalidate];
+    // The notifier should have been called at least twice: once at the beginning and at least once
+    // to report progress.
+    XCTAssert(callCount > 1);
+    XCTAssert(transferred >= transferrable,
+              @"Transferred (%@) needs to be greater than or equal to transferrable (%@)",
+              @(transferred), @(transferrable));
+}
+
+#pragma mark - Download Realm
+
+- (void)testDownloadRealm {
+    const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    if (self.isParent) {
+        // Wait for the child process to upload everything.
+        RLMRunChildAndWait();
+        XCTestExpectation *ex = [self expectationWithDescription:@"download-realm"];
+        RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:true];
+        XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil]);
+        [RLMRealm asyncOpenWithConfiguration:c
+                               callbackQueue:dispatch_get_main_queue()
+                                    callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
+            XCTAssertNil(error);
+            CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+            [ex fulfill];
+        }];
+        NSUInteger (^fileSize)(NSString *) = ^NSUInteger(NSString *path) {
+            NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
+            if (attributes)
+                return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
+
+            return 0;
+        };
+        NSUInteger sizeBefore = fileSize(c.pathOnDisk);
+        @autoreleasepool {
+            // We have partial transaction logs but no data
+            XCTAssertGreaterThan(sizeBefore, 0U);
+            XCTAssertTrue([[RLMRealm realmWithConfiguration:c error:nil] isEmpty]);
+        }
+        XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
+        [self waitForExpectationsWithTimeout:10.0 handler:nil];
+        XCTAssertGreaterThan(fileSize(c.pathOnDisk), sizeBefore);
+        XCTAssertNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Write lots of data to the Realm, then wait for it to be uploaded.
+        [realm beginWriteTransaction];
+        for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
+            [realm addObject:[HugeSyncObject object]];
+        }
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+    }
+}
+
+- (void)testDownloadAlreadyOpenRealm {
+    const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    if (!self.isParent) {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Write lots of data to the Realm, then wait for it to be uploaded.
+        [realm beginWriteTransaction];
+        for (NSInteger i = 0; i < NUMBER_OF_BIG_OBJECTS; i++) {
+            [realm addObject:[HugeSyncObject object]];
+        }
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+        return;
+    }
+
+    // Wait for the child process to upload everything.
+    RLMRunChildAndWait();
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"download-realm"];
+    RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:true];
+    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil]);
+    RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:nil];
+    CHECK_COUNT(0, HugeSyncObject, realm);
+    [RLMRealm asyncOpenWithConfiguration:c
+                           callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
+        XCTAssertNil(error);
+        CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+        [ex fulfill];
+    }];
+    auto fileSize = ^NSUInteger(NSString *path) {
+        NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
+        return [(NSNumber *)attributes[NSFileSize] unsignedLongLongValue];
+    };
+    NSUInteger sizeBefore = fileSize(c.pathOnDisk);
+    XCTAssertGreaterThan(sizeBefore, 0U);
+    XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    XCTAssertGreaterThan(fileSize(c.pathOnDisk), sizeBefore);
+    XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
+    CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+}
+
+- (void)testDownloadWhileOpeningRealm {
+    const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
+    NSURL *url = REALM_URL();
+    // Log in the user.
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    if (self.isParent) {
+        // Wait for the child process to upload everything.
+        RLMRunChildAndWait();
+        XCTestExpectation *ex = [self expectationWithDescription:@"download-realm"];
+        RLMRealmConfiguration *c = [user configurationWithURL:url fullSynchronization:true];
+        XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:c.pathOnDisk isDirectory:nil]);
+        [RLMRealm asyncOpenWithConfiguration:c
+                               callbackQueue:dispatch_get_main_queue()
+                                    callback:^(RLMRealm * _Nullable realm, NSError * _Nullable error) {
+            XCTAssertNil(error);
+            CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+            [ex fulfill];
+        }];
+        RLMRealm *realm = [RLMRealm realmWithConfiguration:c error:nil];
+        CHECK_COUNT(0, HugeSyncObject, realm);
+        XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
+        [self waitForExpectationsWithTimeout:10.0 handler:nil];
+        CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+        XCTAssertNotNil(RLMGetAnyCachedRealmForPath(c.pathOnDisk.UTF8String));
+    } else {
+        RLMRealm *realm = [self openRealmForURL:url user:user];
+        // Write lots of data to the Realm, then wait for it to be uploaded.
+        [realm beginWriteTransaction];
+        for (NSInteger i=0; i<NUMBER_OF_BIG_OBJECTS; i++) {
+            [realm addObject:[HugeSyncObject object]];
+        }
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+        CHECK_COUNT(NUMBER_OF_BIG_OBJECTS, HugeSyncObject, realm);
+    }
+}
+
+- (void)testDownloadCancelsOnAuthError {
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:self.isParent]
+                                               server:[RLMObjectServerTests authServerURL]];
+    auto c = [user configurationWithURL:[NSURL URLWithString:@"realm://127.0.0.1:9080/invalid"] fullSynchronization:true];
+    auto ex = [self expectationWithDescription:@"async open"];
+    [RLMRealm asyncOpenWithConfiguration:c callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm *realm, NSError *error) {
+                                    XCTAssertNil(realm);
+                                    XCTAssertNotNil(error);
+                                    [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+#pragma mark - Compact on Launch
+
+- (void)testCompactOnLaunch {
+    NSURL *url = REALM_URL();
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+
+    NSString *path;
+    // Create a large object and then delete it in the next transaction so that
+    // the file is bloated
+    @autoreleasepool {
+        auto realm = [self openRealmForURL:url user:user];
+        [realm beginWriteTransaction];
+        [realm addObject:[HugeSyncObject object]];
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+
+        [realm beginWriteTransaction];
+        [realm deleteAllObjects];
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+        [self waitForDownloadsForRealm:realm];
+
+        path = realm.configuration.pathOnDisk;
+    }
+
+    auto fileManager = NSFileManager.defaultManager;
+    auto initialSize = [[fileManager attributesOfItemAtPath:path error:nil][NSFileSize] unsignedLongLongValue];
+
+    // Reopen the file with a shouldCompactOnLaunch block and verify that it is
+    // actually compacted
+    auto config = [user configurationWithURL:url fullSynchronization:true];
+    __block bool blockCalled = false;
+    config.shouldCompactOnLaunch = ^(NSUInteger, NSUInteger){
+        blockCalled = true;
+        return YES;
+    };
+
+    @autoreleasepool {
+        [RLMRealm realmWithConfiguration:config error:nil];
+    }
+    XCTAssertTrue(blockCalled);
+
+    auto finalSize = [[fileManager attributesOfItemAtPath:path error:nil][NSFileSize] unsignedLongLongValue];
+    XCTAssertLessThan(finalSize, initialSize);
+    XCTAssertLessThan(finalSize, 10000U);
+}
+
+#pragma mark - Offline Client Reset
+
+- (void)testOfflineClientReset {
+    NSError *error;
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                            register:YES]
+                                               server:[RLMObjectServerTests authServerURL]];
+    NSURL *sourceFileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"sync-1.x" withExtension:@"realm"];
+    NSString *fileName = [NSString stringWithFormat:@"%@.realm", [NSUUID new]];
+    NSURL *fileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName]];
+    [NSFileManager.defaultManager copyItemAtURL:sourceFileURL toURL:fileURL error:&error];
+    XCTAssertNil(error);
+    if (error) {
+        return;
+    }
+
+    RLMRealmConfiguration *configuration = [user configurationWithURL:REALM_URL() fullSynchronization:true];
+    RLMSyncConfiguration *syncConfig = configuration.syncConfiguration;
+    syncConfig.customFileURL = fileURL;
+    configuration.syncConfiguration = syncConfig;
+
+    RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:&error];
+    XCTAssertNil(realm);
+    XCTAssertEqualObjects(error.domain, RLMErrorDomain);
+    XCTAssertEqual(error.code, RLMErrorIncompatibleSyncedFile);
+    RLMRealmConfiguration *backupConfiguration = error.userInfo[RLMBackupRealmConfigurationErrorKey];
+    XCTAssertNotNil(backupConfiguration);
+
+    // Open the backup Realm with a schema subset since it was created using the schema from .NET's unit tests.
+    // The Person class is declared in SwiftObjectServerTests.swift.
+    backupConfiguration.objectClasses = @[NSClassFromString(@"Person")];
+
+    error = nil;
+    RLMRealm *backupRealm = [RLMRealm realmWithConfiguration:backupConfiguration error:&error];
+    XCTAssertNotNil(backupRealm);
+    XCTAssertNil(error);
+
+    RLMResults *people = [backupRealm allObjects:@"Person"];
+    XCTAssertEqual(people.count, 1u);
+    XCTAssertEqualObjects([people[0] valueForKey:@"FirstName"], @"John");
+    XCTAssertEqualObjects([people[0] valueForKey:@"LastName"], @"Smith");
+
+    error = nil;
+    realm = [RLMRealm realmWithConfiguration:configuration error:&error];
+    XCTAssertNotNil(realm);
+    XCTAssertNil(error);
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+- (void)testAutomaticSyncConfiguration {
+    NSURL *server = [RLMObjectServerTests authServerURL];
+
+    // Automatic configuration should throw when there are no logged-in users.
+    XCTAssertThrows([RLMSyncConfiguration automaticConfiguration]);
+
+    RLMSyncCredentials *credsA = [RLMObjectServerTests basicCredentialsWithName:@"a" register:YES];
+    RLMSyncUser *userA = [self logInUserForCredentials:credsA server:server];
+
+    // Now that there's a logged-in user, we should be able to retrieve the configuration.
+    RLMRealmConfiguration *configuration = [RLMSyncConfiguration automaticConfiguration];
+    XCTAssert(configuration);
+
+    @autoreleasepool {
+        // And open it successfully.
+        RLMRealm *realm = [self openRealmWithConfiguration:configuration];
+        [self waitForDownloadsForRealm:realm];
+    }
+
+
+    RLMSyncCredentials *credsB = [RLMObjectServerTests basicCredentialsWithName:@"b" register:YES];
+    RLMSyncUser *userB = [self logInUserForCredentials:credsB server:server];
+
+    // Automatic configuration should throw since there's more than one logged-in user.
+    XCTAssertThrows([RLMSyncConfiguration automaticConfiguration]);
+
+    // It should still be possible to explicitly retrieve an automatic configuration for a user.
+    RLMRealmConfiguration *configurationA = [RLMSyncConfiguration automaticConfigurationForUser:userA];
+    XCTAssert(configurationA);
+    XCTAssertEqualObjects(configuration.syncConfiguration, configurationA.syncConfiguration);
+
+    RLMRealmConfiguration *configurationB = [RLMSyncConfiguration automaticConfigurationForUser:userB];
+    XCTAssert(configurationB);
+    XCTAssertNotEqualObjects(configuration.syncConfiguration, configurationB.syncConfiguration);
+
+
+    [userB logOut];
+
+    // Now that we're back to a single logged-in user, we should be able to retrieve the configuration.
+    configuration = [RLMSyncConfiguration automaticConfiguration];
+    XCTAssert(configuration);
+}
+#pragma clang diagnostic pop
+
+#pragma mark - Partial sync
+
+- (void)testPartialSync {
+    // Make credentials.
+    NSString *name = NSStringFromSelector(_cmd);
+    NSURL *server = [RLMObjectServerTests authServerURL];
+
+    // Log in and populate the Realm.
+    @autoreleasepool {
+        RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:name register:YES];
+        RLMSyncUser *user = [self logInUserForCredentials:creds server:server];
+        RLMRealmConfiguration *configuration = [user configuration];
+        RLMRealm *realm = [self openRealmWithConfiguration:configuration];
+        [realm beginWriteTransaction];
+        // FIXME: make this less hideous
+        // Add ten of each object
+        [realm addObject:[PartialSyncObjectA objectWithNumber:0 string:@"realm"]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:1 string:@""]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:2 string:@""]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:3 string:@""]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:4 string:@"realm"]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:5 string:@"sync"]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:6 string:@"partial"]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:7 string:@"partial"]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:8 string:@"partial"]];
+        [realm addObject:[PartialSyncObjectA objectWithNumber:9 string:@"partial"]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:0 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:1 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:2 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:3 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:4 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:5 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:6 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:7 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:8 firstString:@"" secondString:@""]];
+        [realm addObject:[PartialSyncObjectB objectWithNumber:9 firstString:@"" secondString:@""]];
+        [realm commitWriteTransaction];
+        [self waitForUploadsForRealm:realm];
+    }
+
+    // Log back in and do partial sync stuff.
+    @autoreleasepool {
+        RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:name register:NO];
+        RLMSyncUser *user = [self logInUserForCredentials:creds server:server];
+        RLMRealmConfiguration *configuration = [user configuration];
+        RLMRealm *realm = [self openRealmWithConfiguration:configuration];
+
+        // Perform some partial sync queries
+        RLMResults *objects = [PartialSyncObjectA objectsInRealm:realm where:@"number > 5"];
+        RLMSyncSubscription *subscription = [objects subscribeWithName:@"query"];
+
+        // Wait for the results to become available.
+        XCTestExpectation *ex = [[XCTKVOExpectation alloc] initWithKeyPath:@"state" object:subscription expectedValue:@(RLMSyncSubscriptionStateComplete)];
+        [self waitForExpectations:@[ex] timeout:20.0];
+
+        // Verify that we got what we're looking for
+        XCTAssertEqual(objects.count, 4U);
+        for (PartialSyncObjectA *object in objects) {
+            XCTAssertGreaterThan(object.number, 5);
+            XCTAssertEqualObjects(object.string, @"partial");
+        }
+
+        // Verify that we didn't get any other objects
+        XCTAssertEqual([PartialSyncObjectA allObjectsInRealm:realm].count, objects.count);
+        XCTAssertEqual([PartialSyncObjectB allObjectsInRealm:realm].count, 0u);
+
+
+        // Create a subscription with the same name but a different query. This should trigger an error.
+        RLMResults *objects2 = [PartialSyncObjectA objectsInRealm:realm where:@"number < 5"];
+        RLMSyncSubscription *subscription2 = [objects2 subscribeWithName:@"query"];
+
+        // Wait for the error to be reported.
+        XCTestExpectation *ex2 = [[XCTKVOExpectation alloc] initWithKeyPath:@"state" object:subscription2 expectedValue:@(RLMSyncSubscriptionStateError)];
+        [self waitForExpectations:@[ex2] timeout:20.0];
+        XCTAssertNotNil(subscription2.error);
+
+        // Unsubscribe from the query, and ensure that it correctly transitions to the invalidated state.
+        [subscription unsubscribe];
+        XCTestExpectation *ex3 = [[XCTKVOExpectation alloc] initWithKeyPath:@"state" object:subscription expectedValue:@(RLMSyncSubscriptionStateInvalidated)];
+        [self waitForExpectations:@[ex3] timeout:20.0];
+    }
+}
+
+#pragma mark - Certificate pinning
+
+- (void)attemptLoginWithUsername:(NSString *)userName callback:(void (^)(RLMSyncUser *, NSError *))callback {
+    NSURL *url = [RLMObjectServerTests secureAuthServerURL];
+    RLMSyncCredentials *creds = [RLMObjectServerTests basicCredentialsWithName:userName register:YES];
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@"HTTP login"];
+    [RLMSyncUser logInWithCredentials:creds authServerURL:url
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+                             NSLog(@"onCompletion %@ %@", user, error);
+                             callback(user, error);
+                             [expectation fulfill];
+                         }];
+    [self waitForExpectationsWithTimeout:4.0 handler:nil];
+}
+
+- (void)testHTTPSLoginFailsWithoutCertificate {
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
+        XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
+    }];
+}
+
+static NSURL *certificateURL(NSString *filename) {
+    return [NSURL fileURLWithPath:[[[@(__FILE__) stringByDeletingLastPathComponent]
+                                    stringByAppendingPathComponent:@"certificates"]
+                                   stringByAppendingPathComponent:filename]];
+}
+
+- (void)testHTTPSLoginFailsWithIncorrectCertificate {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"not-localhost.cer")};
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
+        XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
+    }];
+}
+
+- (void)testHTTPSLoginFailsWithInvalidPathToCertificate {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"nonexistent.pem")};
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSCocoaErrorDomain);
+        XCTAssertEqual(error.code, NSFileReadNoSuchFileError);
+    }];
+}
+
+- (void)testHTTPSLoginFailsWithDifferentValidCert {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost-other.cer")};
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
+        XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
+    }];
+}
+
+- (void)testHTTPSLoginFailsWithFileThatIsNotACert {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"../test-ros-server.js")};
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSOSStatusErrorDomain);
+        XCTAssertEqual(error.code, errSecUnknownFormat);
+    }];
+}
+
+- (void)testHTTPSLoginDoesNotUseCertificateForDifferentDomain {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"example.com": certificateURL(@"localhost.cer")};
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
+        XCTAssertEqual(error.code, NSURLErrorServerCertificateUntrusted);
+    }];
+}
+
+- (void)testHTTPSLoginSucceedsWithValidSelfSignedCertificate {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost.cer")};
+
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNotNil(user);
+        XCTAssertNil(error);
+    }];
+}
+
+- (void)testConfigurationFromUserAutomaticallyUsesCert {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost.cer")};
+
+    __block RLMSyncUser *user;
+    [self attemptLoginWithUsername:NSStringFromSelector(_cmd) callback:^(RLMSyncUser *u, NSError *error) {
+        XCTAssertNotNil(u);
+        XCTAssertNil(error);
+        user = u;
+    }];
+
+    RLMRealmConfiguration *config = [user configuration];
+    XCTAssertEqualObjects(config.syncConfiguration.realmURL.scheme, @"realms");
+    XCTAssertEqualObjects(config.syncConfiguration.pinnedCertificateURL, certificateURL(@"localhost.cer"));
+
+    // Verify that we can actually open the Realm
+    auto realm = [self openRealmWithConfiguration:config];
+    NSError *error;
+    [self waitForUploadsForRealm:realm error:&error];
+    XCTAssertNil(error);
+}
+
+- (void)verifyOpenSucceeds:(RLMRealmConfiguration *)config {
+    auto realm = [self openRealmWithConfiguration:config];
+    NSError *error;
+    [self waitForUploadsForRealm:realm error:&error];
+    XCTAssertNil(error);
+}
+
+- (void)verifyOpenFails:(RLMRealmConfiguration *)config {
+    [self openRealmWithConfiguration:config];
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@"wait for error"];
+    RLMSyncManager.sharedManager.errorHandler = ^(NSError *error, __unused RLMSyncSession *session) {
+        XCTAssertTrue([error.domain isEqualToString:RLMSyncErrorDomain]);
+        XCTAssertFalse([[error.userInfo[kRLMSyncUnderlyingErrorKey] domain] isEqualToString:RLMSyncErrorDomain]);
+        [expectation fulfill];
+    };
+
+    [self waitForExpectationsWithTimeout:20.0 handler:nil];
+}
+
+- (void)testConfigurationFromInsecureUserAutomaticallyUsesCert {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"localhost.cer")};
+
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                       register:YES]
+                                               server:[RLMSyncTestCase authServerURL]];
+
+    RLMRealmConfiguration *config = [user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]];
+    XCTAssertEqualObjects(config.syncConfiguration.realmURL.scheme, @"realms");
+    XCTAssertEqualObjects(config.syncConfiguration.pinnedCertificateURL, certificateURL(@"localhost.cer"));
+
+    [self verifyOpenSucceeds:config];
+}
+
+- (void)testOpenSecureRealmWithNoCert {
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                       register:YES]
+                                               server:[RLMSyncTestCase authServerURL]];
+    [self verifyOpenFails:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]]];
+}
+
+- (void)testOpenSecureRealmWithIncorrectCert {
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"not-localhost.cer")};
+
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                       register:YES]
+                                               server:[RLMSyncTestCase authServerURL]];
+    [self verifyOpenFails:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]]];
+}
+
+- (void)DISABLE_testOpenSecureRealmWithMissingCertFile {
+    // FIXME: this currently crashes inside the sync library
+    RLMSyncManager.sharedManager.pinnedCertificatePaths = @{@"localhost": certificateURL(@"nonexistent.pem")};
+
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:NSStringFromSelector(_cmd)
+                                                                                       register:YES]
+                                               server:[RLMSyncTestCase authServerURL]];
+    [self verifyOpenFails:[user configurationWithURL:[NSURL URLWithString:@"realms://localhost:9443/~/default"]]];
+}
+
+@end

+ 1196 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMPermissionsAPITests.m

@@ -0,0 +1,1196 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import <XCTest/XCTest.h>
+
+// FIXME: Many permission tests appears to fail with the ROS 3.0.0 alpha releases.
+
+#import "RLMSyncTestCase.h"
+
+#import "RLMTestUtils.h"
+
+#define APPLY_PERMISSION(ma_permission, ma_user) \
+    APPLY_PERMISSION_WITH_MESSAGE(ma_permission, ma_user, @"Setting a permission should work")
+
+#define APPLY_PERMISSION_WITH_MESSAGE(ma_permission, ma_user, ma_message) {                                            \
+    XCTestExpectation *ex = [self expectationWithDescription:ma_message];                                              \
+    [ma_user applyPermission:ma_permission callback:^(NSError *err) {                                                  \
+        XCTAssertNil(err, @"Received an error when applying permission: %@", err);                                     \
+        [ex fulfill];                                                                                                  \
+    }];                                                                                                                \
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];                                                            \
+}                                                                                                                      \
+
+static NSURL *makeTestURL(NSString *name, RLMSyncUser *owner) {
+    NSString *userID = [owner identity] ?: @"~";
+    return [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@/%@", userID, name]];
+}
+
+static NSURL *makeTestGlobalURL(NSString *name) {
+    return [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@", name]];
+}
+
+static NSURL *makeTildeSubstitutedURL(NSURL *url, RLMSyncUser *user) {
+    return [NSURL URLWithString:[[url absoluteString] stringByReplacingOccurrencesOfString:@"~" withString:user.identity]];
+}
+
+@interface RLMPermissionsAPITests : RLMSyncTestCase
+
+@property (nonatomic, strong) NSString *currentUsernameBase;
+
+@property (nonatomic, strong) RLMSyncUser *userA;
+@property (nonatomic, strong) RLMSyncUser *userB;
+@property (nonatomic, strong) RLMSyncUser *userC;
+
+@property (nonatomic, strong) NSString *userBUsername;
+
+@end
+
+@implementation RLMPermissionsAPITests
+
+- (void)setUp {
+    [super setUp];
+    NSString *accountNameBase = [[NSUUID UUID] UUIDString];
+    self.currentUsernameBase = accountNameBase;
+    NSString *userNameA = [accountNameBase stringByAppendingString:@"a"];
+    self.userA = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameA register:YES]
+                                        server:[RLMSyncTestCase authServerURL]];
+
+    NSString *userNameB = [accountNameBase stringByAppendingString:@"b"];
+    self.userBUsername = userNameB;
+    self.userB = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameB register:YES]
+                                        server:[RLMSyncTestCase authServerURL]];
+
+    NSString *userNameC = [accountNameBase stringByAppendingString:@"c"];
+    self.userC = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameC register:YES]
+                                        server:[RLMSyncTestCase authServerURL]];
+}
+
+- (void)tearDown {
+    self.currentUsernameBase = nil;
+    [self.userA logOut];
+    [self.userB logOut];
+    [self.userC logOut];
+    self.userBUsername = nil;
+    [super tearDown];
+}
+
+#pragma mark - Permission validation methods
+
+// This macro is only used for the validation methods below.
+#define RECORD_FAILURE(ma_msg) [self recordFailureWithDescription:ma_msg inFile:file atLine:line expected:YES]
+
+#define CHECK_PERMISSION_PRESENT(ma_results, ma_permission) \
+    [self checkPresenceOfPermission:ma_permission inResults:ma_results line:__LINE__ file:@(__FILE__)]
+
+/// Check that the targeted permission is present in, or eventually appears in the results.
+- (void)checkPresenceOfPermission:(RLMSyncPermission *)permission
+                        inResults:(RLMResults<RLMSyncPermission *> *)results
+                             line:(NSUInteger)line
+                             file:(NSString *)file {
+    XCTestExpectation *ex = [self expectationWithDescription:@"Checking presence of permission..."];
+    RLMNotificationToken *token = [results addNotificationBlock:^(RLMResults *r, __unused id c, NSError *err) {
+        if (err) {
+            RECORD_FAILURE(@"Failed to retrieve permissions.");
+            [ex fulfill];
+            return;
+        }
+        if ([r indexOfObject:permission] != NSNotFound) {
+            [ex fulfill];
+        }
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:^(NSError *error) {
+        if (error) {
+            NSLog(@"Timed out. The final state of the permissions is %@; the desired permission was %@",
+                  results, permission);
+        }
+    }];
+    [token invalidate];
+}
+
+#define CHECK_PERMISSION_ABSENT(ma_results, ma_permission) \
+    [self checkAbsenceOfPermission:ma_permission inResults:ma_results line:__LINE__ file:@(__FILE__)]
+
+/// Check that the targeted permission is absent from, or eventually disappears from the results.
+- (void)checkAbsenceOfPermission:(RLMSyncPermission *)permission
+                        inResults:(RLMResults<RLMSyncPermission *> *)results
+                             line:(NSUInteger)line
+                             file:(NSString *)file {
+    XCTestExpectation *ex = [self expectationWithDescription:@"Checking absence of permission..."];
+    RLMNotificationToken *token = [results addNotificationBlock:^(RLMResults *r, __unused id c, NSError *err) {
+        if (err) {
+            RECORD_FAILURE(@"Failed to retrieve permissions.");
+            [ex fulfill];
+            return;
+        }
+        if ([r indexOfObject:permission] == NSNotFound) {
+            [ex fulfill];
+        }
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:^(NSError *error) {
+        if (error) {
+            NSLog(@"Timed out. The final state of the permissions is %@; the permission to check for %@",
+                  results, permission);
+        }
+    }];
+    [token invalidate];
+}
+
+#define CHECK_PERMISSION_COUNT_AT_LEAST(ma_results, ma_count) \
+    [self checkPermissionCountOfResults:ma_results atLeast:ma_count exact:NO line:__LINE__ file:@(__FILE__)];
+
+#define CHECK_PERMISSION_COUNT(ma_results, ma_count) \
+    [self checkPermissionCountOfResults:ma_results atLeast:ma_count exact:YES line:__LINE__ file:@(__FILE__)];
+
+- (void)checkPermissionCountOfResults:(RLMResults<RLMSyncPermission *> *)results
+                              atLeast:(NSInteger)count
+                                exact:(BOOL)exact
+                                 line:(NSUInteger)line
+                                 file:(NSString *)file {
+    // Check first.
+    if ((NSInteger)results.count == count || (!exact && (NSInteger)results.count > count)) {
+        return;
+    }
+    XCTestExpectation *ex = [self expectationWithDescription:@"Checking presence of permission..."];
+    RLMNotificationToken *token = [results addNotificationBlock:^(RLMResults *r, __unused id c, NSError *err) {
+        if (err) {
+            RECORD_FAILURE(@"Failed to retrieve permissions.");
+            [ex fulfill];
+            return;
+        }
+        NSInteger actualCount = (NSInteger)r.count;
+        if (actualCount == count || (!exact && actualCount > count)) {
+            [ex fulfill];
+            return;
+        }
+    }];
+    [self waitForExpectations:@[ex] timeout:20.0];
+    [token invalidate];
+}
+
+#undef RECORD_FAILURE
+
+#pragma mark - Helper methods
+
+- (RLMResults<RLMSyncPermission *> *)getPermissionResultsFor:(RLMSyncUser *)user {
+    return [self getPermissionResultsFor:user message:@"Get permission results"];
+}
+
+- (RLMResults<RLMSyncPermission *> *)getPermissionResultsFor:(RLMSyncUser *)user message:(NSString *)message {
+    // Get a reference to the permission results.
+    XCTestExpectation *ex = [self expectationWithDescription:message];
+    __block RLMResults<RLMSyncPermission *> *results = nil;
+    [user retrievePermissionsWithCallback:^(RLMResults<RLMSyncPermission *> *r, NSError *error) {
+        XCTAssertNil(error);
+        XCTAssertNotNil(r);
+        results = r;
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+    XCTAssertNotNil(results, @"getPermissionResultsFor: failed for user %@. No results.", user.identity);
+    return results;
+}
+
+#pragma mark - Permissions
+
+// FIXME ROS 2.0: works when ROS is manually provided, not when ROS is run as part of tests
+/// If user A grants user B read access to a Realm, user B should be able to read from it.
+- (void)testReadAccess {
+    __block void(^errorBlock)(NSError *) = nil;
+    [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
+        if (errorBlock) {
+            errorBlock(error);
+            errorBlock = nil;
+        } else {
+            XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
+        }
+    }];
+
+    NSString *testName = NSStringFromSelector(_cmd);
+    // Open a Realm for user A.
+    NSURL *userAURL = makeTestURL(testName, nil);
+    RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
+
+    // Have user A add some items to the Realm.
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:self.userA url:userAURL];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // Give user B read permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+    // Set the read permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Open the same Realm for user B.
+    NSURL *userBURL = makeTestURL(testName, self.userA);
+    RLMRealmConfiguration *userBConfig = [RLMRealmConfiguration defaultConfiguration];
+    userBConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userB realmURL:userBURL];
+    __block RLMRealm *userBRealm = nil;
+    XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
+    [RLMRealm asyncOpenWithConfiguration:userBConfig
+                           callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm *realm, NSError *err){
+                                    XCTAssertNil(err);
+                                    XCTAssertNotNil(realm);
+                                    userBRealm = realm;
+                                    [asyncOpenEx fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+    CHECK_COUNT(3, SyncObject, userBRealm);
+
+    // Ensure user B can't actually write to the Realm.
+    // Run this portion of the test on a background queue, since the error handler is dispatched onto the main queue.
+    XCTestExpectation *deniedEx = [self expectationWithDescription:@"Expect a permission denied error."];
+    errorBlock = ^(NSError *err) {
+        // Expect an error from the global error handler.
+        XCTAssertNotNil(err);
+        XCTAssertEqual(err.code, RLMSyncErrorPermissionDeniedError);
+        [deniedEx fulfill];
+    };
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    [self waitForExpectations:@[deniedEx] timeout:20.0];
+
+    // TODO: if we can get the session itself we can check to see if it's been errored out (as expected).
+
+    // Perhaps obviously, there should be no new objects.
+    CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userARealm);
+
+    // Administering the Realm should fail.
+    RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userBURL path]
+                                                                identity:self.userC.identity
+                                                             accessLevel:RLMSyncAccessLevelRead];
+    XCTestExpectation *manageEx = [self expectationWithDescription:@"Managing a Realm you can't manage should fail."];
+    [self.userB applyPermission:p2 callback:^(NSError *error) {
+        XCTAssertNotNil(error);
+        [manageEx fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// If user A grants user B write access to a Realm, user B should be able to write to it.
+- (void)testWriteAccess {
+    __block void(^errorBlock)(NSError *) = nil;
+    [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
+        if (errorBlock) {
+            errorBlock(error);
+            errorBlock = nil;
+        } else {
+            XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
+        }
+    }];
+
+    NSString *testName = NSStringFromSelector(_cmd);
+    // Open a Realm for user A.
+    NSURL *userAURL = makeTestURL(testName, nil);
+    RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
+
+    // Have user A add some items to the Realm.
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:self.userA url:userAURL];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // Give user B write permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelWrite];
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Open the Realm for user B. Since user B has write privileges, they should be able to open it 'normally'.
+    NSURL *userBURL = makeTestURL(testName, self.userA);
+    RLMRealm *userBRealm = [self openRealmForURL:userBURL user:self.userB];
+    CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
+
+    // Add some objects using user B.
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
+    [self waitForUploadsForUser:self.userB url:userBURL];
+    CHECK_COUNT(5, SyncObject, userBRealm);
+    CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
+
+    // Administering the Realm should fail.
+    RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userBURL path]
+                                                                identity:self.userC.identity
+                                                             accessLevel:RLMSyncAccessLevelRead];
+    XCTestExpectation *manageEx = [self expectationWithDescription:@"Managing a Realm you can't manage should fail."];
+    [self.userB applyPermission:p2 callback:^(NSError *error) {
+        XCTAssertNotNil(error);
+        [manageEx fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+/// If user A grants user B manage access to a Realm, user B should be able to set a permission for user C.
+- (void)testManageAccess {
+    __block void(^errorBlock)(NSError *) = nil;
+    [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
+        if (errorBlock) {
+            errorBlock(error);
+            errorBlock = nil;
+        } else {
+            XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
+        }
+    }];
+
+    NSString *testName = NSStringFromSelector(_cmd);
+    // Unresolved URL: ~/testManageAccess
+    NSURL *userAURLUnresolved = makeTestURL(testName, nil);
+    // Resolved URL: <User A ID>/testManageAccess
+    NSURL *userAURLResolved = makeTestURL(testName, self.userA);
+
+    // Open a Realm for user A.
+    RLMRealm *userARealm = [self openRealmForURL:userAURLUnresolved user:self.userA];
+
+    // Have user A add some items to the Realm.
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:self.userA url:userAURLUnresolved];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // Give user B admin permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURLUnresolved path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelAdmin];
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Open the Realm for user B. Since user B has admin privileges, they should be able to open it 'normally'.
+    RLMRealm *userBRealm = [self openRealmForURL:userAURLResolved user:self.userB];
+    CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
+
+    // Add some objects using user B.
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
+    [self waitForUploadsForUser:self.userB url:userAURLResolved];
+    CHECK_COUNT(5, SyncObject, userBRealm);
+    CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
+
+    // User B should be able to give user C write permissions to user A's Realm.
+    RLMSyncPermission *p2 = [[RLMSyncPermission alloc] initWithRealmPath:[userAURLResolved path]
+                                                                identity:self.userC.identity
+                                                             accessLevel:RLMSyncAccessLevelWrite];
+    APPLY_PERMISSION_WITH_MESSAGE(p2, self.userB, @"User B should be able to give C write permissions to A's Realm.");
+
+    // User C should be able to write to the Realm.
+    RLMRealm *userCRealm = [self openRealmForURL:userAURLResolved user:self.userC];
+    CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8"]];
+    [self waitForUploadsForUser:self.userC url:userAURLResolved];
+    CHECK_COUNT(8, SyncObject, userCRealm);
+    CHECK_COUNT_PENDING_DOWNLOAD(8, SyncObject, userARealm);
+    CHECK_COUNT_PENDING_DOWNLOAD(8, SyncObject, userBRealm);
+}
+
+/// If user A grants user B write access to a Realm via username, user B should be able to write to it.
+- (void)testWriteAccessViaUsername {
+    __block void(^workBlock)(NSError *) = ^(NSError *err) {
+        XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", err);
+    };
+    [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, __unused RLMSyncSession *session) {
+        if (workBlock) {
+            workBlock(error);
+        }
+    }];
+
+    NSString *testName = NSStringFromSelector(_cmd);
+    // Open a Realm for user A.
+    NSURL *userAURL = makeTestURL(testName, nil);
+    RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
+
+    // Have user A add some items to the Realm.
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:self.userA url:userAURL];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // Give user B write permissions to that Realm via user B's username.
+    NSString *userAFullPath = [makeTildeSubstitutedURL(userAURL, self.userA) path];
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:userAFullPath
+                                                               username:self.userBUsername
+                                                            accessLevel:RLMSyncAccessLevelWrite];
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Open the Realm for user B. Since user B has write privileges, they should be able to open it 'normally'.
+    NSURL *userBURL = makeTestURL(testName, self.userA);
+    RLMRealm *userBRealm = [self openRealmForURL:userBURL user:self.userB];
+    CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
+
+    // Add some objects using user B.
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
+    [self waitForUploadsForUser:self.userB url:userBURL];
+    CHECK_COUNT(5, SyncObject, userBRealm);
+    CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userARealm);
+}
+
+/// Setting a permission for all users should work.
+- (void)testWildcardWriteAccess {
+    // Open a Realm for user A.
+    NSString *testName = NSStringFromSelector(_cmd);
+    NSURL *ownerURL = makeTestURL(testName, nil);
+    NSURL *guestURL = makeTestURL(testName, self.userA);
+    RLMRealm *userARealm = [self openRealmForURL:ownerURL user:self.userA];
+
+    // Give all users write permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[ownerURL path]
+                                                               identity:@"*"
+                                                            accessLevel:RLMSyncAccessLevelWrite];
+
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Have user A write a few objects first.
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:self.userA url:ownerURL];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // User B should be able to write to the Realm.
+    RLMRealm *userBRealm = [self openRealmForURL:guestURL user:self.userB];
+    CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
+    [self waitForUploadsForUser:self.userB url:guestURL];
+    CHECK_COUNT(5, SyncObject, userBRealm);
+
+    // User C should be able to write to the Realm.
+    RLMRealm *userCRealm = [self openRealmForURL:guestURL user:self.userC];
+    CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8", @"child-9"]];
+    [self waitForUploadsForUser:self.userC url:guestURL];
+    CHECK_COUNT(9, SyncObject, userCRealm);
+}
+
+/// It should be possible to grant read-only access to a global Realm.
+- (void)testWildcardGlobalRealmReadAccess {
+    RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
+                                            username:[[NSUUID UUID] UUIDString]];
+
+    // Open a Realm for the admin user.
+    NSString *testName = NSStringFromSelector(_cmd);
+    NSURL *globalRealmURL = makeTestGlobalURL(testName);
+    RLMRealm *adminUserRealm = [self openRealmForURL:globalRealmURL user:admin];
+
+    // Give all users read permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
+                                                               identity:@"*"
+                                                            accessLevel:RLMSyncAccessLevelRead];
+
+    // Set the permission.
+    APPLY_PERMISSION_WITH_MESSAGE(p, admin, @"Setting wildcard permission should work.");
+
+    // Have the admin user write a few objects first.
+    [self addSyncObjectsToRealm:adminUserRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:admin url:globalRealmURL];
+    CHECK_COUNT(3, SyncObject, adminUserRealm);
+
+    // User B should be able to read from the Realm.
+    __block RLMRealm *userBRealm = nil;
+    RLMRealmConfiguration *userBConfig = [RLMRealmConfiguration defaultConfiguration];
+    userBConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userB realmURL:globalRealmURL];
+    XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
+    [RLMRealm asyncOpenWithConfiguration:userBConfig
+                           callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm *realm, NSError *err){
+                                    XCTAssertNil(err);
+                                    XCTAssertNotNil(realm);
+                                    userBRealm = realm;
+                                    [asyncOpenEx fulfill];
+                                }];
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+    CHECK_COUNT(3, SyncObject, userBRealm);
+
+    // User C should be able to read from the Realm.
+    __block RLMRealm *userCRealm = nil;
+    RLMRealmConfiguration *userCConfig = [RLMRealmConfiguration defaultConfiguration];
+    userCConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userC realmURL:globalRealmURL];
+    XCTestExpectation *asyncOpenEx2 = [self expectationWithDescription:@"Should asynchronously open a Realm"];
+    [RLMRealm asyncOpenWithConfiguration:userCConfig
+                           callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm *realm, NSError *err){
+                                    XCTAssertNil(err);
+                                    XCTAssertNotNil(realm);
+                                    userCRealm = realm;
+                                    [asyncOpenEx2 fulfill];
+                                }];
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+    CHECK_COUNT(3, SyncObject, userCRealm);
+}
+
+/// Setting a permission for all users on a global Realm (no `~`) should work.
+- (void)testWildcardGlobalRealmWriteAccess {
+    RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
+                                            username:[[NSUUID UUID] UUIDString]];
+
+    // Open a Realm for the admin user.
+    NSString *testName = NSStringFromSelector(_cmd);
+    NSURL *globalRealmURL = makeTestGlobalURL(testName);
+    RLMRealm *adminUserRealm = [self openRealmForURL:globalRealmURL user:admin];
+
+    // Give all users write permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[globalRealmURL path]
+                                                               identity:@"*"
+                                                            accessLevel:RLMSyncAccessLevelWrite];
+
+    // Set the permission.
+    APPLY_PERMISSION(p, admin);
+
+    // Have the admin user write a few objects first.
+    [self addSyncObjectsToRealm:adminUserRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:admin url:globalRealmURL];
+    CHECK_COUNT(3, SyncObject, adminUserRealm);
+
+    // User B should be able to write to the Realm.
+    RLMRealm *userBRealm = [self openRealmForURL:globalRealmURL user:self.userB];
+    CHECK_COUNT_PENDING_DOWNLOAD(3, SyncObject, userBRealm);
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5"]];
+    [self waitForUploadsForUser:self.userB url:globalRealmURL];
+    CHECK_COUNT(5, SyncObject, userBRealm);
+
+    // User C should be able to write to the Realm.
+    RLMRealm *userCRealm = [self openRealmForURL:globalRealmURL user:self.userC];
+    CHECK_COUNT_PENDING_DOWNLOAD(5, SyncObject, userCRealm);
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-6", @"child-7", @"child-8", @"child-9"]];
+    [self waitForUploadsForUser:self.userC url:globalRealmURL];
+    CHECK_COUNT(9, SyncObject, userCRealm);
+}
+
+#pragma mark - Permission change API
+
+/// Setting a permission should work, and then that permission should be able to be retrieved.
+- (void)testSettingPermission {
+    // First, there should be no permissions.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userA];
+    CHECK_PERMISSION_COUNT(results, 0);
+
+    // Open a Realm for user A.
+    NSURL *url = REALM_URL();
+    [self openRealmForURL:url user:self.userA];
+
+    // Give user B read permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+    
+    // Now retrieve the permissions again and make sure the new permission is properly set.
+    results = [self getPermissionResultsFor:self.userB message:@"One permission after setting the permission."];
+
+    // Expected permission: applies to user B, but for user A's Realm.
+    CHECK_PERMISSION_PRESENT(results, p);
+
+    // Check getting permission by its index.
+    NSUInteger index = [results indexOfObject:p];
+    XCTAssertNotEqual(index, NSNotFound);
+    XCTAssertEqualObjects(p, [results objectAtIndex:index]);
+}
+
+/// Deleting a permission should work.
+- (void)testDeletingPermission {
+    // Open a Realm for user A.
+    NSURL *url = REALM_URL();
+    [self openRealmForURL:url user:self.userA];
+
+    // Give user B read permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Now retrieve the permissions again and make sure the new permission is properly set.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB
+                                                                     message:@"Setting new permission."];
+    CHECK_PERMISSION_PRESENT(results, p);
+
+    // Delete the permission.
+    XCTestExpectation *ex3 = [self expectationWithDescription:@"Deleting a permission should work."];
+    [self.userA revokePermission:p callback:^(NSError *error) {
+        XCTAssertNil(error);
+        [ex3 fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Make sure the permission deletion is properly reflected.
+    CHECK_PERMISSION_COUNT(results, 0);
+}
+
+/// Observing permission changes should work.
+- (void)testObservingPermission {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    // Open a Realm for user A.
+    NSURL *url = REALM_URL();
+    [self openRealmForURL:url user:self.userA];
+
+    // Register notifications.
+    XCTestExpectation *noteEx = [self expectationWithDescription:@"Notification should fire."];
+    RLMNotificationToken *token = [results addNotificationBlock:^(__unused id r, __unused id c, NSError *error) {
+        XCTAssertNil(error);
+        if (results.count > 0) {
+            [noteEx fulfill];
+        }
+    }];
+
+    // Give user B read permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+
+    // Set the permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    // Wait for the notification to be fired.
+    [self waitForExpectations:@[noteEx] timeout:2.0];
+    [token invalidate];
+    CHECK_PERMISSION_PRESENT(results, p);
+}
+
+/// KVC getting and setting should work properly for `RLMResults<RLMSyncPermission>`.
+- (void)testKVCWithPermissionsResults {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    NSURL *url1 = CUSTOM_REALM_URL(@"r1");
+    NSURL *url2 = CUSTOM_REALM_URL(@"r2");
+    __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
+    NSString *uB = self.userB.identity;
+
+    // Give user B read permissions to r1 and r2.
+    NSString *path1 = [makeTildeSubstitutedURL(url1, self.userA) path];
+    id p1 = [[RLMSyncPermission alloc] initWithRealmPath:path1
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r1 permission for user B should work.");
+    NSString *path2 = [makeTildeSubstitutedURL(url2, self.userA) path];
+    id p2 = [[RLMSyncPermission alloc] initWithRealmPath:path2
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r2 permission for user B should work.");
+
+    // Wait for all the permissions to show up.
+    CHECK_PERMISSION_PRESENT(results, p1);
+    CHECK_PERMISSION_PRESENT(results, p2);
+
+    // Now use `valueForKey`
+    NSArray *selfValues = [results valueForKey:@"self"];
+    XCTAssert(selfValues.count == results.count);
+    for (id object in selfValues) {
+        XCTAssert([object isKindOfClass:[RLMSyncPermission class]]);
+    }
+
+    NSArray *identityValues = [results valueForKey:@"path"];
+    XCTAssert(identityValues.count == results.count);
+    XCTAssert([identityValues containsObject:path1]);
+    XCTAssert([identityValues containsObject:path2]);
+
+    // Since `RLMSyncPermission`s are read-only, KVC setting should fail.
+    RLMAssertThrows([results setValue:@"foobar" forKey:@"path"]);
+}
+
+/// Filtering permissions results should work.
+- (void)testFilteringPermissions {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    // Open two Realms
+    NSURL *url1 = CUSTOM_REALM_URL(@"r1");
+    NSURL *url2 = CUSTOM_REALM_URL(@"r2");
+    NSURL *url3 = CUSTOM_REALM_URL(@"r3");
+    __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
+    NSString *uB = self.userB.identity;
+
+    // Give user B permissions to realms r1, r2, and r3.
+    id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url1, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r1 permission for user B should work.");
+    NSString *finalPath = [makeTildeSubstitutedURL(url2, self.userA) path];
+    id p2 = [[RLMSyncPermission alloc] initWithRealmPath:finalPath
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r2 permission for user B should work.");
+    id p3 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url3, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, @"Setting r3 permission for user B should work.");
+
+    // Wait for all the permissions to show up.
+    CHECK_PERMISSION_PRESENT(results, p1);
+    CHECK_PERMISSION_PRESENT(results, p2);
+    CHECK_PERMISSION_PRESENT(results, p3);
+
+    // Now make a filter.
+    RLMResults<RLMSyncPermission *> *filtered = [results objectsWithPredicate:[NSPredicate predicateWithFormat:@"%K == %@",
+                                                                               RLMSyncPermissionSortPropertyPath,
+                                                                               finalPath]];
+    CHECK_PERMISSION_ABSENT(filtered, p1);
+    CHECK_PERMISSION_PRESENT(filtered, p2);
+    CHECK_PERMISSION_ABSENT(filtered, p3);
+}
+
+- (void)testSortingPermissionsOnUserID {
+    // Get a reference to my own permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userA];
+
+    // Open my Realm.
+    NSURL *url = REALM_URL();
+    __attribute__((objc_precise_lifetime)) RLMRealm *r = [self openRealmForURL:url user:self.userA];
+
+    // Give users B and C access to my Realm.
+    id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                identity:self.userB.identity
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r permission for user B should work.");
+    id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                identity:self.userC.identity
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r permission for user C should work.");
+
+    // Now sort on user ID.
+    RLMResults<RLMSyncPermission *> *sorted = [results sortedResultsUsingKeyPath:RLMSyncPermissionSortPropertyUserID
+                                                                       ascending:YES];
+    // Wait for changes to propagate
+    CHECK_PERMISSION_COUNT(sorted, 3);
+    NSMutableArray *sortedIDs = [NSMutableArray array];
+    for (NSUInteger i = 0; i < sorted.count; i++) {
+        [sortedIDs addObject:[sorted objectAtIndex:i].identity];
+    }
+    // Make sure the IDs in sortedIDs are actually sorted.
+    for (NSUInteger i = 0; i < sorted.count - 1; i++) {
+        XCTAssertEqual([sortedIDs[i] compare:sortedIDs[i + 1]], NSOrderedAscending);
+    }
+    // Make sure the IDs in sortedIDs contain all 3 users' IDs.
+    NSSet *sortedIDSet = [NSSet setWithArray:sortedIDs];
+    XCTAssertTrue([sortedIDSet containsObject:self.userA.identity]);
+    XCTAssertTrue([sortedIDSet containsObject:self.userB.identity]);
+    XCTAssertTrue([sortedIDSet containsObject:self.userC.identity]);
+}
+
+- (void)testSortingPermissionsOnPath {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    // Open three Realms
+    NSURL *url1 = CUSTOM_REALM_URL(@"r1");
+    NSURL *url2 = CUSTOM_REALM_URL(@"r2");
+    NSURL *url3 = CUSTOM_REALM_URL(@"r3");
+    __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
+    NSString *uB = self.userB.identity;
+
+    // Give user B read permissions for all three Realms.
+    id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url1, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r1 permission for user B should work.");
+    id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url2, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r2 permission for user B should work.");
+    id p3 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url3, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, @"Setting r3 permission for user B should work.");
+
+    // Now sort on Realm URL.
+    RLMResults<RLMSyncPermission *> *sorted = [results sortedResultsUsingKeyPath:RLMSyncPermissionSortPropertyPath
+                                                                        ascending:YES];
+    // Wait for changes to propagate
+    CHECK_PERMISSION_COUNT(sorted, 3);
+
+    CHECK_PERMISSION_PRESENT(sorted, p1);
+    CHECK_PERMISSION_PRESENT(sorted, p2);
+    CHECK_PERMISSION_PRESENT(sorted, p3);
+    NSUInteger idx1 = [sorted indexOfObject:p1];
+    NSUInteger idx2 = [sorted indexOfObject:p2];
+    NSUInteger idx3 = [sorted indexOfObject:p3];
+    // Make sure they are actually in ascending order.
+    XCTAssertNotEqual(idx1, NSNotFound);
+    XCTAssertNotEqual(idx2, NSNotFound);
+    XCTAssertNotEqual(idx3, NSNotFound);
+    XCTAssertLessThan(idx1, idx2);
+    XCTAssertLessThan(idx2, idx3);
+}
+
+- (void)testSortingPermissionsOnDate {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    // Open three Realms
+    NSURL *url1 = CUSTOM_REALM_URL(@"-r1");
+    NSURL *url2 = CUSTOM_REALM_URL(@"-r2");
+    NSURL *url3 = CUSTOM_REALM_URL(@"-r3");
+    __attribute__((objc_precise_lifetime)) RLMRealm *r1 = [self openRealmForURL:url1 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r2 = [self openRealmForURL:url2 user:self.userA];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r3 = [self openRealmForURL:url3 user:self.userA];
+    NSString *uB = self.userB.identity;
+
+    // Give user B read permissions for all three Realms.
+    id p1 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url3, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r3 permission for user B should work.");
+    id p2 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url1, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p2, self.userA, @"Setting r1 permission for user B should work.");
+    id p3 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url2, self.userA) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p3, self.userA, @"Setting r2 permission for user B should work.");
+
+    // Now sort on date.
+    RLMResults<RLMSyncPermission *> *sorted = [results sortedResultsUsingKeyPath:RLMSyncPermissionSortPropertyUpdated
+                                                                       ascending:YES];
+
+    // Wait for changes to propagate
+    CHECK_PERMISSION_COUNT(sorted, 3);
+    RLMSyncPermission *n1 = [sorted objectAtIndex:0];
+    RLMSyncPermission *n2 = [sorted objectAtIndex:1];
+    RLMSyncPermission *n3 = [sorted objectAtIndex:2];
+
+    XCTAssertTrue([n1.path rangeOfString:@"r3"].location != NSNotFound);
+    XCTAssertTrue([n2.path rangeOfString:@"r1"].location != NSNotFound);
+    XCTAssertTrue([n3.path rangeOfString:@"r2"].location != NSNotFound);
+
+    // Make sure they are actually in ascending order.
+    XCTAssertLessThan([n1.updatedAt timeIntervalSinceReferenceDate], [n2.updatedAt timeIntervalSinceReferenceDate]);
+    XCTAssertLessThan([n2.updatedAt timeIntervalSinceReferenceDate], [n3.updatedAt timeIntervalSinceReferenceDate]);
+}
+
+- (void)testPermissionResultsIndexOfObject {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    NSString *uB = self.userB.identity;
+
+    // Have A open a Realm and grant a permission to B.
+    NSURL *url = REALM_URL();
+    NSString *tildeSubstitutedPath = [makeTildeSubstitutedURL(url, self.userA) path];
+    __attribute__((objc_precise_lifetime)) RLMRealm *r = [self openRealmForURL:url user:self.userA];
+    id p1 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION_WITH_MESSAGE(p1, self.userA, @"Setting r permission for user B should work.");
+
+    // Wait for the permission to show up.
+    CHECK_PERMISSION_COUNT(results, 1);
+    // Should be able to get the permission based on the actual permission.
+    XCTAssertEqual(((NSInteger)[results indexOfObject:p1]), 0);
+    // A permission with a differing access level should not match.
+    id p2 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelAdmin];
+    XCTAssertEqual([results indexOfObject:p2], NSNotFound);
+    // A permission with a differing identity should not match.
+    id p3 = [[RLMSyncPermission alloc] initWithRealmPath:tildeSubstitutedPath
+                                                identity:self.userA.identity
+                                             accessLevel:RLMSyncAccessLevelRead];
+    XCTAssertEqual([results indexOfObject:p3], NSNotFound);
+    // A permission with a differing path should not match.
+    id p4 = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userB) path]
+                                                identity:uB
+                                             accessLevel:RLMSyncAccessLevelRead];
+    XCTAssertEqual([results indexOfObject:p4], NSNotFound);
+}
+
+- (void)testPermissionResultsIndexOfObjectWithPredicate {
+    // Get a reference to the permission results.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB];
+
+    NSString *uB = self.userB.identity;
+    // Open a Realm
+    {
+        NSURL *url = CUSTOM_REALM_URL(@"r1");
+        __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:self.userA];
+
+        // Give user B read permission for the Realm.
+        RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                                   identity:uB
+                                                                accessLevel:RLMSyncAccessLevelRead];
+        APPLY_PERMISSION_WITH_MESSAGE(p, self.userA, @"Setting r1 permission for user B should work.");
+    }
+
+    NSString *finalPath;
+    {
+        // Do this again so there's more than one permission in the permission Realm.
+        NSURL *url = CUSTOM_REALM_URL(@"r2");
+        __attribute__((objc_precise_lifetime)) RLMRealm *realm = [self openRealmForURL:url user:self.userA];
+
+        // Give user B read permission for the Realm.
+        finalPath = [makeTildeSubstitutedURL(url, self.userA) path];
+        RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:finalPath
+                                                                   identity:uB
+                                                                accessLevel:RLMSyncAccessLevelRead];
+        APPLY_PERMISSION_WITH_MESSAGE(p, self.userA, @"Setting r2 permission for user B should work.");
+    }
+
+    // Wait for changes to propagate
+    CHECK_PERMISSION_COUNT_AT_LEAST(results, 2);
+
+    // Create the predicate and retrieve the index of the object.
+    NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K == %@", RLMSyncPermissionSortPropertyPath, finalPath];
+    NSUInteger index = [results indexOfObjectWithPredicate:pred];
+    XCTAssertNotEqual(index, NSNotFound);
+    if (index == NSNotFound) {
+        return;
+    }
+    RLMSyncPermission *target = [results objectAtIndex:index];
+    XCTAssertEqualObjects(target.path, finalPath);
+}
+
+/// User should not be able to change a permission for a Realm they don't own.
+- (void)testSettingUnownedRealmPermission {
+    // Open a Realm for user A.
+    NSURL *url = REALM_URL();
+    [self openRealmForURL:url user:self.userA];
+
+    // Try to have user B give user C permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[makeTildeSubstitutedURL(url, self.userA) path]
+                                                               identity:self.userC.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+
+    // Set the permission.
+    XCTestExpectation *ex2 = [self expectationWithDescription:@"Setting an invalid permission should fail."];
+    [self.userB applyPermission:p callback:^(NSError *error) {
+        XCTAssertNotNil(error);
+        XCTAssertEqual(error.domain, RLMSyncPermissionErrorDomain);
+        XCTAssertEqual(error.code, RLMSyncPermissionErrorChangeFailed);
+        [ex2 fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Now retrieve the permissions again and make sure the new permission was not set.
+    RLMResults<RLMSyncPermission *> *results = [self getPermissionResultsFor:self.userB
+                                                                     message:@"Retrieving the results should work."];
+    CHECK_PERMISSION_ABSENT(results, p);
+}
+
+- (void)testRetrievingPermissionsChecksThreadHasRunLoop {
+    [self dispatchAsyncAndWait:^{
+        RLMAssertThrowsWithReason([self.userA retrievePermissionsWithCallback:^(__unused RLMResults *r, __unused NSError *e) {
+            XCTFail(@"callback should not have been invoked");
+        }], @"Can only access or modify permissions from a thread which has a run loop");
+    }];
+}
+
+#pragma mark - Permission offer/response
+
+/// Get a token which can be used to offer the permissions as defined
+- (void)testPermissionOffer {
+    NSURL *url = REALM_URL();
+    // Open the Realm.
+    __unused RLMRealm *realm = [self openRealmForURL:url user:self.userA];
+
+    // Create the offer.
+    __block NSString *token = nil;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Should get a token when making an offer."];
+    [self.userA createOfferForRealmAtURL:url
+                             accessLevel:RLMSyncAccessLevelWrite
+                              expiration:[NSDate dateWithTimeIntervalSinceNow:30 * 24 * 60 * 60]
+                                callback:^(NSString *t, NSError *error) {
+                                    XCTAssertNil(error);
+                                    XCTAssertNotNil(t);
+                                    token = t;
+                                    [ex fulfill];
+                                }];
+    [self waitForExpectations:@[ex] timeout:10.0];
+    XCTAssertTrue([token length] > 0);
+}
+
+/// Failed to process a permission offer object due to the offer expired
+- (void)testPermissionOfferIsExpired {
+    NSURL *url = REALM_URL();
+    // Open the Realm.
+    __unused RLMRealm *realm = [self openRealmForURL:url user:self.userA];
+
+    // Create the offer.
+    __block NSError *error = nil;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Server should process the permission offer."];
+    [self.userA createOfferForRealmAtURL:url
+                             accessLevel:RLMSyncAccessLevelWrite
+                              expiration:[NSDate dateWithTimeIntervalSinceNow:-30 * 24 * 60 * 60]
+                                callback:^(NSString *token, NSError *err) {
+                                    XCTAssertNotNil(err);
+                                    XCTAssertNil(token);
+                                    error = err;
+                                    [ex fulfill];
+                                }];
+    [self waitForExpectations:@[ex] timeout:10.0];
+    XCTAssertEqual(error.code, RLMSyncPermissionErrorOfferFailed);
+    XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"The permission offer is expired.");
+}
+
+/// Get a permission offer token, then permission offer response will be processed, then open another user's Realm file
+- (void)testPermissionOfferResponse {
+    NSURL *url = REALM_URL();
+    // Open the Realm.
+    __unused RLMRealm *realm = [self openRealmForURL:url user:self.userA];
+
+    // Create the offer.
+    __block NSString *token = nil;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Should get a token when making an offer."];
+    [self.userA createOfferForRealmAtURL:url
+                             accessLevel:RLMSyncAccessLevelWrite
+                              expiration:[NSDate dateWithTimeIntervalSinceNow:30 * 24 * 60 * 60]
+                                callback:^(NSString *t, NSError *error) {
+                                    XCTAssertNil(error);
+                                    XCTAssertNotNil(t);
+                                    token = t;
+                                    [ex fulfill];
+                                }];
+    [self waitForExpectations:@[ex] timeout:10.0];
+    XCTAssertTrue([token length] > 0);
+
+    // Accept the offer.
+    __block NSURL *realmURL = nil;
+    XCTestExpectation *ex2 = [self expectationWithDescription:@"Server should process offer acceptance."];
+    [self.userB acceptOfferForToken:token callback:^(NSURL *returnedURL, NSError *error) {
+        XCTAssertNil(error);
+        XCTAssertNotNil(returnedURL);
+        realmURL = returnedURL;
+        [ex2 fulfill];
+    }];
+    [self waitForExpectations:@[ex2] timeout:20.0];
+    XCTAssertEqualObjects([realmURL path], [makeTildeSubstitutedURL(url, self.userA) path]);
+
+    // Open the Realm.
+    XCTAssertNotNil([self openRealmForURL:realmURL user:self.userB]);
+}
+
+/// Failed to process a permission offer response object due to `token` is invalid
+- (void)testPermissionOfferResponseInvalidToken {
+    NSString *badToken = @"invalid token";
+
+    // Expect an error.
+    __block NSError *error = nil;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
+    [self.userA acceptOfferForToken:badToken callback:^(NSURL *returnedURL, NSError *err) {
+        XCTAssertNil(returnedURL);
+        XCTAssertNotNil(err);
+        error = err;
+        [ex fulfill];
+    }];
+    [self waitForExpectations:@[ex] timeout:20.0];
+    XCTAssertEqual(error.code, RLMSyncPermissionErrorAcceptOfferFailed);
+    XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"Your request parameters did not validate.");
+}
+
+/// Failed to process a permission offer response object due to `token` represents a Realm that does not exist
+- (void)testPermissionOfferResponseTokenNotExist {
+    NSString *fakeToken = @"00000000000000000000000000000000:00000000-0000-0000-0000-000000000000";
+
+    // Expect an error.
+    __block NSError *error = nil;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Server should process offer acceptance."];
+    [self.userA acceptOfferForToken:fakeToken callback:^(NSURL *returnedURL, NSError *err) {
+        XCTAssertNil(returnedURL);
+        XCTAssertNotNil(err);
+        error = err;
+        [ex fulfill];
+    }];
+    [self waitForExpectations:@[ex] timeout:20.0];
+    XCTAssertEqual(error.code, RLMSyncPermissionErrorAcceptOfferFailed);
+    XCTAssertEqualObjects(error.userInfo[NSLocalizedDescriptionKey], @"Your request parameters did not validate.");
+}
+
+#pragma mark - Delete Realm upon permission denied
+
+// FIXME ROS 2.0: works when ROS is manually provided, not when ROS is run as part of tests
+/// A Realm which is opened improperly should report an error allowing the app to recover.
+- (void)testDeleteRealmUponPermissionDenied {
+    __block void(^errorBlock)(NSError *, RLMSyncSession *session) = nil;
+    [[RLMSyncManager sharedManager] setErrorHandler:^(NSError *error, RLMSyncSession *session) {
+        if (errorBlock) {
+            errorBlock(error, session);
+            errorBlock = nil;
+        } else {
+            XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
+        }
+    }];
+
+    NSString *testName = NSStringFromSelector(_cmd);
+    // Open a Realm for user A.
+    NSURL *userAURL = makeTestURL(testName, nil);
+    RLMRealm *userARealm = [self openRealmForURL:userAURL user:self.userA];
+
+    // Have user A add some items to the Realm.
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    [self waitForUploadsForUser:self.userA url:userAURL];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // Give user B read permissions to that Realm.
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[userAURL path]
+                                                               identity:self.userB.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+    // Set the read permission.
+    APPLY_PERMISSION(p, self.userA);
+
+    NSURL *userBURL = makeTestURL(testName, self.userA);
+    RLMRealmConfiguration *userBConfig = [RLMRealmConfiguration defaultConfiguration];
+    userBConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:self.userB realmURL:userBURL];
+    __block NSError *theError = nil;
+
+    // Incorrectly open the Realm for user B.
+    NSURL *onDiskPath;
+    @autoreleasepool {
+        NSString *sessionName = NSStringFromSelector(_cmd);
+        XCTestExpectation *ex2 = [self expectationWithDescription:@"We should get a permission denied error."];
+        errorBlock = ^(NSError *err, RLMSyncSession *session) {
+            // Make sure we're actually looking at the right session.
+            XCTAssertTrue([[session.realmURL absoluteString] rangeOfString:sessionName].location != NSNotFound);
+            theError = err;
+            [ex2 fulfill];
+        };
+        __attribute__((objc_precise_lifetime)) RLMRealm *bad = [RLMRealm realmWithConfiguration:userBConfig error:nil];
+        [self waitForExpectationsWithTimeout:10.0 handler:nil];
+        onDiskPath = [RLMSyncTestCase onDiskPathForSyncedRealm:bad];
+    }
+    XCTAssertNotNil(onDiskPath);
+    XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:[onDiskPath path]]);
+
+    // Check the error and perform the Realm deletion.
+    XCTAssertNotNil(theError);
+    RLMSyncErrorActionToken *errorToken = [theError rlmSync_errorActionToken];
+    XCTAssertNotNil(errorToken);
+    [RLMSyncSession immediatelyHandleError:errorToken];
+
+    // Ensure the file is no longer on disk.
+    XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:[onDiskPath path]]);
+
+    // Correctly open the same Realm for user B.
+    __block RLMRealm *userBRealm = nil;
+    XCTestExpectation *asyncOpenEx = [self expectationWithDescription:@"Should asynchronously open a Realm"];
+    [RLMRealm asyncOpenWithConfiguration:userBConfig
+                           callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm *realm, NSError *err){
+                                    XCTAssertNil(err);
+                                    XCTAssertNotNil(realm);
+                                    userBRealm = realm;
+                                    [asyncOpenEx fulfill];
+                                }];
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+    CHECK_COUNT(3, SyncObject, userBRealm);
+}
+
+@end

+ 622 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMPermissionsTests.mm

@@ -0,0 +1,622 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2018 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import <XCTest/XCTest.h>
+
+#import "RLMSyncTestCase.h"
+
+#import "RLMTestUtils.h"
+
+#define APPLY_PERMISSION(ma_permission, ma_user) do { \
+    XCTestExpectation *ex = [self expectationWithDescription:@"apply permission"]; \
+    [ma_user applyPermission:ma_permission callback:^(NSError *err) {              \
+        XCTAssertNil(err, @"Received an error when applying permission: %@", err); \
+        [ex fulfill];                                                              \
+    }];                                                                            \
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];                        \
+} while (0)                                                                        \
+
+@interface ObjectWithPermissions : RLMObject
+@property (nonatomic) int value;
+@property (nonatomic) RLMArray<RLMPermission *><RLMPermission> *permissions;
+@end
+@implementation ObjectWithPermissions
+@end
+
+@interface LinkToObjectWithPermissions : RLMObject
+@property (nonatomic) int value;
+@property (nonatomic) ObjectWithPermissions *link;
+@property (nonatomic) RLMArray<RLMPermission *><RLMPermission> *permissions;
+@end
+@implementation LinkToObjectWithPermissions
+@end
+
+@interface RLMPermissionsTests : RLMSyncTestCase
+@property (nonatomic, strong) RLMSyncUser *userA;
+@property (nonatomic, strong) RLMSyncUser *userB;
+@property (nonatomic, strong) RLMSyncUser *userC;
+
+@property (nonatomic, strong) void (^errorBlock)(NSError *);
+@end
+
+@implementation RLMPermissionsTests
+
+- (void)setUp {
+    [super setUp];
+    NSString *accountNameBase = [[NSUUID UUID] UUIDString];
+    NSString *userNameA = [accountNameBase stringByAppendingString:@"a"];
+    self.userA = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameA register:YES]
+                                        server:[RLMSyncTestCase authServerURL]];
+
+    NSString *userNameB = [accountNameBase stringByAppendingString:@"b"];
+    self.userB = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameB register:YES]
+                                        server:[RLMSyncTestCase authServerURL]];
+
+    NSString *userNameC = [accountNameBase stringByAppendingString:@"c"];
+    self.userC = [self logInUserForCredentials:[RLMSyncTestCase basicCredentialsWithName:userNameC register:YES]
+                                        server:[RLMSyncTestCase authServerURL]];
+
+    RLMSyncManager.sharedManager.errorHandler = ^(NSError *error, __unused RLMSyncSession *session) {
+        if (self.errorBlock) {
+            self.errorBlock(error);
+            self.errorBlock = nil;
+        } else {
+            XCTFail(@"Error handler should not be called unless explicitly expected. Error: %@", error);
+        }
+    };
+}
+
+- (void)tearDown {
+    [self.userA logOut];
+    [self.userB logOut];
+    [self.userC logOut];
+    RLMSyncManager.sharedManager.errorHandler = nil;
+    [super tearDown];
+}
+
+#pragma mark - Helper methods
+
+- (BOOL)isPartial {
+    return YES;
+}
+
+- (NSError *)subscribeToRealm:(RLMRealm *)realm type:(Class)cls where:(NSString *)pred {
+    __block NSError *error;
+    XCTestExpectation *ex = [self expectationWithDescription:@"Should be able to successfully complete a query"];
+    [realm subscribeToObjects:cls where:pred callback:^(__unused RLMResults *results, NSError *err) {
+        error = err;
+        [ex fulfill];
+    }];
+    [self waitForExpectations:@[ex] timeout:20.0];
+    return error;
+}
+
+- (NSURL *)createRealmWithName:(SEL)sel permissions:(void (^)(RLMRealm *realm))block {
+    // Create a new Realm with an admin user
+    RLMSyncUser *admin = [self createAdminUserForURL:[RLMSyncTestCase authServerURL]
+                                            username:[[NSUUID UUID] UUIDString]];
+
+    auto url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/%@", NSStringFromSelector(sel)]];
+    RLMRealm *adminRealm = [self openRealmForURL:url user:admin];
+    [self addSyncObjectsToRealm:adminRealm descriptions:@[@"child-1", @"child-2", @"child-3"]];
+    CHECK_COUNT(3, SyncObject, adminRealm);
+    [self waitForUploadsForRealm:adminRealm error:nil];
+    [self waitForDownloadsForRealm:adminRealm error:nil];
+
+    // FIXME: we currently need to add a subscription to get the permissions types sent to us
+    [adminRealm refresh];
+    CHECK_COUNT(0, SyncObject, adminRealm);
+    [self subscribeToRealm:adminRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_COUNT(3, SyncObject, adminRealm);
+
+    // Set up permissions on the Realm
+    [adminRealm transactionWithBlock:^{ block(adminRealm); }];
+
+    // FIXME: we currently need to also add the old realm-level permissions
+    RLMSyncPermission *p = [[RLMSyncPermission alloc] initWithRealmPath:[url path]
+                                                               identity:self.userA.identity
+                                                            accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION(p, admin);
+    p = [[RLMSyncPermission alloc] initWithRealmPath:[url path] identity:self.userB.identity
+                                         accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION(p, admin);
+    p = [[RLMSyncPermission alloc] initWithRealmPath:[url path] identity:self.userC.identity
+                                         accessLevel:RLMSyncAccessLevelRead];
+    APPLY_PERMISSION(p, admin);
+    [self waitForSync:adminRealm];
+
+    return url;
+}
+
+- (void)waitForSync:(RLMRealm *)realm {
+    [self waitForUploadsForRealm:realm error:nil];
+    [self waitForDownloadsForRealm:realm error:nil];
+    [realm refresh];
+}
+
+#pragma mark - Permissions
+
+static RLMPermissionRole *getRole(RLMRealm *realm, NSString *roleName) {
+    return [RLMPermissionRole createOrUpdateInRealm:realm withValue:@{@"name": roleName}];
+}
+
+static void addUserToRole(RLMRealm *realm, NSString *roleName, NSString *user) {
+    [getRole(realm, roleName).users addObject:[RLMPermissionUser userInRealm:realm withIdentity:user]];
+}
+
+static void createPermissions(RLMArray<RLMPermission> *permissions) {
+    auto permission = [RLMPermission permissionForRoleNamed:@"everyone" inArray:permissions];
+    permission.canCreate = false;
+    permission.canRead = false;
+    permission.canQuery = false;
+    permission.canDelete = false;
+    permission.canUpdate = false;
+    permission.canModifySchema = false;
+    permission.canSetPermissions = false;
+
+    permission = [RLMPermission permissionForRoleNamed:@"reader" inArray:permissions];
+    permission.canRead = true;
+    permission.canQuery = true;
+
+    permission = [RLMPermission permissionForRoleNamed:@"writer" inArray:permissions];
+    permission.canUpdate = true;
+    permission.canCreate = true;
+    permission.canDelete = true;
+
+    permission = [RLMPermission permissionForRoleNamed:@"admin" inArray:permissions];
+    permission.canSetPermissions = true;
+}
+
+#define CHECK_REALM_PRIVILEGE(realm, ...) do { \
+    RLMRealmPrivileges expected{__VA_ARGS__}; \
+    auto actual = [realm privilegesForRealm]; \
+    XCTAssertEqual(expected.read, actual.read); \
+    XCTAssertEqual(expected.update, actual.update); \
+    XCTAssertEqual(expected.setPermissions, actual.setPermissions); \
+    XCTAssertEqual(expected.modifySchema, actual.modifySchema); \
+} while (0)
+
+#define CHECK_CLASS_PRIVILEGE(realm, ...) do { \
+    RLMClassPrivileges expected{__VA_ARGS__}; \
+    auto actual = [realm privilegesForClass:SyncObject.class]; \
+    XCTAssertEqual(expected.read, actual.read); \
+    XCTAssertEqual(expected.create, actual.create); \
+    XCTAssertEqual(expected.update, actual.update); \
+    XCTAssertEqual(expected.subscribe, actual.subscribe); \
+    XCTAssertEqual(expected.setPermissions, actual.setPermissions); \
+} while (0)
+
+#define CHECK_OBJECT_PRIVILEGE(realm, ...) do { \
+    RLMObjectPrivileges expected{__VA_ARGS__}; \
+    auto actual = [realm privilegesForObject:[SyncObject allObjectsInRealm:realm].firstObject]; \
+    XCTAssertEqual(expected.read, actual.read); \
+    XCTAssertEqual(expected.del, actual.del); \
+    XCTAssertEqual(expected.update, actual.update); \
+    XCTAssertEqual(expected.setPermissions, actual.setPermissions); \
+} while (0)
+
+- (void)testRealmReadAccess {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMRealmPermission objectInRealm:realm].permissions);
+        addUserToRole(realm, @"reader", self.userA.identity);
+    }];
+
+    // userA should now be able to open the Realm and see objects
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userARealm, .read = true);
+    CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userARealm, .read = true);
+
+    // userA should not be able to create new objects
+    CHECK_COUNT(3, SyncObject, userARealm);
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(6, SyncObject, userARealm);
+    [self waitForSync:userARealm];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // userB should not be able to read any objects
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userBRealm, .read = false);
+    CHECK_CLASS_PRIVILEGE(userBRealm, .read = false);
+    CHECK_COUNT(0, SyncObject, userBRealm);
+}
+
+- (void)testRealmWriteAccess {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMRealmPermission objectInRealm:realm].permissions);
+
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"writer", self.userA.identity);
+
+        addUserToRole(realm, @"reader", self.userB.identity);
+    }];
+
+    // userA should be able to add objects
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true);
+    CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true,
+                          .update = true, .create = true, .setPermissions = true);
+    CHECK_OBJECT_PRIVILEGE(userARealm, .read = true, .update = true, .del = true, .setPermissions = true);
+
+    CHECK_COUNT(3, SyncObject, userARealm);
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(6, SyncObject, userARealm);
+    [self waitForSync:userARealm];
+    CHECK_COUNT(6, SyncObject, userARealm);
+
+    // userB's insertions should be reverted
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userBRealm, .read = true);
+    CHECK_CLASS_PRIVILEGE(userBRealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userBRealm, .read = true);
+
+    CHECK_COUNT(6, SyncObject, userBRealm);
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(9, SyncObject, userBRealm);
+    [self waitForSync:userBRealm];
+    CHECK_COUNT(6, SyncObject, userBRealm);
+}
+
+- (void)testRealmManagePermissions {
+    // FIXME: this test is wrong; setPermission doesn't govern adding users to roles
+#if 0
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMRealmPermission objectInRealm:realm].permissions);
+
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"writer", self.userA.identity);
+        addUserToRole(realm, @"admin", self.userA.identity);
+
+        addUserToRole(realm, @"reader", self.userB.identity);
+        addUserToRole(realm, @"writer", self.userB.identity);
+
+        addUserToRole(realm, @"reader", self.userC.identity);
+    }];
+
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    RLMRealm *userCRealm = [self openRealmForURL:url user:self.userC];
+
+    // userC should initially not be able to write to the Realm
+    [self subscribeToRealm:userCRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userCRealm, .read = true);
+    CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true);
+
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    [self waitForSync:userCRealm];
+    CHECK_COUNT(3, SyncObject, userCRealm);
+
+    // userB should not be able to grant write permissions to userC
+    [userBRealm transactionWithBlock:^{
+        addUserToRole(userBRealm, @"writer", self.userC.identity);
+    }];
+    [self waitForSync:userBRealm];
+    [self waitForSync:userCRealm];
+
+    CHECK_REALM_PRIVILEGE(userCRealm, .read = true);
+    CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true);
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    [self waitForSync:userCRealm];
+    CHECK_COUNT(3, SyncObject, userCRealm);
+
+    // userA should be able to grant write permissions to userC
+    [userARealm transactionWithBlock:^{
+        addUserToRole(userARealm, @"writer", self.userC.identity);
+    }];
+    [self waitForSync:userARealm];
+    [self waitForSync:userCRealm];
+
+    CHECK_REALM_PRIVILEGE(userCRealm, .read = true, .update = true);
+    CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true, .update = true, .create = true);
+    CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true, .update = true, .del = true);
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    [self waitForSync:userCRealm];
+    CHECK_COUNT(6, SyncObject, userCRealm);
+#endif
+}
+
+- (void)testRealmModifySchema {
+    // awkward to test due to that reverts will normally crash
+    // probably need to spawn a child process?
+}
+
+- (void)testClassRead {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
+        addUserToRole(realm, @"reader", self.userA.identity);
+    }];
+
+    // userA should now be able to open the Realm and see objects
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userARealm, .read = true);
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // userA should not be able to create new objects
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(6, SyncObject, userARealm);
+    [self waitForSync:userARealm];
+    CHECK_COUNT(3, SyncObject, userARealm);
+
+    // userB should not be able to read any objects
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userBRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userBRealm, .read = false);
+    CHECK_COUNT(0, SyncObject, userBRealm);
+}
+
+- (void)testClassUpdate {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
+
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"writer", self.userA.identity);
+
+        addUserToRole(realm, @"reader", self.userB.identity);
+    }];
+
+    // userA should be able to mutate objects
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true, .update = true, .create = true);
+    CHECK_OBJECT_PRIVILEGE(userARealm, .read = true, .update = true, .del = true, .setPermissions = true);
+
+    SyncObject *objA = [SyncObject allObjectsInRealm:userARealm].firstObject;
+    [userARealm transactionWithBlock:^{
+        objA.stringProp = @"new value";
+    }];
+    [self waitForSync:userARealm];
+    XCTAssertEqualObjects(objA.stringProp, @"new value");
+
+    // userB's mutations should be reverted
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userBRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userBRealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userBRealm, .read = true);
+
+    SyncObject *objB = [SyncObject allObjectsInRealm:userBRealm].firstObject;
+    [userBRealm transactionWithBlock:^{
+        objB.stringProp = @"new value 2";
+    }];
+    XCTAssertEqualObjects(objB.stringProp, @"new value 2");
+    [self waitForSync:userBRealm];
+    XCTAssertEqualObjects(objB.stringProp, @"new value");
+}
+
+- (void)testClassCreate {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
+
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"writer", self.userA.identity);
+
+        addUserToRole(realm, @"reader", self.userB.identity);
+    }];
+
+    // userA should be able to add objects
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userARealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userARealm, .read = true, .subscribe = true, .update = true, .create = true);
+    CHECK_OBJECT_PRIVILEGE(userARealm, .read = true, .update = true, .del = true, .setPermissions = true);
+
+    CHECK_COUNT(3, SyncObject, userARealm);
+    [self addSyncObjectsToRealm:userARealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(6, SyncObject, userARealm);
+    [self waitForSync:userARealm];
+    CHECK_COUNT(6, SyncObject, userARealm);
+
+    // userB's insertions should be reverted
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userBRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userBRealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userBRealm, .read = true);
+
+    CHECK_COUNT(6, SyncObject, userBRealm);
+    [self addSyncObjectsToRealm:userBRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(9, SyncObject, userBRealm);
+    [self waitForSync:userBRealm];
+    CHECK_COUNT(6, SyncObject, userBRealm);
+}
+
+- (void)testClassSetPermissions {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        createPermissions([RLMClassPermission objectInRealm:realm forClass:SyncObject.class].permissions);
+
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"writer", self.userA.identity);
+        addUserToRole(realm, @"admin", self.userA.identity);
+
+        addUserToRole(realm, @"reader", self.userB.identity);
+        addUserToRole(realm, @"writer", self.userB.identity);
+
+        addUserToRole(realm, @"reader", self.userC.identity);
+    }];
+
+    // Despite having write access userB should not be able to add "update" access to "reader"
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    [userBRealm transactionWithBlock:^{
+        auto permission = [RLMPermission permissionForRoleNamed:@"reader" onClass:SyncObject.class realm:userBRealm];
+        permission.canCreate = true;
+        permission.canUpdate = true;
+
+    }];
+    [self waitForSync:userBRealm];
+
+    // userC should be unable to create objects
+    RLMRealm *userCRealm = [self openRealmForURL:url user:self.userC];
+    [self subscribeToRealm:userCRealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    CHECK_REALM_PRIVILEGE(userCRealm, .read = true, .update = true, .setPermissions = true, .modifySchema = true);
+    CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true);
+    CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true);
+
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(6, SyncObject, userCRealm);
+    [self waitForSync:userCRealm];
+    CHECK_COUNT(3, SyncObject, userCRealm);
+
+    // userA should able to add "update" access to "reader"
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[SyncObject class] where:@"TRUEPREDICATE"];
+    [userARealm transactionWithBlock:^{
+        auto permission = [RLMPermission permissionForRoleNamed:@"reader" onClass:SyncObject.class realm:userARealm];
+        permission.canCreate = true;
+        permission.canUpdate = true;
+    }];
+    [self waitForSync:userARealm];
+
+    // userC should now be able to create objects
+    [self waitForSync:userCRealm];
+    CHECK_CLASS_PRIVILEGE(userCRealm, .read = true, .subscribe = true, .update = true, .create = true);
+    CHECK_OBJECT_PRIVILEGE(userCRealm, .read = true, .update = true, .del = true, .setPermissions = true);
+
+    [self addSyncObjectsToRealm:userCRealm descriptions:@[@"child-4", @"child-5", @"child-6"]];
+    CHECK_COUNT(6, SyncObject, userCRealm);
+    [self waitForSync:userCRealm];
+    CHECK_COUNT(6, SyncObject, userCRealm);
+}
+
+- (void)testObjectRead {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        addUserToRole(realm, @"reader", self.userA.identity);
+        auto obj1 = [ObjectWithPermissions createInRealm:realm withValue:@[@1]];
+        createPermissions(obj1.permissions);
+        [ObjectWithPermissions createInRealm:realm withValue:@[@2]];
+    }];
+
+    // userA should be able to see both objects
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    CHECK_COUNT(1, ObjectWithPermissions, userARealm);
+
+    // userB should not be able to read any objects
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    CHECK_COUNT(0, ObjectWithPermissions, userBRealm);
+}
+
+- (void)testObjectTransitiveRead {
+}
+
+- (void)testObjectUpdate {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"reader", self.userB.identity);
+        addUserToRole(realm, @"writer", self.userB.identity);
+        auto obj1 = [ObjectWithPermissions createInRealm:realm withValue:@[@1]];
+        createPermissions(obj1.permissions);
+    }];
+
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    ObjectWithPermissions *objA = [ObjectWithPermissions allObjectsInRealm:userARealm].firstObject;
+    [userARealm transactionWithBlock:^{
+        objA.value = 3;
+    }];
+    [self waitForSync:userARealm];
+    XCTAssertEqual(objA.value, 1);
+
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    ObjectWithPermissions *objB = [ObjectWithPermissions allObjectsInRealm:userBRealm].firstObject;
+    [userBRealm transactionWithBlock:^{
+        objB.value = 3;
+    }];
+    [self waitForSync:userBRealm];
+    [self waitForSync:userARealm];
+
+    XCTAssertEqual(objA.value, 3);
+    XCTAssertEqual(objB.value, 3);
+}
+
+- (void)testObjectDelete {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        addUserToRole(realm, @"reader", self.userA.identity);
+        addUserToRole(realm, @"reader", self.userB.identity);
+        addUserToRole(realm, @"writer", self.userB.identity);
+        auto obj1 = [ObjectWithPermissions createInRealm:realm withValue:@[@1]];
+        createPermissions(obj1.permissions);
+    }];
+
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    ObjectWithPermissions *objA = [ObjectWithPermissions allObjectsInRealm:userARealm].firstObject;
+    [userARealm transactionWithBlock:^{
+        [userARealm deleteObject:objA];
+    }];
+    CHECK_COUNT(0, ObjectWithPermissions, userARealm);
+    [self waitForSync:userARealm];
+    CHECK_COUNT(1, ObjectWithPermissions, userARealm);
+    objA = [ObjectWithPermissions allObjectsInRealm:userARealm].firstObject;
+
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    ObjectWithPermissions *objB = [ObjectWithPermissions allObjectsInRealm:userBRealm].firstObject;
+    [userBRealm transactionWithBlock:^{
+        [userBRealm deleteObject:objB];
+    }];
+    [self waitForSync:userBRealm];
+    [self waitForSync:userARealm];
+
+    CHECK_COUNT(0, ObjectWithPermissions, userARealm);
+    CHECK_COUNT(0, ObjectWithPermissions, userBRealm);
+    XCTAssertTrue(objA.invalidated);
+    XCTAssertTrue(objB.invalidated);
+}
+
+- (void)testObjectSetPermissions {
+    NSURL *url = [self createRealmWithName:_cmd permissions:^(RLMRealm *) {}];
+
+    RLMRealm *userARealm = [self openRealmForURL:url user:self.userA];
+    [self subscribeToRealm:userARealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    [userARealm transactionWithBlock:^{
+        auto obj = [ObjectWithPermissions createInRealm:userARealm withValue:@[@1]];
+        auto permissions = [RLMPermission permissionForRoleNamed:@"foo" onObject:obj];
+        permissions.canRead = true;
+        addUserToRole(userARealm, @"foo", self.userB.identity);
+    }];
+
+    CHECK_COUNT(1, ObjectWithPermissions, userARealm);
+    [self waitForSync:userARealm];
+    CHECK_COUNT(0, ObjectWithPermissions, userARealm);
+
+    RLMRealm *userBRealm = [self openRealmForURL:url user:self.userB];
+    [self subscribeToRealm:userBRealm type:[ObjectWithPermissions class] where:@"TRUEPREDICATE"];
+    CHECK_COUNT(1, ObjectWithPermissions, userBRealm);
+}
+
+- (void)testRetrieveClassPermissionsForRenamedClass {
+    [self createRealmWithName:_cmd permissions:^(RLMRealm *realm) {
+        XCTAssertNotNil([RLMClassPermission objectInRealm:realm forClass:RLMPermissionRole.class]);
+    }];
+}
+
+@end

+ 25 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.h

@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncManager.h"
+
+@interface RLMSyncManager (ObjectServerTests)
+
+- (void)prepareForDestruction;
+
+@end

+ 44 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.m

@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncManager_Private.h"
+#import "RLMSyncManager+ObjectServerTests.h"
+#import "RLMSyncTestCase.h"
+#import "RLMTestUtils.h"
+
+@interface RLMSyncManager ()
+- (NSArray<RLMSyncUser *> *)_allUsers;
+@end
+
+@implementation RLMSyncManager (ObjectServerTests)
+
++ (void)load {
+    RLMSwapOutClassMethod(self, @selector(sharedManager),  @selector(ost_sharedManager));
+}
+
++ (instancetype)ost_sharedManager {
+    return [RLMSyncTestCase managerForCurrentTest];
+}
+
+- (void)prepareForDestruction {
+    // Log out all the logged-in users.
+    [[self _allUsers] makeObjectsPerformSelector:@selector(logOut)];
+    [RLMSyncManager resetForTesting];
+}
+
+@end

+ 25 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncSessionRefreshHandle+ObjectServerTests.h

@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncSessionRefreshHandle.h"
+
+@interface RLMSyncSessionRefreshHandle (ObjectServerTests)
+
++ (void)calculateFireDateUsingTestLogic:(BOOL)forTest blockOnRefreshCompletion:(void(^)(BOOL))block;
+
+@end

+ 66 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncSessionRefreshHandle+ObjectServerTests.m

@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
+
+#import "RLMTestUtils.h"
+
+static BOOL s_calculateFireDatesWithTestLogic = NO;
+static void(^s_onRefreshCompletedOrErrored)(BOOL) = nil;
+
+@interface RLMSyncSessionRefreshHandle ()
++ (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)date;
+- (BOOL)_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json;
+@end
+
+@implementation RLMSyncSessionRefreshHandle (ObjectServerTests)
+
++ (void)calculateFireDateUsingTestLogic:(BOOL)forTest blockOnRefreshCompletion:(void(^)(BOOL))block {
+    s_onRefreshCompletedOrErrored = block;
+    s_calculateFireDatesWithTestLogic = forTest;
+}
+
++ (void)load {
+    RLMSwapOutClassMethod(self,
+                          @selector(fireDateForTokenExpirationDate:nowDate:),
+                          @selector(ost_fireDateForTokenExpirationDate:nowDate:));
+    RLMSwapOutInstanceMethod(self,
+                             @selector(_onRefreshCompletionWithError:json:),
+                             @selector(ost_onRefreshCompletionWithError:json:));
+}
+
++ (NSDate *)ost_fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate {
+    if (s_calculateFireDatesWithTestLogic) {
+        // Force the refresh to take place one second later.
+        return [NSDate dateWithTimeIntervalSinceNow:1];
+    } else {
+        // Use the original logic.
+        return [self ost_fireDateForTokenExpirationDate:date nowDate:nowDate];
+    }
+}
+
+- (BOOL)ost_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json {
+    BOOL status = [self ost_onRefreshCompletionWithError:error json:json];
+    // For the sake of testing, call a callback afterwards to let the test update its state.
+    if (s_onRefreshCompletedOrErrored) {
+        s_onRefreshCompletedOrErrored(status);
+    }
+    return status;
+}
+
+@end

+ 145 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.h

@@ -0,0 +1,145 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMMultiProcessTestCase.h"
+#import "RLMSyncConfiguration_Private.h"
+
+typedef void(^RLMSyncBasicErrorReportingBlock)(NSError * _Nullable);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RLMSyncManager ()
+- (void)setSessionCompletionNotifier:(RLMSyncBasicErrorReportingBlock)sessionCompletionNotifier;
+@end
+
+@interface SyncObject : RLMObject
+@property NSString *stringProp;
+@end
+
+@interface HugeSyncObject : RLMObject
+@property NSData *dataProp;
++ (instancetype)object;
+@end
+
+@interface RLMSyncTestCase : RLMMultiProcessTestCase
+
++ (RLMSyncManager *)managerForCurrentTest;
+
++ (NSURL *)authServerURL;
++ (NSURL *)secureAuthServerURL;
+
++ (RLMSyncCredentials *)basicCredentialsWithName:(NSString *)name register:(BOOL)shouldRegister;
+
++ (NSURL *)onDiskPathForSyncedRealm:(RLMRealm *)realm;
+
+/// Retrieve the administrator token.
+- (NSString *)adminToken;
+
+/// Read and delete the last email sent by ROS to the given address.
+/// Returns nil if none has been sent to that address.
+- (nullable NSString *)emailForAddress:(NSString *)email;
+
+/// Synchronously open a synced Realm and wait until the binding process has completed or failed.
+- (RLMRealm *)openRealmForURL:(NSURL *)url user:(RLMSyncUser *)user;
+
+/// Synchronously open a synced Realm and wait until the binding process has completed or failed.
+- (RLMRealm *)openRealmWithConfiguration:(RLMRealmConfiguration *)configuration;
+
+/// Synchronously open a synced Realm. Also run a block right after the Realm is created.
+- (RLMRealm *)openRealmForURL:(NSURL *)url
+                         user:(RLMSyncUser *)user
+             immediatelyBlock:(nullable void(^)(void))block;
+
+/// Synchronously open a synced Realm with encryption key and stop policy.
+/// Also run a block right after the Realm is created.
+- (RLMRealm *)openRealmForURL:(NSURL *)url
+                         user:(RLMSyncUser *)user
+                encryptionKey:(nullable NSData *)encryptionKey
+                   stopPolicy:(RLMSyncStopPolicy)stopPolicy
+             immediatelyBlock:(nullable void(^)(void))block;
+
+/// Synchronously open a synced Realm and wait until the binding process has completed or failed.
+/// Also run a block right after the Realm is created.
+- (RLMRealm *)openRealmWithConfiguration:(RLMRealmConfiguration *)configuration
+                        immediatelyBlock:(nullable void(^)(void))block;
+;
+
+/// Immediately open a synced Realm.
+- (RLMRealm *)immediatelyOpenRealmForURL:(NSURL *)url user:(RLMSyncUser *)user;
+
+/// Immediately open a synced Realm with encryption key and stop policy.
+- (RLMRealm *)immediatelyOpenRealmForURL:(NSURL *)url
+                                    user:(RLMSyncUser *)user
+                           encryptionKey:(nullable NSData *)encryptionKey
+                              stopPolicy:(RLMSyncStopPolicy)stopPolicy;
+
+/// Synchronously create, log in, and return a user.
+- (RLMSyncUser *)logInUserForCredentials:(RLMSyncCredentials *)credentials
+                                  server:(NSURL *)url;
+
+/// Create and log in an admin user.
+- (RLMSyncUser *)createAdminUserForURL:(NSURL *)url username:(NSString *)username;
+
+/// Add a number of objects to a Realm.
+- (void)addSyncObjectsToRealm:(RLMRealm *)realm descriptions:(NSArray<NSString *> *)descriptions;
+
+/// Synchronously wait for downloads to complete for any number of Realms, and then check their `SyncObject` counts.
+- (void)waitForDownloadsForUser:(RLMSyncUser *)user
+                         realms:(NSArray<RLMRealm *> *)realms
+                      realmURLs:(NSArray<NSURL *> *)realmURLs
+                 expectedCounts:(NSArray<NSNumber *> *)counts;
+
+/// "Prime" the sync manager to signal the given semaphore the next time a session is bound. This method should be
+/// called right before a Realm is opened if that Realm's session is the one to be monitored.
+- (void)primeSyncManagerWithSemaphore:(nullable dispatch_semaphore_t)semaphore;
+
+/// Wait for downloads to complete; drop any error.
+- (void)waitForDownloadsForRealm:(RLMRealm *)realm;
+- (void)waitForDownloadsForRealm:(RLMRealm *)realm error:(NSError **)error;
+
+/// Wait for uploads to complete; drop any error.
+- (void)waitForUploadsForRealm:(RLMRealm *)realm;
+- (void)waitForUploadsForRealm:(RLMRealm *)realm error:(NSError **)error;
+
+/// Wait for downloads to complete while spinning the runloop. This method uses expectations.
+- (void)waitForDownloadsForUser:(RLMSyncUser *)user
+                            url:(NSURL *)url
+                    expectation:(nullable XCTestExpectation *)expectation
+                          error:(NSError **)error;
+
+/// Manually set the refresh token for a user. Used for testing invalid token conditions.
+- (void)manuallySetRefreshTokenForUser:(RLMSyncUser *)user value:(NSString *)tokenValue;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#define WAIT_FOR_SEMAPHORE(macro_semaphore, macro_timeout) \
+{                                                                                                                      \
+    int64_t delay_in_ns = (int64_t)(macro_timeout * NSEC_PER_SEC);                                                     \
+    BOOL sema_success = dispatch_semaphore_wait(macro_semaphore, dispatch_time(DISPATCH_TIME_NOW, delay_in_ns)) == 0;  \
+    XCTAssertTrue(sema_success, @"Semaphore timed out.");                                                              \
+}
+
+#define CHECK_COUNT(d_count, macro_object_type, macro_realm) \
+{                                                                                                       \
+    [macro_realm refresh];                                                                              \
+    NSInteger c = [macro_object_type allObjectsInRealm:macro_realm].count;                              \
+    NSString *w = self.isParent ? @"parent" : @"child";                                                 \
+    XCTAssert(d_count == c, @"Expected %@ items, but actually got %@ (%@)", @(d_count), @(c), w);       \
+}

+ 529 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.mm

@@ -0,0 +1,529 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncTestCase.h"
+
+#import <XCTest/XCTest.h>
+#import <Realm/Realm.h>
+
+#import "RLMRealm_Dynamic.h"
+#import "RLMRealm_Private.hpp"
+#import "RLMRealmConfiguration_Private.h"
+#import "RLMSyncManager+ObjectServerTests.h"
+#import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
+#import "RLMSyncConfiguration_Private.h"
+#import "RLMUtil.hpp"
+
+#import "sync/sync_manager.hpp"
+#import "sync/sync_session.hpp"
+#import "sync/sync_user.hpp"
+
+// Set this to 1 if you want the test ROS instance to log its debug messages to console.
+#define LOG_ROS_OUTPUT 0
+
+#if !TARGET_OS_MAC
+#error These tests can only be run on a macOS host.
+#endif
+
+static NSString *nodePath() {
+    static NSString *path = [] {
+        NSDictionary *environment = NSProcessInfo.processInfo.environment;
+        if (NSString *path = environment[@"REALM_NODE_PATH"]) {
+            return path;
+        }
+        return @"/usr/local/bin/node";
+    }();
+    return path;
+}
+
+@interface RLMSyncManager ()
++ (void)_setCustomBundleID:(NSString *)customBundleID;
+- (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory;
+@end
+
+@interface RLMSyncTestCase ()
+@property (nonatomic) NSTask *task;
+@end
+
+@interface RLMSyncCredentials ()
++ (instancetype)credentialsWithDebugUserID:(NSString *)userID isAdmin:(BOOL)isAdmin;
+@end
+
+@interface RLMSyncSession ()
+- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback;
+- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback;
+@end
+
+@interface RLMSyncUser()
+- (std::shared_ptr<realm::SyncUser>)_syncUser;
+@end
+
+@implementation SyncObject
+@end
+
+@implementation HugeSyncObject
+
++ (instancetype)object  {
+    const NSInteger fakeDataSize = 1000000;
+    HugeSyncObject *object = [[self alloc] init];
+    char fakeData[fakeDataSize];
+    memset(fakeData, sizeof(fakeData), 16);
+    object.dataProp = [NSData dataWithBytes:fakeData length:sizeof(fakeData)];
+    return object;
+}
+
+@end
+
+static NSTask *s_task;
+static RLMSyncManager *s_managerForTest;
+
+static NSURL *syncDirectoryForChildProcess() {
+    NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
+    NSBundle *bundle = [NSBundle mainBundle];
+    NSString *bundleIdentifier = bundle.bundleIdentifier ?: bundle.executablePath.lastPathComponent;
+    path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-child", bundleIdentifier]];
+    return [NSURL fileURLWithPath:path isDirectory:YES];
+}
+
+@interface RealmObjectServer : NSObject
+@property (nonatomic, readonly) NSURL *serverDataRoot;
+
++ (instancetype)sharedServer;
+
+- (void)launch;
+@end
+
+@implementation RealmObjectServer {
+    NSTask *_task;
+    NSURL *_serverDataRoot;
+}
++ (instancetype)sharedServer {
+    static RealmObjectServer *instance = [RealmObjectServer new];
+    return instance;
+}
+
+- (instancetype)init {
+    if (self = [super init]) {
+        _serverDataRoot = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"test-ros-data"]];
+    }
+    return self;
+}
+
+- (void)launch {
+    if (_task) {
+        return;
+    }
+    // Clean up any old state from the server
+    [[NSTask launchedTaskWithLaunchPath:@"/usr/bin/pkill"
+                              arguments:@[@"-f", @"node.*test-ros-server.js"]] waitUntilExit];
+    [NSFileManager.defaultManager removeItemAtURL:self.serverDataRoot error:nil];
+    [NSFileManager.defaultManager createDirectoryAtURL:self.serverDataRoot
+                           withIntermediateDirectories:YES attributes:nil error:nil];
+
+    // Install ROS if it isn't already present
+    [self downloadObjectServer];
+
+    // Set up the actual ROS task
+    NSPipe *pipe = [NSPipe pipe];
+    _task = [[NSTask alloc] init];
+    _task.currentDirectoryPath = self.serverDataRoot.path;
+    _task.launchPath = nodePath();
+    NSString *directory = [@(__FILE__) stringByDeletingLastPathComponent];
+    _task.arguments = @[[directory stringByAppendingPathComponent:@"test-ros-server.js"],
+                        self.serverDataRoot.path];
+    _task.standardOutput = pipe;
+    [_task launch];
+
+    NSData *childStdout = pipe.fileHandleForReading.readDataToEndOfFile;
+    if (![childStdout isEqual:[@"started\n" dataUsingEncoding:NSUTF8StringEncoding]]) {
+        abort();
+    }
+
+    atexit([] {
+        auto self = RealmObjectServer.sharedServer;
+        [self->_task terminate];
+        [self->_task waitUntilExit];
+        [NSFileManager.defaultManager removeItemAtURL:self->_serverDataRoot error:nil];
+    });
+}
+
+- (NSString *)desiredObjectServerVersion {
+    auto path = [[[[@(__FILE__) stringByDeletingLastPathComponent] // RLMSyncTestCase.mm
+                 stringByDeletingLastPathComponent] // ObjectServerTests
+                 stringByDeletingLastPathComponent] // Realm
+                 stringByAppendingPathComponent:@"dependencies.list"];
+    auto file = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
+    if (!file) {
+        NSLog(@"Failed to read dependencies.list");
+        abort();
+    }
+
+    auto regex = [NSRegularExpression regularExpressionWithPattern:@"^REALM_OBJECT_SERVER_VERSION=(.*)$"
+                                                           options:NSRegularExpressionAnchorsMatchLines error:nil];
+    auto match = [regex firstMatchInString:file options:0 range:{0, file.length}];
+    if (!match) {
+        NSLog(@"Failed to read REALM_OBJECT_SERVER_VERSION from dependencies.list");
+        abort();
+    }
+    return [file substringWithRange:[match rangeAtIndex:1]];
+}
+
+- (NSString *)currentObjectServerVersion {
+    auto path = [[[[@(__FILE__) stringByDeletingLastPathComponent] // RLMSyncTestCase.mm
+                 stringByAppendingPathComponent:@"node_modules"]
+                 stringByAppendingPathComponent:@"realm-object-server"]
+                 stringByAppendingPathComponent:@"package.json"];
+    auto file = [NSData dataWithContentsOfFile:path];
+    if (!file) {
+        return nil;
+    }
+
+    NSError *error;
+    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:file options:0 error:&error];
+    if (!json) {
+        NSLog(@"Error reading version from installed ROS: %@", error);
+        abort();
+    }
+
+    return json[@"version"];
+}
+
+- (void)downloadObjectServer {
+    NSString *desiredVersion = [self desiredObjectServerVersion];
+    NSString *currentVersion = [self currentObjectServerVersion];
+    if ([currentVersion isEqualToString:desiredVersion]) {
+        return;
+    }
+
+    NSLog(@"Installing Realm Object Server %@", desiredVersion);
+    NSTask *task = [[NSTask alloc] init];
+    task.currentDirectoryPath = [@(__FILE__) stringByDeletingLastPathComponent];
+    task.launchPath = nodePath();
+    task.arguments = @[[[nodePath() stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"npm"],
+                       @"--scripts-prepend-node-path=auto",
+                       @"--no-color",
+                       @"--no-progress",
+                       @"--no-save",
+                       @"--no-package-lock",
+                       @"install",
+                       [@"realm-object-server@" stringByAppendingString:desiredVersion]
+                       ];
+    [task launch];
+    [task waitUntilExit];
+}
+@end
+
+@implementation RLMSyncTestCase
+
++ (RLMSyncManager *)managerForCurrentTest {
+    return s_managerForTest;
+}
+
+#pragma mark - Helper methods
+
+- (BOOL)isPartial {
+    return NO;
+}
+
++ (NSURL *)authServerURL {
+    return [NSURL URLWithString:@"http://127.0.0.1:9080"];
+}
+
++ (NSURL *)secureAuthServerURL {
+    return [NSURL URLWithString:@"https://localhost:9443"];
+}
+
++ (RLMSyncCredentials *)basicCredentialsWithName:(NSString *)name register:(BOOL)shouldRegister {
+    return [RLMSyncCredentials credentialsWithUsername:name
+                                              password:@"a"
+                                              register:shouldRegister];
+}
+
++ (NSURL *)onDiskPathForSyncedRealm:(RLMRealm *)realm {
+    return [NSURL fileURLWithPath:@(realm->_realm->config().path.data())];
+}
+
+- (void)addSyncObjectsToRealm:(RLMRealm *)realm descriptions:(NSArray<NSString *> *)descriptions {
+    [realm beginWriteTransaction];
+    for (NSString *desc in descriptions) {
+        [SyncObject createInRealm:realm withValue:@[desc]];
+    }
+    [realm commitWriteTransaction];
+}
+
+- (void)waitForDownloadsForUser:(RLMSyncUser *)user
+                         realms:(NSArray<RLMRealm *> *)realms
+                      realmURLs:(NSArray<NSURL *> *)realmURLs
+                 expectedCounts:(NSArray<NSNumber *> *)counts {
+    NSAssert(realms.count == counts.count && realms.count == realmURLs.count,
+             @"Test logic error: all array arguments must be the same size.");
+    for (NSUInteger i = 0; i < realms.count; i++) {
+        [self waitForDownloadsForUser:user url:realmURLs[i] expectation:nil error:nil];
+        [realms[i] refresh];
+        CHECK_COUNT([counts[i] integerValue], SyncObject, realms[i]);
+    }
+}
+
+- (RLMRealm *)openRealmForURL:(NSURL *)url user:(RLMSyncUser *)user {
+    return [self openRealmForURL:url user:user immediatelyBlock:nil];
+}
+
+- (RLMRealm *)openRealmForURL:(NSURL *)url user:(RLMSyncUser *)user immediatelyBlock:(void(^)(void))block {
+    return [self openRealmForURL:url
+                            user:user
+                   encryptionKey:nil
+                      stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
+                immediatelyBlock:block];
+}
+
+- (RLMRealm *)openRealmForURL:(NSURL *)url
+                         user:(RLMSyncUser *)user
+                encryptionKey:(nullable NSData *)encryptionKey
+                   stopPolicy:(RLMSyncStopPolicy)stopPolicy
+             immediatelyBlock:(nullable void(^)(void))block {
+    const NSTimeInterval timeout = 4;
+    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+    RLMSyncManager.sharedManager.sessionCompletionNotifier = ^(NSError *error) {
+        if (error) {
+            XCTFail(@"Received an asynchronous error when trying to open Realm at '%@' for user '%@': %@ (process: %@)",
+                    url, user.identity, error, self.isParent ? @"parent" : @"child");
+        }
+        dispatch_semaphore_signal(sema);
+    };
+
+    RLMRealm *realm = [self immediatelyOpenRealmForURL:url user:user encryptionKey:encryptionKey stopPolicy:stopPolicy];
+    if (block) {
+        block();
+    }
+    // Wait for login to succeed or fail.
+    XCTAssert(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0,
+              @"Timed out while trying to asynchronously open Realm for URL: %@", url);
+    return realm;
+}
+
+- (RLMRealm *)openRealmWithConfiguration:(RLMRealmConfiguration *)configuration {
+    return [self openRealmWithConfiguration:configuration immediatelyBlock:nullptr];
+}
+
+- (RLMRealm *)openRealmWithConfiguration:(RLMRealmConfiguration *)configuration
+             immediatelyBlock:(nullable void(^)(void))block {
+    const NSTimeInterval timeout = 4;
+    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+    RLMSyncConfiguration *syncConfig = configuration.syncConfiguration;
+    RLMSyncManager.sharedManager.sessionCompletionNotifier = ^(NSError *error) {
+        if (error) {
+            XCTFail(@"Received an asynchronous error when trying to open Realm at '%@' for user '%@': %@ (process: %@)",
+                    syncConfig.realmURL, syncConfig.user.identity, error, self.isParent ? @"parent" : @"child");
+        }
+        dispatch_semaphore_signal(sema);
+    };
+
+    RLMRealm *realm = [RLMRealm realmWithConfiguration:configuration error:nullptr];
+    if (block) {
+        block();
+    }
+    // Wait for login to succeed or fail.
+    XCTAssert(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0,
+              @"Timed out while trying to asynchronously open Realm for URL: %@", syncConfig.realmURL);
+    return realm;
+}
+- (RLMRealm *)immediatelyOpenRealmForURL:(NSURL *)url user:(RLMSyncUser *)user {
+    return [self immediatelyOpenRealmForURL:url
+                                       user:user
+                              encryptionKey:nil
+                                 stopPolicy:RLMSyncStopPolicyAfterChangesUploaded];
+}
+
+- (RLMRealm *)immediatelyOpenRealmForURL:(NSURL *)url
+                                    user:(RLMSyncUser *)user
+                           encryptionKey:(NSData *)encryptionKey
+                              stopPolicy:(RLMSyncStopPolicy)stopPolicy {
+    auto c = [user configurationWithURL:url fullSynchronization:!self.isPartial];
+    c.encryptionKey = encryptionKey;
+    RLMSyncConfiguration *syncConfig = c.syncConfiguration;
+    syncConfig.stopPolicy = stopPolicy;
+    c.syncConfiguration = syncConfig;
+    return [RLMRealm realmWithConfiguration:c error:nil];
+}
+
+- (RLMSyncUser *)logInUserForCredentials:(RLMSyncCredentials *)credentials
+                                  server:(NSURL *)url {
+    NSString *process = self.isParent ? @"parent" : @"child";
+    __block RLMSyncUser *theUser = nil;
+    XCTestExpectation *expectation = [self expectationWithDescription:@"Should log in the user properly"];
+    [RLMSyncUser logInWithCredentials:credentials
+                        authServerURL:url
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+                             XCTAssertTrue(NSThread.isMainThread);
+                             XCTAssertNil(error,
+                                          @"Error when trying to log in a user: %@ (process: %@)",
+                                          error, process);
+                             XCTAssertNotNil(user);
+                             theUser = user;
+                             [expectation fulfill];
+                         }];
+    [self waitForExpectationsWithTimeout:4.0 handler:nil];
+    XCTAssertTrue(theUser.state == RLMSyncUserStateActive,
+                  @"User should have been valid, but wasn't. (process: %@)", process);
+    return theUser;
+}
+
+- (RLMSyncUser *)createAdminUserForURL:(NSURL *)url username:(NSString *)username {
+    return [self logInUserForCredentials:[RLMSyncCredentials credentialsWithDebugUserID:username isAdmin:YES]
+                                  server:url];
+}
+
+- (NSString *)adminToken {
+    NSURL *target = [RealmObjectServer.sharedServer.serverDataRoot
+                     URLByAppendingPathComponent:@"/keys/admin.json"];
+    if (![[NSFileManager defaultManager] fileExistsAtPath:[target path]]) {
+        XCTFail(@"Could not find the JSON file containing the admin token.");
+        return nil;
+    }
+    NSData *raw = [NSData dataWithContentsOfURL:target];
+    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:raw options:0 error:nil];
+    NSString *token = json[@"ADMIN_TOKEN"];
+    if ([token length] == 0) {
+        XCTFail(@"Could not successfully extract the token.");
+    }
+    return token;
+}
+
+- (NSString *)emailForAddress:(NSString *)email {
+    NSURL *target = [[RealmObjectServer.sharedServer.serverDataRoot
+                     URLByAppendingPathComponent:@"/email"]
+                     URLByAppendingPathComponent:email];
+    NSString *body = [NSString stringWithContentsOfURL:target encoding:NSUTF8StringEncoding error:nil];
+    if (body) {
+        [NSFileManager.defaultManager removeItemAtURL:target error:nil];
+    }
+    return body;
+}
+
+- (void)waitForDownloadsForRealm:(RLMRealm *)realm {
+    [self waitForDownloadsForRealm:realm error:nil];
+}
+
+- (void)waitForUploadsForRealm:(RLMRealm *)realm {
+    [self waitForUploadsForRealm:realm error:nil];
+}
+
+- (void)waitForDownloadsForUser:(RLMSyncUser *)user
+                            url:(NSURL *)url
+                    expectation:(XCTestExpectation *)expectation
+                          error:(NSError **)error {
+    RLMSyncSession *session = [user sessionForURL:url];
+    NSAssert(session, @"Cannot call with invalid URL");
+    XCTestExpectation *ex = expectation ?: [self expectationWithDescription:@"Wait for download completion"];
+    __block NSError *theError = nil;
+    BOOL queued = [session waitForDownloadCompletionOnQueue:nil callback:^(NSError *err) {
+        theError = err;
+        [ex fulfill];
+    }];
+    if (!queued) {
+        XCTFail(@"Download waiter did not queue; session was invalid or errored out.");
+        return;
+    }
+    [self waitForExpectations:@[ex] timeout:20.0];
+    if (error) {
+        *error = theError;
+    }
+}
+
+- (void)waitForUploadsForRealm:(RLMRealm *)realm error:(NSError **)error {
+    RLMSyncSession *session = realm.syncSession;
+    NSAssert(session, @"Cannot call with invalid Realm");
+    XCTestExpectation *ex = [self expectationWithDescription:@"Wait for upload completion"];
+    __block NSError *completionError;
+    BOOL queued = [session waitForUploadCompletionOnQueue:nil callback:^(NSError *error) {
+        completionError = error;
+        [ex fulfill];
+    }];
+    if (!queued) {
+        XCTFail(@"Upload waiter did not queue; session was invalid or errored out.");
+        return;
+    }
+    [self waitForExpectations:@[ex] timeout:20.0];
+    if (error)
+        *error = completionError;
+}
+
+- (void)waitForDownloadsForRealm:(RLMRealm *)realm error:(NSError **)error {
+    RLMSyncSession *session = realm.syncSession;
+    NSAssert(session, @"Cannot call with invalid Realm");
+    XCTestExpectation *ex = [self expectationWithDescription:@"Wait for download completion"];
+    __block NSError *completionError;
+    BOOL queued = [session waitForDownloadCompletionOnQueue:nil callback:^(NSError *error) {
+        completionError = error;
+        [ex fulfill];
+    }];
+    if (!queued) {
+        XCTFail(@"Download waiter did not queue; session was invalid or errored out.");
+        return;
+    }
+    [self waitForExpectations:@[ex] timeout:20.0];
+    if (error)
+        *error = completionError;
+}
+
+- (void)manuallySetRefreshTokenForUser:(RLMSyncUser *)user value:(NSString *)tokenValue {
+    [user _syncUser]->update_refresh_token(tokenValue.UTF8String);
+}
+
+// FIXME: remove this API once the new token system is implemented.
+- (void)primeSyncManagerWithSemaphore:(dispatch_semaphore_t)semaphore {
+    if (semaphore == nil) {
+        [[RLMSyncManager sharedManager] setSessionCompletionNotifier:^(__unused NSError *error){ }];
+        return;
+    }
+    [[RLMSyncManager sharedManager] setSessionCompletionNotifier:^(NSError *error) {
+        XCTAssertNil(error, @"Session completion block returned with an error: %@", error);
+        dispatch_semaphore_signal(semaphore);
+    }];
+}
+
+#pragma mark - XCUnitTest Lifecycle
+
+- (void)setUp {
+    [super setUp];
+    self.continueAfterFailure = NO;
+    NSURL *clientDataRoot;
+    if (self.isParent) {
+        [RealmObjectServer.sharedServer launch];
+        clientDataRoot = [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)];
+    }
+    else {
+        clientDataRoot = syncDirectoryForChildProcess();
+    }
+    NSError *error;
+    [NSFileManager.defaultManager removeItemAtURL:clientDataRoot error:&error];
+    [NSFileManager.defaultManager createDirectoryAtURL:clientDataRoot
+                           withIntermediateDirectories:YES attributes:nil error:&error];
+    s_managerForTest = [[RLMSyncManager alloc] initWithCustomRootDirectory:clientDataRoot];
+    [RLMSyncManager sharedManager].logLevel = RLMSyncLogLevelOff;
+}
+
+- (void)tearDown {
+    [s_managerForTest prepareForDestruction];
+    s_managerForTest = nil;
+    [RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:NO blockOnRefreshCompletion:nil];
+
+    [super tearDown];
+}
+
+@end

+ 34 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncUser+ObjectServerTests.h

@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import <Realm/Realm.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RLMSyncUser (ObjectServerTests)
+
+- (BOOL)waitForUploadToFinish:(NSURL *)url;
+- (BOOL)waitForDownloadToFinish:(NSURL *)url;
+
+- (void)simulateClientResetErrorForSession:(NSURL *)url;
+
+@end
+
+FOUNDATION_EXTERN bool RLMHasCachedRealmForPath(NSString *path);
+
+NS_ASSUME_NONNULL_END

+ 76 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncUser+ObjectServerTests.mm

@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSyncUser+ObjectServerTests.h"
+
+#import "RLMSyncSession_Private.hpp"
+#import "RLMRealmUtil.hpp"
+
+#import "sync/sync_session.hpp"
+
+using namespace realm;
+
+@implementation RLMSyncUser (ObjectServerTests)
+
+- (BOOL)waitForUploadToFinish:(NSURL *)url {
+    const NSTimeInterval timeout = 20;
+    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+    RLMSyncSession *session = [self sessionForURL:url];
+    NSAssert(session, @"Cannot call with invalid URL");
+    BOOL couldWait = [session waitForUploadCompletionOnQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
+                                                    callback:^(NSError *){
+                                                        dispatch_semaphore_signal(sema);
+                                                    }];
+    if (!couldWait) {
+        return NO;
+    }
+    return dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0;
+}
+
+- (BOOL)waitForDownloadToFinish:(NSURL *)url {
+    const NSTimeInterval timeout = 20;
+    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+    RLMSyncSession *session = [self sessionForURL:url];
+    NSAssert(session, @"Cannot call with invalid URL");
+    BOOL couldWait = [session waitForDownloadCompletionOnQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
+                                                      callback:^(NSError *){
+                                                          dispatch_semaphore_signal(sema);
+                                                      }];
+    if (!couldWait) {
+        return NO;
+    }
+    return dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0;
+}
+
+- (void)simulateClientResetErrorForSession:(NSURL *)url {
+    RLMSyncSession *session = [self sessionForURL:url];
+    NSAssert(session, @"Cannot call with invalid URL");
+
+    std::shared_ptr<SyncSession> raw_session = session->_session.lock();
+    std::error_code code = std::error_code{
+        static_cast<int>(realm::sync::ProtocolError::bad_client_file_ident),
+        realm::sync::protocol_error_category()
+    };
+    SyncSession::OnlyForTesting::handle_error(*raw_session, {code, "Not a real error message", false});
+}
+
+@end
+
+bool RLMHasCachedRealmForPath(NSString *path) {
+    return RLMGetAnyCachedRealmForPath(path.UTF8String);
+}

+ 26 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMTestUtils.h

@@ -0,0 +1,26 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+void RLMSwapOutClassMethod(id classObject, SEL original, SEL swizzled);
+void RLMSwapOutInstanceMethod(id classObject, SEL original, SEL swizzled);
+
+#ifndef CUSTOM_REALM_URL
+#define CUSTOM_REALM_URL(realm_identifier) \
+[NSURL URLWithString:[NSString stringWithFormat:@"realm://127.0.0.1:9080/~/%@%@", NSStringFromSelector(_cmd), realm_identifier]]
+#define REALM_URL() CUSTOM_REALM_URL(@"")
+#endif

+ 53 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMTestUtils.m

@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import <Foundation/Foundation.h>
+
+#import "RLMTestUtils.h"
+
+#import <objc/runtime.h>
+
+static void RLMSwapOutMethod(Class class,
+                             SEL original, Method originalMethod,
+                             SEL swizzled, Method swizzledMethod) {
+    if (class_addMethod(class,
+                        original,
+                        method_getImplementation(swizzledMethod),
+                        method_getTypeEncoding(swizzledMethod))) {
+        class_replaceMethod(class,
+                            swizzled,
+                            method_getImplementation(originalMethod),
+                            method_getTypeEncoding(originalMethod));
+    } else {
+        method_exchangeImplementations(originalMethod, swizzledMethod);
+    }
+}
+
+void RLMSwapOutClassMethod(id classObject, SEL original, SEL swizzled) {
+    Class class = object_getClass((id)classObject);
+    RLMSwapOutMethod(class,
+                     original, class_getClassMethod(class, original),
+                     swizzled, class_getClassMethod(class, swizzled));
+}
+
+void RLMSwapOutInstanceMethod(id classObject, SEL original, SEL swizzled) {
+    Class class = [classObject class];
+    RLMSwapOutMethod(class,
+                     original, class_getInstanceMethod(class, original),
+                     swizzled, class_getInstanceMethod(class, swizzled));
+}

+ 627 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftObjectServerTests.swift

@@ -0,0 +1,627 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+import XCTest
+import RealmSwift
+
+// Used by testOfflineClientReset
+// The naming here is nonstandard as the sync-1.x.realm test file comes from the .NET unit tests.
+// swiftlint:disable identifier_name
+@objc(Person)
+class Person: Object {
+    @objc dynamic var FirstName: String?
+    @objc dynamic var LastName: String?
+
+    override class func shouldIncludeInDefaultSchema() -> Bool { return false }
+}
+
+class SwiftObjectServerTests: SwiftSyncTestCase {
+
+    /// It should be possible to successfully open a Realm configured for sync.
+    func testBasicSwiftSync() {
+        let url = URL(string: "realm://127.0.0.1:9080/~/testBasicSync")!
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: true), server: authURL)
+            let realm = try synchronouslyOpenRealm(url: url, user: user)
+            XCTAssert(realm.isEmpty, "Freshly synced Realm was not empty...")
+        } catch {
+            XCTFail("Got an error: \(error)")
+        }
+    }
+
+    /// If client B adds objects to a Realm, client A should see those new objects.
+    func testSwiftAddObjects() {
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+            if isParent {
+                waitForDownloads(for: realm)
+                checkCount(expected: 0, realm, SwiftSyncObject.self)
+                executeChild()
+                waitForDownloads(for: realm)
+                checkCount(expected: 3, realm, SwiftSyncObject.self)
+            } else {
+                // Add objects
+                try realm.write {
+                    realm.add(SwiftSyncObject(value: ["child-1"]))
+                    realm.add(SwiftSyncObject(value: ["child-2"]))
+                    realm.add(SwiftSyncObject(value: ["child-3"]))
+                }
+                waitForUploads(for: realm)
+                checkCount(expected: 3, realm, SwiftSyncObject.self)
+            }
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    /// If client B removes objects from a Realm, client A should see those changes.
+    func testSwiftDeleteObjects() {
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+            if isParent {
+                try realm.write {
+                    realm.add(SwiftSyncObject(value: ["child-1"]))
+                    realm.add(SwiftSyncObject(value: ["child-2"]))
+                    realm.add(SwiftSyncObject(value: ["child-3"]))
+                }
+                waitForUploads(for: realm)
+                checkCount(expected: 3, realm, SwiftSyncObject.self)
+                executeChild()
+                waitForDownloads(for: realm)
+                checkCount(expected: 0, realm, SwiftSyncObject.self)
+            } else {
+                try realm.write {
+                    realm.deleteAll()
+                }
+                waitForUploads(for: realm)
+                checkCount(expected: 0, realm, SwiftSyncObject.self)
+            }
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+#if swift(>=3.2)
+    func testConnectionState() {
+        let user = try! synchronouslyLogInUser(for: basicCredentials(register: true), server: authURL)
+        let realm = try! synchronouslyOpenRealm(url: realmURL, user: user)
+        let session = realm.syncSession!
+
+        func wait(forState desiredState: SyncSession.ConnectionState) {
+            let ex = expectation(description: "Wait for connection state: \(desiredState)")
+            let token = session.observe(\SyncSession.connectionState, options: .initial) { s, _ in
+                if s.connectionState == desiredState {
+                    ex.fulfill()
+                }
+            }
+            waitForExpectations(timeout: 2.0)
+            token.invalidate()
+        }
+
+        wait(forState: .connected)
+
+        session.suspend()
+        wait(forState: .disconnected)
+
+        session.resume()
+        wait(forState: .connecting)
+        wait(forState: .connected)
+    }
+#endif // Swift >= 3.2
+
+    // MARK: - Client reset
+
+    func testClientReset() {
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+
+            var theError: SyncError?
+            let ex = expectation(description: "Waiting for error handler to be called...")
+            SyncManager.shared.errorHandler = { (error, session) in
+                if let error = error as? SyncError {
+                    theError = error
+                } else {
+                    XCTFail("Error \(error) was not a sync error. Something is wrong.")
+                }
+                ex.fulfill()
+            }
+            user.simulateClientResetError(forSession: realmURL)
+            waitForExpectations(timeout: 10, handler: nil)
+            XCTAssertNotNil(theError)
+            XCTAssertTrue(theError!.code == SyncError.Code.clientResetError)
+            let resetInfo = theError!.clientResetInfo()
+            XCTAssertNotNil(resetInfo)
+            XCTAssertTrue(resetInfo!.0.contains("io.realm.object-server-recovered-realms/recovered_realm"))
+            XCTAssertNotNil(realm)
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    func testClientResetManualInitiation() {
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            var theError: SyncError?
+
+            try autoreleasepool {
+                let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+                let ex = expectation(description: "Waiting for error handler to be called...")
+                SyncManager.shared.errorHandler = { (error, session) in
+                    if let error = error as? SyncError {
+                        theError = error
+                    } else {
+                        XCTFail("Error \(error) was not a sync error. Something is wrong.")
+                    }
+                    ex.fulfill()
+                }
+                user.simulateClientResetError(forSession: realmURL)
+                waitForExpectations(timeout: 10, handler: nil)
+                XCTAssertNotNil(theError)
+                XCTAssertNotNil(realm)
+            }
+            let (path, errorToken) = theError!.clientResetInfo()!
+            XCTAssertFalse(FileManager.default.fileExists(atPath: path))
+            SyncSession.immediatelyHandleError(errorToken)
+            XCTAssertTrue(FileManager.default.fileExists(atPath: path))
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    // MARK: - Progress notifiers
+
+    func testStreamingDownloadNotifier() {
+        let bigObjectCount = 2
+        do {
+            var callCount = 0
+            var transferred = 0
+            var transferrable = 0
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+            if isParent {
+                let session = realm.syncSession
+                XCTAssertNotNil(session)
+                let ex = expectation(description: "streaming-downloads-expectation")
+                var hasBeenFulfilled = false
+                let token = session!.addProgressNotification(for: .download, mode: .reportIndefinitely) { p in
+                    callCount += 1
+                    XCTAssert(p.transferredBytes >= transferred)
+                    XCTAssert(p.transferrableBytes >= transferrable)
+                    transferred = p.transferredBytes
+                    transferrable = p.transferrableBytes
+                    if p.transferredBytes > 0 && p.isTransferComplete && !hasBeenFulfilled {
+                        ex.fulfill()
+                        hasBeenFulfilled = true
+                    }
+                }
+                // Wait for the child process to upload all the data.
+                executeChild()
+                waitForExpectations(timeout: 10.0, handler: nil)
+                token!.invalidate()
+                XCTAssert(callCount > 1)
+                XCTAssert(transferred >= transferrable)
+            } else {
+                try realm.write {
+                    for _ in 0..<bigObjectCount {
+                        realm.add(SwiftHugeSyncObject())
+                    }
+                }
+                waitForUploads(for: realm)
+                checkCount(expected: bigObjectCount, realm, SwiftHugeSyncObject.self)
+            }
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    func testStreamingUploadNotifier() {
+        let bigObjectCount = 2
+        do {
+            var callCount = 0
+            var transferred = 0
+            var transferrable = 0
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+            let session = realm.syncSession
+            XCTAssertNotNil(session)
+            let ex = expectation(description: "streaming-uploads-expectation")
+            var hasBeenFulfilled = false
+            let token = session!.addProgressNotification(for: .upload, mode: .reportIndefinitely) { p in
+                callCount += 1
+                XCTAssert(p.transferredBytes >= transferred)
+                XCTAssert(p.transferrableBytes >= transferrable)
+                transferred = p.transferredBytes
+                transferrable = p.transferrableBytes
+                if p.transferredBytes > 0 && p.isTransferComplete && !hasBeenFulfilled {
+                    ex.fulfill()
+                    hasBeenFulfilled = true
+                }
+            }
+            try realm.write {
+                for _ in 0..<bigObjectCount {
+                    realm.add(SwiftHugeSyncObject())
+                }
+            }
+            waitForExpectations(timeout: 10.0, handler: nil)
+            token!.invalidate()
+            XCTAssert(callCount > 1)
+            XCTAssert(transferred >= transferrable)
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    // MARK: - Download Realm
+
+    func testDownloadRealm() {
+        let bigObjectCount = 2
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(register: isParent), server: authURL)
+            if isParent {
+                // Wait for the child process to upload everything.
+                executeChild()
+                let ex = expectation(description: "download-realm")
+                let config = user.configuration(realmURL: realmURL, fullSynchronization: true)
+                let pathOnDisk = ObjectiveCSupport.convert(object: config).pathOnDisk
+                XCTAssertFalse(FileManager.default.fileExists(atPath: pathOnDisk))
+                Realm.asyncOpen(configuration: config) { realm, error in
+                    XCTAssertNil(error)
+                    self.checkCount(expected: bigObjectCount, realm!, SwiftHugeSyncObject.self)
+                    ex.fulfill()
+                }
+                func fileSize(path: String) -> Int {
+                    if let attr = try? FileManager.default.attributesOfItem(atPath: path) {
+                        return attr[.size] as! Int
+                    }
+                    return 0
+                }
+                let sizeBefore = fileSize(path: pathOnDisk)
+                autoreleasepool {
+                    // We have partial transaction logs but no data
+                    XCTAssertGreaterThan(sizeBefore, 0)
+                    XCTAssert(try! Realm(configuration: config).isEmpty)
+                }
+                XCTAssertFalse(RLMHasCachedRealmForPath(pathOnDisk))
+                waitForExpectations(timeout: 10.0, handler: nil)
+                XCTAssertGreaterThan(fileSize(path: pathOnDisk), sizeBefore)
+                XCTAssertFalse(RLMHasCachedRealmForPath(pathOnDisk))
+            } else {
+                let realm = try synchronouslyOpenRealm(url: realmURL, user: user)
+                // Write lots of data to the Realm, then wait for it to be uploaded.
+                try realm.write {
+                    for _ in 0..<bigObjectCount {
+                        realm.add(SwiftHugeSyncObject())
+                    }
+                }
+                waitForUploads(for: realm)
+                checkCount(expected: bigObjectCount, realm, SwiftHugeSyncObject.self)
+            }
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    // MARK: - Administration
+
+    func testRetrieveUserInfo() {
+        let adminUsername = "jyaku.swift"
+        let nonAdminUsername = "meela.swift@realm.example.org"
+        let password = "p"
+        let server = SwiftObjectServerTests.authServerURL()
+
+        // Create a non-admin user.
+        _ = logInUser(for: .init(username: nonAdminUsername, password: password, register: true),
+                      server: server)
+        // Create an admin user.
+        let adminUser = createAdminUser(for: server, username: adminUsername)
+
+        // Look up information about the non-admin user from the admin user.
+        let ex = expectation(description: "Should be able to look up user information")
+        adminUser.retrieveInfo(forUser: nonAdminUsername, identityProvider: .usernamePassword) { (userInfo, err) in
+            XCTAssertNil(err)
+            XCTAssertNotNil(userInfo)
+            guard let userInfo = userInfo else {
+                return
+            }
+            let account = userInfo.accounts.first!
+            XCTAssertEqual(account.providerUserIdentity, nonAdminUsername)
+            XCTAssertEqual(account.provider, Provider.usernamePassword)
+            XCTAssertFalse(userInfo.isAdmin)
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: 10.0, handler: nil)
+    }
+
+    // MARK: - Authentication
+
+    func testInvalidCredentials() {
+        do {
+            let username = "testInvalidCredentialsUsername"
+            let credentials = SyncCredentials.usernamePassword(username: username,
+                                                               password: "THIS_IS_A_PASSWORD",
+                                                               register: true)
+            _ = try synchronouslyLogInUser(for: credentials, server: authURL)
+            // Now log in the same user, but with a bad password.
+            let ex = expectation(description: "wait for user login")
+            let credentials2 = SyncCredentials.usernamePassword(username: username, password: "NOT_A_VALID_PASSWORD")
+            SyncUser.logIn(with: credentials2, server: authURL) { user, error in
+                XCTAssertNil(user)
+                XCTAssertTrue(error is SyncAuthError)
+                let castError = error as! SyncAuthError
+                XCTAssertEqual(castError.code, SyncAuthError.invalidCredential)
+                ex.fulfill()
+            }
+            waitForExpectations(timeout: 2.0, handler: nil)
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    // MARK: - User-specific functionality
+
+    func testUserExpirationCallback() {
+        do {
+            let user = try synchronouslyLogInUser(for: basicCredentials(), server: authURL)
+
+            // Set a callback on the user
+            var blockCalled = false
+            let ex = expectation(description: "Error callback should fire upon receiving an error")
+            user.errorHandler = { (u, error) in
+                XCTAssertEqual(u.identity, user.identity)
+                XCTAssertEqual(error.code, .accessDeniedOrInvalidPath)
+                blockCalled = true
+                ex.fulfill()
+            }
+
+            // Screw up the token on the user.
+            manuallySetRefreshToken(for: user, value: "not-a-real-token")
+
+            // Try to open a Realm with the user; this will cause our errorHandler block defined above to be fired.
+            XCTAssertFalse(blockCalled)
+            _ = try immediatelyOpenRealm(url: realmURL, user: user)
+            waitForExpectations(timeout: 10.0, handler: nil)
+            XCTAssertEqual(user.state, .loggedOut)
+        } catch {
+            XCTFail("Got an error: \(error) (process: \(isParent ? "parent" : "child"))")
+        }
+    }
+
+    // MARK: - Offline client reset
+
+    func testOfflineClientReset() {
+        let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
+
+        let sourceFileURL = Bundle(for: type(of: self)).url(forResource: "sync-1.x", withExtension: "realm")!
+        let fileName = "\(UUID()).realm"
+        let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
+        try! FileManager.default.copyItem(at: sourceFileURL, to: fileURL)
+
+        let syncConfig = RLMSyncConfiguration(user: user, realmURL: realmURL)
+        syncConfig.customFileURL = fileURL
+        let config = Realm.Configuration(syncConfiguration: ObjectiveCSupport.convert(object: syncConfig))
+        do {
+            _ = try Realm(configuration: config)
+        } catch let e as Realm.Error where e.code == .incompatibleSyncedFile {
+            var backupConfiguration = e.backupConfiguration
+            XCTAssertNotNil(backupConfiguration)
+
+            // Open the backup Realm with a schema subset since it was created using the schema from .NET's unit tests.
+            backupConfiguration!.objectTypes = [Person.self]
+            let backupRealm = try! Realm(configuration: backupConfiguration!)
+
+            let people = backupRealm.objects(Person.self)
+            XCTAssertEqual(people.count, 1)
+            XCTAssertEqual(people[0].FirstName, "John")
+            XCTAssertEqual(people[0].LastName, "Smith")
+
+            // Verify that we can now successfully open the original synced Realm.
+            _ = try! Realm(configuration: config)
+        } catch {
+            fatalError("Unexpected error: \(error)")
+        }
+    }
+
+    // MARK: - Partial sync
+
+// Partial sync subscriptions are only available in Swift 3.2 and newer.
+#if swift(>=3.2)
+    func populateTestRealm(_ username: String) {
+        autoreleasepool {
+            let credentials = SyncCredentials.usernamePassword(username: username, password: "a", register: true)
+            let user = try! synchronouslyLogInUser(for: credentials, server: authURL)
+            let realm = try! synchronouslyOpenRealm(configuration: user.configuration())
+
+            try! realm.write {
+                realm.add(SwiftPartialSyncObjectA(number: 0, string: "realm"))
+                realm.add(SwiftPartialSyncObjectA(number: 1, string: ""))
+                realm.add(SwiftPartialSyncObjectA(number: 2, string: ""))
+                realm.add(SwiftPartialSyncObjectA(number: 3, string: ""))
+                realm.add(SwiftPartialSyncObjectA(number: 4, string: "realm"))
+                realm.add(SwiftPartialSyncObjectA(number: 5, string: "sync"))
+                realm.add(SwiftPartialSyncObjectA(number: 6, string: "partial"))
+                realm.add(SwiftPartialSyncObjectA(number: 7, string: "partial"))
+                realm.add(SwiftPartialSyncObjectA(number: 8, string: "partial"))
+                realm.add(SwiftPartialSyncObjectA(number: 9, string: "partial"))
+                realm.add(SwiftPartialSyncObjectB(number: 0, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 1, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 2, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 3, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 4, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 5, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 6, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 7, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 8, firstString: "", secondString: ""))
+                realm.add(SwiftPartialSyncObjectB(number: 9, firstString: "", secondString: ""))
+            }
+            waitForUploads(for: realm)
+        }
+    }
+
+    func testPartialSync() {
+        populateTestRealm(#function)
+
+        let credentials = SyncCredentials.usernamePassword(username: #function, password: "a")
+        let user = try! synchronouslyLogInUser(for: credentials, server: authURL)
+        let realm = try! synchronouslyOpenRealm(configuration: user.configuration())
+
+        let results = realm.objects(SwiftPartialSyncObjectA.self).filter("number > 5")
+        let subscription = results.subscribe(named: "query")
+        XCTAssertEqual(subscription.state, .creating)
+        waitForState(subscription, .complete)
+
+        // Verify that we got what we're looking for
+        XCTAssertEqual(results.count, 4)
+        for object in results {
+            XCTAssertGreaterThan(object.number, 5)
+            XCTAssertEqual(object.string, "partial")
+        }
+
+        // And that we didn't get anything else.
+        XCTAssertEqual(realm.objects(SwiftPartialSyncObjectA.self).count, results.count)
+        XCTAssertTrue(realm.objects(SwiftPartialSyncObjectB.self).isEmpty)
+
+        // Re-subscribing to an existing named query may not report the query's state immediately,
+        // but it should report it eventually.
+        let subscription2 = realm.objects(SwiftPartialSyncObjectA.self).filter("number > 5").subscribe(named: "query")
+        waitForState(subscription2, .complete)
+
+        // Creating a subscription with the same name but different query should raise an error.
+        let subscription3 = realm.objects(SwiftPartialSyncObjectA.self).filter("number < 5").subscribe(named: "query")
+        waitForError(subscription3)
+
+        // Unsubscribing should move the subscription to the invalidated state.
+        subscription.unsubscribe()
+        waitForState(subscription, .invalidated)
+    }
+
+    func testPartialSyncLimit() {
+        populateTestRealm(#function)
+
+        let credentials = SyncCredentials.usernamePassword(username: #function, password: "a")
+        let user = try! synchronouslyLogInUser(for: credentials, server: authURL)
+        let realm = try! synchronouslyOpenRealm(configuration: user.configuration())
+
+        let results = realm.objects(SwiftPartialSyncObjectA.self).filter("number > 5")
+        waitForState(results.subscribe(named: "query", limit: 1), .complete)
+        XCTAssertEqual(results.count, 1)
+        XCTAssertEqual(realm.objects(SwiftPartialSyncObjectA.self).count, 1)
+        if let object = results.first {
+            XCTAssertGreaterThan(object.number, 5)
+            XCTAssertEqual(object.string, "partial")
+        }
+
+        let results2 = realm.objects(SwiftPartialSyncObjectA.self).sorted(byKeyPath: "number", ascending: false)
+        waitForState(results2.subscribe(named: "query2", limit: 2), .complete)
+        XCTAssertEqual(results2.count, 3)
+        XCTAssertEqual(realm.objects(SwiftPartialSyncObjectA.self).count, 3)
+        for object in results2 {
+            XCTAssertTrue(object.number == 6 || object.number >= 8,
+                          "\(object.number) == 6 || \(object.number) >= 8")
+            XCTAssertEqual(object.string, "partial")
+        }
+    }
+
+    func waitForState<T>(_ subscription: SyncSubscription<T>, _ desiredState: SyncSubscriptionState) {
+        let ex = expectation(description: "Waiting for state \(desiredState)")
+        let token = subscription.observe(\.state, options: .initial) { state in
+            if state == desiredState {
+                ex.fulfill()
+            }
+        }
+        waitForExpectations(timeout: 20.0)
+        token.invalidate()
+    }
+
+    func waitForError<T>(_ subscription: SyncSubscription<T>) {
+        let ex = expectation(description: "Waiting for error state")
+        let token = subscription.observe(\.state, options: .initial) { state in
+            if case .error(_) = state {
+                ex.fulfill()
+            }
+        }
+        waitForExpectations(timeout: 20.0)
+        token.invalidate()
+    }
+#endif // Swift >= 3.2
+
+    // MARK: - Certificate Pinning
+
+    func testSecureConnectionToLocalhostWithDefaultSecurity() {
+        let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
+        let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
+                                        serverValidationPolicy: .system)
+
+        let ex = expectation(description: "Waiting for error handler to be called")
+        SyncManager.shared.errorHandler = { (error, session) in
+            ex.fulfill()
+        }
+
+        _ = try! Realm(configuration: config)
+        self.waitForExpectations(timeout: 4.0)
+    }
+
+    func testSecureConnectionToLocalhostWithValidationDisabled() {
+        let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
+        let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
+                                        serverValidationPolicy: .none)
+        SyncManager.shared.errorHandler = { (error, session) in
+            XCTFail("Unexpected connection failure: \(error)")
+        }
+
+        let realm = try! Realm(configuration: config)
+        self.waitForUploads(for: realm)
+    }
+
+    func testSecureConnectionToLocalhostWithPinnedCertificate() {
+        let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
+        let certURL = URL(string: #file)!
+            .deletingLastPathComponent()
+            .appendingPathComponent("certificates")
+            .appendingPathComponent("localhost.cer")
+
+        let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
+                                        serverValidationPolicy: .pinCertificate(path: certURL))
+        SyncManager.shared.errorHandler = { (error, session) in
+            XCTFail("Unexpected connection failure: \(error)")
+        }
+
+        let realm = try! Realm(configuration: config)
+        self.waitForUploads(for: realm)
+    }
+
+    func testSecureConnectionToLocalhostWithIncorrectPinnedCertificate() {
+        let user = try! synchronouslyLogInUser(for: basicCredentials(), server: authURL)
+        let certURL = URL(string: #file)!
+            .deletingLastPathComponent()
+            .appendingPathComponent("certificates")
+            .appendingPathComponent("localhost-other.cer")
+        let config = user.configuration(realmURL: URL(string: "realms://localhost:9443/~/default"),
+                                        serverValidationPolicy: .pinCertificate(path: certURL))
+
+        let ex = expectation(description: "Waiting for error handler to be called")
+        SyncManager.shared.errorHandler = { (error, session) in
+            ex.fulfill()
+        }
+
+        _ = try! Realm(configuration: config)
+        self.waitForExpectations(timeout: 4.0)
+    }
+}

+ 306 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftPermissionsAPITests.swift

@@ -0,0 +1,306 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+import XCTest
+import RealmSwift
+
+class SwiftPermissionsAPITests: SwiftSyncTestCase {
+    var userA: SyncUser!
+    var userB: SyncUser!
+    var userC: SyncUser!
+
+    override func setUp() {
+        super.setUp()
+        let baseName = UUID().uuidString
+        userA = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "a", password: "a", register: true),
+                                            server: SwiftSyncTestCase.authServerURL())
+        userB = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "b", password: "a", register: true),
+                                            server: SwiftSyncTestCase.authServerURL())
+        userC = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "c", password: "a", register: true),
+                                            server: SwiftSyncTestCase.authServerURL())
+    }
+
+    override func tearDown() {
+        userA.logOut()
+        userB.logOut()
+        userC.logOut()
+        super.tearDown()
+    }
+
+    private func checkPermissionCount(results: SyncPermissionResults,
+                                      expected: Int,
+                                      file: StaticString = #file,
+                                      line: UInt = #line) {
+        let ex = expectation(description: "Checking permission count")
+        let token = results.observe { (change) in
+            if case let .error(theError) = change {
+                XCTFail("Notification returned error '\(theError)' when running test at \(file):\(line)")
+                return
+            }
+            if results.count == expected {
+                ex.fulfill()
+            }
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+        token.invalidate()
+    }
+
+    private func get(permission: SyncPermission,
+                     from results: SyncPermissionResults,
+                     file: StaticString = #file,
+                     line: UInt = #line) -> SyncPermission? {
+        let ex = expectation(description: "Retrieving permission")
+        var finalValue: SyncPermission?
+        let token = results.observe { (change) in
+            if case let .error(theError) = change {
+                XCTFail("Notification returned error '\(theError)' when running test at \(file):\(line)")
+                return
+            }
+            for result in results where result == permission {
+                finalValue = result
+                ex.fulfill()
+                return
+            }
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+        token.invalidate()
+        return finalValue
+    }
+
+    /// Ensure the absence of a permission from a results after an elapsed time interval.
+    /// This method is intended to be used to check that a permission never becomes
+    /// present within a results to begin with.
+    private func ensureAbsence(of permission: SyncPermission,
+                               from results: SyncPermissionResults,
+                               after wait: Double = 0.5,
+                               file: StaticString = #file,
+                               line: UInt = #line) {
+        let ex = expectation(description: "Looking for permission")
+        var isPresent = false
+        let token = results.observe { (change) in
+            if case let .error(theError) = change {
+                XCTFail("Notification returned error '\(theError)' when running test at \(file):\(line)")
+                return
+            }
+            isPresent = results.contains(permission)
+        }
+        DispatchQueue.main.asyncAfter(deadline: .now() + wait) {
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: wait + 1.0, handler: nil)
+        token.invalidate()
+        XCTAssertFalse(isPresent, "Permission '\(permission)' was spuriously present (\(file):\(line))")
+    }
+
+    private func tildeSubstitutedURL(for url: URL, user: SyncUser) -> URL {
+        XCTAssertNotNil(user.identity)
+        let identity = user.identity!
+        return URL(string: url.absoluteString.replacingOccurrences(of: "~", with: identity))!
+    }
+
+    /// Setting a permission should work, and then that permission should be able to be retrieved.
+    func testSettingPermissions() {
+        // First, there should be no permissions.
+        let ex = expectation(description: "No permissions for newly created user.")
+        var results: SyncPermissionResults!
+        userB.retrievePermissions { (r, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(r)
+            results = r
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+        checkPermissionCount(results: results, expected: 0)
+
+        // Open a Realm for user A.
+        let uuid = UUID().uuidString
+        let url = SwiftSyncTestCase.uniqueRealmURL(customName: uuid)
+        _ = try! synchronouslyOpenRealm(url: url, user: userA)
+
+        // Give user B read permissions to that Realm.
+        let p = SyncPermission(realmPath: tildeSubstitutedURL(for: url, user: userA).path,
+                               identity: userB.identity!,
+                               accessLevel: .read)
+
+        // Set the permission.
+        let ex2 = expectation(description: "Setting a permission should work.")
+        userA.apply(p) { (error) in
+            XCTAssertNil(error)
+            ex2.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+
+        // Now retrieve the permissions again and make sure the new permission is properly set.
+        let ex3 = expectation(description: "One permission in results after setting the permission.")
+        userB.retrievePermissions { (r, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(r)
+            results = r
+            ex3.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+        // Expected permission: applies to user B, but for user A's Realm.
+        let finalValue = get(permission: p, from: results)
+        XCTAssertNotNil(finalValue, "Did not find the permission \(p)")
+
+        // Check getting permission by its index.
+        let index = results.index(of: p)
+        XCTAssertNotNil(index)
+        XCTAssertTrue(p == results[index!])
+    }
+
+    /// Observing permission changes should work.
+    func testObservingPermissions() {
+        // Get a reference to the permission results.
+        let ex = expectation(description: "Retrieve permission results.")
+        var results: SyncPermissionResults!
+        userB.retrievePermissions { (r, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(r)
+            results = r
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+
+        // Open a Realm for user A.
+        let uuid = UUID().uuidString
+        let url = SwiftSyncTestCase.uniqueRealmURL(customName: uuid)
+        _ = try! synchronouslyOpenRealm(url: url, user: userA)
+
+        // Register notifications.
+        let noteEx = expectation(description: "Notification should fire")
+        let token = results.observe { (change) in
+            if case .error = change {
+                XCTFail("Should not return an error")
+                return
+            }
+            if results.count > 0 {
+                noteEx.fulfill()
+            }
+        }
+
+        // Give user B read permissions to that Realm.
+        let p = SyncPermission(realmPath: tildeSubstitutedURL(for: url, user: userA).path,
+                               identity: userB.identity!,
+                               accessLevel: .read)
+
+        // Set the permission.
+        let ex2 = expectation(description: "Setting a permission should work.")
+        userA.apply(p) { (error) in
+            XCTAssertNil(error)
+            ex2.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+
+        // Wait for the notification to be fired.
+        wait(for: [noteEx], timeout: 2.0)
+        token.invalidate()
+        let finalValue = get(permission: p, from: results)
+        XCTAssertNotNil(finalValue, "Did not find the permission \(p)")
+    }
+
+    /// User should not be able to change a permission for a Realm they don't own.
+    func testSettingUnownedRealmPermission() {
+        // Open a Realm for user A.
+        let uuid = UUID().uuidString
+        let url = SwiftSyncTestCase.uniqueRealmURL(customName: uuid)
+        _ = try! synchronouslyOpenRealm(url: url, user: userA)
+
+        // Try to have user B give user C permissions to that Realm.
+        let p = SyncPermission(realmPath: url.path, identity: userC.identity!, accessLevel: .read)
+
+        // Attempt to set the permission.
+        let ex2 = expectation(description: "Setting an invalid permission should fail.")
+        userB.apply(p) { (error) in
+            XCTAssertNotNil(error)
+            ex2.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+
+        // Now retrieve the permissions again and make sure the new permission was not set.
+        var results: SyncPermissionResults!
+        let ex3 = expectation(description: "Retrieving the results should work.")
+        userB.retrievePermissions { (r, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(r)
+            results = r
+            ex3.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+        ensureAbsence(of: p, from: results)
+    }
+
+    // MARK: - Offer/response
+
+    func testPermissionOffer() {
+        _ = try! synchronouslyOpenRealm(url: realmURL, user: userA)
+        var token: String?
+
+        // Create an offer.
+        let ex = expectation(description: "A new permission offer will be processed by the server.")
+        userA.createOfferForRealm(at: realmURL, accessLevel: .write) { (t, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(t)
+            token = t
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: 10.0, handler: nil)
+        XCTAssertGreaterThan(token!.lengthOfBytes(using: .utf8), 0)
+    }
+
+    func testPermissionOfferResponse() {
+        _ = try! synchronouslyOpenRealm(url: realmURL, user: userA)
+        var token: String?
+
+        // Create an offer.
+        let ex = expectation(description: "A new permission offer will be processed by the server.")
+        userA.createOfferForRealm(at: realmURL, accessLevel: .write) { (t, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(t)
+            token = t
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: 10.0, handler: nil)
+        guard let theToken = token else {
+            XCTFail("We expected an offer token, but did not get one. Aborting the test.")
+            return
+        }
+        XCTAssertGreaterThan(theToken.lengthOfBytes(using: .utf8), 0)
+
+        // Accept the offer.
+        let ex2 = expectation(description: "A permission offer response will be processed by the server.")
+        var url: URL?
+        userB.acceptOffer(forToken: theToken) { (u, error) in
+            XCTAssertNil(error)
+            XCTAssertNotNil(u)
+            url = u
+            ex2.fulfill()
+        }
+        waitForExpectations(timeout: 10.0, handler: nil)
+        guard let theURL = url else {
+            XCTFail("We expected a Realm URL, but did not get one. Aborting the test.")
+            return
+        }
+        XCTAssertEqual(theURL.path, tildeSubstitutedURL(for: realmURL, user: userA).path)
+        do {
+            _ = try synchronouslyOpenRealm(url: theURL, user: userB)
+        } catch {
+            XCTFail("Was not able to successfully open the Realm with user B after accepting the offer.")
+        }
+    }
+}

+ 253 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftPermissionsTests.swift

@@ -0,0 +1,253 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2018 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+import XCTest
+import RealmSwift
+
+final class PermissionUser: Object {
+    // A class with a name that conflicts with an Object class from RealmSwift to verify
+    // that it doesn't break anything
+}
+
+class SwiftPermissionsAPITests: SwiftSyncTestCase {
+    var userA: SyncUser!
+    var userB: SyncUser!
+    var userC: SyncUser!
+
+    override func setUp() {
+        super.setUp()
+        let baseName = UUID().uuidString
+        userA = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "-a", password: "a", register: true),
+                                            server: SwiftSyncTestCase.authServerURL())
+        userB = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "-b", password: "a", register: true),
+                                            server: SwiftSyncTestCase.authServerURL())
+        userC = try! synchronouslyLogInUser(for: .usernamePassword(username: baseName + "-c", password: "a", register: true),
+                                            server: SwiftSyncTestCase.authServerURL())
+    }
+
+    override func tearDown() {
+        userA.logOut()
+        userB.logOut()
+        userC.logOut()
+        super.tearDown()
+    }
+
+    // MARK: Helper functions
+
+    func openRealm(_ url: URL, _ user: SyncUser) -> Realm {
+        let realm = try! Realm(configuration: user.configuration(realmURL: url))
+        waitForSync(realm)
+        return realm
+    }
+
+    func subscribe<T: Object>(realm: Realm, type: T.Type, _ filter: String = "TRUEPREDICATE") {
+        let ex = expectation(description: "Add partial sync query")
+        realm.subscribe(to: type, where: filter) { _, err in
+            if let err = err {
+                XCTFail("Partial sync subsription failed: \(err)")
+            } else {
+                ex.fulfill()
+            }
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+    }
+
+    func waitForSync(_ realm: Realm) {
+        waitForUploads(for: realm)
+        waitForDownloads(for: realm)
+        realm.refresh()
+    }
+
+    func createRealm(name: String, permissions: (Realm) -> Void) -> URL {
+        // Create a new Realm with an admin user
+        let admin = createAdminUser(for: SwiftSyncTestCase.authServerURL(),
+                                    username: UUID().uuidString + "-admin")
+        let url = URL(string: "realm://127.0.0.1:9080/\(name)")!
+        let adminRealm = openRealm(url, admin)
+        // FIXME: we currently need to add a subscription to get the permissions types sent to us
+        subscribe(realm: adminRealm, type: SwiftSyncObject.self)
+
+        // Set up permissions on the Realm
+        try! adminRealm.write {
+            adminRealm.create(SwiftSyncObject.self, value: ["obj 1"])
+            permissions(adminRealm)
+        }
+
+        // FIXME: we currently need to also add the old realm-level permissions
+        let ex1 = expectation(description: "Setting a permission should work.")
+        let ex2 = expectation(description: "Setting a permission should work.")
+        let ex3 = expectation(description: "Setting a permission should work.")
+        admin.apply(SyncPermission(realmPath: url.path, identity: userA.identity!, accessLevel: .read)) { error in
+            XCTAssertNil(error)
+            ex1.fulfill()
+        }
+        admin.apply(SyncPermission(realmPath: url.path, identity: userB.identity!, accessLevel: .read)) { error in
+            XCTAssertNil(error)
+            ex2.fulfill()
+        }
+        admin.apply(SyncPermission(realmPath: url.path, identity: userC.identity!, accessLevel: .read)) { error in
+            XCTAssertNil(error)
+            ex3.fulfill()
+        }
+        waitForExpectations(timeout: 2.0, handler: nil)
+        waitForSync(adminRealm)
+
+        return url
+    }
+
+    func createDefaultPermisisons(_ permissions: List<Permission>) {
+        var p = permissions.findOrCreate(forRoleNamed: "everyone")
+        p.canCreate = false
+        p.canRead = false
+        p.canQuery = false
+        p.canDelete = false
+        p.canUpdate = false
+        p.canModifySchema = false
+        p.canSetPermissions = false
+
+        p = permissions.findOrCreate(forRoleNamed: "reader")
+        p.canRead = true
+        p.canQuery = true
+
+        p = permissions.findOrCreate(forRoleNamed: "writer")
+        p.canUpdate = true
+        p.canCreate = true
+        p.canDelete = true
+
+        p = permissions.findOrCreate(forRoleNamed: "admin")
+        p.canSetPermissions = true
+    }
+
+    func add(user: SyncUser, toRole roleName: String, inRealm realm: Realm) {
+        let user = realm.create(RealmSwift.PermissionUser.self, value: [user.identity!], update: true)
+        realm.create(PermissionRole.self, value: [roleName], update: true).users.append(user)
+    }
+
+
+    // MARK: Tests
+
+    func testRealmRead() {
+        let url = createRealm(name: "testRealmRead") { realm in
+            createDefaultPermisisons(realm.permissions)
+            add(user: userA, toRole: "reader", inRealm: realm)
+        }
+
+        // userA should now be able to open the Realm and see objects
+        let realmA = openRealm(url, userA)
+        subscribe(realm: realmA, type: SwiftSyncObject.self)
+        XCTAssertEqual(realmA.getPrivileges(), [.read])
+        XCTAssertEqual(realmA.getPrivileges(SwiftSyncObject.self), [.read, .subscribe])
+        XCTAssertEqual(realmA.getPrivileges(realmA.objects(SwiftSyncObject.self).first!), [.read])
+
+        // userA should not be able to create new objects
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 1)
+        try! realmA.write {
+            realmA.create(SwiftSyncObject.self, value: ["obj 2"])
+        }
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 2)
+        waitForSync(realmA)
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 1)
+
+        // userB should not be able to read any objects
+        let realmB = openRealm(url, userB)
+        subscribe(realm: realmB, type: SwiftSyncObject.self)
+        XCTAssertEqual(realmB.getPrivileges(), [])
+        XCTAssertEqual(realmB.getPrivileges(SwiftSyncObject.self), [])
+        XCTAssertEqual(realmB.objects(SwiftSyncObject.self).count, 0)
+    }
+
+    func testRealmWrite() {
+        let url = createRealm(name: "testRealmWrite") { realm in
+            createDefaultPermisisons(realm.permissions)
+            add(user: userA, toRole: "reader", inRealm: realm)
+            add(user: userA, toRole: "writer", inRealm: realm)
+            add(user: userB, toRole: "reader", inRealm: realm)
+        }
+
+        // userA should now be able to open the Realm and see objects
+        let realmA = openRealm(url, userA)
+        subscribe(realm: realmA, type: SwiftSyncObject.self)
+        XCTAssertEqual(realmA.getPrivileges(), [.read, .update])
+        XCTAssertEqual(realmA.getPrivileges(SwiftSyncObject.self),
+                       [.read, .subscribe, .update, .create, .setPermissions])
+        XCTAssertEqual(realmA.getPrivileges(realmA.objects(SwiftSyncObject.self).first!),
+                       [.read, .update, .delete, .setPermissions])
+
+        // userA should be able to create new objects
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 1)
+        try! realmA.write {
+            realmA.create(SwiftSyncObject.self, value: ["obj 2"])
+        }
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 2)
+        waitForSync(realmA)
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 2)
+
+        // userB's insertions should be reverted
+        let realmB = openRealm(url, userB)
+        subscribe(realm: realmB, type: SwiftSyncObject.self)
+        XCTAssertEqual(realmB.objects(SwiftSyncObject.self).count, 2)
+        try! realmB.write {
+            realmB.create(SwiftSyncObject.self, value: ["obj 3"])
+        }
+        XCTAssertEqual(realmB.objects(SwiftSyncObject.self).count, 3)
+        waitForSync(realmB)
+        XCTAssertEqual(realmB.objects(SwiftSyncObject.self).count, 2)
+    }
+    func testRealmSetPermissions() {
+
+    }
+    func testRealmModifySchema() {
+
+    }
+
+    func testClassRead() {
+        let url = createRealm(name: "testClassRead") { realm in
+            createDefaultPermisisons(realm.permissions(forType: SwiftSyncObject.self))
+            add(user: userA, toRole: "reader", inRealm: realm)
+        }
+
+        // userA should now be able to open the Realm and see objects
+        let realmA = openRealm(url, userA)
+        subscribe(realm: realmA, type: SwiftSyncObject.self)
+        XCTAssertEqual(realmA.getPrivileges(), [.read, .update, .setPermissions, .modifySchema])
+        XCTAssertEqual(realmA.getPrivileges(SwiftSyncObject.self), [.read, .subscribe])
+        XCTAssertEqual(realmA.getPrivileges(realmA.objects(SwiftSyncObject.self).first!), [.read])
+
+        // userA should not be able to create new objects
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 1)
+        try! realmA.write {
+            realmA.create(SwiftSyncObject.self, value: ["obj 2"])
+        }
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 2)
+        waitForSync(realmA)
+        XCTAssertEqual(realmA.objects(SwiftSyncObject.self).count, 1)
+
+        // userB should not be able to read any objects
+        let realmB = openRealm(url, userB)
+        subscribe(realm: realmB, type: SwiftSyncObject.self)
+        XCTAssertEqual(realmB.getPrivileges(), [.read, .update, .setPermissions, .modifySchema])
+        XCTAssertEqual(realmB.getPrivileges(SwiftSyncObject.self), [])
+        XCTAssertEqual(realmB.objects(SwiftSyncObject.self).count, 0)
+    }
+    func testClassWrite() {
+
+    }
+    func testClassSetPermissions() {
+
+    }
+}

+ 168 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftSyncTestCase.swift

@@ -0,0 +1,168 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+import XCTest
+import RealmSwift
+
+class SwiftSyncObject: Object {
+    @objc dynamic var stringProp: String = ""
+}
+
+class SwiftHugeSyncObject: Object {
+    @objc dynamic var dataProp: NSData?
+
+    required init() {
+        super.init()
+        let size = 1000000
+        let ptr = malloc(size)
+        dataProp = NSData(bytes: ptr, length: size)
+        free(ptr)
+    }
+
+    required init(realm: RLMRealm, schema: RLMObjectSchema) {
+        fatalError("init(realm:schema:) has not been implemented")
+    }
+    required init(value: Any, schema: RLMSchema) {
+        fatalError("init(value:schema:) has not been implemented")
+    }
+}
+
+class SwiftPartialSyncObjectA: Object {
+    @objc dynamic var number: Int = 0
+    @objc dynamic var string: String = ""
+
+    convenience init(number: Int, string: String) {
+        self.init()
+        self.number = number
+        self.string = string
+    }
+}
+
+class SwiftPartialSyncObjectB: Object {
+    @objc dynamic var number: Int = 0
+    @objc dynamic var firstString: String = ""
+    @objc dynamic var secondString: String = ""
+
+    convenience init(number: Int, firstString: String, secondString: String) {
+        self.init()
+        self.number = number
+        self.firstString = firstString
+        self.secondString = secondString
+    }
+}
+
+// MARK: Test case
+
+class SwiftSyncTestCase: RLMSyncTestCase {
+
+    var task: Process?
+
+    let authURL: URL = URL(string: "http://127.0.0.1:9080")!
+    let realmURL: URL = URL(string: "realm://127.0.0.1:9080/~/testBasicSync")!
+
+    /// For testing, make a unique Realm URL of the form "realm://127.0.0.1:9080/~/X",
+    /// where X is either a custom string passed as an argument, or an UUID string.
+    static func uniqueRealmURL(customName: String? = nil) -> URL {
+        return URL(string: "realm://127.0.0.1:9080/~/\(customName ?? UUID().uuidString)")!
+    }
+
+    func executeChild(file: StaticString = #file, line: UInt = #line) {
+        XCTAssert(0 == runChildAndWait(), "Tests in child process failed", file: file, line: line)
+    }
+
+    func basicCredentials(register: Bool = true,
+                          usernameSuffix: String = "",
+                          file: StaticString = #file,
+                          line: UInt = #line) -> SyncCredentials {
+        let filename = URL(fileURLWithPath: String(describing: file)).deletingPathExtension().lastPathComponent
+        return .usernamePassword(username: "\(filename)\(line)\(usernameSuffix)", password: "a", register: register)
+    }
+
+    func synchronouslyOpenRealm(url: URL, user: SyncUser, file: StaticString = #file, line: UInt = #line) throws -> Realm {
+        let config = user.configuration(realmURL: url, fullSynchronization: true)
+        return try synchronouslyOpenRealm(configuration: config)
+    }
+
+    func synchronouslyOpenRealm(configuration: Realm.Configuration, file: StaticString = #file, line: UInt = #line) throws -> Realm {
+        let semaphore = DispatchSemaphore(value: 0)
+        let basicBlock = { (error: Error?) in
+            if let error = error {
+                let process = self.isParent ? "parent" : "child"
+                XCTFail("Received an asynchronous error: \(error) (process: \(process))", file: file, line: line)
+            }
+            semaphore.signal()
+        }
+        SyncManager.shared.setSessionCompletionNotifier(basicBlock)
+        let realm = try Realm(configuration: configuration)
+        let result = semaphore.wait(timeout: .now() + DispatchTimeInterval.seconds(20))
+        XCTAssertEqual(result, .success)
+        return realm
+    }
+
+    func immediatelyOpenRealm(url: URL, user: SyncUser) throws -> Realm {
+        return try Realm(configuration: user.configuration(realmURL: url, fullSynchronization: true))
+    }
+
+    func synchronouslyLogInUser(for credentials: SyncCredentials,
+                                server url: URL,
+                                file: StaticString = #file,
+                                line: UInt = #line) throws -> SyncUser {
+        let process = isParent ? "parent" : "child"
+        var theUser: SyncUser?
+        var theError: Error?
+        let ex = expectation(description: "Should log in the user properly")
+        SyncUser.logIn(with: credentials, server: url) { user, error in
+            XCTAssertNotNil(user, file: file, line: line)
+            XCTAssertNil(error,
+                         "Error when trying to log in a user: \(error!) (process: \(process))",
+                         file: file,
+                         line: line)
+            theUser = user
+            theError = error
+            ex.fulfill()
+        }
+        waitForExpectations(timeout: 10, handler: nil)
+        XCTAssertNotNil(theUser, file: file, line: line)
+        XCTAssertEqual(theUser?.state, .active,
+                       "User should have been valid, but wasn't. (process: \(process), error: "
+                        + "\(theError != nil ? String(describing: theError!) : "n/a"))",
+                       file: file,
+                       line: line)
+        return theUser!
+    }
+
+    func waitForUploads(for realm: Realm) {
+        waitForUploads(for: ObjectiveCSupport.convert(object: realm))
+    }
+
+    func waitForDownloads(for realm: Realm) {
+        waitForDownloads(for: ObjectiveCSupport.convert(object: realm))
+    }
+
+    func checkCount<T: Object>(expected: Int,
+                               _ realm: Realm,
+                               _ type: T.Type,
+                               file: StaticString = #file,
+                               line: UInt = #line) {
+        let actual = realm.objects(type).count
+        XCTAssert(actual == expected,
+                  "Error: expected \(expected) items, but got \(actual) (process: \(isParent ? "parent" : "child"))",
+                  file: file,
+                  line: line)
+    }
+}

+ 27 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/ca-key.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA2jW6EOo+UQGlUGtEqQX1OD54tsqQ6xDZ5YWJqiRx33CdeGc0
++KlC+Y9hGiOxM+lb0CJuV/w1kHRqlfwIkQNSfQYlREdkdDW3hqBiHBfscahuBBJJ
+q36HBeynxUBcdlxlOYPlZ7QmcEb6CMWmUERbScgZFQVB1VFT2/Ev05g832I3XtrJ
+OCJvDElsnXOvi1Pd2qvmNYe6ap4CsVRDli7k+URyKBIQ7ejHBDx4ufs9bSSMPsFd
+hq7/oek1qWZ4vomI/kv2A1IWpFxzfkXSknm385+ioecEy9f4+ObYJAaR8vcHTzqb
+yKkS50zgO0inW6YvbT8hnzZkoma/w9CHRC6o5wIDAQABAoIBAE9OlZiOOiXg7j9j
+LSotQym08tSknLUCg986gIU5B7YIzQK0p/j0M6ZWKRmB/WZ7muXzjZ0myiT9Wru2
+RTrHNVTBRgwh0m22FES2x9GkFEheydVq/CF7NAHAOF2lfbV68UNNH7RoMkw+T6Oe
+ikrD/VW8hvkv1vR7wXm/l/1UaFNbSBSVou8EjHGK9/3fgvHRMALN0Jr1mHRMgK56
+cU7t7oziOmjr/bjm/VowNbJKpF43FT+95/Af6n6H2bLFSfG+4rVAAk37UnCKp0Pb
+AlWVl8lpMXjK64qNK2I4yf+sQXRNwlsNWxd6gdNfyOqXbfU+c0F1kQL+ODYu5pit
+DNTfObECgYEA5JuY1SsBNVxF0U9Q9QhcZ23h0hgrxvD/DgLNL9/2+C1Lc1iC+qSh
+MV8q5faUAqbkAI0+NhEmmrRMNtvhJXfrO5SO1CO/rRrGHF4MJ+Sj1TpefNktYR/6
+fo2BIbfmAoY3O142RWi1u/6Cv1GONp0EyLbBnxin0PB36OUkNxUjhvkCgYEA9Fsu
+Ar9EolZPJYtLqGitIhkyCXHSyMEHfQRNyz8kUORtFA6yjvD6vFAtVf2DNaY4FQ0a
+TYQ3O0akyPM2lG2c6TtojXKHkDLPJOV0Kh6BMImAqDq2Fsj+nTdoojNw4ypAMU4m
+KLAiA3nd03X7es7a6uvrSvQxJRB9fwiBzO70Rt8CgYEAm7ipuLscjZ6XKGbg/Kh/
+WSzuYFB6sX9EHeUmo+/pqVAhTycBwX4XFyx+ajs2wz+vm/iaYfX41/Ts3YmVqhIv
+uFwPls3rKR1NydD+csY6G2sxJdZCJSDFXyNAzRkZoqqOQPCCA3G6KZ7KrUv+lZEL
+yzVCWv9OgPLsm0ZLDwJlOvECgYEA7+SkIyZL52P8h8tdF5TMhHFf4k3Qti5rf5y+
+Ew+GQ7Q+MjbLrfF+92lvWMBuFDl/TYtziy6GWrdcB7xelRGXvpIIbvVFiZeYLYzm
+ooMYKeKUYJRjN7NT5F0FaFhAN4S/SKiEZeWlPuxhjryBi2uRGJlMgmWB6fVqf1CG
+vf7J6tkCgYEAqySHnPPm8aHPo9/Vx8JMbalPDgR/18jv3rABbQiMSU+C8Z8rVEmH
+MI1DmbWx+9yCqMff1i5xGKt4QDaqGLW7+xy142f1eu+tr7bxx2PusaEpAF2uoAW4
+c/DgWecBLDEhEW3NowHsI3ddNTpNQiNfZRd0TNdr9gwy/N8BfzMZtB4=
+-----END RSA PRIVATE KEY-----

+ 19 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/ca.pem

@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCTCCAfGgAwIBAgIIcWSopWiXJPMwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
+AxMVbWluaWNhIHJvb3QgY2EgNzE2NGE4MCAXDTE4MDgyOTE5MTcxM1oYDzIxMTgw
+ODI5MjAxNzEzWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA3MTY0YTgwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaNboQ6j5RAaVQa0SpBfU4Pni2
+ypDrENnlhYmqJHHfcJ14ZzT4qUL5j2EaI7Ez6VvQIm5X/DWQdGqV/AiRA1J9BiVE
+R2R0NbeGoGIcF+xxqG4EEkmrfocF7KfFQFx2XGU5g+VntCZwRvoIxaZQRFtJyBkV
+BUHVUVPb8S/TmDzfYjde2sk4Im8MSWydc6+LU93aq+Y1h7pqngKxVEOWLuT5RHIo
+EhDt6McEPHi5+z1tJIw+wV2Grv+h6TWpZni+iYj+S/YDUhakXHN+RdKSebfzn6Kh
+5wTL1/j45tgkBpHy9wdPOpvIqRLnTOA7SKdbpi9tPyGfNmSiZr/D0IdELqjnAgMB
+AAGjRTBDMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
+BQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEARxf4
+85JGwLkxN6bxwc7o3dJow+0xX9zZ0AY3Kvlsp9PSMrydRme1qvzejCfn0n8a9381
+1AerczkhnjJwKFYdy+TxcL0KcnTnJRnSwum+Aq7RXH0NVt/2X28/oX3m4u9rHkjB
+jQ3QxBzfBHWelZ8twxU8wXJVrb45G5Hw5cXOHs4GIXGbCZjJNvyyz9/60qpUV9sh
+q8DMuWkP2eHKP4BjsJ0eX6UitAaZjR0VWZK40yGkouxqLjrE7LesNioQhDGS3Te2
+gg/hAbN4c3lpsPwjeR4RspqjMZzUhY3cJ0t81jVFpbRhKjfU02ww6EDmexjQNAOt
+3yrKmabyT80mBaiL9Q==
+-----END CERTIFICATE-----

+ 27 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-cert-key.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAyh8jYiwYLiZdZNx8fwugGElA0z4km+0qColhU2iaUfK3YalX
+DS11rjg+vO+zrE85/+UVaQ2X0G81PVH5wid+NcFm6N95Mysqum/iNnPwdOOkRPb3
+8bOFxZ7xRs8L+UjptFXwAqD9CvE1q8CegmAcTMrZInxmwjvnOaHriutQoN6eWDsL
+dRxdak3plfNRlR0A8BRhe/uVi/YksLZRBSpij/8ehA7aaDglyvE+K1JTiAbSDm2h
+P+QADkXK5TIUA2tdDpG4Ey1q/NKCrEoVv8XTraa6+t5BC3chuVcsc4o0aTQpNAug
+TY/riBNV0Tsq9OLU3rA2loWCkW08DbnoLhUpuwIDAQABAoIBAGnH8CHCCAmnJHvT
+9QgcknYDaHzl6gz9C2KmUZxwg0teJuFi2d9Yql06rIL/RF5qvGUThKgNaCW6/fcQ
+vxEA0Enb5Tr9MhY4gk0+nvp2pSLXvLInOs5xcRJjQ80WMSMPKNirnUgS7zvZz4gK
+8Co1mLoL86Xby3/eD/6Woop66yC1Uri8RDndgMR8ntC8VtmA0Z2zOM1zMVBz0kam
+YS576pgJh4xJ1Q7SCKr3HTO7/7HeopzXrSP5xQWdYNBo7VPgG2bRPZ2zUaFfm+Ps
+T5Lw5wByqexGnUiCZ98gF6lnFHDOzQSJy45ce7XuQSkO7+0WhGz1QZ+VlEiR/1x6
+0colZEECgYEAz373IfktDj8+L3F930cQK8oKD1LUEMgd44OydvICO+yHUZWgmTWE
+aJPmn8Dw4GuPyjesACR56RCP990T7zdvcwkwj/5h+509xAxNHJZoYRL8m6fw+uo5
+wW34uTCDg+MxP2ipJ2E1iW1NIEYq4wInw6RiEDZLPu33pnX+Ad/6BeECgYEA+V6P
+mFJyGYaw+OUqMT6M40BSTD6TT+yUvUPZa+sCKs296xYc3BEGXe62L/XVE0MrbSvG
+KPNMG5u3IoAGdKc4wbtsdBe+S9zu71ZIKsdkWDl9GwZ2xHX0tqV0/Ca140zlkxAY
++dJL9nGq82WcEfhPcXwInPBrMpzOlyJXptNx6xsCgYEAmyxVPwfshPIQ3EQgoQCw
+/D5tBYao5x/xEjtkFIXp28yIah/e6ZTXP6oT17bfrMVj1BOMQtMEhKKJOBESHlyz
+sTDXK2hO+G9gSKP2awGkb6xWU0Xl9o+Bv8ExN7UrNU+LfeMUVUniUrL18cPnwLrU
+5/+gAoXIAfjOsqMc4WQRw6ECgYEAzr/XzjKM5x0FHVbi5HE33jI2CYDYIivEJida
+3F68LUDndUGgK9Txsm94Hct0HcRS/PCOGuWc9EbmT3RV5eG+7OC3yojk/YDvmP+w
+Vcd7Kqp/TyjMz5X8jnIfy+9MXmgi7wspqfbxhCI52hMkksGNHEn52iR6vDvGDQgs
+I+SrToECgYAdbzZ0FNeBAOvoEi6svApUNw4w3fHujVpg8b/FhadKaAx+kJBxcqy7
+Xfj3UrfKU7TuR8kO/EK9z9YXo5MiNVWP56QfzbLkvr5Obfi/sJrIHAYqeqPKpgaz
+acclIzLL5HsrbVjKiJs9Q8j9+Rx4RgIFTG0lM+25J8JnOnVFnPWdvw==
+-----END RSA PRIVATE KEY-----

+ 19 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-cert.pem

@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDTCCAfWgAwIBAgIISzy7BX9hEJkwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
+AxMVbWluaWNhIHJvb3QgY2EgNzE2NGE4MCAXDTE4MDgyOTE5MTcxNFoYDzIxMDgw
+ODI5MjAxNzE0WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDKHyNiLBguJl1k3Hx/C6AYSUDTPiSb7SoKiWFTaJpR
+8rdhqVcNLXWuOD6877OsTzn/5RVpDZfQbzU9UfnCJ341wWbo33kzKyq6b+I2c/B0
+46RE9vfxs4XFnvFGzwv5SOm0VfACoP0K8TWrwJ6CYBxMytkifGbCO+c5oeuK61Cg
+3p5YOwt1HF1qTemV81GVHQDwFGF7+5WL9iSwtlEFKmKP/x6EDtpoOCXK8T4rUlOI
+BtIObaE/5AAORcrlMhQDa10OkbgTLWr80oKsShW/xdOtprr63kELdyG5VyxzijRp
+NCk0C6BNj+uIE1XROyr04tTesDaWhYKRbTwNueguFSm7AgMBAAGjVTBTMA4GA1Ud
+DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T
+AQH/BAIwADAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEB
+AIYlOrkjRZJ/4SubG7MJB9FK5un+0vDPXWmlz90szGCIhM8/SBxwIZyMWEDAPhJ9
+PmhBngrDZjJt1C5TfsH2utGwh2ONErnBp8MC/DK5qSwiCuZTFRGwyZVI7Rpmk2RJ
+AtMUKsR8TRlaZ9QBlDF+6owMgKUb+VN+I806ryaBNKHuS4cmGdqOxzhb5/+9MXh/
+UfAnHNoie0r8eZhE0mUJ0JjTQOKhZMhAmHR5Pm/9S2f+VwsBriR0v3Fxw+9TN9cV
+PXfwR1gRJ1hpCvYUMp6PLsEwSAdTpBm6+vea5iRytSjny+R4E27q1ohTey0+6sUk
+WnVQqUUINxQulv7m60R2foY=
+-----END CERTIFICATE-----

+ 19 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-other-cert.pem

@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDTCCAfWgAwIBAgIIUfBnG/nIaSAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
+AxMVbWluaWNhIHJvb3QgY2EgNzE2NGE4MCAXDTE4MDgzMDE5MzE1NFoYDzIxMDgw
+ODMwMjAzMTU0WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCvm9qn97bX+M7Vu/z7Hd1OUcVynryrISrVdHbs/dv6
+UsXhrl4Z47y0/U0I013gGK8+yblxRLYyLE3jua7nL+pBGOULG2P95KWHVgLtJECl
+wDKLgnn2TUZ6eL72Mu3XkrYmDRd50queGgEIkDvXRGbOs2VfH2giY0ttQK3pXAa9
+0sBuOhFWUd8oyOCplBlRUf+PsjbcyW/LC58WtmizO7QC40ANz0dpR04UZxNe0+gM
+ATKEksELQwSKQoVQwCp1VOZ5jyzk9RtBS5ZJP5n8cl6/ZVSOekLBoE8DQibR3ga2
+E0I/r7bSzCYJr9K4jTFYGwPoY/U2Yk38mGp8/d9x35W1AgMBAAGjVTBTMA4GA1Ud
+DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T
+AQH/BAIwADAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEB
+ABoxyKJmBAWuxDAYGnmopWhBkAqh6GHvC4TkVAOaNGhfs+U0g5lwgVm2sFz1+lsZ
+zF/ECVsZhQ5MTZaaMKrlMZ/NEvOll3C5L6LutFMeSQoffnkcIUDmveX9Yp+KoLdV
+tbvVAt2D6BNsNDhmftyB2i8FH0fe1M+oxtAU54pqWGPoQo9+7N7UytFVimbCPNAt
+57rzegPyu4AcDdGO3SZBnhqT/rxUzO5nPJ+6MiXkCROSm26X8IvUR9/bO/QuC0t+
+CT6NJlEcn8X9eLsbSLrjHfXknbneteNCfZWU3qYbEh1Va98vYI3jku62pGyv9A7k
+Fs6VtIfrN3GRpSxs0Xw8hPw=
+-----END CERTIFICATE-----

BIN
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost-other.cer


BIN
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/localhost.cer


+ 19 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/not-localhost-cert.pem

@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIINSZ+oqsybpIwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
+AxMVbWluaWNhIHJvb3QgY2EgMzdjNzNiMCAXDTE4MDgyOTIwMTg0N1oYDzIxMDgw
+ODI5MjExODQ3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAN1if5VuZLU2WsWjOsYR7CPUwl3BD8Yw96T7c4ZX
+Nx33lNn8lz/RmMEeyNjavykqAUGpQj1ly3ZHK05SDxBiDA1p0mGm7uxh9ZhkNGiJ
+M4yFsqGDgmDiodGFQuCSg+idXDJkXnUngNSnfi8lLoU8KWHdNygjBZrLvg6TpK/t
+45UZaLIhGb91u6qam7cmofbywIW7dvWDyXSBkplXwkX1uNV7VSFVjcuzvuQdEIta
+plGH/bF2ykzhtJnbS93k3nZUl5HHI7ZV5IriDQ758orXFGLdEmLUd4zkyXJ2U9gz
+hI/90qw6RmDjia55R91LcRuWRic4Etu5Qxy8l9wuYvYl97MCAwEAAaNXMFUwDgYD
+VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
+HRMBAf8EAjAAMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUA
+A4IBAQC7kLaUNOFpWaVB5g3qlDtiklMbgV1LykK3ad+EfaPFzk/ALIzRNu5RmreH
+0GyoYoV+t/9iDgAi6CbBXMTibm0lVPGxoFLEYbYA4wREHBeJ1T16gfJf01/EB7H2
+stw8a6sHTpevqIfjs8T93RRZMzz2FHwWy7a6y0+mWJ+bKK0/GCkhlzNyznUHpphN
+OfrMtXU7uJp+6ml1qhfTppDChO74laFX/JwgDq2sQQUBCKlVtHJo5bm/yS5FSr0B
+S2ub4YgqiHFAozpJgiaPkya9vmUlp35zVSVZ4tTsTrnkArufbyKAfkMAG1Rskjo5
+lOMer8+75TUT9aTM+iE1RTbA5102
+-----END CERTIFICATE-----

BIN
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/certificates/not-localhost.cer


+ 80 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/test-ros-server.js

@@ -0,0 +1,80 @@
+const ROS = require('realm-object-server');
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+
+// Bypass the mandatory email prompt.
+process.env.ROS_TOS_EMAIL_ADDRESS = 'ci@realm.io';
+process.env.DOCKER_DATA_PATH = '/tmp';
+
+// Don't bother calling fsync() because we're throwing away all the files
+// between runs anyway
+process.env.REALM_DISABLE_SYNC_TO_DISK = 'true';
+
+// Workaround for <https://github.com/realm/realm-object-server-private/issues/950>.
+process.env.ROS_SUPERAGENT_RETRY_DELAY = '0';
+
+// Enable timestamps in the logs
+process.env.ROS_LOG_TIMESTAMP = '1';
+
+if (!process.env.SYNC_WORKER_FEATURE_TOKEN) {
+    try {
+        require(os.homedir() + '/.ros-feature-token.js');
+    }
+    catch (e) {
+        console.error('ROS feature token not found. Running Object Server tests requires setting the SYNC_WORKER_FEATURE_TOKEN environment variable.');
+        process.exit(1);
+    }
+}
+
+// A "email handler" which actually just writes the tokens to files that the
+// tests can read
+class PasswordEmailHandler {
+    constructor(dataRoot) {
+        this.dataRoot = dataRoot;
+        fs.mkdirSync(this.dataRoot);
+    }
+
+    resetPassword(email, token, userAgent, remoteIp) {
+        fs.writeFileSync(path.join(this.dataRoot, email), token);
+        return new Promise(r => setTimeout(r, 0));
+    }
+
+    confirmEmail(email, token) {
+        fs.writeFileSync(path.join(this.dataRoot, email), token);
+        return new Promise(r => setTimeout(r, 0));
+    }
+}
+
+const server = new ROS.BasicServer();
+server.start({
+    // The desired logging threshold. Can be one of: all, trace, debug, detail, info, warn, error, fatal, off)
+    logLevel: 'off',
+
+    // For all the full list of configuration parameters see:
+    // https://realm.io/docs/realm-object-server/latest/api/ros/interfaces/serverconfig.html
+
+    address: '0.0.0.0',
+    port: 9080,
+    httpsPort: 9443,
+
+    https: true,
+    httpsKeyPath: __dirname + '/certificates/localhost-cert-key.pem',
+    httpsCertChainPath: __dirname + '/certificates/localhost-cert.pem',
+    httpsForInternalComponents: false,
+
+    dataPath: process.argv[2],
+    authProviders: [
+        new ROS.auth.DebugAuthProvider(),
+        new ROS.auth.PasswordAuthProvider({
+            autoCreateAdminUser: true,
+            emailHandler: new PasswordEmailHandler(path.join(process.argv[2], 'email')),
+        }),
+    ],
+    autoKeyGen: true,
+}).then(() => {
+    console.log('started');
+    fs.closeSync(1);
+}).catch(err => {
+    console.error(`Error starting Realm Object Server: ${err.message}`)
+});

+ 1 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.dockerignore

@@ -0,0 +1 @@
+.gitignore

+ 32 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitignore

@@ -0,0 +1,32 @@
+# CMake
+.ninja_deps
+.ninja_log
+CMakeCache.txt
+CMakeFiles/
+Makefile
+build.ninja
+cmake_install.cmake
+rules.ninja
+CMakeScripts/
+realm-object-store.xcodeproj/
+
+# Build products
+src/librealm-object-store.a
+tests/tests
+*.build/
+Debug/
+Release/
+RelWithDebInfo/
+MinSizeRel/
+
+# IntelliJ
+.idea/
+
+# Visual Studio
+/.vs
+
+# Linters
+compile_commands.json
+
+# vim
+*.swp

+ 3 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/.gitmodules

@@ -0,0 +1,3 @@
+[submodule "external/catch"]
+	path = external/catch
+	url = https://github.com/catchorg/Catch2

+ 67 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CodeCoverage.cmake

@@ -0,0 +1,67 @@
+###########################################################################
+#
+# Copyright 2016 Realm Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+find_program(LCOV_PATH lcov)
+find_program(GENHTML_PATH genhtml)
+find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
+
+set(CMAKE_CXX_FLAGS_COVERAGE "-g -O0 -fprofile-arcs -ftest-coverage -DCATCH_CONFIG_FAST_COMPILE"
+    CACHE STRING "Flags used by the C++ compiler during coverage builds.")
+mark_as_advanced(CMAKE_CXX_FLAGS_COVERAGE)
+
+if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
+  if(NOT (LCOV_PATH AND GENHTML_PATH AND GCOVR_PATH))
+    message(FATAL_ERROR "Generating a coverage report requires lcov and gcovr")
+  endif()
+
+  function(create_coverage_target targetname testrunner)
+    add_custom_target(${targetname}
+      # Clear previous coverage information
+      COMMAND ${LCOV_PATH} --directory . --zerocounters
+
+      # Run the tests
+      COMMAND ${testrunner}
+
+      # Generate new coverage report
+      COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info
+      COMMAND ${LCOV_PATH} --extract coverage.info '${CMAKE_SOURCE_DIR}/src/*' --output-file coverage.info.cleaned
+      COMMAND ${GENHTML_PATH} -o coverage coverage.info.cleaned
+      COMMAND ${CMAKE_COMMAND} -E remove coverage.info coverage.info.cleaned
+
+      COMMAND echo Open coverage/index.html in your browser to view the coverage report.
+
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+    )
+
+    add_custom_target(${targetname}-cobertura
+      COMMAND ${testrunner}
+      COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR}/src ./src -o coverage.xml
+      COMMAND echo Code coverage report written to coverage.xml
+
+      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+    )
+  endfunction()
+else()
+  function(create_coverage_target targetname testrunner)
+    add_custom_target(${targetname}
+      COMMAND echo "Configure with -DCMAKE_BUILD_TYPE=Coverage to generate coverage reports")
+
+    add_custom_target(${targetname}-cobertura
+      COMMAND echo "Configure with -DCMAKE_BUILD_TYPE=Coverage to generate coverage reports")
+  endfunction()
+endif()

+ 124 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/CompilerFlags.cmake

@@ -0,0 +1,124 @@
+###########################################################################
+#
+# Copyright 2016 Realm Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+include(CheckSymbolExists)
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED on)
+set(CMAKE_CXX_EXTENSIONS off)
+
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS
+    $<$<CONFIG:DEBUG>:REALM_DEBUG>
+    $<$<CONFIG:COVERAGE>:REALM_DEBUG>
+)
+
+if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
+    add_compile_options(
+        -Wall
+        -Wextra
+        -Wno-missing-field-initializers
+        -Wempty-body
+        -Wparentheses
+        -Wunknown-pragmas
+        -Wunreachable-code
+        -DREALM_HAVE_CONFIG
+    )
+endif()
+
+if(MSVC)
+    add_definitions(
+        /D_UNICODE
+        /DWIN32_LEAN_AND_MEAN
+        /D_CRT_SECURE_NO_WARNINGS
+        /D_SCL_SECURE_NO_WARNINGS
+        /D_ENABLE_EXTENDED_ALIGNED_STORAGE #https://developercommunity.visualstudio.com/comments/279328/view.html
+    )
+    add_compile_options(
+        /MP # Enable multi-processor compilation
+    )
+    if(NOT WINDOWS_STORE)
+        # Statically link the run-time library
+        # https://docs.microsoft.com/bg-bg/cpp/build/reference/md-mt-ld-use-run-time-library
+        # https://cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F
+        foreach(flag_var
+            CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+            CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+            if(${flag_var} MATCHES "/MD")
+                string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+            endif()
+        endforeach()
+    endif()
+endif()
+
+if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
+    add_compile_options(
+        -Wassign-enum
+        -Wbool-conversion
+        -Wconditional-uninitialized
+        -Wconstant-conversion
+        -Wenum-conversion
+        -Wint-conversion
+        -Wmissing-prototypes
+        -Wnewline-eof
+        -Wshorten-64-to-32
+        -Wimplicit-fallthrough
+    )
+endif()
+
+if(${CMAKE_GENERATOR} STREQUAL "Ninja")
+    if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
+    elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
+    endif()
+endif()
+
+if(APPLE)
+    find_library(CF_LIBRARY CoreFoundation)
+    list(APPEND PLATFORM_LIBRARIES ${CF_LIBRARY})
+elseif(REALM_PLATFORM STREQUAL "Android")
+    find_library(ANDROID_LIBRARY android)
+    find_library(ANDROID_LOG_LIBRARY log)
+    list(APPEND PLATFORM_LIBRARIES ${ANDROID_LIBRARY})
+    list(APPEND PLATFORM_LIBRARIES ${ANDROID_LOG_LIBRARY})
+    set(PLATFORM_DEFINES "__STDC_CONSTANT_MACROS=1")
+endif()
+
+if(NOT REALM_PLATFORM OR REALM_PLATFORM STREQUAL "Node")
+    find_library(UV_LIBRARY NAMES uv libuv)
+    if(UV_LIBRARY)
+        find_path(UV_INCLUDE_DIR uv.h)
+
+        list(APPEND PLATFORM_LIBRARIES ${UV_LIBRARY})
+        add_definitions(-DREALM_HAVE_UV)
+    endif()
+endif()
+
+if(REALM_PLATFORM STREQUAL "Node")
+    set(PLATFORM_DEFINES "REALM_PLATFORM_NODE=1")
+    if(NOT UV_LIBRARY)
+        message(FATAL_ERROR "Platform set to Node but libuv was not found!")
+    endif()
+endif()
+
+check_symbol_exists(epoll_create sys/epoll.h REALM_HAVE_EPOLL)
+if(REALM_HAVE_EPOLL)
+    add_definitions(-DREALM_HAVE_EPOLL)
+endif()

+ 425 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/RealmCore.cmake

@@ -0,0 +1,425 @@
+###########################################################################
+#
+# Copyright 2016 Realm Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+include(ExternalProject)
+include(ProcessorCount)
+
+find_package(Threads)
+
+# Load dependency info from dependencies.list into REALM_FOO_VERSION variables.
+set(DEPENDENCIES_FILE "dependencies.list" CACHE STRING "path to dependencies list")
+file(STRINGS ${DEPENDENCIES_FILE} DEPENDENCIES)
+message("Dependencies: ${DEPENDENCIES}")
+foreach(DEPENDENCY IN LISTS DEPENDENCIES)
+    string(REGEX MATCHALL "([^=]+)" COMPONENT_AND_VERSION ${DEPENDENCY})
+    list(GET COMPONENT_AND_VERSION 0 COMPONENT)
+    list(GET COMPONENT_AND_VERSION 1 VERSION)
+    set(${COMPONENT} ${VERSION})
+endforeach()
+
+
+if(APPLE)
+    find_library(FOUNDATION_FRAMEWORK Foundation)
+    find_library(SECURITY_FRAMEWORK Security)
+
+    set(CRYPTO_LIBRARIES "")
+    set(SSL_LIBRARIES ${FOUNDATION_FRAMEWORK} ${SECURITY_FRAMEWORK})
+elseif(REALM_PLATFORM STREQUAL "Android")
+    set(CRYPTO_LIBRARIES crypto)
+    set(SSL_LIBRARIES ssl)
+elseif(CMAKE_SYSTEM_NAME MATCHES "^Windows")
+    # Windows doesn't do crypto right now, but that is subject to change
+    set(CRYPTO_LIBRARIES "")
+    set(SSL_LIBRARIES "")
+else()
+    find_package(OpenSSL REQUIRED)
+
+    set(CRYPTO_LIBRARIES OpenSSL::Crypto)
+    set(SSL_LIBRARIES OpenSSL::SSL)
+endif()
+
+
+set(MAKE_FLAGS "REALM_HAVE_CONFIG=1")
+
+if(SANITIZER_FLAGS)
+    set(MAKE_FLAGS ${MAKE_FLAGS} "EXTRA_CFLAGS=${SANITIZER_FLAGS}" "EXTRA_LDFLAGS=${SANITIZER_FLAGS}")
+endif()
+
+ProcessorCount(NUM_JOBS)
+if(NOT NUM_JOBS EQUAL 0)
+    set(MAKE_FLAGS ${MAKE_FLAGS} "-j${NUM_JOBS}")
+endif()
+
+if (${CMAKE_VERSION} VERSION_GREATER "3.4.0")
+    set(USES_TERMINAL_BUILD USES_TERMINAL_BUILD 1)
+endif()
+
+function(use_realm_core enable_sync core_prefix sync_prefix)
+    if(core_prefix)
+        build_existing_realm_core(${core_prefix})
+        if(sync_prefix)
+            build_existing_realm_sync(${sync_prefix})
+        endif()
+    elseif(enable_sync)
+        # FIXME: Support building against prebuilt sync binaries.
+        clone_and_build_realm_core("v${REALM_CORE_VERSION}")
+        clone_and_build_realm_sync("v${REALM_SYNC_VERSION}")
+    else()
+        if(APPLE OR REALM_PLATFORM STREQUAL "Android" OR CMAKE_SYSTEM_NAME MATCHES "^Windows")
+            download_realm_core(${REALM_CORE_VERSION})
+        else()
+            clone_and_build_realm_core("v${REALM_CORE_VERSION}")
+        endif()
+    endif()
+endfunction()
+
+function(download_realm_tarball url target libraries)
+    get_filename_component(tarball_name "${url}" NAME)
+
+    set(tarball_parent_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}")
+    set(tarball_path "${tarball_parent_directory}/${tarball_name}")
+    set(temp_tarball_path "/tmp/${tarball_name}")
+
+    if (NOT EXISTS ${tarball_path})
+        if (NOT EXISTS ${temp_tarball_path})
+            message("Downloading ${url}.")
+            file(DOWNLOAD ${url} ${temp_tarball_path}.tmp SHOW_PROGRESS)
+            file(RENAME ${temp_tarball_path}.tmp ${temp_tarball_path})
+        endif()
+        file(COPY ${temp_tarball_path} DESTINATION ${tarball_parent_directory})
+    endif()
+
+    if(APPLE)
+        add_custom_command(
+            COMMENT "Extracting ${tarball_name}"
+            OUTPUT ${libraries}
+            COMMAND ${CMAKE_COMMAND} -E tar xf ${tarball_path}
+            COMMAND ${CMAKE_COMMAND} -E remove_directory ${target}
+            COMMAND ${CMAKE_COMMAND} -E rename core ${target}
+            COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${libraries})
+    elseif(REALM_PLATFORM STREQUAL "Android" OR CMAKE_SYSTEM_NAME MATCHES "^Windows")
+        add_custom_command(
+            COMMENT "Extracting ${tarball_name}"
+            OUTPUT ${libraries}
+            COMMAND "${CMAKE_COMMAND}" -E make_directory "${target}"
+            COMMAND "${CMAKE_COMMAND}" -E chdir "${target}" "${CMAKE_COMMAND}" -E tar xf "${tarball_path}"
+            COMMAND "${CMAKE_COMMAND}" -E touch_nocreate ${libraries})
+    endif()
+endfunction()
+
+function(download_android_openssl)
+    if(ANDROID)
+        string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
+        set(OPENSSL_FILENAME "openssl-${BUILD_TYPE}-${ANDROID_OPENSSL_VERSION}-Android-${ANDROID_ABI}")
+        set(OPENSSL_URL "http://static.realm.io/downloads/openssl/${ANDROID_OPENSSL_VERSION}/Android/${ANDROID_ABI}/${OPENSSL_FILENAME}.tar.gz")
+
+        message(STATUS "Downloading OpenSSL...")
+        file(DOWNLOAD "${OPENSSL_URL}" "${CMAKE_BINARY_DIR}/${OPENSSL_FILENAME}.tar.gz")
+
+        message(STATUS "Uncompressing OpenSSL...")
+        execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz "${OPENSSL_FILENAME}.tar.gz")
+
+        message(STATUS "Importing OpenSSL...")
+        include(${CMAKE_BINARY_DIR}/${OPENSSL_FILENAME}/openssl.cmake)
+        get_target_property(OPENSSL_INCLUDE_DIR crypto INTERFACE_INCLUDE_DIRECTORIES)
+        get_target_property(CRYPTO_LIB crypto IMPORTED_LOCATION)
+        get_target_property(SSL_LIB ssl IMPORTED_LOCATION)
+    endif()
+endfunction()
+
+function(download_realm_core core_version)
+    if(CMAKE_SYSTEM_NAME MATCHES "Windows")
+        set(compression "tar.gz")
+        set(library_directory "lib")
+
+        if(CMAKE_GENERATOR_PLATFORM MATCHES "^[Aa][Rr][Mm]$")
+            set(architecture "ARM")
+        elseif(CMAKE_GENERATOR_PLATFORM MATCHES "^[Xx]64$" OR CMAKE_SIZEOF_VOID_P EQUAL 8)
+            set(architecture "x64")
+        else()
+            set(architecture "Win32")
+        endif()
+
+        if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
+            set(platform "Windows")
+        elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+            set(platform "UWP")
+        endif()
+
+        set(tarball_name_debug "realm-core-Debug-v${core_version}-${platform}-${architecture}-devel.tar.gz")
+        set(tarball_name_release "realm-core-Release-v${core_version}-${platform}-${architecture}-devel.tar.gz")
+
+        set(url_debug "https://static.realm.io/downloads/core/${tarball_name_debug}")
+        set(url_release "https://static.realm.io/downloads/core/${tarball_name_release}")
+
+        set(core_directory_parent "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}")
+        set(core_directory_debug "${core_directory_parent}/realm-core-${core_version}-debug")
+        set(core_directory_release "${core_directory_parent}/realm-core-${core_version}-release")
+        set(core_directory "${core_directory_debug}")
+
+        set(core_library_debug "${core_directory_debug}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
+        set(core_library_release "${core_directory_release}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm${CMAKE_STATIC_LIBRARY_SUFFIX}")
+        set(core_parser_library_debug "${core_directory_debug}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
+        set(core_parser_library_release "${core_directory_release}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${CMAKE_STATIC_LIBRARY_SUFFIX}")
+        set(core_libraries ${core_library_debug} ${core_library_release} ${core_parser_library_debug} ${core_parser_library_release})
+
+        download_realm_tarball(${url_debug} ${core_directory_debug} ${core_library_debug} ${core_parser_library_debug})
+        download_realm_tarball(${url_release} ${core_directory_release} ${core_library_release} ${core_parser_library_release})
+    else()
+        if(APPLE)
+            set(basename "realm-core-cocoa")
+            set(compression "tar.xz")
+            set(platform "-macosx")
+        elseif(REALM_PLATFORM STREQUAL "Android")
+            set(basename "realm-core-android")
+            set(compression "tar.gz")
+            set(platform "-android-${ANDROID_ABI}")
+        endif()
+
+        set(tarball_name "${basename}-v${core_version}.${compression}")
+        set(url "https://static.realm.io/downloads/core/${tarball_name}")
+        set(core_directory_parent "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}")
+        set(core_directory "${core_directory_parent}/realm-core-${core_version}")
+
+        set(core_library_debug ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm${platform}-dbg${CMAKE_STATIC_LIBRARY_SUFFIX})
+        set(core_library_release ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm${platform}${CMAKE_STATIC_LIBRARY_SUFFIX})
+        set(core_parser_library_debug ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${platform}-dbg${CMAKE_STATIC_LIBRARY_SUFFIX})
+        set(core_parser_library_release ${core_directory}/${library_directory}/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${platform}${CMAKE_STATIC_LIBRARY_SUFFIX})
+        set(core_libraries ${core_library_debug} ${core_library_release} ${core_parser_library_debug} ${core_parser_library_release})
+
+        download_realm_tarball(${url} ${core_directory} "${core_libraries}")
+        download_android_openssl()
+    endif()
+
+    add_custom_target(realm-core DEPENDS ${core_libraries})
+
+    add_library(realm STATIC IMPORTED)
+    add_dependencies(realm realm-core)
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION_DEBUG ${core_library_debug})
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION_COVERAGE ${core_library_debug})
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION_RELEASE ${core_library_release})
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION ${core_library_release})
+
+    set_property(TARGET realm PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ${CRYPTO_LIBRARIES})
+
+    # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
+    # configure time, when they'd otherwise not be created until we download and extract core.
+    file(MAKE_DIRECTORY ${core_directory}/include)
+    set_property(TARGET realm PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${core_directory}/include)
+
+    add_library(realm-parser STATIC IMPORTED)
+    add_dependencies(realm-parser realm-core)
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_DEBUG ${core_parser_library_debug})
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_COVERAGE ${core_parser_library_debug})
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_RELEASE ${core_parser_library_release})
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION ${core_parser_library_release})
+endfunction()
+
+macro(build_realm_core)
+    set(core_prefix_directory "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/realm-core")
+
+    ExternalProject_Add(realm-core
+        PREFIX ${core_prefix_directory}
+        BUILD_IN_SOURCE 1
+        UPDATE_DISCONNECTED 1
+        INSTALL_COMMAND ""
+        CONFIGURE_COMMAND ${CMAKE_COMMAND} -E make_directory build.debug
+                        && cd build.debug
+                        && cmake -D CMAKE_BUILD_TYPE=Debug -DREALM_BUILD_LIB_ONLY=YES -G Ninja ..
+                        && cd ..
+                        && ${CMAKE_COMMAND} -E make_directory build.release
+                        && cd build.release
+                        && cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DREALM_BUILD_LIB_ONLY=YES -G Ninja ..
+
+        BUILD_COMMAND cd build.debug
+                   && cmake --build .
+                   && cd ..
+                   && cd build.release
+                   && cmake --build .
+        ${USES_TERMINAL_BUILD}
+        ${ARGN}
+        )
+    ExternalProject_Get_Property(realm-core SOURCE_DIR)
+
+    set(core_debug_binary_dir "${SOURCE_DIR}/build.debug")
+    set(core_release_binary_dir "${SOURCE_DIR}/build.release")
+    set(core_library_debug "${core_debug_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
+    set(core_library_release "${core_release_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm${CMAKE_STATIC_LIBRARY_SUFFIX}")
+    set(core_parser_library_debug "${core_debug_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser-dbg${CMAKE_STATIC_LIBRARY_SUFFIX}")
+    set(core_parser_library_release "${core_release_binary_dir}/src/realm/${CMAKE_STATIC_LIBRARY_PREFIX}realm-parser${CMAKE_STATIC_LIBRARY_SUFFIX}")
+
+    ExternalProject_Add_Step(realm-core ensure-libraries
+        DEPENDEES build
+        BYPRODUCTS ${core_library_debug} ${core_library_release}
+                   ${core_parser_library_debug} ${core_parser_library_release}
+        )
+
+    set(core_generated_headers_dir_debug "${core_debug_binary_dir}/src")
+    set(core_generated_headers_dir_release "${core_release_binary_dir}/src")
+
+    add_library(realm STATIC IMPORTED)
+    add_dependencies(realm realm-core)
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION_DEBUG ${core_library_debug})
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION_COVERAGE ${core_library_debug})
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION_RELEASE ${core_library_release})
+    set_property(TARGET realm PROPERTY IMPORTED_LOCATION ${core_library_release})
+
+    set_property(TARGET realm PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ${CRYPTO_LIBRARIES})
+
+    # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
+    # configure time, when they'd otherwise not be created until we download and build core.
+    file(MAKE_DIRECTORY "${core_generated_headers_dir_debug}" "${core_generated_headers_dir_release}" "${SOURCE_DIR}/src")
+
+    set_property(TARGET realm PROPERTY INTERFACE_INCLUDE_DIRECTORIES
+        ${SOURCE_DIR}/src
+        $<$<CONFIG:Debug>:${core_generated_headers_dir_debug}>
+        $<$<NOT:$<CONFIG:Debug>>:${core_generated_headers_dir_release}>
+    )
+
+    add_library(realm-parser STATIC IMPORTED)
+    add_dependencies(realm realm-core)
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_DEBUG ${core_parser_library_debug})
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_COVERAGE ${core_parser_library_debug})
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION_RELEASE ${core_parser_library_release})
+    set_property(TARGET realm-parser PROPERTY IMPORTED_LOCATION ${core_parser_library_release})
+endmacro()
+
+function(clone_and_build_realm_core branch)
+    build_realm_core(GIT_REPOSITORY "https://github.com/realm/realm-core.git"
+                     GIT_TAG ${branch}
+                     )
+endfunction()
+
+function(build_existing_realm_core core_directory)
+    get_filename_component(core_directory ${core_directory} ABSOLUTE)
+
+    build_realm_core(SOURCE_DIR ${core_directory}
+                     URL ""
+                     BUILD_ALWAYS 1
+                     )
+endfunction()
+
+macro(build_realm_sync)
+    set(cmake_files ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
+    if(REALM_PLATFORM STREQUAL "Android")
+        set(build_cmd sh build.sh build-android)
+    else()
+        set(build_cmd make -C src/realm librealm-sync.a librealm-sync-dbg.a librealm-server.a librealm-server-dbg.a ${MAKE_FLAGS})
+    endif()
+
+    ExternalProject_Add(realm-sync-lib
+        DEPENDS realm-core
+        PREFIX ${cmake_files}/realm-sync
+        BUILD_IN_SOURCE 1
+        UPDATE_DISCONNECTED 1
+        BUILD_COMMAND ${build_cmd}
+        CONFIGURE_COMMAND ""
+        INSTALL_COMMAND ""
+        ${USES_TERMINAL_BUILD}
+        ${ARGN}
+        )
+
+    if(APPLE)
+        set(platform "")
+    elseif(REALM_PLATFORM STREQUAL "Android")
+        if(ANDROID_ABI STREQUAL "armeabi-v7a")
+            set(platform "-android-arm-v7a")
+        else()
+            set(platform "-android-${ANDROID_ABI}")
+        endif()
+    endif()
+
+
+    ExternalProject_Get_Property(realm-sync-lib SOURCE_DIR)
+    set(sync_directory ${SOURCE_DIR})
+    if(REALM_PLATFORM STREQUAL "Android")
+        set(sync_library_directory ${sync_directory}/android-lib)
+    else()
+        set(sync_library_directory ${sync_directory}/src/realm)
+    endif()
+
+    set(sync_library_debug ${sync_library_directory}/librealm-sync${platform}-dbg.a)
+    set(sync_library_release ${sync_library_directory}/librealm-sync${platform}.a)
+    set(sync_libraries ${sync_library_debug} ${sync_library_release})
+
+    ExternalProject_Add_Step(realm-sync-lib ensure-libraries
+        BYPRODUCTS ${sync_libraries}
+        DEPENDEES build
+        )
+
+    add_library(realm-sync STATIC IMPORTED)
+    add_dependencies(realm-sync realm-sync-lib)
+
+    set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_DEBUG ${sync_library_debug})
+    set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_COVERAGE ${sync_library_debug})
+    set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION_RELEASE ${sync_library_release})
+    set_property(TARGET realm-sync PROPERTY IMPORTED_LOCATION ${sync_library_release})
+
+    set_property(TARGET realm-sync PROPERTY INTERFACE_LINK_LIBRARIES ${SSL_LIBRARIES})
+
+    # Create directories that are included in INTERFACE_INCLUDE_DIRECTORIES, as CMake requires they exist at
+    # configure time, when they'd otherwise not be created until we download and build sync.
+    file(MAKE_DIRECTORY ${sync_directory}/src)
+    set_property(TARGET realm-sync PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${sync_directory}/src)
+
+    # Sync server library is built as part of the sync library build
+    set(sync_server_library_debug ${sync_library_directory}/librealm-server${platform}-dbg.a)
+    set(sync_server_library_release ${sync_library_directory}/librealm-server${platform}.a)
+    set(sync_server_libraries ${sync_server_library_debug} ${sync_server_library_release})
+
+    ExternalProject_Add_Step(realm-sync-lib ensure-server-libraries
+        BYPRODUCTS ${sync_server_libraries}
+        DEPENDEES build
+        )
+
+    add_library(realm-sync-server STATIC IMPORTED)
+    add_dependencies(realm-sync-server realm-sync-lib)
+
+    set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION_DEBUG ${sync_server_library_debug})
+    set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION_COVERAGE ${sync_server_library_debug})
+    set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION_RELEASE ${sync_server_library_release})
+    set_property(TARGET realm-sync-server PROPERTY IMPORTED_LOCATION ${sync_server_library_release})
+
+    find_package(PkgConfig)
+    pkg_check_modules(YAML QUIET yaml-cpp)
+    set_property(TARGET realm-sync-server PROPERTY INTERFACE_LINK_LIBRARIES ${SSL_LIBRARIES} ${YAML_LDFLAGS})
+endmacro()
+
+function(build_existing_realm_sync sync_directory)
+    get_filename_component(sync_directory ${sync_directory} ABSOLUTE)
+    build_realm_sync(URL ""
+                     SOURCE_DIR ${sync_directory}
+                     BUILD_ALWAYS 1
+                     )
+
+endfunction()
+
+function(clone_and_build_realm_sync branch)
+    set(cmake_files ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY})
+    if(REALM_PLATFORM STREQUAL "Android")
+        set(config_cmd test -f src/config.mk || REALM_CORE_PREFIX=${cmake_files}/realm-core/src/realm-core REALM_FORCE_OPENSSL=YES REALM_ENABLE_ASSERTIONS= sh build.sh config && echo "ENABLE_ENCRYPTION    = yes" >> src/config.mk)
+    else()
+        set(config_cmd test -f src/config.mk || REALM_CORE_PREFIX=${cmake_files}/realm-core/src/realm-core sh build.sh config)
+    endif()
+
+    build_realm_sync(GIT_REPOSITORY "git@github.com:realm/realm-sync.git"
+                     GIT_TAG ${branch}
+                     CONFIGURE_COMMAND ${config_cmd}
+                     )
+
+endfunction()

+ 38 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/Sanitizers.cmake

@@ -0,0 +1,38 @@
+###########################################################################
+#
+# Copyright 2016 Realm Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+option(SANITIZE_ADDRESS "build with ASan")
+option(SANITIZE_THREAD "build with TSan")
+option(SANITIZE_UNDEFINED "build with UBSan")
+
+if(SANITIZE_ADDRESS)
+    set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=address")
+endif()
+
+if(SANITIZE_THREAD)
+    set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=thread")
+endif()
+
+if(SANITIZE_UNDEFINED)
+    set(SANITIZER_FLAGS "${SANITIZER_FLAGS} -fsanitize=undefined")
+endif()
+
+if(SANITIZE_ADDRESS OR SANITIZE_THREAD OR SANITIZE_UNDEFINED)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS} -fno-omit-frame-pointer")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
+endif()

+ 1703 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMake/android.toolchain.cmake

@@ -0,0 +1,1703 @@
+# Copyright (c) 2010-2011, Ethan Rublee
+# Copyright (c) 2011-2014, Andrey Kamaev
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1.  Redistributions of source code must retain the above copyright notice,
+#     this list of conditions and the following disclaimer.
+#
+# 2.  Redistributions in binary form must reproduce the above copyright notice,
+#     this list of conditions and the following disclaimer in the documentation
+#     and/or other materials provided with the distribution.
+#
+# 3.  Neither the name of the copyright holder nor the names of its
+#     contributors may be used to endorse or promote products derived from this
+#     software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# ------------------------------------------------------------------------------
+#  Android CMake toolchain file, for use with the Android NDK r5-r10d
+#  Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended).
+#  See home page: https://github.com/taka-no-me/android-cmake
+#
+#  Usage Linux:
+#   $ export ANDROID_NDK=/absolute/path/to/the/android-ndk
+#   $ mkdir build && cd build
+#   $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake ..
+#   $ make -j8
+#
+#  Usage Windows:
+#     You need native port of make to build your project.
+#     Android NDK r7 (and newer) already has make.exe on board.
+#     For older NDK you have to install it separately.
+#     For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm
+#
+#   $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk
+#   $ mkdir build && cd build
+#   $ cmake.exe -G"MinGW Makefiles"
+#       -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake
+#       -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" ..
+#   $ cmake.exe --build .
+#
+#
+#  Options (can be set as cmake parameters: -D<option_name>=<value>):
+#    ANDROID_NDK=/opt/android-ndk - path to the NDK root.
+#      Can be set as environment variable. Can be set only at first cmake run.
+#
+#    ANDROID_ABI=armeabi-v7a - specifies the target Application Binary
+#      Interface (ABI). This option nearly matches to the APP_ABI variable
+#      used by ndk-build tool from Android NDK.
+#
+#      Possible targets are:
+#        "armeabi" - ARMv5TE based CPU with software floating point operations
+#        "armeabi-v7a" - ARMv7 based devices with hardware FPU instructions
+#            this ABI target is used by default
+#        "armeabi-v7a with NEON" - same as armeabi-v7a, but
+#            sets NEON as floating-point unit
+#        "armeabi-v7a with VFPV3" - same as armeabi-v7a, but
+#            sets VFPV3 as floating-point unit (has 32 registers instead of 16)
+#        "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP
+#        "x86" - IA-32 instruction set
+#        "mips" - MIPS32 instruction set
+#
+#      64-bit ABIs for NDK r10 and newer:
+#        "arm64-v8a" - ARMv8 AArch64 instruction set
+#        "x86_64" - Intel64 instruction set (r1)
+#        "mips64" - MIPS64 instruction set (r6)
+#
+#    ANDROID_NATIVE_API_LEVEL=android-9 - level of Android API compile for.
+#      Option is read-only when standalone toolchain is used.
+#      Note: building for "android-L" requires explicit configuration.
+#
+#    ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 - the name of compiler
+#      toolchain to be used. The list of possible values depends on the NDK
+#      version. For NDK r10c the possible values are:
+#
+#        * aarch64-linux-android-4.9
+#        * aarch64-linux-android-clang3.4
+#        * aarch64-linux-android-clang3.5
+#        * arm-linux-androideabi-4.6
+#        * arm-linux-androideabi-4.8
+#        * arm-linux-androideabi-4.9 (default)
+#        * arm-linux-androideabi-clang3.4
+#        * arm-linux-androideabi-clang3.5
+#        * mips64el-linux-android-4.9
+#        * mips64el-linux-android-clang3.4
+#        * mips64el-linux-android-clang3.5
+#        * mipsel-linux-android-4.6
+#        * mipsel-linux-android-4.8
+#        * mipsel-linux-android-4.9
+#        * mipsel-linux-android-clang3.4
+#        * mipsel-linux-android-clang3.5
+#        * x86-4.6
+#        * x86-4.8
+#        * x86-4.9
+#        * x86-clang3.4
+#        * x86-clang3.5
+#        * x86_64-4.9
+#        * x86_64-clang3.4
+#        * x86_64-clang3.5
+#
+#    ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions
+#      instead of Thumb. Is not available for "armeabi-v6 with VFP"
+#      (is forced to be ON) ABI.
+#
+#    ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker
+#      errors even if they are not used.
+#
+#    ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared
+#      libraries. Automatically turned for NDK r5x and r6x due to GLESv2
+#      problems.
+#
+#    ANDROID_STL=gnustl_static - specify the runtime to use.
+#
+#      Possible values are:
+#        none           -> Do not configure the runtime.
+#        system         -> Use the default minimal system C++ runtime library.
+#                          Implies -fno-rtti -fno-exceptions.
+#                          Is not available for standalone toolchain.
+#        system_re      -> Use the default minimal system C++ runtime library.
+#                          Implies -frtti -fexceptions.
+#                          Is not available for standalone toolchain.
+#        gabi++_static  -> Use the GAbi++ runtime as a static library.
+#                          Implies -frtti -fno-exceptions.
+#                          Available for NDK r7 and newer.
+#                          Is not available for standalone toolchain.
+#        gabi++_shared  -> Use the GAbi++ runtime as a shared library.
+#                          Implies -frtti -fno-exceptions.
+#                          Available for NDK r7 and newer.
+#                          Is not available for standalone toolchain.
+#        stlport_static -> Use the STLport runtime as a static library.
+#                          Implies -fno-rtti -fno-exceptions for NDK before r7.
+#                          Implies -frtti -fno-exceptions for NDK r7 and newer.
+#                          Is not available for standalone toolchain.
+#        stlport_shared -> Use the STLport runtime as a shared library.
+#                          Implies -fno-rtti -fno-exceptions for NDK before r7.
+#                          Implies -frtti -fno-exceptions for NDK r7 and newer.
+#                          Is not available for standalone toolchain.
+#        gnustl_static  -> Use the GNU STL as a static library.
+#                          Implies -frtti -fexceptions.
+#        gnustl_shared  -> Use the GNU STL as a shared library.
+#                          Implies -frtti -fno-exceptions.
+#                          Available for NDK r7b and newer.
+#                          Silently degrades to gnustl_static if not available.
+#
+#    ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on
+#      chosen runtime. If disabled, then the user is responsible for settings
+#      these options.
+#
+#  What?:
+#    android-cmake toolchain searches for NDK/toolchain in the following order:
+#      ANDROID_NDK - cmake parameter
+#      ANDROID_NDK - environment variable
+#      ANDROID_STANDALONE_TOOLCHAIN - cmake parameter
+#      ANDROID_STANDALONE_TOOLCHAIN - environment variable
+#      ANDROID_NDK - default locations
+#      ANDROID_STANDALONE_TOOLCHAIN - default locations
+#
+#    Make sure to do the following in your scripts:
+#      SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" )
+#      SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" )
+#    The flags will be prepopulated with critical flags, so don't loose them.
+#    Also be aware that toolchain also sets configuration-specific compiler
+#    flags and linker flags.
+#
+#    ANDROID and BUILD_ANDROID will be set to true, you may test any of these
+#    variables to make necessary Android-specific configuration changes.
+#
+#    Also ARMEABI or ARMEABI_V7A or X86 or MIPS or ARM64_V8A or X86_64 or MIPS64
+#    will be set true, mutually exclusive. NEON option will be set true
+#    if VFP is set to NEON.
+#
+# ------------------------------------------------------------------------------
+
+# FIXME:
+# This is copied from https://dl.google.com/android/repository/cmake-3.4.2909474-linux-x86_64.zip
+# because of the android.toolchain.cmake shipped with Android SDK CMake 3.6 doesn't work with our
+# JNI build currently (lack of lto linking support.). Two modifications are made to avoid warnings
+# with CMake 3.6 -- disable CMAKE_FORCE_CXX_COMPILER & CMAKE_FORCE_C_COMPILER.
+# This file should be removed and use the one from Android SDK cmake package when it supports lto.
+
+cmake_minimum_required( VERSION 2.6.3 )
+
+if( DEFINED CMAKE_CROSSCOMPILING )
+ # subsequent toolchain loading is not really needed
+ return()
+endif()
+
+if( CMAKE_TOOLCHAIN_FILE )
+ # touch toolchain variable to suppress "unused variable" warning
+endif()
+
+# inherit settings in recursive loads
+get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE )
+if( _CMAKE_IN_TRY_COMPILE )
+ include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL )
+endif()
+
+# this one is important
+if( CMAKE_VERSION VERSION_GREATER "3.0.99" )
+ set( CMAKE_SYSTEM_NAME Android )
+else()
+ set( CMAKE_SYSTEM_NAME Linux )
+endif()
+
+# this one not so much
+set( CMAKE_SYSTEM_VERSION 1 )
+
+# rpath makes low sense for Android
+set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" )
+set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." )
+
+# NDK search paths
+set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" )
+if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS )
+ if( CMAKE_HOST_WIN32 )
+  file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS )
+  set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" )
+ else()
+  file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS )
+  set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" )
+ endif()
+endif()
+if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH )
+ set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain )
+endif()
+
+# known ABIs
+set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" )
+set( ANDROID_SUPPORTED_ABIS_arm64 "arm64-v8a" )
+set( ANDROID_SUPPORTED_ABIS_x86 "x86" )
+set( ANDROID_SUPPORTED_ABIS_x86_64 "x86_64" )
+set( ANDROID_SUPPORTED_ABIS_mips "mips" )
+set( ANDROID_SUPPORTED_ABIS_mips64 "mips64" )
+
+# API level defaults
+set( ANDROID_DEFAULT_NDK_API_LEVEL 9 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_arm64 21 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_x86_64 21 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 )
+
+
+macro( __LIST_FILTER listvar regex )
+  if( ${listvar} )
+    foreach( __val ${${listvar}} )
+      if( __val MATCHES "${regex}" )
+        list( REMOVE_ITEM ${listvar} "${__val}" )
+      endif()
+    endforeach()
+  endif()
+endmacro()
+
+macro( __INIT_VARIABLE var_name )
+  set( __test_path 0 )
+  foreach( __var ${ARGN} )
+    if( __var STREQUAL "PATH" )
+      set( __test_path 1 )
+      break()
+    endif()
+  endforeach()
+
+  if( __test_path AND NOT EXISTS "${${var_name}}" )
+    unset( ${var_name} CACHE )
+  endif()
+
+  if( " ${${var_name}}" STREQUAL " " )
+    set( __values 0 )
+    foreach( __var ${ARGN} )
+      if( __var STREQUAL "VALUES" )
+        set( __values 1 )
+      elseif( NOT __var STREQUAL "PATH" )
+        if( __var MATCHES "^ENV_.*$" )
+          string( REPLACE "ENV_" "" __var "${__var}" )
+          set( __value "$ENV{${__var}}" )
+        elseif( DEFINED ${__var} )
+          set( __value "${${__var}}" )
+        elseif( __values )
+          set( __value "${__var}" )
+        else()
+          set( __value "" )
+        endif()
+
+        if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") )
+          set( ${var_name} "${__value}" )
+          break()
+        endif()
+      endif()
+    endforeach()
+    unset( __value )
+    unset( __values )
+  endif()
+
+  if( __test_path )
+    file( TO_CMAKE_PATH "${${var_name}}" ${var_name} )
+  endif()
+  unset( __test_path )
+endmacro()
+
+macro( __DETECT_NATIVE_API_LEVEL _var _path )
+  set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" )
+  file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" )
+  if( NOT __apiFileContent )
+    message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." )
+  endif()
+  string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" )
+  unset( __apiFileContent )
+  unset( __ndkApiLevelRegex )
+endmacro()
+
+macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root )
+ if( EXISTS "${_root}" )
+    file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" )
+    __LIST_FILTER( __gccExePath "^[.].*" )
+    list( LENGTH __gccExePath __gccExePathsCount )
+    if( NOT __gccExePathsCount EQUAL 1  AND NOT _CMAKE_IN_TRY_COMPILE )
+      message( WARNING "Could not determine machine name for compiler from ${_root}" )
+      set( ${_var} "" )
+    else()
+      get_filename_component( __gccExeName "${__gccExePath}" NAME_WE )
+      string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" )
+    endif()
+    unset( __gccExePath )
+    unset( __gccExePathsCount )
+    unset( __gccExeName )
+  else()
+    set( ${_var} "" )
+  endif()
+endmacro()
+
+
+# fight against cygwin
+set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools")
+mark_as_advanced( ANDROID_FORBID_SYGWIN )
+if( ANDROID_FORBID_SYGWIN )
+ if( CYGWIN )
+  message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." )
+ endif()
+
+ if( CMAKE_HOST_WIN32 )
+  # remove cygwin from PATH
+  set( __new_path "$ENV{PATH}")
+  __LIST_FILTER( __new_path "cygwin" )
+  set(ENV{PATH} "${__new_path}")
+  unset(__new_path)
+ endif()
+endif()
+
+
+# detect current host platform
+if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) )
+ set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" )
+ mark_as_advanced( ANDROID_NDK_HOST_X64 )
+endif()
+
+set( TOOL_OS_SUFFIX "" )
+if( CMAKE_HOST_APPLE )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" )
+elseif( CMAKE_HOST_WIN32 )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" )
+ set( TOOL_OS_SUFFIX ".exe" )
+elseif( CMAKE_HOST_UNIX )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" )
+else()
+ message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" )
+endif()
+
+if( NOT ANDROID_NDK_HOST_X64 )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} )
+endif()
+
+# see if we have path to Android NDK
+if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN )
+  __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK )
+endif()
+if( NOT ANDROID_NDK )
+ # see if we have path to Android standalone toolchain
+ __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN )
+
+ if( NOT ANDROID_STANDALONE_TOOLCHAIN )
+  #try to find Android NDK in one of the the default locations
+  set( __ndkSearchPaths )
+  foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} )
+   foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} )
+    list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" )
+   endforeach()
+  endforeach()
+  __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} )
+  unset( __ndkSearchPaths )
+
+  if( ANDROID_NDK )
+   message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" )
+   message( STATUS "  If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" )
+  else()
+   #try to find Android standalone toolchain in one of the the default locations
+   __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH )
+
+   if( ANDROID_STANDALONE_TOOLCHAIN )
+    message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" )
+    message( STATUS "  If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" )
+   endif( ANDROID_STANDALONE_TOOLCHAIN )
+  endif( ANDROID_NDK )
+ endif( NOT ANDROID_STANDALONE_TOOLCHAIN )
+endif( NOT ANDROID_NDK )
+
+# remember found paths
+if( ANDROID_NDK )
+ get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE )
+ set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE )
+ set( BUILD_WITH_ANDROID_NDK True )
+ if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" )
+  file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX "r[0-9]+[a-z]?" )
+  string( REGEX MATCH "r([0-9]+)([a-z]?)" ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" )
+ else()
+  set( ANDROID_NDK_RELEASE "r1x" )
+  set( ANDROID_NDK_RELEASE_FULL "unreleased" )
+ endif()
+ string( REGEX REPLACE "r([0-9]+)([a-z]?)" "\\1*1000" ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE}" )
+ string( FIND " abcdefghijklmnopqastuvwxyz" "${CMAKE_MATCH_2}" __ndkReleaseLetterNum )
+ math( EXPR ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE_NUM}+${__ndkReleaseLetterNum}" )
+elseif( ANDROID_STANDALONE_TOOLCHAIN )
+ get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE )
+ # try to detect change
+ if( CMAKE_AR )
+  string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length )
+  string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath )
+  if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN )
+   message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." )
+  endif()
+  unset( __androidStandaloneToolchainPreviousPath )
+  unset( __length )
+ endif()
+ set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE )
+ set( BUILD_WITH_STANDALONE_TOOLCHAIN True )
+else()
+ list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH)
+ message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain.
+    You should either set an environment variable:
+      export ANDROID_NDK=~/my-android-ndk
+    or
+      export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain
+    or put the toolchain or NDK in the default path:
+      sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk
+      sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" )
+endif()
+
+# android NDK layout
+if( BUILD_WITH_ANDROID_NDK )
+ if( NOT DEFINED ANDROID_NDK_LAYOUT )
+  # try to automatically detect the layout
+  if( EXISTS "${ANDROID_NDK}/RELEASE.TXT")
+   set( ANDROID_NDK_LAYOUT "RELEASE" )
+  elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" )
+   set( ANDROID_NDK_LAYOUT "LINARO" )
+  elseif( EXISTS "${ANDROID_NDK}/../../gcc/" )
+   set( ANDROID_NDK_LAYOUT "ANDROID" )
+  endif()
+ endif()
+ set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" )
+ mark_as_advanced( ANDROID_NDK_LAYOUT )
+ if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" )
+  set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment
+  set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" )
+  set( ANDROID_NDK_TOOLCHAINS_SUBPATH  "" )
+  set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" )
+ elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" )
+  set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment
+  set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" )
+  set( ANDROID_NDK_TOOLCHAINS_SUBPATH  "" )
+  set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" )
+ else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE"
+  set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" )
+  set( ANDROID_NDK_TOOLCHAINS_SUBPATH  "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" )
+  set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" )
+ endif()
+ get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE )
+
+ # try to detect change of NDK
+ if( CMAKE_AR )
+  string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length )
+  string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath )
+  if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH )
+   message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first.
+   " )
+  endif()
+  unset( __androidNdkPreviousPath )
+  unset( __length )
+ endif()
+endif()
+
+
+# get all the details about standalone toolchain
+if( BUILD_WITH_STANDALONE_TOOLCHAIN )
+ __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" )
+ set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} )
+ set( __availableToolchains "standalone" )
+ __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" )
+ if( NOT __availableToolchainMachines )
+  message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." )
+ endif()
+ if( __availableToolchainMachines MATCHES x86_64 )
+  set( __availableToolchainArchs "x86_64" )
+ elseif( __availableToolchainMachines MATCHES i686 )
+  set( __availableToolchainArchs "x86" )
+ elseif( __availableToolchainMachines MATCHES aarch64 )
+  set( __availableToolchainArchs "arm64" )
+ elseif( __availableToolchainMachines MATCHES arm )
+  set( __availableToolchainArchs "arm" )
+ elseif( __availableToolchainMachines MATCHES mips64el )
+  set( __availableToolchainArchs "mips64" )
+ elseif( __availableToolchainMachines MATCHES mipsel )
+  set( __availableToolchainArchs "mips" )
+ endif()
+ execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion
+                  OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE )
+ string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" )
+ if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" )
+  list( APPEND __availableToolchains "standalone-clang" )
+  list( APPEND __availableToolchainMachines ${__availableToolchainMachines} )
+  list( APPEND __availableToolchainArchs ${__availableToolchainArchs} )
+  list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} )
+ endif()
+endif()
+
+macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath )
+ foreach( __toolchain ${${__availableToolchainsLst}} )
+  if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" )
+   SET( __toolchainVersionRegex "^TOOLCHAIN_VERSION[\t ]+:=[\t ]+(.*)$" )
+   FILE( STRINGS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}/setup.mk" __toolchainVersionStr REGEX "${__toolchainVersionRegex}" )
+   if( __toolchainVersionStr )
+    string( REGEX REPLACE "${__toolchainVersionRegex}" "\\1" __toolchainVersionStr "${__toolchainVersionStr}" )
+    string( REGEX REPLACE "-clang3[.][0-9]$" "-${__toolchainVersionStr}" __gcc_toolchain "${__toolchain}" )
+   else()
+    string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" )
+   endif()
+   unset( __toolchainVersionStr )
+   unset( __toolchainVersionRegex )
+  else()
+   set( __gcc_toolchain "${__toolchain}" )
+  endif()
+  __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" )
+  if( __machine )
+   string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" )
+   if( __machine MATCHES x86_64 )
+    set( __arch "x86_64" )
+   elseif( __machine MATCHES i686 )
+    set( __arch "x86" )
+   elseif( __machine MATCHES aarch64 )
+    set( __arch "arm64" )
+   elseif( __machine MATCHES arm )
+    set( __arch "arm" )
+   elseif( __machine MATCHES mips64el )
+    set( __arch "mips64" )
+   elseif( __machine MATCHES mipsel )
+    set( __arch "mips" )
+   else()
+    set( __arch "" )
+   endif()
+   #message("machine: !${__machine}!\narch: !${__arch}!\nversion: !${__version}!\ntoolchain: !${__toolchain}!\n")
+   if (__arch)
+    list( APPEND __availableToolchainMachines "${__machine}" )
+    list( APPEND __availableToolchainArchs "${__arch}" )
+    list( APPEND __availableToolchainCompilerVersions "${__version}" )
+    list( APPEND ${__availableToolchainsVar} "${__toolchain}" )
+   endif()
+  endif()
+  unset( __gcc_toolchain )
+ endforeach()
+endmacro()
+
+# get all the details about NDK
+if( BUILD_WITH_ANDROID_NDK )
+ file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" )
+ string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" )
+ set( __availableToolchains "" )
+ set( __availableToolchainMachines "" )
+ set( __availableToolchainArchs "" )
+ set( __availableToolchainCompilerVersions "" )
+ if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" )
+  # do not go through all toolchains if we know the name
+  set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" )
+  __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+  if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 )
+   __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" )
+   if( __availableToolchains )
+    set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} )
+   endif()
+  endif()
+ endif()
+ if( NOT __availableToolchains )
+  file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" )
+  if( __availableToolchainsLst )
+   list(SORT __availableToolchainsLst) # we need clang to go after gcc
+  endif()
+  __LIST_FILTER( __availableToolchainsLst "^[.]" )
+  __LIST_FILTER( __availableToolchainsLst "llvm" )
+  __LIST_FILTER( __availableToolchainsLst "renderscript" )
+  __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+  if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 )
+   __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" )
+   if( __availableToolchains )
+    set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} )
+   endif()
+  endif()
+ endif()
+ if( NOT __availableToolchains )
+  message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." )
+ endif()
+endif()
+
+# build list of available ABIs
+set( ANDROID_SUPPORTED_ABIS "" )
+set( __uniqToolchainArchNames ${__availableToolchainArchs} )
+list( REMOVE_DUPLICATES __uniqToolchainArchNames )
+list( SORT __uniqToolchainArchNames )
+foreach( __arch ${__uniqToolchainArchNames} )
+ list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} )
+endforeach()
+unset( __uniqToolchainArchNames )
+if( NOT ANDROID_SUPPORTED_ABIS )
+ message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." )
+endif()
+
+# choose target ABI
+__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} )
+# verify that target ABI is supported
+list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx )
+if( __androidAbiIdx EQUAL -1 )
+ string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS  "${ANDROID_SUPPORTED_ABIS}" )
+ message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain.
+   Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\"
+   " )
+endif()
+unset( __androidAbiIdx )
+
+# set target ABI options
+if( ANDROID_ABI STREQUAL "x86" )
+ set( X86 true )
+ set( ANDROID_NDK_ABI_NAME "x86" )
+ set( ANDROID_ARCH_NAME "x86" )
+ set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "i686" )
+elseif( ANDROID_ABI STREQUAL "x86_64" )
+ set( X86 true )
+ set( X86_64 true )
+ set( ANDROID_NDK_ABI_NAME "x86_64" )
+ set( ANDROID_ARCH_NAME "x86_64" )
+ set( CMAKE_SYSTEM_PROCESSOR "x86_64" )
+ set( ANDROID_LLVM_TRIPLE "x86_64-none-linux-android" )
+elseif( ANDROID_ABI STREQUAL "mips64" )
+ set( MIPS64 true )
+ set( ANDROID_NDK_ABI_NAME "mips64" )
+ set( ANDROID_ARCH_NAME "mips64" )
+ set( ANDROID_LLVM_TRIPLE "mips64el-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "mips64" )
+elseif( ANDROID_ABI STREQUAL "mips" )
+ set( MIPS true )
+ set( ANDROID_NDK_ABI_NAME "mips" )
+ set( ANDROID_ARCH_NAME "mips" )
+ set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "mips" )
+elseif( ANDROID_ABI STREQUAL "arm64-v8a" )
+ set( ARM64_V8A true )
+ set( ANDROID_NDK_ABI_NAME "arm64-v8a" )
+ set( ANDROID_ARCH_NAME "arm64" )
+ set( ANDROID_LLVM_TRIPLE "aarch64-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "aarch64" )
+ set( VFPV3 true )
+ set( NEON true )
+elseif( ANDROID_ABI STREQUAL "armeabi" )
+ set( ARMEABI true )
+ set( ANDROID_NDK_ABI_NAME "armeabi" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv5te" )
+elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" )
+ set( ARMEABI_V6 true )
+ set( ANDROID_NDK_ABI_NAME "armeabi" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv6" )
+ # need always fallback to older platform
+ set( ARMEABI true )
+elseif( ANDROID_ABI STREQUAL "armeabi-v7a")
+ set( ARMEABI_V7A true )
+ set( ANDROID_NDK_ABI_NAME "armeabi-v7a" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv7-a" )
+elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" )
+ set( ARMEABI_V7A true )
+ set( ANDROID_NDK_ABI_NAME "armeabi-v7a" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv7-a" )
+ set( VFPV3 true )
+elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" )
+ set( ARMEABI_V7A true )
+ set( ANDROID_NDK_ABI_NAME "armeabi-v7a" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv7-a" )
+ set( VFPV3 true )
+ set( NEON true )
+else()
+ message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." )
+endif()
+
+if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" )
+ # really dirty hack
+ # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run...
+ file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" )
+endif()
+
+if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 )
+ __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF )
+ set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE )
+ mark_as_advanced( ANDROID_FORCE_ARM_BUILD )
+else()
+ unset( ANDROID_FORCE_ARM_BUILD CACHE )
+endif()
+
+# choose toolchain
+if( ANDROID_TOOLCHAIN_NAME )
+ list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx )
+ if( __toolchainIdx EQUAL -1 )
+  list( SORT __availableToolchains )
+  string( REPLACE ";" "\n  * " toolchains_list "${__availableToolchains}" )
+  set( toolchains_list "  * ${toolchains_list}")
+  message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain.
+To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" )
+ endif()
+ list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch )
+ if( NOT __toolchainArch STREQUAL ANDROID_ARCH_NAME )
+  message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." )
+ endif()
+else()
+ set( __toolchainIdx -1 )
+ set( __applicableToolchains "" )
+ set( __toolchainMaxVersion "0.0.0" )
+ list( LENGTH __availableToolchains __availableToolchainsCount )
+ math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" )
+ foreach( __idx RANGE ${__availableToolchainsCount} )
+  list( GET __availableToolchainArchs ${__idx} __toolchainArch )
+  if( __toolchainArch STREQUAL ANDROID_ARCH_NAME )
+   list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion )
+   string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}")
+   if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion )
+    set( __toolchainMaxVersion "${__toolchainVersion}" )
+    set( __toolchainIdx ${__idx} )
+   endif()
+  endif()
+ endforeach()
+ unset( __availableToolchainsCount )
+ unset( __toolchainMaxVersion )
+ unset( __toolchainVersion )
+endif()
+unset( __toolchainArch )
+if( __toolchainIdx EQUAL -1 )
+ message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." )
+endif()
+list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME )
+list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME )
+list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION )
+
+unset( __toolchainIdx )
+unset( __availableToolchains )
+unset( __availableToolchainMachines )
+unset( __availableToolchainArchs )
+unset( __availableToolchainCompilerVersions )
+
+# choose native API level
+__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL )
+string( REPLACE "android-" "" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" )
+string( STRIP "${ANDROID_NATIVE_API_LEVEL}" ANDROID_NATIVE_API_LEVEL )
+# adjust API level
+set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} )
+foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} )
+ if( (__level LESS ANDROID_NATIVE_API_LEVEL OR __level STREQUAL ANDROID_NATIVE_API_LEVEL) AND NOT __level LESS __real_api_level )
+  set( __real_api_level ${__level} )
+ endif()
+endforeach()
+if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL STREQUAL __real_api_level )
+ message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'")
+ set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} )
+endif()
+unset(__real_api_level)
+# validate
+list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx )
+if( __levelIdx EQUAL -1 )
+ message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." )
+else()
+ if( BUILD_WITH_ANDROID_NDK )
+  __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" )
+  if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL AND NOT __realApiLevel GREATER 9000 )
+   message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." )
+  endif()
+  unset( __realApiLevel )
+ endif()
+ set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE )
+ set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} )
+ if( CMAKE_VERSION VERSION_GREATER "2.8" )
+  list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS )
+  set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} )
+ endif()
+endif()
+unset( __levelIdx )
+
+
+# remember target ABI
+set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE )
+if( CMAKE_VERSION VERSION_GREATER "2.8" )
+ list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME} )
+ set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME}} )
+endif()
+
+
+# runtime choice (STL, rtti, exceptions)
+if( NOT ANDROID_STL )
+  set( ANDROID_STL gnustl_static )
+endif()
+set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" )
+set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" )
+mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES )
+
+if( BUILD_WITH_ANDROID_NDK )
+ if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared)$")
+  message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\".
+The possible values are:
+  none           -> Do not configure the runtime.
+  system         -> Use the default minimal system C++ runtime library.
+  system_re      -> Same as system but with rtti and exceptions.
+  gabi++_static  -> Use the GAbi++ runtime as a static library.
+  gabi++_shared  -> Use the GAbi++ runtime as a shared library.
+  stlport_static -> Use the STLport runtime as a static library.
+  stlport_shared -> Use the STLport runtime as a shared library.
+  gnustl_static  -> (default) Use the GNU STL as a static library.
+  gnustl_shared  -> Use the GNU STL as a shared library.
+" )
+ endif()
+elseif( BUILD_WITH_STANDALONE_TOOLCHAIN )
+ if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$")
+  message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\".
+The possible values are:
+  none           -> Do not configure the runtime.
+  gnustl_static  -> (default) Use the GNU STL as a static library.
+  gnustl_shared  -> Use the GNU STL as a shared library.
+" )
+ endif()
+endif()
+
+unset( ANDROID_RTTI )
+unset( ANDROID_EXCEPTIONS )
+unset( ANDROID_STL_INCLUDE_DIRS )
+unset( __libstl )
+unset( __libsupcxx )
+
+if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" )
+ message( WARNING  "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf).
+You are strongly recommended to switch to another NDK release.
+" )
+endif()
+
+if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" )
+  message( WARNING  "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header:
+See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2
+  diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h
+  index 5e28c64..65892a1 100644
+  --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h
+  +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h
+  @@ -51,7 +51,11 @@ typedef long int       ssize_t;
+   #endif
+   #ifndef _PTRDIFF_T
+   #define _PTRDIFF_T
+  -typedef long           ptrdiff_t;
+  +#  ifdef __ANDROID__
+  +     typedef int            ptrdiff_t;
+  +#  else
+  +     typedef long           ptrdiff_t;
+  +#  endif
+   #endif
+" )
+endif()
+
+
+# setup paths and STL for standalone toolchain
+if( BUILD_WITH_STANDALONE_TOOLCHAIN )
+ set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" )
+ set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" )
+ set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" )
+
+ if( NOT ANDROID_STL STREQUAL "none" )
+  set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" )
+  if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" )
+   # old location ( pre r8c )
+   set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" )
+  endif()
+  if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" )
+   list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" )
+  elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" )
+   list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" )
+  else()
+   list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" )
+  endif()
+  # always search static GNU STL to get the location of libsupc++.a
+  if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" )
+   set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" )
+  elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" )
+   set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" )
+  elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" )
+   set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" )
+  elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" )
+   set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" )
+  endif()
+  if( __libstl )
+   set( __libsupcxx "${__libstl}/libsupc++.a" )
+   set( __libstl    "${__libstl}/libstdc++.a" )
+  endif()
+  if( NOT EXISTS "${__libsupcxx}" )
+   message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain.
+ Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c.
+ You need to either upgrade to newer NDK or manually copy
+     $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a
+ to
+     ${__libsupcxx}
+   " )
+  endif()
+  if( ANDROID_STL STREQUAL "gnustl_shared" )
+   if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" )
+    set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" )
+   elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" )
+    set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" )
+   elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" )
+    set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" )
+   endif()
+  endif()
+ endif()
+endif()
+
+# clang
+if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" )
+ set( ANDROID_COMPILER_IS_CLANG 1 )
+ execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE )
+ string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}")
+elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" )
+ string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}")
+ string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-${ANDROID_COMPILER_VERSION}" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" )
+ if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" )
+  message( FATAL_ERROR "Could not find the Clang compiler driver" )
+ endif()
+ set( ANDROID_COMPILER_IS_CLANG 1 )
+ set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+else()
+ set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" )
+ unset( ANDROID_COMPILER_IS_CLANG CACHE )
+endif()
+
+string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" )
+if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" )
+ set( _clang_name "clang" )
+endif()
+
+
+# setup paths and STL for NDK
+if( BUILD_WITH_ANDROID_NDK )
+ set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+ set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" )
+
+ if( ANDROID_STL STREQUAL "none" )
+  # do nothing
+ elseif( ANDROID_STL STREQUAL "system" )
+  set( ANDROID_RTTI             OFF )
+  set( ANDROID_EXCEPTIONS       OFF )
+  set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" )
+ elseif( ANDROID_STL STREQUAL "system_re" )
+  set( ANDROID_RTTI             ON )
+  set( ANDROID_EXCEPTIONS       ON )
+  set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" )
+ elseif( ANDROID_STL MATCHES "gabi" )
+  if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7
+   message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.")
+  endif()
+  set( ANDROID_RTTI             ON )
+  set( ANDROID_EXCEPTIONS       OFF )
+  set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" )
+  set( __libstl                 "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" )
+ elseif( ANDROID_STL MATCHES "stlport" )
+  if( NOT ANDROID_NDK_RELEASE_NUM LESS 8004 ) # before r8d
+   set( ANDROID_EXCEPTIONS       ON )
+  else()
+   set( ANDROID_EXCEPTIONS       OFF )
+  endif()
+  if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7
+   set( ANDROID_RTTI            OFF )
+  else()
+   set( ANDROID_RTTI            ON )
+  endif()
+  set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" )
+  set( __libstl                 "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" )
+ elseif( ANDROID_STL MATCHES "gnustl" )
+  set( ANDROID_EXCEPTIONS       ON )
+  set( ANDROID_RTTI             ON )
+  if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" )
+   if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" )
+    # gnustl binary for 4.7 compiler is buggy :(
+    # TODO: look for right fix
+    set( __libstl                "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" )
+   else()
+    set( __libstl                "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" )
+   endif()
+  else()
+   set( __libstl                "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" )
+  endif()
+  set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" )
+  if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" )
+   set( __libstl                "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" )
+  else()
+   set( __libstl                "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" )
+  endif()
+ else()
+  message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" )
+ endif()
+ # find libsupc++.a - rtti & exceptions
+ if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" )
+  set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer
+  if( NOT EXISTS "${__libsupcxx}" )
+   set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8
+  endif()
+  if( NOT EXISTS "${__libsupcxx}" ) # before r7
+   if( ARMEABI_V7A )
+    if( ANDROID_FORCE_ARM_BUILD )
+     set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" )
+    else()
+     set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" )
+    endif()
+   elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD )
+    set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" )
+   else()
+    set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" )
+   endif()
+  endif()
+  if( NOT EXISTS "${__libsupcxx}")
+   message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.")
+  endif()
+ endif()
+endif()
+
+
+# case of shared STL linkage
+if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl )
+ string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" )
+ # TODO: check if .so file exists before the renaming
+endif()
+
+
+# ccache support
+__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE )
+if( _ndk_ccache )
+ if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE )
+  unset( NDK_CCACHE CACHE )
+ endif()
+ find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary")
+else()
+ unset( NDK_CCACHE CACHE )
+endif()
+unset( _ndk_ccache )
+
+
+# setup the cross-compiler
+if( NOT CMAKE_C_COMPILER )
+ if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" )
+  set( CMAKE_C_COMPILER   "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" )
+  set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" )
+  if( ANDROID_COMPILER_IS_CLANG )
+   set( CMAKE_C_COMPILER_ARG1   "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}"   CACHE PATH "C compiler")
+   set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
+  else()
+   set( CMAKE_C_COMPILER_ARG1   "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler")
+   set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
+  endif()
+ else()
+  if( ANDROID_COMPILER_IS_CLANG )
+   set( CMAKE_C_COMPILER   "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}"   CACHE PATH "C compiler")
+   set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
+  else()
+   set( CMAKE_C_COMPILER   "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}"    CACHE PATH "C compiler" )
+   set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}"    CACHE PATH "C++ compiler" )
+  endif()
+ endif()
+ set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}"     CACHE PATH "assembler" )
+ set( CMAKE_STRIP        "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}"   CACHE PATH "strip" )
+ if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" )
+  # Use gcc-ar if we have it for better LTO support.
+  set( CMAKE_AR           "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}"      CACHE PATH "archive" )
+ else()
+  set( CMAKE_AR           "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}"      CACHE PATH "archive" )
+ endif()
+ set( CMAKE_LINKER       "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}"      CACHE PATH "linker" )
+ set( CMAKE_NM           "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}"      CACHE PATH "nm" )
+ set( CMAKE_OBJCOPY      "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" )
+ set( CMAKE_OBJDUMP      "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" )
+ set( CMAKE_RANLIB       "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}"  CACHE PATH "ranlib" )
+endif()
+
+set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" )
+if( CMAKE_VERSION VERSION_LESS 2.8.5 )
+ set( CMAKE_ASM_COMPILER_ARG1 "-c" )
+endif()
+if( APPLE )
+ find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool )
+ if( NOT CMAKE_INSTALL_NAME_TOOL )
+  message( FATAL_ERROR "Could not find install_name_tool, please check your installation." )
+ endif()
+ mark_as_advanced( CMAKE_INSTALL_NAME_TOOL )
+endif()
+
+# Force set compilers because standard identification works badly for us
+include( CMakeForceCompiler )
+# CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU )
+if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_C_COMPILER_ID Clang )
+endif()
+set( CMAKE_C_PLATFORM_ID Linux )
+if( X86_64 OR MIPS64 OR ARM64_V8A )
+ set( CMAKE_C_SIZEOF_DATA_PTR 8 )
+else()
+ set( CMAKE_C_SIZEOF_DATA_PTR 4 )
+endif()
+set( CMAKE_C_HAS_ISYSROOT 1 )
+set( CMAKE_C_COMPILER_ABI ELF )
+# CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU )
+if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_CXX_COMPILER_ID Clang)
+endif()
+set( CMAKE_CXX_PLATFORM_ID Linux )
+set( CMAKE_CXX_SIZEOF_DATA_PTR ${CMAKE_C_SIZEOF_DATA_PTR} )
+set( CMAKE_CXX_HAS_ISYSROOT 1 )
+set( CMAKE_CXX_COMPILER_ABI ELF )
+set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C )
+# force ASM compiler (required for CMake < 2.8.5)
+set( CMAKE_ASM_COMPILER_ID_RUN TRUE )
+set( CMAKE_ASM_COMPILER_ID GNU )
+set( CMAKE_ASM_COMPILER_WORKS TRUE )
+set( CMAKE_ASM_COMPILER_FORCED TRUE )
+set( CMAKE_COMPILER_IS_GNUASM 1)
+set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm )
+
+foreach( lang C CXX ASM )
+ if( ANDROID_COMPILER_IS_CLANG )
+  set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} )
+ else()
+  set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} )
+ endif()
+endforeach()
+
+# flags and definitions
+remove_definitions( -DANDROID )
+add_definitions( -DANDROID )
+
+if( ANDROID_SYSROOT MATCHES "[ ;\"]" )
+ if( CMAKE_HOST_WIN32 )
+  # try to convert path to 8.3 form
+  file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" )
+  execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}"
+                   OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE
+                   RESULT_VARIABLE __result ERROR_QUIET )
+  if( __result EQUAL 0 )
+   file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT )
+   set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" )
+  else()
+   set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" )
+  endif()
+ else()
+  set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" )
+ endif()
+ if( NOT _CMAKE_IN_TRY_COMPILE )
+  # quotes can break try_compile and compiler identification
+  message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n")
+ endif()
+else()
+ set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" )
+endif()
+
+# NDK flags
+if (ARM64_V8A )
+ set( ANDROID_CXX_FLAGS         "${ANDROID_CXX_FLAGS} -funwind-tables" )
+ set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" )
+ set( ANDROID_CXX_FLAGS_DEBUG   "-fno-omit-frame-pointer -fno-strict-aliasing" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+  set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" )
+ endif()
+elseif( ARMEABI OR ARMEABI_V7A)
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" )
+ if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 )
+  set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" )
+  set( ANDROID_CXX_FLAGS_DEBUG   "-marm -fno-omit-frame-pointer -fno-strict-aliasing" )
+  if( NOT ANDROID_COMPILER_IS_CLANG )
+   set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" )
+  endif()
+ else()
+  # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI
+  set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" )
+  set( ANDROID_CXX_FLAGS_DEBUG   "-marm -fno-omit-frame-pointer -fno-strict-aliasing" )
+  if( NOT ANDROID_COMPILER_IS_CLANG )
+   set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" )
+  endif()
+ endif()
+elseif( X86 OR X86_64 )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+  set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" )
+ endif()
+ set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" )
+ set( ANDROID_CXX_FLAGS_DEBUG   "-fno-omit-frame-pointer -fno-strict-aliasing" )
+elseif( MIPS OR MIPS64 )
+ set( ANDROID_CXX_FLAGS         "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" )
+ set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" )
+ set( ANDROID_CXX_FLAGS_DEBUG   "-fno-omit-frame-pointer" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+  set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" )
+  set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" )
+ endif()
+elseif()
+ set( ANDROID_CXX_FLAGS_RELEASE "" )
+ set( ANDROID_CXX_FLAGS_DEBUG   "" )
+endif()
+
+set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries
+
+if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" )
+endif()
+
+if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/
+endif()
+
+# ABI-specific flags
+if( ARMEABI_V7A )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" )
+ if( NEON )
+  set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" )
+ elseif( VFPV3 )
+  set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" )
+ else()
+  set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" )
+ endif()
+elseif( ARMEABI_V6 )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2
+elseif( ARMEABI )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" )
+endif()
+
+if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") )
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE  "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_LINK_EXECUTABLE       "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" )
+else()
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE  "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_LINK_EXECUTABLE       "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" )
+endif()
+
+# STL
+if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" )
+ if( EXISTS "${__libstl}" )
+  set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" )
+  set( CMAKE_CXX_CREATE_SHARED_MODULE  "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" )
+  set( CMAKE_CXX_LINK_EXECUTABLE       "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" )
+ endif()
+ if( EXISTS "${__libsupcxx}" )
+  set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" )
+  set( CMAKE_CXX_CREATE_SHARED_MODULE  "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" )
+  set( CMAKE_CXX_LINK_EXECUTABLE       "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" )
+  # C objects:
+  set( CMAKE_C_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+  set( CMAKE_C_CREATE_SHARED_MODULE  "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+  set( CMAKE_C_LINK_EXECUTABLE       "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" )
+  set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" )
+  set( CMAKE_C_CREATE_SHARED_MODULE  "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" )
+  set( CMAKE_C_LINK_EXECUTABLE       "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" )
+ endif()
+ if( ANDROID_STL MATCHES "gnustl" )
+  if( NOT EXISTS "${ANDROID_LIBM_PATH}" )
+   set( ANDROID_LIBM_PATH -lm )
+  endif()
+  set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" )
+  set( CMAKE_CXX_CREATE_SHARED_MODULE  "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" )
+  set( CMAKE_CXX_LINK_EXECUTABLE       "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" )
+ endif()
+endif()
+
+# variables controlling optional build flags
+if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7
+ # libGLESv2.so in NDK's prior to r7 refers to missing external symbols.
+ # So this flag option is required for all projects using OpenGL from native.
+ __INIT_VARIABLE( ANDROID_SO_UNDEFINED                      VALUES ON )
+else()
+ __INIT_VARIABLE( ANDROID_SO_UNDEFINED                      VALUES OFF )
+endif()
+__INIT_VARIABLE( ANDROID_NO_UNDEFINED                       VALUES ON )
+__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING             VALUES ON )
+__INIT_VARIABLE( ANDROID_GOLD_LINKER                        VALUES ON )
+__INIT_VARIABLE( ANDROID_NOEXECSTACK                        VALUES ON )
+__INIT_VARIABLE( ANDROID_RELRO                              VALUES ON )
+
+set( ANDROID_NO_UNDEFINED           ${ANDROID_NO_UNDEFINED}           CACHE BOOL "Show all undefined symbols as linker errors" )
+set( ANDROID_SO_UNDEFINED           ${ANDROID_SO_UNDEFINED}           CACHE BOOL "Allows or disallows undefined symbols in shared libraries" )
+set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" )
+set( ANDROID_GOLD_LINKER            ${ANDROID_GOLD_LINKER}            CACHE BOOL "Enables gold linker" )
+set( ANDROID_NOEXECSTACK            ${ANDROID_NOEXECSTACK}            CACHE BOOL "Allows or disallows undefined symbols in shared libraries" )
+set( ANDROID_RELRO                  ${ANDROID_RELRO}                  CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" )
+mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO )
+
+# linker flags
+set( ANDROID_LINKER_FLAGS "" )
+
+if( ARMEABI_V7A )
+ # this is *required* to use the following linker flags that routes around
+ # a CPU bug in some Cortex-A8 implementations:
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" )
+endif()
+
+if( ANDROID_NO_UNDEFINED )
+ if( MIPS )
+  # there is some sysroot-related problem in mips linker...
+  if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" )
+   set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" )
+  endif()
+ else()
+  set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" )
+ endif()
+endif()
+
+if( ANDROID_SO_UNDEFINED )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" )
+endif()
+
+if( ANDROID_FUNCTION_LEVEL_LINKING )
+ set( ANDROID_CXX_FLAGS    "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" )
+endif()
+
+if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" )
+ if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE_NUM GREATER 8002) AND (ARMEABI OR ARMEABI_V7A OR X86) )
+  set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" )
+ elseif( ANDROID_NDK_RELEASE_NUM GREATER 8002 ) # after r8b
+  set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" )
+ elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE )
+  message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342
+  On Linux and OS X host platform you can workaround this problem using gold linker (default).
+  Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems.
+" )
+ endif()
+endif() # version 4.6
+
+if( ANDROID_NOEXECSTACK )
+ if( ANDROID_COMPILER_IS_CLANG )
+  set( ANDROID_CXX_FLAGS    "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" )
+ else()
+  set( ANDROID_CXX_FLAGS    "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" )
+ endif()
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" )
+endif()
+
+if( ANDROID_RELRO )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" )
+endif()
+
+if( ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} -Qunused-arguments ${ANDROID_CXX_FLAGS}" )
+ if( BUILD_WITH_ANDROID_NDK )
+  set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" )
+ endif()
+endif()
+
+# cache flags
+set( CMAKE_CXX_FLAGS           ""                                  CACHE STRING "c++ flags" )
+set( CMAKE_C_FLAGS             ""                                  CACHE STRING "c flags" )
+set( CMAKE_CXX_FLAGS_RELEASE   "-O3 -DNDEBUG"                      CACHE STRING "c++ Release flags" )
+set( CMAKE_C_FLAGS_RELEASE     "-O3 -DNDEBUG"                      CACHE STRING "c Release flags" )
+set( CMAKE_CXX_FLAGS_DEBUG     "-O0 -g -DDEBUG -D_DEBUG"           CACHE STRING "c++ Debug flags" )
+set( CMAKE_C_FLAGS_DEBUG       "-O0 -g -DDEBUG -D_DEBUG"           CACHE STRING "c Debug flags" )
+set( CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id"                    CACHE STRING "shared linker flags" )
+set( CMAKE_MODULE_LINKER_FLAGS "-Wl,--build-id"                    CACHE STRING "module linker flags" )
+set( CMAKE_EXE_LINKER_FLAGS    "-Wl,--build-id -Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" )
+
+# put flags to cache (for debug purpose only)
+set( ANDROID_CXX_FLAGS         "${ANDROID_CXX_FLAGS}"         CACHE INTERNAL "Android specific c/c++ flags" )
+set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" )
+set( ANDROID_CXX_FLAGS_DEBUG   "${ANDROID_CXX_FLAGS_DEBUG}"   CACHE INTERNAL "Android specific c/c++ Debug flags" )
+set( ANDROID_LINKER_FLAGS      "${ANDROID_LINKER_FLAGS}"      CACHE INTERNAL "Android specific c/c++ linker flags" )
+
+# finish flags
+set( CMAKE_CXX_FLAGS           "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" )
+set( CMAKE_C_FLAGS             "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" )
+set( CMAKE_CXX_FLAGS_RELEASE   "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" )
+set( CMAKE_C_FLAGS_RELEASE     "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" )
+set( CMAKE_CXX_FLAGS_DEBUG     "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" )
+set( CMAKE_C_FLAGS_DEBUG       "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" )
+set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" )
+set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" )
+set( CMAKE_EXE_LINKER_FLAGS    "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" )
+
+if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" )
+ set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" )
+ set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" )
+ set( CMAKE_EXE_LINKER_FLAGS    "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" )
+endif()
+
+# pie/pic
+if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) )
+ set( CMAKE_POSITION_INDEPENDENT_CODE TRUE )
+ set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")
+else()
+ set( CMAKE_POSITION_INDEPENDENT_CODE FALSE )
+ set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" )
+ set( CMAKE_C_FLAGS   "-fpic ${CMAKE_C_FLAGS}" )
+endif()
+
+# configure rtti
+if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES )
+ if( ANDROID_RTTI )
+  set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" )
+ else()
+  set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" )
+ endif()
+endif()
+
+# configure exceptios
+if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES )
+ if( ANDROID_EXCEPTIONS )
+  set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" )
+  set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" )
+ else()
+  set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" )
+  set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" )
+ endif()
+endif()
+
+# global includes and link directories
+include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} )
+get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning
+link_directories( "${__android_install_path}" )
+
+# detect if need link crtbegin_so.o explicitly
+if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK )
+ set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" )
+ string( REPLACE "<CMAKE_CXX_COMPILER>" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_C_COMPILER>"   "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}"   __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_SHARED_LIBRARY_CXX_FLAGS>" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" )
+ string( REPLACE "<LANGUAGE_COMPILE_FLAGS>" "" __cmd "${__cmd}" )
+ string( REPLACE "<LINK_FLAGS>" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS>" "-shared" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG>" "" __cmd "${__cmd}" )
+ string( REPLACE "<TARGET_SONAME>" "" __cmd "${__cmd}" )
+ string( REPLACE "<TARGET>" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" )
+ string( REPLACE "<OBJECTS>" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" )
+ string( REPLACE "<LINK_LIBRARIES>" "" __cmd "${__cmd}" )
+ separate_arguments( __cmd )
+ foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN )
+  if( ${__var} )
+   set( __tmp "${${__var}}" )
+   separate_arguments( __tmp )
+   string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}")
+  endif()
+ endforeach()
+ string( REPLACE "'" "" __cmd "${__cmd}" )
+ string( REPLACE "\"" "" __cmd "${__cmd}" )
+ execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET )
+ if( __cmd_result EQUAL 0 )
+  set( ANDROID_EXPLICIT_CRT_LINK ON )
+ else()
+  set( ANDROID_EXPLICIT_CRT_LINK OFF )
+ endif()
+endif()
+
+if( ANDROID_EXPLICIT_CRT_LINK )
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE  "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" )
+endif()
+
+# setup output directories
+set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" )
+
+if( DEFINED LIBRARY_OUTPUT_PATH_ROOT
+      OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml"
+      OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") )
+  set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" )
+  if( NOT _CMAKE_IN_TRY_COMPILE )
+    if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" )
+      set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" )
+    else()
+      set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" )
+    endif()
+    set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" )
+  endif()
+endif()
+
+# copy shaed stl library to build directory
+if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH )
+  get_filename_component( __libstlname "${__libstl}" NAME )
+  execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess )
+  if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}")
+    message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" )
+  endif()
+  unset( __fileCopyProcess )
+  unset( __libstlname )
+endif()
+
+
+# set these global flags for cmake client scripts to change behavior
+set( ANDROID True )
+set( BUILD_ANDROID True )
+
+# where is the target environment
+set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_ROOT}/bin" "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" "${ANDROID_SYSROOT}" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share" )
+
+# only search for libraries and includes in the ndk toolchain
+set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY )
+set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
+set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
+
+
+# macro to find packages on the host OS
+macro( find_host_package )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER )
+ if( CMAKE_HOST_WIN32 )
+  SET( WIN32 1 )
+  SET( UNIX )
+ elseif( CMAKE_HOST_APPLE )
+  SET( APPLE 1 )
+  SET( UNIX )
+ endif()
+ find_package( ${ARGN} )
+ SET( WIN32 )
+ SET( APPLE )
+ SET( UNIX 1 )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
+endmacro()
+
+
+# macro to find programs on the host OS
+macro( find_host_program )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER )
+ if( CMAKE_HOST_WIN32 )
+  SET( WIN32 1 )
+  SET( UNIX )
+ elseif( CMAKE_HOST_APPLE )
+  SET( APPLE 1 )
+  SET( UNIX )
+ endif()
+ find_program( ${ARGN} )
+ SET( WIN32 )
+ SET( APPLE )
+ SET( UNIX 1 )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
+endmacro()
+
+
+# export toolchain settings for the try_compile() command
+if( NOT _CMAKE_IN_TRY_COMPILE )
+ set( __toolchain_config "")
+ foreach( __var NDK_CCACHE  LIBRARY_OUTPUT_PATH_ROOT  ANDROID_FORBID_SYGWIN
+                ANDROID_NDK_HOST_X64
+                ANDROID_NDK
+                ANDROID_NDK_LAYOUT
+                ANDROID_STANDALONE_TOOLCHAIN
+                ANDROID_TOOLCHAIN_NAME
+                ANDROID_ABI
+                ANDROID_NATIVE_API_LEVEL
+                ANDROID_STL
+                ANDROID_STL_FORCE_FEATURES
+                ANDROID_FORCE_ARM_BUILD
+                ANDROID_NO_UNDEFINED
+                ANDROID_SO_UNDEFINED
+                ANDROID_FUNCTION_LEVEL_LINKING
+                ANDROID_GOLD_LINKER
+                ANDROID_NOEXECSTACK
+                ANDROID_RELRO
+                ANDROID_LIBM_PATH
+                ANDROID_EXPLICIT_CRT_LINK
+                ANDROID_APP_PIE
+                )
+  if( DEFINED ${__var} )
+   if( ${__var} MATCHES " ")
+    set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" )
+   else()
+    set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" )
+   endif()
+  endif()
+ endforeach()
+ file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" )
+ unset( __toolchain_config )
+endif()
+
+
+# force cmake to produce / instead of \ in build commands for Ninja generator
+if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 )
+ # it is a bad hack after all
+ # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW
+ set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW
+ set( CMAKE_CROSSCOMPILING TRUE )    # stop recursion
+ enable_language( C )
+ enable_language( CXX )
+ # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it
+ unset( MINGW )
+endif()
+
+# Variables need by cmAndroidGradleBuild to generate android_gradle_build.json
+set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
+
+
+# Variables controlling behavior or set by cmake toolchain:
+#   ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64"
+#   ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version)
+#   ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none
+#   ANDROID_FORBID_SYGWIN : ON/OFF
+#   ANDROID_NO_UNDEFINED : ON/OFF
+#   ANDROID_SO_UNDEFINED : OFF/ON  (default depends on NDK version)
+#   ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF
+#   ANDROID_GOLD_LINKER : ON/OFF
+#   ANDROID_NOEXECSTACK : ON/OFF
+#   ANDROID_RELRO : ON/OFF
+#   ANDROID_FORCE_ARM_BUILD : ON/OFF
+#   ANDROID_STL_FORCE_FEATURES : ON/OFF
+#   ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product/<product_name>/obj/lib/libm.so) to workaround unresolved `sincos`
+# Can be set only at the first run:
+#   ANDROID_NDK : path to your NDK install
+#   NDK_CCACHE : path to your ccache executable
+#   ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain
+#   ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems)
+#   ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID)
+#   LIBRARY_OUTPUT_PATH_ROOT : <any valid path>
+#   ANDROID_STANDALONE_TOOLCHAIN
+#
+# Primary read-only variables:
+#   ANDROID : always TRUE
+#   ARMEABI : TRUE for arm v6 and older devices
+#   ARMEABI_V6 : TRUE for arm v6
+#   ARMEABI_V7A : TRUE for arm v7a
+#   ARM64_V8A : TRUE for arm64-v8a
+#   NEON : TRUE if NEON unit is enabled
+#   VFPV3 : TRUE if VFP version 3 is enabled
+#   X86 : TRUE if configured for x86
+#   X86_64 : TRUE if configured for x86_64
+#   MIPS : TRUE if configured for mips
+#   MIPS64 : TRUE if configured for mips64
+#   BUILD_WITH_ANDROID_NDK : TRUE if NDK is used
+#   BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used
+#   ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform
+#   ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI
+#   ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK
+#   ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor)
+#   ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI
+#   ANDROID_SYSROOT : path to the compiler sysroot
+#   TOOL_OS_SUFFIX : "" or ".exe" depending on host platform
+#   ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used
+#
+# Secondary (less stable) read-only variables:
+#   ANDROID_COMPILER_VERSION : GCC version used (not Clang version)
+#   ANDROID_CLANG_VERSION : version of clang compiler if clang is used
+#   ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform
+#   ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI
+#   ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux"
+#   ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK)
+#   ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools
+#   ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK
+#   ANDROID_STL_INCLUDE_DIRS : stl include paths
+#   ANDROID_RTTI : if rtti is enabled by the runtime
+#   ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime
+#   ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used
+#
+# Defaults:
+#   ANDROID_DEFAULT_NDK_API_LEVEL
+#   ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH}
+#   ANDROID_NDK_SEARCH_PATHS
+#   ANDROID_SUPPORTED_ABIS_${ARCH}
+#   ANDROID_SUPPORTED_NDK_VERSIONS

+ 43 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/CMakeLists.txt

@@ -0,0 +1,43 @@
+cmake_minimum_required(VERSION 3.2.0)
+
+if(REALM_PLATFORM STREQUAL "Android")
+    # This must be before project()
+    set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMake/android.toolchain.cmake")
+    set(ANDROID_ABI "x86" CACHE STRING "")
+    set(ANDROID_NATIVE_API_LEVEL "android-16" CACHE STRING "")
+endif()
+
+set(CMAKE_BUILD_TYPE Debug CACHE STRING "")
+project(realm-object-store)
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
+
+include(CodeCoverage)
+include(CompilerFlags)
+include(Sanitizers)
+
+# Sync is disabled unless -DREALM_ENABLE_SYNC=1 is specified when invoking CMake.
+# FIXME: Flip the default once we can build against prebuilt sync binaries.
+set(REALM_ENABLE_SYNC OFF CACHE BOOL "")
+
+if(REALM_SYNC_PREFIX AND NOT REALM_CORE_PREFIX)
+    message(FATAL_ERROR "REALM_CORE_PREFIX must be set when specifying REALM_SYNC_PREFIX.")
+endif()
+if(REALM_SYNC_PREFIX AND NOT REALM_ENABLE_SYNC)
+    message(FATAL_ERROR "REALM_ENABLE_SYNC must be set when specifying REALM_SYNC_PREFIX.")
+endif()
+if(REALM_CORE_PREFIX AND REALM_ENABLE_SYNC AND NOT REALM_SYNC_PREFIX)
+    message(FATAL_ERROR "REALM_SYNC_PREFIX must be set when specifying REALM_CORE_PREFIX when REALM_ENABLE_SYNC is set.")
+endif()
+
+include(RealmCore)
+use_realm_core("${REALM_ENABLE_SYNC}" "${REALM_CORE_PREFIX}" "${REALM_SYNC_PREFIX}")
+
+if(REALM_ENABLE_SYNC)
+  add_definitions(-DREALM_ENABLE_SYNC)
+endif()
+
+set(PEGTL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/pegtl)
+
+add_subdirectory(src)
+add_subdirectory(tests)

+ 23 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Dockerfile

@@ -0,0 +1,23 @@
+FROM ubuntu:xenial
+
+RUN apt-get update && \
+    apt-get install -y wget build-essential lcov curl cmake gcovr libssl-dev \
+      git python-cheetah libuv1-dev ninja-build adb xutils-dev
+
+# Install the Android NDK
+RUN mkdir -p /tmp/android-ndk && \
+    cd /tmp/android-ndk && \
+    wget -q http://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin -O android-ndk.bin && \
+    chmod a+x ./android-ndk.bin && sync && ./android-ndk.bin && \
+    mv ./android-ndk-r10e /opt/android-ndk && \
+    chmod -R a+rX /opt/android-ndk && \
+    rm -rf /tmp/android-ndk
+
+ENV ANDROID_NDK_PATH /opt/android-ndk
+
+# Ensure a new enough version of CMake is available.
+RUN cd /opt \
+    && wget https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.tar.gz \
+        && tar zxvf cmake-3.7.2-Linux-x86_64.tar.gz
+
+ENV PATH "/opt/cmake-3.7.2-Linux-x86_64/bin:$PATH"

+ 189 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/Jenkinsfile

@@ -0,0 +1,189 @@
+#!groovy
+def getSourceArchive() {
+  deleteDir()
+  unstash 'source'
+}
+
+def readGitTag() {
+  sh "git describe --exact-match --tags HEAD | tail -n 1 > tag.txt 2>&1 || true"
+  def tag = readFile('tag.txt').trim()
+  return tag
+}
+
+def readGitSha() {
+  sh "git rev-parse HEAD | cut -b1-8 > sha.txt"
+  def sha = readFile('sha.txt').readLines().last().trim()
+  return sha
+}
+
+def buildDockerEnv(name, dockerfile='Dockerfile', extra_args='') {
+  docker.withRegistry("https://${env.DOCKER_REGISTRY}", "ecr:eu-west-1:aws-ci-user") {
+    sh "sh ./workflow/docker_build_wrapper.sh $name . ${extra_args}"
+  }
+  return docker.image(name)
+}
+
+def publishReport(String label) {
+  // Unfortunately, we cannot add a title or tag to individual coverage reports.
+  echo "Unstashing coverage-${label}"
+  unstash("coverage-${label}")
+  step([
+    $class: 'CoberturaPublisher',
+    autoUpdateHealth: false,
+    autoUpdateStability: false,
+    coberturaReportFile: "${label}.build/coverage.xml",
+    failNoReports: true,
+    failUnhealthy: false,
+    failUnstable: false,
+    maxNumberOfBuilds: 0,
+    onlyStable: false,
+    sourceEncoding: 'ASCII',
+    zoomCoverageChart: false
+  ])
+}
+
+if (env.BRANCH_NAME == 'master') {
+  env.DOCKER_PUSH = "1"
+}
+
+def doDockerBuild(String flavor, Boolean withCoverage, Boolean enableSync) {
+  def sync = enableSync ? "sync" : ""
+  def label = "${flavor}${enableSync ? '-sync' : ''}"
+
+  return {
+    node('docker') {
+      getSourceArchive()
+      def image = buildDockerEnv("ci/realm-object-store:${flavor}")
+      sshagent(['realm-ci-ssh']) {
+        image.inside("-v /etc/passwd:/etc/passwd:ro -v ${env.HOME}:${env.HOME} -v ${env.SSH_AUTH_SOCK}:${env.SSH_AUTH_SOCK} -e HOME=${env.HOME}") {
+          if(withCoverage) {
+            sh "rm -rf coverage.build ${label}.build && ./workflow/test_coverage.sh ${sync} && mv coverage.build ${label}.build"
+          } else {
+            sh "./workflow/build.sh ${flavor} ${sync}"
+          }
+        }
+      }
+      if(withCoverage) {
+        echo "Stashing coverage-${label}"
+        stash includes: "${label}.build/coverage.xml", name: "coverage-${label}"
+      }
+    }
+  }
+}
+
+def doAndroidDockerBuild() {
+  return {
+    node('docker') {
+      getSourceArchive()
+      wrap([$class: 'AnsiColorBuildWrapper']) {
+        def image = buildDockerEnv('ci/realm-object-store:android')
+        docker.image('tracer0tong/android-emulator').withRun { emulator ->
+          image.inside("--link ${emulator.id}:emulator") {
+            sh '''rm -rf build
+              mkdir build
+              cd build
+              cmake -DREALM_PLATFORM=Android -DANDROID_NDK=/opt/android-ndk -GNinja ..
+              ninja
+              adb connect emulator
+              timeout 10m adb wait-for-device
+              adb push tests/tests /data/local/tmp
+              adb shell '/data/local/tmp/tests || echo __ADB_FAIL__' | tee adb.log
+              ! grep __ADB_FAIL__ adb.log
+            '''
+          }
+        }
+      }
+    }
+  }
+}
+
+def doBuild(String nodeSpec, String flavor, Boolean enableSync, String version) {
+  def sync = enableSync ? "sync" : "false"
+  def label = "${flavor}${enableSync ? '-sync' : ''}"
+  return {
+    node(nodeSpec) {
+      getSourceArchive()
+      sshagent(['realm-ci-ssh']) {
+        sh "./workflow/test_coverage.sh ${sync} ${version} && mv coverage.build ${label}.build"
+      }
+      echo "Stashing coverage-${label}"
+      stash includes: "${label}.build/coverage.xml", name: "coverage-${label}"
+    }
+  }
+}
+
+def doWindowsBuild() {
+  return {
+    node('windows') {
+      getSourceArchive()
+
+      bat """
+        "${tool 'cmake'}" . -DCMAKE_SYSTEM_VERSION="8.1"
+        "${tool 'cmake'}" --build . --config Release
+        tests\\Release\\tests.exe
+      """
+    }
+  }
+}
+
+def doWindowsUniversalBuild() {
+  return {
+    node('windows') {
+      getSourceArchive()
+
+      bat """
+        "${tool 'cmake'}" . -DCMAKE_SYSTEM_NAME="WindowsStore" -DCMAKE_SYSTEM_VERSION="10.0"
+        "${tool 'cmake'}" --build . --config Release --target realm-object-store
+      """
+    }
+  }
+}
+
+def setBuildName(newBuildName) {
+  currentBuild.displayName = "${currentBuild.displayName} - ${newBuildName}"
+}
+
+stage('prepare') {
+  node('docker') {
+    checkout scm
+    sh 'git clean -ffdx -e .????????'
+    sshagent(['realm-ci-ssh']) {
+      sh 'git submodule update --init --recursive'
+    }
+
+    gitTag = readGitTag()
+    gitSha = readGitSha()
+    echo "tag: ${gitTag}"
+    if (gitTag == "") {
+      echo "No tag given for this build"
+      setBuildName("${gitSha}")
+    } else {
+      echo "Building release: '${gitTag}'"
+      setBuildName("Tag ${gitTag}")
+    }
+
+    stash includes: '**', name: 'source'
+  }
+}
+
+stage('unit-tests') {
+  parallel(
+    linux: doDockerBuild('linux', true, false),
+    linux_sync: doDockerBuild('linux', true, true),
+    android: doAndroidDockerBuild(),
+    macos: doBuild('osx', 'macOS', false, ''),
+    macos_sync: doBuild('osx', 'macOS', true, ''),
+    win32: doWindowsBuild(),
+    windows_universal: doWindowsUniversalBuild()
+  )
+  currentBuild.result = 'SUCCESS'
+}
+
+stage('publish') {
+  node('docker') {
+    publishReport('linux')
+    publishReport('linux-sync')
+    publishReport('macOS')
+    publishReport('macOS-sync')
+  }
+}

+ 269 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/LICENSE

@@ -0,0 +1,269 @@
+TABLE OF CONTENTS
+
+1. Apache License version 2.0
+2. Realm Components
+3. Export Compliance
+
+-------------------------------------------------------------------------------
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+REALM COMPONENTS
+
+This software contains components with separate copyright and license terms.
+Your use of these components is subject to the terms and conditions of the
+following licenses.
+
+For the Realm Core component
+
+  Realm Core Binary License
+
+  Copyright (c) 2011-2016 Realm Inc All rights reserved
+
+  Redistribution and use in binary form, with or without modification, is
+  permitted provided that the following conditions are met:
+
+  1. You agree not to attempt to decompile, disassemble, reverse engineer or
+  otherwise discover the source code from which the binary code was derived.
+  You may, however, access and obtain a separate license for most of the
+  source code from which this Software was created, at
+  http://realm.io/pricing/.
+
+  2. Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+  3. Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from this
+  software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+
+EXPORT COMPLIANCE
+
+You understand that the Software may contain cryptographic functions that may be
+subject to export restrictions, and you represent and warrant that you are not
+located in a country that is subject to United States export restriction or embargo,
+including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region, and that you
+are not on the Department of Commerce list of Denied Persons, Unverified Parties,
+or affiliated with a Restricted Entity.
+
+You agree to comply with all export, re-export and import restrictions and
+regulations of the Department of Commerce or other agency or authority of the
+United States or other applicable countries. You also agree not to transfer, or
+authorize the transfer of, directly or indirectly, the Software to any prohibited
+country, including Cuba, Iran, North Korea, Sudan, Syria or the Crimea region,
+or to any person or organization on or affiliated with the Department of
+Commerce lists of Denied Persons, Unverified Parties or Restricted Entities, or
+otherwise in violation of any such restrictions or regulations.

+ 4 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list

@@ -0,0 +1,4 @@
+REALM_CORE_VERSION=5.8.0
+REALM_SYNC_VERSION=3.9.3
+ANDROID_OPENSSL_VERSION=1.0.2k
+REALM_CORE_PACKAGING=2

+ 11 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/.gitattributes

@@ -0,0 +1,11 @@
+# This sets the default behaviour, overriding core.autocrlf
+* text=auto
+
+# All source files should have unix line-endings in the repository,
+# but convert to native line-endings on checkout
+*.cpp text
+*.h text
+*.hpp text
+
+# Windows specific files should retain windows line-endings
+*.sln text eol=crlf

+ 29 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/.gitignore

@@ -0,0 +1,29 @@
+*.build
+*.pbxuser
+*.mode1v3
+*.ncb
+*.suo
+Debug
+Release
+*.user
+*.xcuserstate
+.DS_Store
+xcuserdata
+CatchSelfTest.xcscheme
+Breakpoints.xcbkptlist
+projects/VS2010/TestCatch/_UpgradeReport_Files/
+projects/VS2010/TestCatch/TestCatch/TestCatch.vcxproj.filters
+projects/VisualStudio/TestCatch/UpgradeLog.XML
+projects/CMake/.idea
+projects/CMake/cmake-build-debug
+UpgradeLog.XML
+Resources/DWARF
+projects/Generated
+*.pyc
+DerivedData
+*.xccheckout
+Build
+.idea
+cmake-build-debug
+cmake-build-release
+.vs

+ 268 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/.travis.yml

@@ -0,0 +1,268 @@
+language: cpp
+sudo: false
+
+matrix:
+  include:
+
+    # 1/ Linux Clang Builds
+    - os: linux
+      compiler: clang
+      addons: &clang34
+        apt:
+          sources: ['llvm-toolchain-precise', 'ubuntu-toolchain-r-test']
+          packages: ['clang']
+      env: COMPILER='clang++' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons: *clang34
+      env: COMPILER='clang++' BUILD_TYPE='Debug'
+
+    - os: linux
+      compiler: clang
+      addons: &clang35
+        apt:
+          sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test']
+          packages: ['clang-3.5']
+      env: COMPILER='clang++-3.5' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons: *clang35
+      env: COMPILER='clang++-3.5' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: clang
+      addons: &clang36
+        apt:
+          sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test']
+          packages: ['clang-3.6']
+      env: COMPILER='clang++-3.6' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons: *clang36
+      env: COMPILER='clang++-3.6' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: clang
+      addons: &clang37
+        apt:
+          sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test']
+          packages: ['clang-3.7']
+      env: COMPILER='clang++-3.7' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons: *clang37
+      env: COMPILER='clang++-3.7' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: clang
+      addons: &clang38
+        apt:
+          sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test']
+          packages: ['clang-3.8']
+      env: COMPILER='clang++-3.8' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: clang
+      addons: *clang38
+      env: COMPILER='clang++-3.8' BUILD_TYPE='Debug'
+
+
+    # 2/ Linux GCC Builds
+    - os: linux
+      compiler: gcc
+      addons: &gcc44
+        apt:
+         sources: ['ubuntu-toolchain-r-test']
+         packages: ['g++-4.4']
+      env: COMPILER='g++-4.4' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc44
+      env: COMPILER='g++-4.4' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: gcc
+      addons: &gcc47
+        apt:
+         sources: ['ubuntu-toolchain-r-test']
+         packages: ['g++-4.7']
+      env: COMPILER='g++-4.7' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc47
+      env: COMPILER='g++-4.7' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: gcc
+      addons: &gcc48
+        apt:
+         sources: ['ubuntu-toolchain-r-test']
+         packages: ['g++-4.8']
+      env: COMPILER='g++-4.8' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc48
+      env: COMPILER='g++-4.8' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: gcc
+      addons: &gcc49
+        apt:
+          sources: ['ubuntu-toolchain-r-test']
+          packages: ['g++-4.9']
+      env: COMPILER='g++-4.9' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc49
+      env: COMPILER='g++-4.9' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: gcc
+      addons: &gcc5
+        apt:
+          sources: ['ubuntu-toolchain-r-test']
+          packages: ['g++-5']
+      env: COMPILER='g++-5' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc5
+      env: COMPILER='g++-5' BUILD_TYPE='Debug'
+
+
+    - os: linux
+      compiler: gcc
+      addons: &gcc6
+        apt:
+          sources: ['ubuntu-toolchain-r-test']
+          packages: ['g++-6']
+      env: COMPILER='g++-6' BUILD_TYPE='Release'
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc6
+      env: COMPILER='g++-6' BUILD_TYPE='Debug'
+
+    # 3a/ Linux C++11 GCC builds
+    - os: linux
+      compiler: gcc
+      addons: *gcc48
+      env: COMPILER='g++-4.8' BUILD_TYPE='Release' CPP11=1
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc48
+      env: COMPILER='g++-4.8' BUILD_TYPE='Debug' CPP11=1
+
+    # 3b/ Linux C++11 Clang builds
+    - os: linux
+      compiler: clang
+      addons: *clang38
+      env: COMPILER='clang++-3.8' BUILD_TYPE='Release' CPP11=1
+
+    - os: linux
+      compiler: clang
+      addons: *clang38
+      env: COMPILER='clang++-3.8' BUILD_TYPE='Debug' CPP11=1
+
+    # 4a/ Linux C++14 GCC builds
+    - os: linux
+      compiler: gcc
+      addons: *gcc6
+      env: COMPILER='g++-6' BUILD_TYPE='Release' CPP14=1
+
+    - os: linux
+      compiler: gcc
+      addons: *gcc6
+      env: COMPILER='g++-6' BUILD_TYPE='Debug' CPP14=1
+
+    # 4b/ Linux C++14 Clang builds
+#    - os: linux
+#      compiler: clang
+#      addons: *clang38
+#      env: COMPILER='clang++-3.8' BUILD_TYPE='Release' CPP14=1
+#
+#    - os: linux
+#      compiler: clang
+#      addons: *clang38
+#      env: COMPILER='clang++-3.8' BUILD_TYPE='Debug' CPP14=1
+
+
+    # 5/ OSX Clang Builds
+    - os: osx
+      osx_image: xcode7.3
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Debug'
+
+    - os: osx
+      osx_image: xcode7.3
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Release'
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Debug'
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Release'
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Debug' USE_CPP11=1
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Release' USE_CPP11=1
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Debug' USE_CPP14=1
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env: COMPILER='clang++' BUILD_TYPE='Release' USE_CPP14=1
+
+
+install:
+  - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
+  - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
+  - |
+    if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
+      CMAKE_URL="http://www.cmake.org/files/v3.3/cmake-3.3.2-Linux-x86_64.tar.gz"
+      mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
+      export PATH=${DEPS_DIR}/cmake/bin:${PATH}
+    elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
+      which cmake || brew install cmake
+    fi
+
+before_script:
+  - export CXX=${COMPILER}
+  - cd ${TRAVIS_BUILD_DIR}
+  - cmake -H. -BBuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -Wdev -DUSE_CPP11=${CPP11} -DUSE_CPP14=${CPP14}
+  - cd Build
+
+script:
+  - make -j 2
+  - ctest -V -j 2

+ 284 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/CMakeLists.txt

@@ -0,0 +1,284 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(CatchSelfTest)
+
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+# define some folders
+set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest)
+set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark)
+set(HEADER_DIR ${CATCH_DIR}/include)
+
+if(USE_CPP11)
+    ## We can't turn this on by default, since it breaks on travis
+    message(STATUS "Enabling C++11")
+    set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+elseif(USE_CPP14)
+    message(STATUS "Enabling C++14")
+    set(CMAKE_CXX_FLAGS "-std=c++14 ${CMAKE_CXX_FLAGS}")
+endif()
+
+if(USE_WMAIN)
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup")
+endif()
+
+#checks that the given hard-coded list contains all headers + sources in the given folder
+function(CheckFileList LIST_VAR FOLDER)
+  set(MESSAGE " should be added to the variable ${LIST_VAR}")
+  set(MESSAGE "${MESSAGE} in ${CMAKE_CURRENT_LIST_FILE}\n")
+  file(GLOB GLOBBED_LIST "${FOLDER}/*.cpp"
+                         "${FOLDER}/*.hpp"
+                         "${FOLDER}/*.h")
+  list(REMOVE_ITEM GLOBBED_LIST ${${LIST_VAR}})
+  foreach(EXTRA_ITEM ${GLOBBED_LIST})
+    string(REPLACE "${CATCH_DIR}/" "" RELATIVE_FILE_NAME "${EXTRA_ITEM}")
+    message(AUTHOR_WARNING "The file \"${RELATIVE_FILE_NAME}\"${MESSAGE}")
+  endforeach()
+endfunction()
+
+function(CheckFileListRec LIST_VAR FOLDER)
+  set(MESSAGE " should be added to the variable ${LIST_VAR}")
+  set(MESSAGE "${MESSAGE} in ${CMAKE_CURRENT_LIST_FILE}\n")
+  file(GLOB_RECURSE GLOBBED_LIST "${FOLDER}/*.cpp"
+                                 "${FOLDER}/*.hpp"
+                                 "${FOLDER}/*.h")
+  list(REMOVE_ITEM GLOBBED_LIST ${${LIST_VAR}})
+  foreach(EXTRA_ITEM ${GLOBBED_LIST})
+    string(REPLACE "${CATCH_DIR}/" "" RELATIVE_FILE_NAME "${EXTRA_ITEM}")
+    message(AUTHOR_WARNING "The file \"${RELATIVE_FILE_NAME}\"${MESSAGE}")
+  endforeach()
+endfunction()
+
+# define the sources of the self test
+# Please keep these ordered alphabetically
+set(TEST_SOURCES
+        ${SELF_TEST_DIR}/ApproxTests.cpp
+        ${SELF_TEST_DIR}/BDDTests.cpp
+        ${SELF_TEST_DIR}/ClassTests.cpp
+        ${SELF_TEST_DIR}/CmdLineTests.cpp
+        ${SELF_TEST_DIR}/CompilationTests.cpp
+        ${SELF_TEST_DIR}/ConditionTests.cpp
+        ${SELF_TEST_DIR}/DecompositionTests.cpp
+        ${SELF_TEST_DIR}/EnumToString.cpp
+        ${SELF_TEST_DIR}/ExceptionTests.cpp
+        ${SELF_TEST_DIR}/GeneratorTests.cpp
+        ${SELF_TEST_DIR}/MessageTests.cpp
+        ${SELF_TEST_DIR}/MiscTests.cpp
+        ${SELF_TEST_DIR}/PartTrackerTests.cpp
+        ${SELF_TEST_DIR}/TagAliasTests.cpp
+        ${SELF_TEST_DIR}/TestMain.cpp
+        ${SELF_TEST_DIR}/ToStringGeneralTests.cpp
+        ${SELF_TEST_DIR}/ToStringPair.cpp
+        ${SELF_TEST_DIR}/ToStringTuple.cpp
+        ${SELF_TEST_DIR}/ToStringVector.cpp
+        ${SELF_TEST_DIR}/ToStringWhich.cpp
+        ${SELF_TEST_DIR}/TrickyTests.cpp
+        ${SELF_TEST_DIR}/VariadicMacrosTests.cpp
+        ${SELF_TEST_DIR}/MatchersTests.cpp
+        )
+CheckFileList(TEST_SOURCES ${SELF_TEST_DIR})
+
+# A set of impl files that just #include a single header
+# Please keep these ordered alphabetically
+set(IMPL_SOURCES
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_common.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_console_colour.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_debugger.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_capture.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_config.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_exception.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_generators.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_registry_hub.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_reporter.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_runner.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_interfaces_testcase.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_message.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_option.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_ptr.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_stream.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_streambuf.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_test_spec.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_xmlwriter.cpp
+        ${SELF_TEST_DIR}/SurrogateCpps/catch_test_case_tracker.cpp
+        )
+CheckFileList(IMPL_SOURCES ${SELF_TEST_DIR}/SurrogateCpps)
+
+
+# Please keep these ordered alphabetically
+set(TOP_LEVEL_HEADERS
+        ${HEADER_DIR}/catch.hpp
+        ${HEADER_DIR}/catch_session.hpp
+        ${HEADER_DIR}/catch_with_main.hpp
+        )
+CheckFileList(TOP_LEVEL_HEADERS ${HEADER_DIR})
+
+# Please keep these ordered alphabetically
+set(EXTERNAL_HEADERS
+        ${HEADER_DIR}/external/clara.h
+        ${HEADER_DIR}/external/tbc_text_format.h
+        )
+CheckFileList(EXTERNAL_HEADERS ${HEADER_DIR}/external)
+
+
+# Please keep these ordered alphabetically
+set(INTERNAL_HEADERS
+        ${HEADER_DIR}/internal/catch_approx.hpp
+        ${HEADER_DIR}/internal/catch_assertionresult.h
+        ${HEADER_DIR}/internal/catch_assertionresult.hpp
+        ${HEADER_DIR}/internal/catch_capture.hpp
+        ${HEADER_DIR}/internal/catch_clara.h
+        ${HEADER_DIR}/internal/catch_commandline.hpp
+        ${HEADER_DIR}/internal/catch_common.h
+        ${HEADER_DIR}/internal/catch_common.hpp
+        ${HEADER_DIR}/internal/catch_compiler_capabilities.h
+        ${HEADER_DIR}/internal/catch_config.hpp
+        ${HEADER_DIR}/internal/catch_console_colour.hpp
+        ${HEADER_DIR}/internal/catch_console_colour_impl.hpp
+        ${HEADER_DIR}/internal/catch_context.h
+        ${HEADER_DIR}/internal/catch_context_impl.hpp
+        ${HEADER_DIR}/internal/catch_debugger.h
+        ${HEADER_DIR}/internal/catch_debugger.hpp
+        ${HEADER_DIR}/internal/catch_default_main.hpp
+        ${HEADER_DIR}/internal/catch_errno_guard.hpp
+        ${HEADER_DIR}/internal/catch_evaluate.hpp
+        ${HEADER_DIR}/internal/catch_exception_translator_registry.hpp
+        ${HEADER_DIR}/internal/catch_expression_lhs.hpp
+        ${HEADER_DIR}/internal/catch_fatal_condition.hpp
+        ${HEADER_DIR}/internal/catch_generators.hpp
+        ${HEADER_DIR}/internal/catch_generators_impl.hpp
+        ${HEADER_DIR}/internal/catch_impl.hpp
+        ${HEADER_DIR}/internal/catch_interfaces_capture.h
+        ${HEADER_DIR}/internal/catch_interfaces_config.h
+        ${HEADER_DIR}/internal/catch_interfaces_exception.h
+        ${HEADER_DIR}/internal/catch_interfaces_generators.h
+        ${HEADER_DIR}/internal/catch_interfaces_registry_hub.h
+        ${HEADER_DIR}/internal/catch_interfaces_reporter.h
+        ${HEADER_DIR}/internal/catch_interfaces_runner.h
+        ${HEADER_DIR}/internal/catch_interfaces_tag_alias_registry.h
+        ${HEADER_DIR}/internal/catch_interfaces_testcase.h
+        ${HEADER_DIR}/internal/catch_legacy_reporter_adapter.h
+        ${HEADER_DIR}/internal/catch_legacy_reporter_adapter.hpp
+        ${HEADER_DIR}/internal/catch_list.hpp
+        ${HEADER_DIR}/internal/catch_matchers.hpp
+        ${HEADER_DIR}/internal/catch_matchers_string.h
+        ${HEADER_DIR}/internal/catch_matchers_string.hpp
+        ${HEADER_DIR}/internal/catch_matchers_vector.h
+        ${HEADER_DIR}/internal/catch_message.h
+        ${HEADER_DIR}/internal/catch_message.hpp
+        ${HEADER_DIR}/internal/catch_notimplemented_exception.h
+        ${HEADER_DIR}/internal/catch_notimplemented_exception.hpp
+        ${HEADER_DIR}/internal/catch_objc.hpp
+        ${HEADER_DIR}/internal/catch_objc_arc.hpp
+        ${HEADER_DIR}/internal/catch_option.hpp
+        ${HEADER_DIR}/internal/catch_platform.h
+        ${HEADER_DIR}/internal/catch_ptr.hpp
+        ${HEADER_DIR}/internal/catch_reenable_warnings.h
+        ${HEADER_DIR}/internal/catch_registry_hub.hpp
+        ${HEADER_DIR}/internal/catch_reporter_registrars.hpp
+        ${HEADER_DIR}/internal/catch_reporter_registry.hpp
+        ${HEADER_DIR}/internal/catch_result_builder.h
+        ${HEADER_DIR}/internal/catch_result_builder.hpp
+        ${HEADER_DIR}/internal/catch_result_type.h
+        ${HEADER_DIR}/internal/catch_run_context.hpp
+        ${HEADER_DIR}/internal/catch_section.h
+        ${HEADER_DIR}/internal/catch_section.hpp
+        ${HEADER_DIR}/internal/catch_section_info.h
+        ${HEADER_DIR}/internal/catch_section_info.hpp
+        ${HEADER_DIR}/internal/catch_stream.h
+        ${HEADER_DIR}/internal/catch_stream.hpp
+        ${HEADER_DIR}/internal/catch_streambuf.h
+        ${HEADER_DIR}/internal/catch_suppress_warnings.h
+        ${HEADER_DIR}/internal/catch_tag_alias.h
+        ${HEADER_DIR}/internal/catch_tag_alias_registry.h
+        ${HEADER_DIR}/internal/catch_tag_alias_registry.hpp
+        ${HEADER_DIR}/internal/catch_test_case_info.h
+        ${HEADER_DIR}/internal/catch_test_case_info.hpp
+        ${HEADER_DIR}/internal/catch_test_case_registry_impl.hpp
+        ${HEADER_DIR}/internal/catch_test_case_tracker.hpp
+        ${HEADER_DIR}/internal/catch_test_registry.hpp
+        ${HEADER_DIR}/internal/catch_test_spec.hpp
+        ${HEADER_DIR}/internal/catch_test_spec_parser.hpp
+        ${HEADER_DIR}/internal/catch_text.h
+        ${HEADER_DIR}/internal/catch_timer.h
+        ${HEADER_DIR}/internal/catch_timer.hpp
+        ${HEADER_DIR}/internal/catch_tostring.h
+        ${HEADER_DIR}/internal/catch_tostring.hpp
+        ${HEADER_DIR}/internal/catch_totals.hpp
+        ${HEADER_DIR}/internal/catch_type_traits.hpp
+        ${HEADER_DIR}/internal/catch_version.h
+        ${HEADER_DIR}/internal/catch_version.hpp
+        ${HEADER_DIR}/internal/catch_wildcard_pattern.hpp
+        ${HEADER_DIR}/internal/catch_windows_h_proxy.h
+        ${HEADER_DIR}/internal/catch_xmlwriter.hpp
+        )
+CheckFileList(INTERNAL_HEADERS ${HEADER_DIR}/internal)
+
+# Please keep these ordered alphabetically
+set(REPORTER_HEADERS
+        ${HEADER_DIR}/reporters/catch_reporter_automake.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_bases.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_compact.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_console.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_junit.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_multi.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_tap.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp
+        ${HEADER_DIR}/reporters/catch_reporter_xml.hpp
+        )
+CheckFileList(REPORTER_HEADERS ${HEADER_DIR}/reporters)
+
+# Specify the headers, too, so CLion recognises them as project files
+set(HEADERS
+        ${TOP_LEVEL_HEADERS}
+        ${EXTERNAL_HEADERS}
+        ${INTERNAL_HEADERS}
+        ${REPORTER_HEADERS}
+        )
+
+
+set(BENCH_SOURCES
+        ${BENCHMARK_DIR}/BenchMain.cpp
+        ${BENCHMARK_DIR}/StringificationBench.cpp
+        )
+CheckFileList(BENCH_SOURCES ${BENCHMARK_DIR})
+
+# Provide some groupings for IDEs
+SOURCE_GROUP("Tests" FILES ${TEST_SOURCES})
+SOURCE_GROUP("Surrogates" FILES ${IMPL_SOURCES})
+SOURCE_GROUP("Benchmarks" FILES ${BENCH_SOURCES})
+
+# configure the executable
+include_directories(${HEADER_DIR})
+
+# Projects consuming Catch via ExternalProject_Add might want to use install step
+# without building all of our selftests.
+if (NOT NO_SELFTEST)
+    add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${HEADERS})
+    add_executable(Benchmark ${BENCH_SOURCES} ${HEADERS})
+
+    # Add desired warnings
+    if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
+        target_compile_options( SelfTest PRIVATE -Wall -Wextra )
+        target_compile_options( Benchmark PRIVATE -Wall -Wextra )
+    endif()
+    if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
+        target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX )
+        target_compile_options( Benchmark PRIVATE /W4 )
+    endif()
+
+
+    # configure unit tests via CTest
+    enable_testing()
+    add_test(NAME RunTests COMMAND SelfTest)
+
+    add_test(NAME ListTests COMMAND SelfTest --list-tests)
+    set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases")
+
+    add_test(NAME ListTags COMMAND SelfTest --list-tags)
+    set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags")
+
+endif() # !NO_SELFTEST
+
+
+install(DIRECTORY "single_include/" DESTINATION "include/catch")

+ 46 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/CODE_OF_CONDUCT.md

@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at github@philnash.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/

+ 23 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/LICENSE.txt

@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.

+ 61 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/appveyor.yml

@@ -0,0 +1,61 @@
+# version string format -- This will be overwritten later anyway
+version: "{build}"
+
+os:
+  - Visual Studio 2017
+  - Visual Studio 2015
+  - Visual Studio 2013
+
+environment:
+    matrix:
+        - additional_flags: "/permissive- /std:c++latest"
+          wmain: 0
+
+        - additional_flags: ""
+          wmain: 0
+
+        - additional_flags: "/D_UNICODE /DUNICODE"
+          wmain: 1
+
+matrix:
+    exclude:
+        -
+            additional_flags: "/permissive- /std:c++latest"
+            os: Visual Studio 2015
+        -
+            additional_flags: "/permissive- /std:c++latest"
+            os: Visual Studio 2013
+
+init:
+  - git config --global core.autocrlf input
+  # Set build version to git commit-hash
+  - ps: Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_BRANCH) - $($env:APPVEYOR_REPO_COMMIT)"
+
+# fetch repository as zip archive
+shallow_clone: true
+
+# Win32 and x64 are CMake-compatible solution platform names.
+# This allows us to pass %PLATFORM% to CMake -A.
+platform:
+  - Win32
+  - x64
+
+# build Configurations, i.e. Debug, Release, etc.
+configuration:
+  - Debug
+  - Release
+
+#Cmake will autodetect the compiler, but we set the arch
+before_build:
+  - set CXXFLAGS=%additional_flags%
+  - cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain%
+
+# build with MSBuild
+build:
+  project: Build\CatchSelfTest.sln      # path to Visual Studio solution or project
+  parallel: true                        # enable MSBuild parallel builds
+  verbosity: normal                     # MSBuild verbosity level {quiet|minimal|normal|detailed}
+
+test_script:
+  - cd Build
+  - ctest -V -j 2 -C %CONFIGURATION%

BIN
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/catch-hand-icon.png


BIN
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/catch-icon-tiny.png


BIN
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/catch-logo-small.png


+ 19 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/conanfile.py

@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+from conans import ConanFile
+
+
+class CatchConan(ConanFile):
+    name = "Catch"
+    version = "1.12.2"
+    description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
+    author = "philsquared"
+    generators = "cmake"
+    exports_sources = "single_include/*"
+    url = "https://github.com/philsquared/Catch"
+    license = "Boost Software License - Version 1.0. http://www.boost.org/LICENSE_1_0.txt"
+
+    def package(self):
+        self.copy(pattern="catch.hpp", src="single_include", dst="include")
+
+    def package_id(self):
+            self.info.header_only()

+ 185 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/contrib/ParseAndAddCatchTests.cmake

@@ -0,0 +1,185 @@
+#==================================================================================================#
+#  supported macros                                                                                #
+#    - TEST_CASE,                                                                                  #
+#    - SCENARIO,                                                                                   #
+#    - TEST_CASE_METHOD,                                                                           #
+#    - CATCH_TEST_CASE,                                                                            #
+#    - CATCH_SCENARIO,                                                                             #
+#    - CATCH_TEST_CASE_METHOD.                                                                     #
+#                                                                                                  #
+#  Usage                                                                                           #
+# 1. make sure this module is in the path or add this otherwise:                                   #
+#    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/")              #
+# 2. make sure that you've enabled testing option for the project by the call:                     #
+#    enable_testing()                                                                              #
+# 3. add the lines to the script for testing target (sample CMakeLists.txt):                       #
+#        project(testing_target)                                                                   #
+#        set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/")          #
+#        enable_testing()                                                                          #
+#                                                                                                  #
+#        find_path(CATCH_INCLUDE_DIR "catch.hpp")                                                  #
+#        include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR})                          #
+#                                                                                                  #
+#        file(GLOB SOURCE_FILES "*.cpp")                                                           #
+#        add_executable(${PROJECT_NAME} ${SOURCE_FILES})                                           #
+#                                                                                                  #
+#        include(ParseAndAddCatchTests)                                                            #
+#        ParseAndAddCatchTests(${PROJECT_NAME})                                                    #
+#                                                                                                  #
+# The following variables affect the behavior of the script:                                       #
+#                                                                                                  #
+#    PARSE_CATCH_TESTS_VERBOSE (Default OFF)                                                       #
+#    -- enables debug messages                                                                     #
+#    PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF)                                               #
+#    -- excludes tests marked with [!hide], [.] or [.foo] tags                                     #
+#    PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON)                                       #
+#    -- adds fixture class name to the test name                                                   #
+#    PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON)                                        #
+#    -- adds cmake target name to the test name                                                    #
+#    PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF)                                      #
+#    -- causes CMake to rerun when file with tests changes so that new tests will be discovered    #
+#                                                                                                  #
+#==================================================================================================#
+
+cmake_minimum_required(VERSION 2.8.8)
+
+option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF)
+option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF)
+option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON)
+option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON)
+option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF)
+
+function(PrintDebugMessage)
+    if(PARSE_CATCH_TESTS_VERBOSE)
+            message(STATUS "ParseAndAddCatchTests: ${ARGV}")
+    endif()
+endfunction()
+
+# This removes the contents between
+#  - block comments (i.e. /* ... */)
+#  - full line comments (i.e. // ... )
+# contents have been read into '${CppCode}'.
+# !keep partial line comments
+function(RemoveComments CppCode)
+  string(ASCII 2 CMakeBeginBlockComment)
+  string(ASCII 3 CMakeEndBlockComment)
+  string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
+  string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}")
+  string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}")
+  string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}")
+
+  set(${CppCode} "${${CppCode}}" PARENT_SCOPE)
+endfunction()
+
+# Worker function
+function(ParseFile SourceFile TestTarget)
+    # According to CMake docs EXISTS behavior is well-defined only for full paths.
+    get_filename_component(SourceFile ${SourceFile} ABSOLUTE)
+    if(NOT EXISTS ${SourceFile})
+        message(WARNING "Cannot find source file: ${SourceFile}")
+        return()
+    endif()
+    PrintDebugMessage("parsing ${SourceFile}")
+    file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
+
+    # Remove block and fullline comments
+    RemoveComments(Contents)
+
+    # Find definition of test names
+    string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}")
+
+    if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests)
+      PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property")
+      set_property(
+        DIRECTORY
+        APPEND
+        PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile}
+      )
+    endif()
+
+    foreach(TestName ${Tests})
+        # Strip newlines
+        string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}")
+
+        # Get test type and fixture if applicable
+        string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}")
+        string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}")
+        string(REPLACE "${TestType}(" "" TestFixture "${TestTypeAndFixture}")
+
+        # Get string parts of test definition
+        string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}")
+
+        # Strip wrapping quotation marks
+        string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}")
+        string(REPLACE "\";\"" ";" TestStrings "${TestStrings}")
+
+        # Validate that a test name and tags have been provided
+        list(LENGTH TestStrings TestStringsLength)
+        if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1)
+            message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}")
+        endif()
+
+        # Assign name and tags
+        list(GET TestStrings 0 Name)
+        if("${TestType}" STREQUAL "SCENARIO")
+            set(Name "Scenario: ${Name}")
+        endif()
+        if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture)
+            set(CTestName "${TestFixture}:${Name}")
+        else()
+            set(CTestName "${Name}")
+        endif()
+        if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME)
+            set(CTestName "${TestTarget}:${CTestName}")
+        endif()
+        # add target to labels to enable running all tests added from this target
+        set(Labels ${TestTarget})
+        if(TestStringsLength EQUAL 2)
+            list(GET TestStrings 1 Tags)
+            string(TOLOWER "${Tags}" Tags)
+            # remove target from labels if the test is hidden
+            if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*")
+                list(REMOVE_ITEM Labels ${TestTarget})
+            endif()
+            string(REPLACE "]" ";" Tags "${Tags}")
+            string(REPLACE "[" "" Tags "${Tags}")
+        endif()
+
+        list(APPEND Labels ${Tags})
+
+        list(FIND Labels "!hide" IndexOfHideLabel)
+        set(HiddenTagFound OFF)
+        foreach(label ${Labels})
+            string(REGEX MATCH "^!hide|^\\." result ${label})
+            if(result)
+                set(HiddenTagFound ON)
+                break()
+            endif(result)
+        endforeach(label)
+        if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound})
+            PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label")
+        else()
+            PrintDebugMessage("Adding test \"${CTestName}\"")
+            if(Labels)
+                PrintDebugMessage("Setting labels to ${Labels}")
+            endif()
+
+            # Add the test and set its properties
+            add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters})
+            set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
+                                                    LABELS "${Labels}")
+        endif()
+
+    endforeach()
+endfunction()
+
+# entry point
+function(ParseAndAddCatchTests TestTarget)
+    PrintDebugMessage("Started parsing ${TestTarget}")
+    get_target_property(SourceFiles ${TestTarget} SOURCES)
+    PrintDebugMessage("Found the following sources: ${SourceFiles}")
+    foreach(SourceFile ${SourceFiles})
+        ParseFile(${SourceFile} ${TestTarget})
+    endforeach()
+    PrintDebugMessage("Finished parsing ${TestTarget}")
+endfunction()

+ 141 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/assertions.md

@@ -0,0 +1,141 @@
+# Assertion Macros
+
+Most test frameworks have a large collection of assertion macros to capture all possible conditional forms (```_EQUALS```, ```_NOTEQUALS```, ```_GREATER_THAN``` etc).
+
+Catch is different. Because it decomposes natural C-style conditional expressions most of these forms are reduced to one or two that you will use all the time. That said there are a rich set of auxilliary macros as well. We'll describe all of these here.
+
+Most of these macros come in two forms:
+
+## Natural Expressions
+
+The ```REQUIRE``` family of macros tests an expression and aborts the test case if it fails.
+The ```CHECK``` family are equivalent but execution continues in the same test case even if the assertion fails. This is useful if you have a series of essentially orthogonal assertions and it is useful to see all the results rather than stopping at the first failure.
+
+* **REQUIRE(** _expression_ **)** and  
+* **CHECK(** _expression_ **)**
+
+Evaluates the expression and records the result. If an exception is thrown it is caught, reported, and counted as a failure. These are the macros you will use most of  the time
+
+Examples:
+```
+CHECK( str == "string value" );
+CHECK( thisReturnsTrue() );
+REQUIRE( i == 42 );
+```
+
+* **REQUIRE_FALSE(** _expression_ **)** and  
+* **CHECK_FALSE(** _expression_ **)**
+
+Evaluates the expression and records the _logical NOT_ of the result. If an exception is thrown it is caught, reported, and counted as a failure.
+(these forms exist as a workaround for the fact that ! prefixed expressions cannot be decomposed).
+
+Example:
+```
+REQUIRE_FALSE( thisReturnsFalse() );
+```
+
+Do note that "overly complex" expressions cannot be decomposed and thus will not compile. This is done partly for practical reasons (to keep the underlying expression template machinery to minimum) and partly for philosophical reasons (assertions should be simple and deterministic).
+
+Examples:
+* `CHECK(a == 1 && b == 2);`
+This expression is too complex because of the `&&` operator. If you want to check that 2 or more properties hold, you can either put the expression into parenthesis, which stops decomposition from working, or you need to decompose the expression into two assertions: `CHECK( a == 1 ); CHECK( b == 2);`
+* `CHECK( a == 2 || b == 1 );`
+This expression is too complex because of the `||` operator. If you want to check that one of several properties hold, you can put the expression into parenthesis (unlike with `&&`, expression decomposition into several `CHECK`s is not possible).
+
+
+### Floating point comparisons
+
+When comparing floating point numbers - especially if at least one of them has been computed - great care must be taken to allow for rounding errors and inexact representations.
+
+Catch provides a way to perform tolerant comparisons of floating point values through use of a wrapper class called ```Approx```. ```Approx``` can be used on either side of a comparison expression. It overloads the comparisons operators to take a tolerance into account. Here's a simple example:
+
+```
+REQUIRE( performComputation() == Approx( 2.1 ) );
+```
+
+This way `Approx` is constructed with reasonable defaults, covering most simple cases of rounding errors. If these are insufficient, each `Approx` instance has 3 tuning knobs, that can be used to customize it for your computation.
+
+* __epsilon__ - epsilon serves to set the percentage by which a result can be erroneous, before it is rejected. By default set to `std::numeric_limits<float>::epsilon()*100`.
+* __margin__ - margin serves to set the the absolute value by which a result can be erroneous before it is rejected. By default set to `0.0`.
+* __scale__ - scale serves to adjust the base for comparison used by epsilon. By default set to `1.0`.
+
+#### epsilon example
+```cpp
+Approx target = Approx(100).epsilon(0.01);
+100.0 == target; // Obviously true
+200.0 == target; // Obviously still false
+100.5 == target; // True, because we set target to allow up to 1% error
+```
+
+#### margin example
+_Margin check is used only if the relative (epsilon and scale based) check fails._
+```cpp
+Approx target = Approx(100).margin(5);
+100.0 == target; // Obviously true
+200.0 == target; // Obviously still false
+104.0 == target; // True, because we set target to allow absolute error up to 5
+```
+
+#### scale
+Scale can be useful if the computation leading to the result worked on different scale, than is used by the results (and thus expected errors are on a different scale than would be expected based on the results alone), i.e. an additional not scaling difference will be accepted, because | current - target | < eps_rel + eps_abs (while eps_rel = max(current, target) * epsilon, eps_abs = epsilon * scale) will be checked.
+
+
+## Exceptions
+
+* **REQUIRE_NOTHROW(** _expression_ **)** and  
+* **CHECK_NOTHROW(** _expression_ **)**
+
+Expects that no exception is thrown during evaluation of the expression.
+
+* **REQUIRE_THROWS(** _expression_ **)** and  
+* **CHECK_THROWS(** _expression_ **)**
+
+Expects that an exception (of any type) is be thrown during evaluation of the expression.
+
+* **REQUIRE_THROWS_AS(** _expression_, _exception type_ **)** and  
+* **CHECK_THROWS_AS(** _expression_, _exception type_ **)**
+
+Expects that an exception of the _specified type_ is thrown during evaluation of the expression. Note that the _exception type_ is used verbatim and you should include (const) reference.
+
+* **REQUIRE_THROWS_WITH(** _expression_, _string or string matcher_ **)** and  
+* **CHECK_THROWS_WITH(** _expression_, _string or string matcher_ **)**
+
+Expects that an exception is thrown that, when converted to a string, matches the _string_ or _string matcher_ provided (see next section for Matchers).
+
+e.g.
+```cpp
+REQUIRE_THROWS_WITH( openThePodBayDoors(), Contains( "afraid" ) && Contains( "can't do that" ) );
+REQUIRE_THROWS_WITH( dismantleHal(), "My mind is going" );
+```
+
+
+Please note that the `THROW` family of assertions expects to be passed a single expression, not a statement or series of statements. If you want to check a more complicated sequence of operations, you can use a C++11 lambda function.
+
+```cpp
+REQUIRE_NOTHROW([&](){
+    int i = 1;
+    int j = 2;
+    auto k = i + j;
+    if (k == 3) {
+        throw 1;
+    }
+}());
+```
+
+## Matcher expressions
+
+To support Matchers a slightly different form is used. Matchers have [their own documentation](matchers.md).
+
+* **REQUIRE_THAT(** _lhs_, _matcher expression_ **)** and  
+* **CHECK_THAT(** _lhs_, _matcher expression_ **)**  
+
+Matchers can be composed using `&&`, `||` and `!` operators.
+
+## Thread Safety
+
+Currently assertions in Catch are not thread safe.
+For more details, along with workarounds, see the section on [the limitations page](limitations.md#thread-safe-assertions).
+
+---
+
+[Home](Readme.md)

+ 143 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/build-systems.md

@@ -0,0 +1,143 @@
+# CI and build system integration
+
+Build Systems may refer to low-level tools, like CMake, or larger systems that run on servers, like Jenkins or TeamCity. This page will talk about both.
+
+# Continuous Integration systems
+
+Probably the most important aspect to using Catch with a build server is the use of different reporters. Catch comes bundled with three reporters that should cover the majority of build servers out there - although adding more for better integration with some is always a possibility (currently we also offer TeamCity, TAP and Automake reporters).
+
+Two of these reporters are built in (XML and JUnit) and the third (TeamCity) is included as a separate header. It's possible that the other two may be split out in the future too - as that would make the core of Catch smaller for those that don't need them.
+
+## XML Reporter
+```-r xml``` 
+
+The XML Reporter writes in an XML format that is specific to Catch. 
+
+The advantage of this format is that it corresponds well to the way Catch works (especially the more unusual features, such as nested sections) and is a fully streaming format - that is it writes output as it goes, without having to store up all its results before it can start writing.
+
+The disadvantage is that, being specific to Catch, no existing build servers understand the format natively. It can be used as input to an XSLT transformation that could convert it to, say, HTML - although this loses the streaming advantage, of course.
+
+## JUnit Reporter
+```-r junit```
+
+The JUnit Reporter writes in an XML format that mimics the JUnit ANT schema.
+
+The advantage of this format is that the JUnit Ant schema is widely understood by most build servers and so can usually be consumed with no additional work.
+
+The disadvantage is that this schema was designed to correspond to how JUnit works - and there is a significant mismatch with how Catch works. Additionally the format is not streamable (because opening elements hold counts of failed and passing tests as attributes) - so the whole test run must complete before it can be written.
+
+## Other reporters
+Other reporters are not part of the single-header distribution and need to be downloaded and included separately. All reporters are stored in `include/reporters` directory in the git repository, and are named `catch_reporter_*.hpp`. For example, to use the TeamCity reporter you need to download `include/reporters/catch_reporter_teamcity.hpp` and include it after Catch itself.
+
+```
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+#include "catch_reporter_teamcity.hpp"
+```
+
+### TeamCity Reporter
+```-r teamcity```
+
+The TeamCity Reporter writes TeamCity service messages to stdout. In order to be able to use this reporter an additional header must also be included.
+
+Being specific to TeamCity this is the best reporter to use with it - but it is completely unsuitable for any other purpose. It is a streaming format (it writes as it goes) - although test results don't appear in the TeamCity interface until the completion of a suite (usually the whole test run).
+
+### Automake Reporter
+```-r automake```
+
+The Automake Reporter writes out the [meta tags](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html#Log-files-generation-and-test-results-recording) expected by automake via `make check`.
+
+### TAP (Test Anything Protocol) Reporter
+```-r tap```
+
+Because of the incremental nature of Catch's test suites and ability to run specific tests, our implementation of TAP reporter writes out the number of tests in a suite last.
+
+# Low-level tools
+
+## CMake
+
+In general we recommend "vendoring" Catch's single-include releases inside your own repository. If you do this, the following example shows a minimal CMake project:
+```CMake
+cmake_minimum_required(VERSION 3.0)
+
+project(cmake_test)
+
+# Prepare "Catch" library for other executables
+set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/catch)
+add_library(Catch INTERFACE)
+target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
+
+# Make test executable
+set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
+add_executable(tests ${TEST_SOURCES})
+target_link_libraries(tests Catch)
+```
+Note that it assumes that the path to the Catch's header is `catch/catch.hpp` from the `CMakeLists.txt` file.
+
+
+You can also use the following CMake snippet to automatically fetch the entire Catch repository from github and configure it as an external project:
+```CMake
+cmake_minimum_required(VERSION 2.8.8)
+project(catch_builder CXX)
+include(ExternalProject)
+find_package(Git REQUIRED)
+
+ExternalProject_Add(
+    catch
+    PREFIX ${CMAKE_BINARY_DIR}/catch
+    GIT_REPOSITORY https://github.com/philsquared/Catch.git
+    TIMEOUT 10
+    UPDATE_COMMAND ${GIT_EXECUTABLE} pull
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND ""
+    INSTALL_COMMAND ""
+    LOG_DOWNLOAD ON
+   )
+
+# Expose required variable (CATCH_INCLUDE_DIR) to parent scope
+ExternalProject_Get_Property(catch source_dir)
+set(CATCH_INCLUDE_DIR ${source_dir}/single_include CACHE INTERNAL "Path to include folder for Catch")
+```
+
+If you put it in, e.g., `${PROJECT_SRC_DIR}/${EXT_PROJECTS_DIR}/catch/`, you can use it in your project by adding the following to your root CMake file:
+
+```CMake
+# Includes Catch in the project:
+add_subdirectory(${EXT_PROJECTS_DIR}/catch)
+include_directories(${CATCH_INCLUDE_DIR} ${COMMON_INCLUDES})
+enable_testing(true)  # Enables unit-testing.
+```
+
+The advantage of this approach is that you can always automatically update Catch to the latest release. The disadvantage is that it means bringing in lot more than you need.
+
+
+### Automatic test registration
+If you are also using ctest, `contrib/ParseAndAddCatchTests.cmake` is a CMake script that attempts to parse your test files and automatically register all test cases, using tags as labels. This means that these
+```cpp
+TEST_CASE("Test1", "[unit]") {
+    int a = 1;
+    int b = 2;
+    REQUIRE(a == b);
+}
+
+TEST_CASE("Test2") {
+    int a = 1;
+    int b = 2;
+    REQUIRE(a == b);
+}
+
+TEST_CASE("Test3", "[a][b][c]") {
+    int a = 1;
+    int b = 2;
+    REQUIRE(a == b);
+}
+```
+would be registered as 3 tests, `Test1`, `Test2` and `Test3`, and ctest 4 labels would be created, `a`, `b`, `c` and `unit`.
+
+### CodeCoverage module (GCOV, LCOV...)
+
+If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/fkromer/catch_cmake_coverage
+
+---
+
+[Home](Readme.md)

+ 293 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/command-line.md

@@ -0,0 +1,293 @@
+Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available.
+Click one of the followings links to take you straight to that option - or scroll on to browse the available options.
+
+<a href="#specifying-which-tests-to-run">               `    <test-spec> ...`</a><br />
+<a href="#usage">                                       `    -h, -?, --help`</a><br />
+<a href="#listing-available-tests-tags-or-reporters">   `    -l, --list-tests`</a><br />
+<a href="#listing-available-tests-tags-or-reporters">   `    -t, --list-tags`</a><br />
+<a href="#showing-results-for-successful-tests">        `    -s, --success`</a><br />
+<a href="#breaking-into-the-debugger">                  `    -b, --break`</a><br />
+<a href="#eliding-assertions-expected-to-throw">        `    -e, --nothrow`</a><br />
+<a href="#invisibles">                                  `    -i, --invisibles`</a><br />
+<a href="#sending-output-to-a-file">                    `    -o, --out`</a><br />
+<a href="#choosing-a-reporter-to-use">                  `    -r, --reporter`</a><br />
+<a href="#naming-a-test-run">                           `    -n, --name`</a><br />
+<a href="#aborting-after-a-certain-number-of-failures"> `    -a, --abort`</a><br />
+<a href="#aborting-after-a-certain-number-of-failures"> `    -x, --abortx`</a><br />
+<a href="#warnings">                                    `    -w, --warn`</a><br />
+<a href="#reporting-timings">                           `    -d, --durations`</a><br />
+<a href="#input-file">                                  `    -f, --input-file`</a><br />
+<a href="#run-section">                                 `    -c, --section`</a><br />
+<a href="#filenames-as-tags">                           `    -#, --filenames-as-tags`</a><br />
+
+
+</br>
+
+<a href="#list-test-names-only">                        `    --list-test-names-only`</a><br />
+<a href="#listing-available-tests-tags-or-reporters">   `    --list-reporters`</a><br />
+<a href="#order">                                       `    --order`</a><br />
+<a href="#rng-seed">                                    `    --rng-seed`</a><br />
+<a href="#libidentify">                                 `    --libidentify`</a><br />
+<a href="#wait-for-keypress">                           `    --wait-for-keypress`</a><br />
+
+</br>
+
+
+
+<a id="specifying-which-tests-to-run"></a>
+## Specifying which tests to run
+
+<pre>&lt;test-spec> ...</pre>
+
+Test cases, wildcarded test cases, tags and tag expressions are all passed directly as arguments. Tags are distinguished by being enclosed in square brackets.
+
+If no test specs are supplied then all test cases, except "hidden" tests, are run.
+A test is hidden by giving it any tag starting with (or just) a period (```.```) - or, in the deprecated case, tagged ```[hide]``` or given name starting with `'./'`. To specify hidden tests from the command line ```[.]``` or ```[hide]``` can be used *regardless of how they were declared*.
+
+Specs must be enclosed in quotes if they contain spaces. If they do not contain spaces the quotes are optional.
+
+Wildcards consist of the `*` character at the beginning and/or end of test case names and can substitute for any number of any characters (including none).
+
+Test specs are case insensitive.
+
+If a spec is prefixed with `exclude:` or the `~` character then the pattern matches an exclusion. This means that tests matching the pattern are excluded from the set - even if a prior inclusion spec included them. Subsequent inclusion specs will take precendence, however.
+Inclusions and exclusions are evaluated in left-to-right order.
+
+Test case examples:
+
+<pre>thisTestOnly            Matches the test case called, 'thisTestOnly'
+"this test only"        Matches the test case called, 'this test only'
+these*                  Matches all cases starting with 'these'
+exclude:notThis         Matches all tests except, 'notThis'
+~notThis                Matches all tests except, 'notThis'
+~*private*              Matches all tests except those that contain 'private'
+a* ~ab* abc             Matches all tests that start with 'a', except those that
+                        start with 'ab', except 'abc', which is included
+</pre>
+
+Names within square brackets are interpreted as tags.
+A series of tags form an AND expression wheras a comma-separated sequence forms an OR expression. e.g.:
+
+<pre>[one][two],[three]</pre>
+This matches all tests tagged `[one]` and `[two]`, as well as all tests tagged `[three]`
+
+Test names containing special characters, such as `,` or `[` can specify them on the command line using `\`.
+`\` also escapes itself.
+
+<a id="choosing-a-reporter-to-use"></a>
+## Choosing a reporter to use
+
+<pre>-r, --reporter &lt;reporter></pre>
+
+A reporter is an object that formats and structures the output of running tests, and potentially summarises the results. By default a console reporter is used that writes, IDE friendly, textual output. Catch comes bundled with some alternative reporters, but more can be added in client code.<br />
+The bundled reporters are:
+
+<pre>-r console
+-r compact
+-r xml
+-r junit
+</pre>
+
+The JUnit reporter is an xml format that follows the structure of the JUnit XML Report ANT task, as consumed by a number of third-party tools, including Continuous Integration servers such as Hudson. If not otherwise needed, the standard XML reporter is preferred as this is a streaming reporter, whereas the Junit reporter needs to hold all its results until the end so it can write the overall results into attributes of the root node.
+
+<a id="breaking-into-the-debugger"></a>
+## Breaking into the debugger
+<pre>-b, --break</pre>
+
+In some IDEs (currently XCode and Visual Studio) it is possible for Catch to break into the debugger on a test failure. This can be very helpful during debug sessions - especially when there is more than one path through a particular test.
+
+<a id="showing-results-for-successful-tests"></a>
+## Showing results for successful tests
+<pre>-s, --success</pre>
+
+Usually you only want to see reporting for failed tests. Sometimes it's useful to see *all* the output (especially when you don't trust that that test you just added worked first time!).
+To see successful, as well as failing, test results just pass this option. Note that each reporter may treat this option differently. The Junit reporter, for example, logs all results regardless.
+
+<a id="aborting-after-a-certain-number-of-failures"></a>
+## Aborting after a certain number of failures
+<pre>-a, --abort
+-x, --abortx [&lt;failure threshold>]
+</pre>
+
+If a ```REQUIRE``` assertion fails the test case aborts, but subsequent test cases are still run.
+If a ```CHECK``` assertion fails even the current test case is not aborted.
+
+Sometimes this results in a flood of failure messages and you'd rather just see the first few. Specifying ```-a``` or ```--abort``` on its own will abort the whole test run on the first failed assertion of any kind. Use ```-x``` or ```--abortx``` followed by a number to abort after that number of assertion failures.
+
+<a id="listing-available-tests-tags-or-reporters"></a>
+## Listing available tests, tags or reporters
+<pre>-l, --list-tests
+-t, --list-tags
+--list-reporters
+</pre>
+
+```-l``` or ```--list-tests``` will list all registered tests, along with any tags.
+If one or more test-specs have been supplied too then only the matching tests will be listed.
+
+```-t``` or ```--list-tags``` lists all available tags, along with the number of test cases they match. Again, supplying test specs limits the tags that match.
+
+```--list-reporters``` lists the available reporters.
+
+<a id="sending-output-to-a-file"></a>
+## Sending output to a file
+<pre>-o, --out &lt;filename>
+</pre>
+
+Use this option to send all output to a file. By default output is sent to stdout (note that uses of stdout and stderr *from within test cases* are redirected and included in the report - so even stderr will effectively end up on stdout).
+
+<a id="naming-a-test-run"></a>
+## Naming a test run
+<pre>-n, --name &lt;name for test run></pre>
+
+If a name is supplied it will be used by the reporter to provide an overall name for the test run. This can be useful if you are sending to a file, for example, and need to distinguish different test runs - either from different Catch executables or runs of the same executable with different options. If not supplied the name is defaulted to the name of the executable.
+
+<a id="eliding-assertions-expected-to-throw"></a>
+## Eliding assertions expected to throw
+<pre>-e, --nothrow</pre>
+
+Skips all assertions that test that an exception is thrown, e.g. ```REQUIRE_THROWS```.
+
+These can be a nuisance in certain debugging environments that may break when exceptions are thrown (while this is usually optional for handled exceptions, it can be useful to have enabled if you are trying to track down something unexpected).
+
+Sometimes exceptions are expected outside of one of the assertions that tests for them (perhaps thrown and caught within the code-under-test). The whole test case can be skipped when using ```-e``` by marking it with the ```[!throws]``` tag.
+
+When running with this option any throw checking assertions are skipped so as not to contribute additional noise. Be careful if this affects the behaviour of subsequent tests.
+
+<a id="invisibles"></a>
+## Make whitespace visible
+<pre>-i, --invisibles</pre>
+
+If a string comparison fails due to differences in whitespace - especially leading or trailing whitespace - it can be hard to see what's going on.
+This option transforms tabs and newline characters into ```\t``` and ```\n``` respectively when printing.
+
+<a id="warnings"></a>
+## Warnings
+<pre>-w, --warn &lt;warning name></pre>
+
+Enables reporting of warnings (only one, at time of this writing). If a warning is issued it fails the test.
+
+The ony available warning, presently, is ```NoAssertions```. This warning fails a test case, or (leaf) section if no assertions (```REQUIRE```/ ```CHECK``` etc) are encountered.
+
+<a id="reporting-timings"></a>
+## Reporting timings
+<pre>-d, --durations &lt;yes/no></pre>
+
+When set to ```yes``` Catch will report the duration of each test case, in milliseconds. Note that it does this regardless of whether a test case passes or fails. Note, also, the certain reporters (e.g. Junit) always report test case durations regardless of this option being set or not.
+
+<a id="input-file"></a>
+## Load test names to run from a file
+<pre>-f, --input-file &lt;filename></pre>
+
+Provide the name of a file that contains a list of test case names - one per line. Blank lines are skipped and anything after the comment character, ```#```, is ignored.
+
+A useful way to generate an initial instance of this file is to use the <a href="#list-test-names-only">list-test-names-only</a> option. This can then be manually curated to specify a specific subset of tests - or in a specific order.
+
+<a id="list-test-names-only"></a>
+## Just test names
+<pre>--list-test-names-only</pre>
+
+This option lists all available tests in a non-indented form, one on each line. This makes it ideal for saving to a file and feeding back into the <a href="#input-file">```-f``` or ```--input-file```</a> option.
+
+
+<a id="order"></a>
+## Specify the order test cases are run
+<pre>--order &lt;decl|lex|rand&gt;</pre>
+
+Test cases are ordered one of three ways:
+
+
+### decl
+Declaration order. The order the tests were originally declared in. Note that ordering between files is not guaranteed and is implementation dependent.
+
+### lex
+Lexicographically sorted. Tests are sorted, alpha-numerically, by name.
+
+### rand
+Randomly sorted. Test names are sorted using ```std::random_shuffle()```. By default the random number generator is seeded with 0 - and so the order is repeatable. To control the random seed see <a href="#rng-seed">rng-seed</a>.
+
+<a id="rng-seed"></a>
+## Specify a seed for the Random Number Generator
+<pre>--rng-seed &lt;'time'|number&gt;</pre>
+
+Sets a seed for the random number generator using ```std::srand()```. 
+If a number is provided this is used directly as the seed so the random pattern is repeatable.
+Alternatively if the keyword ```time``` is provided then the result of calling ```std::time(0)``` is used and so the pattern becomes unpredictable.
+
+In either case the actual value for the seed is printed as part of Catch's output so if an issue is discovered that is sensitive to test ordering the ordering can be reproduced - even if it was originally seeded from ```std::time(0)```.
+
+<a id="libidentify"></a>
+## Identify framework and version according to the libIdentify standard
+<pre>--libidentify</pre>
+
+See [The LibIdentify repo for more information and examples](https://github.com/janwilmans/LibIdentify).
+
+<a id="wait-for-keypress"></a>
+## Wait for key before continuing
+<pre>--wait-for-keypress &lt;start|exit|both&gt;</pre>
+
+Will cause the executable to print a message and wait until the return/ enter key is pressed before continuing -
+either before running any tests, after running all tests - or both, depending on the argument.
+
+
+<a id="usage"></a>
+## Usage
+<pre>-h, -?, --help</pre>
+
+Prints the command line arguments to stdout
+
+
+<a id="run-section"></a>
+## Specify the section to run
+<pre>-c, --section &lt;section name&gt;</pre>
+
+To limit execution to a specific section within a test case, use this option one or more times.
+To narrow to sub-sections use multiple instances, where each subsequent instance specifies a deeper nesting level.
+
+E.g. if you have:
+
+<pre>
+TEST_CASE( "Test" ) {
+  SECTION( "sa" ) {
+    SECTION( "sb" ) {
+      /*...*/
+    }
+    SECTION( "sc" ) {
+      /*...*/
+    }
+  }
+  SECTION( "sd" ) {
+    /*...*/
+  }
+}
+</pre>
+
+Then you can run `sb` with:
+<pre>./MyExe Test -c sa -c sb</pre>
+
+Or run just `sd` with:
+<pre>./MyExe Test -c sd</pre>
+
+To run all of `sa`, including `sb` and `sc` use:
+<pre>./MyExe Test -c sa</pre>
+
+There are some limitations of this feature to be aware of:
+- Code outside of sections being skipped will still be executed - e.g. any set-up code in the TEST_CASE before the
+start of the first section.</br>
+- At time of writing, wildcards are not supported in section names.
+- If you specify a section without narrowing to a test case first then all test cases will be executed 
+(but only matching sections within them).
+
+
+<a id="filenames-as-tags"></a>
+## Filenames as tags
+<pre>-#, --filenames-as-tags</pre>
+
+When this option is used then every test is given an additional tag which is formed of the unqualified 
+filename it is found in, with any extension stripped, prefixed with the `#` character.
+
+So, for example,  tests within the file `~\Dev\MyProject\Ferrets.cpp` would be tagged `[#Ferrets]`.
+
+
+---
+
+[Home](Readme.md)

+ 16 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/commercial-users.md

@@ -0,0 +1,16 @@
+# Commercial users of Catch
+
+As well as [Open Source](opensource-users.md) users Catch is widely used within proprietary code bases too.
+Many organisations like to keep this information internal, and that's fine, 
+but if you're more open it would be great if we could list the names of as
+many organisations as possible that use Catch somewhere in their codebase. 
+Enterprise environments often tend to be far more conservative in their tool adoption - 
+and being aware that other companies are using Catch can ease the path in.
+
+So if you are aware of Catch usage in your organisation, and are fairly confident there is no issue with sharing this
+fact then please let us know - either directly, via a PR or 
+[issue](https://github.com/philsquared/Catch/issues), or on the [forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum).
+ 
+ - Bloomberg
+ - NASA
+ - [Inscopix Inc.](https://www.inscopix.com/)

+ 140 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/configuration.md

@@ -0,0 +1,140 @@
+Catch is designed to "just work" as much as possible. For most people the only configuration needed is telling Catch which source file should host all the implementation code (```CATCH_CONFIG_MAIN```).
+
+Nonetheless there are still some occasions where finer control is needed. For these occasions Catch exposes a set of macros for configuring how it is built.
+
+#  main()/ implementation
+
+	CATCH_CONFIG_MAIN	// Designates this as implementation file and defines main()
+	CATCH_CONFIG_RUNNER	// Designates this as implementation file
+
+Although Catch is header only it still, internally, maintains a distinction between interface headers and headers that contain implementation. Only one source file in your test project should compile the implementation headers and this is controlled through the use of one of these macros - one of these identifiers should be defined before including Catch in *exactly one implementation file in your project*.
+
+#  Prefixing Catch macros
+
+	CATCH_CONFIG_PREFIX_ALL
+
+To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```).
+
+
+#  Terminal colour
+
+	CATCH_CONFIG_COLOUR_NONE	// completely disables all text colouring
+	CATCH_CONFIG_COLOUR_WINDOWS	// forces the Win32 console API to be used
+	CATCH_CONFIG_COLOUR_ANSI	// forces ANSI colour codes to be used
+
+Yes, I am English, so I will continue to spell "colour" with a 'u'.
+
+When sending output to the terminal, if it detects that it can, Catch will use colourised text. On Windows the Win32 API, ```SetConsoleTextAttribute```, is used. On POSIX systems ANSI colour escape codes are inserted into the stream.
+
+For finer control you can define one of the above identifiers (these are mutually exclusive - but that is not checked so may behave unexpectedly if you mix them):
+
+Note that when ANSI colour codes are used "unistd.h" must be includable - along with a definition of ```isatty()```
+
+Typically you should place the ```#define``` before #including "catch.hpp" in your main source file - but if you prefer you can define it for your whole project by whatever your IDE or build system provides for you to do so.
+
+#  Console width
+
+	CATCH_CONFIG_CONSOLE_WIDTH = x // where x is a number
+
+Catch formats output intended for the console to fit within a fixed number of characters. This is especially important as indentation is used extensively and uncontrolled line wraps break this.
+By default a console width of 80 is assumed but this can be controlled by defining the above identifier to be a different value.
+
+#  stdout
+
+	CATCH_CONFIG_NOSTDOUT
+
+Catch does not use ```std::cout```, ```std::cerr``` and ```std::clog``` directly but gets them from ```Catch::cout()```, ```Catch::cerr()``` and ```Catch::clog``` respectively. If the above identifier is defined these functions are left unimplemented and you must implement them yourself. Their signatures are:
+
+    std::ostream& cout();
+    std::ostream& cerr();
+    std::ostream& clog();
+
+This can be useful on certain platforms that do not provide the standard iostreams, such as certain embedded systems.
+
+# Default reporter
+
+    CATCH_CONFIG_DEFAULT_REPORTER <reporter>
+
+The default reporter (reporter used when no reporters are explicitly specified) can be overriden during compilation time by using the above macro. Note that desired value of the macro is a C string and quotes have to be escaped during compilation: `clang++ test.cpp -DCATCH_CONFIG_DEFAULT_REPORTER=\"xml\"`.
+
+# C++ conformance toggles
+
+    CATCH_CONFIG_CPP11_NULLPTR                 // nullptr is supported?
+    CATCH_CONFIG_CPP11_NOEXCEPT                // noexcept is supported?
+    CATCH_CONFIG_CPP11_GENERATED_METHODS       // delete and default keywords for methods
+    CATCH_CONFIG_CPP11_IS_ENUM                 // std::is_enum is supported?
+    CATCH_CONFIG_CPP11_TUPLE                   // std::tuple is supported
+    CATCH_CONFIG_VARIADIC_MACROS               // Usually pre-C++11 compiler extensions are sufficient
+    CATCH_CONFIG_CPP11_LONG_LONG               // generates overloads for the long long type
+    CATCH_CONFIG_CPP11_OVERRIDE                // CATCH_OVERRIDE expands to override (for virtual function implementations)
+    CATCH_CONFIG_CPP11_UNIQUE_PTR              // Use std::unique_ptr instead of std::auto_ptr
+    CATCH_CONFIG_CPP11_SHUFFLE                 // Use std::shuffle instead of std::random_shuffle
+    CATCH_CONFIG_CPP11_TYPE_TRAITS             // Use std::enable_if and <type_traits>
+    CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK // Use C++11 expression SFINAE to check if class can be inserted to std::ostream
+
+Catch has some basic compiler detection that will attempt to select the appropriate mix of these macros. However being incomplete - and often without access to the respective compilers - this detection tends to be conservative.
+So overriding control is given to the user. If a compiler supports a feature (and Catch does not already detect it) then one or more of these may be defined to enable it (or suppress it, in some cases). If you do do this please raise an issue, specifying your compiler version (ideally with an idea of how to detect it) and stating that it has such support.
+You may also suppress any of these features by using the `_NO_` form, e.g. `CATCH_CONFIG_CPP11_NO_NULLPTR`.
+
+All C++11 support can be disabled with `CATCH_CONFIG_NO_CPP11`
+
+## `CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK`
+
+This flag is off by default, but allows you to resolve problems caused by types with private base class that are streamable, but the classes themselves are not. Without it, the following code will cause a compilation error:
+```cpp
+#define CATCH_CONFIG_MAIN
+#include <catch.hpp>
+struct A {};
+std::ostream &operator<< (std::ostream &o, const A &v) { return o << 0; }
+
+struct B : private A {
+    bool operator==(int){ return true;}
+};
+
+B f ();
+std::ostream g ();
+
+TEST_CASE ("Error in streamable check") {
+    B x;
+    REQUIRE (x == 4);
+}
+```
+
+# Other toggles
+
+    CATCH_CONFIG_COUNTER                    // Use __COUNTER__ to generate unique names for test cases
+    CATCH_CONFIG_WINDOWS_SEH                // Enable SEH handling on Windows
+    CATCH_CONFIG_FAST_COMPILE               // Sacrifices some (rather minor) features for compilation speed
+    CATCH_CONFIG_POSIX_SIGNALS              // Enable handling POSIX signals
+    CATCH_CONFIG_WINDOWS_CRTDBG             // Enable leak checking using Windows's CRT Debug Heap
+    CATCH_CONFIG_DISABLE_STRINGIFICATION    // Disable stringifying the original expression
+
+Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
+
+`CATCH_CONFIG_POSIX_SIGNALS` is on by default, except when Catch is compiled under `Cygwin`, where it is disabled by default (but can be force-enabled by defining `CATCH_CONFIG_POSIX_SIGNALS`).
+
+`CATCH_CONFIG_WINDOWS_CRTDBG` is off by default. If enabled, Windows's CRT is used to check for memory leaks, and displays them after the tests finish running.
+
+Just as with the C++11 conformance toggles, these toggles can be disabled by using `_NO_` form of the toggle, e.g. `CATCH_CONFIG_NO_WINDOWS_SEH`.
+
+## `CATCH_CONFIG_FAST_COMPILE`
+Defining this flag speeds up compilation of test files by ~20%, by making 2 changes:
+* The `-b` (`--break`) flag no longer makes Catch break into debugger in the same stack frame as the failed test, but rather in a stack frame *below*.
+* The `REQUIRE` family of macros (`REQUIRE`, `REQUIRE_FALSE` and `REQUIRE_THAT`) no longer uses local try-catch block. This disables exception translation, but should not lead to false negatives.
+
+`CATCH_CONFIG_FAST_COMPILE` has to be either defined, or not defined, in all translation units that are linked into single test binary, or the behaviour of setting `-b` flag and throwing unexpected exceptions will be unpredictable.
+
+## `CATCH_CONFIG_DISABLE_STRINGIFICATION`
+This toggle enables a workaround for VS 2017 bug. For details see
+[known limitations](limitations.md#visual-studio-2017----raw-string-literal-in-assert-fails-to-compile).
+
+# Windows header clutter
+
+On Windows Catch includes `windows.h`. To minimize global namespace clutter in the implementation file, it defines `NOMINMAX` and `WIN32_LEAN_AND_MEAN` before including it. You can control this behaviour via two macros:
+
+    CATCH_CONFIG_NO_NOMINMAX            // Stops Catch from using NOMINMAX macro 
+    CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN // Stops Catch from using WIN32_LEAN_AND_MEAN macro
+
+---
+
+[Home](Readme.md)

+ 60 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/contributing.md

@@ -0,0 +1,60 @@
+# Contributing to Catch
+
+So you want to contribute something to Catch? That's great! Whether it's a bug fix, a new feature, support for 
+additional compilers - or just a fix to the documentation - all contributions are very welcome and very much appreciated. 
+Of course so are bug reports and other comments and questions.
+
+If you are contributing to the code base there are a few simple guidelines to keep in mind. This also includes notes to
+help you find your way around. As this is liable to drift out of date please raise an issue or, better still, a pull
+request for this file, if you notice that.
+
+## Branches
+
+Ongoing development is currently on _master_. At some point an integration branch will be set-up and PRs should target
+ that - but for now it's all against master. You may see feature branches come and go from time to time, too.
+
+## Directory structure
+
+_Users_ of Catch primarily use the single header version. _Maintainers_ should work with the full source (which is still, 
+primarily, in headers). This can be found in the `include` folder. There are a set of test files, currently under
+`projects/SelfTest`. The test app can be built via CMake from the `CMakeLists.txt` file in the root, or you can generate
+project files for Visual Studio, XCode, and others (instructions in the `projects` folder). If you have access to CLion,
+it can work with the CMake file directly.
+
+As well as the runtime test files you'll also see a `SurrogateCpps` directory under `projects/SelfTest`.
+This contains a set of .cpp files that each `#include` a single header.
+While these files are not essential to compilation they help to keep the implementation headers self-contained.
+At time of writing this set is not complete but has reasonable coverage.
+If you add additional headers please try to remember to add a surrogate cpp for it.
+
+The other directories are `scripts` which contains a set of python scripts to help in testing Catch as well as
+generating the single include, and `docs`, which contains the documentation as a set of markdown files.
+
+__When submitting a pull request please do not include changes to the single include, or to the version number file
+as these are managed by the scripts!__
+
+
+## Testing your changes
+
+Obviously all changes to Catch's code should be tested. If you added new functionality, you should add tests covering and
+showcasing it. Even if you have only made changes to Catch internals (ie you implemented some performance improvements),
+you should still test your changes.
+
+This means 3 things
+
+* Compiling Catch's SelfTest project -- code that does not compile is evidently incorrect. Obviously, you are not expected to
+have access to all compilers and platforms Catch supports, Catch's CI pipeline will compile your code using supported compilers
+once you open a PR.
+* Running the SelfTest binary. There should be no unexpected failures on simple run.
+* Running Catch's approval tests. Approval tests compare current output of the SelfTest binary in various configurations against
+known good output. Catch's repository provides utility scripts `approvalTests.py` to help you with this. It needs to be passed
+the SelfTest binary compiled with your changes, like so: `$ ./scripts/approvalTests.py clang-build/SelfTest`. The output should
+be fairly self-explanatory.
+
+
+
+ *this document is still in-progress...*
+
+---
+
+[Home](Readme.md)

+ 73 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/event-listeners.md

@@ -0,0 +1,73 @@
+# Event Listeners
+
+A `Listener` is a class you can register with Catch that will then be passed events,
+such as a test case starting or ending, as they happen during a test run.
+`Listeners` are actually types of `Reporters`, with a few small differences:
+ 
+1. Once registered in code they are automatically used - you don't need to specify them on the command line
+2. They are called in addition to (just before) any reporters, and you can register multiple listeners.
+3. They derive from `Catch::TestEventListenerBase`, which has default stubs for all the events,
+so you are not forced to implement events you're not interested in.
+4. You register a listener with `CATCH_REGISTER_LISTENER`
+
+
+## Implementing a Listener
+
+In your main source file (i.e. the one that has the `#define` for `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`),
+simply derive a class from `Catch::TestEventListenerBase` and implement the methods you are interested in.
+Then register it using `INTERNAL_CATCH_REGISTER_LISTENER`.
+
+For example:
+
+```c++
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+struct MyListener : Catch::TestEventListenerBase {
+
+    using TestEventListenerBase::TestEventListenerBase; // inherit constructor
+
+    virtual void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
+        // Perform some setup before a test case is run
+    }
+    
+    virtual void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
+        // Tear-down after a test case is run
+    }    
+};
+CATCH_REGISTER_LISTENER( MyListener )
+```
+
+_Note that you should not use any assertion macros within a Listener!_ 
+
+## Events that can be hooked
+
+The following are the methods that can be overriden in the Listener:
+
+```c++
+// The whole test run, starting and ending
+virtual void testRunStarting( TestRunInfo const& testRunInfo );
+virtual void testRunEnded( TestRunStats const& testRunStats );
+
+// Test cases starting and ending
+virtual void testCaseStarting( TestCaseInfo const& testInfo );
+virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+
+// Sections starting and ending
+virtual void sectionStarting( SectionInfo const& sectionInfo );
+virtual void sectionEnded( SectionStats const& sectionStats );
+
+// Assertions before/ after
+virtual void assertionStarting( AssertionInfo const& assertionInfo );
+virtual bool assertionEnded( AssertionStats const& assertionStats );
+
+// A test is being skipped (because it is "hidden")
+virtual void skipTest( TestCaseInfo const& testInfo );
+```
+
+More information about the events (e.g. name of the test case) is contained in the structs passed as arguments -
+just look in the source code to see what fields are available. 
+
+---
+
+[Home](Readme.md)

+ 131 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/limitations.md

@@ -0,0 +1,131 @@
+# Known limitations
+
+Catch has some known limitations, that we are not planning to change. Some of these are caused by our desire to support C++98 compilers, some of these are caused by our desire to keep Catch crossplatform, some exist because their priority is seen as low compared to the development effort they would need and some other yet are compiler/runtime bugs.
+
+## Features
+This section outlines some missing features, what is their status and their possible workarounds.
+
+### Thread safe assertions
+Because threading support in standard C++98 is limited (well, non-existent), assertion macros in Catch are not thread safe. This does not mean that you cannot use threads inside Catch's test, but that only single thread can interact with Catch's assertions and other macros.
+
+This means that this is ok
+```cpp
+    std::vector<std::thread> threads;
+    std::atomic<int> cnt{ 0 };
+    for (int i = 0; i < 4; ++i) {
+        threads.emplace_back([&]() {
+            ++cnt; ++cnt; ++cnt; ++cnt;
+        });
+    }
+    for (auto& t : threads) { t.join(); }
+    REQUIRE(cnt == 16);
+```
+because only one thread passes the `REQUIRE` macro and this is not
+```cpp
+    std::vector<std::thread> threads;
+    std::atomic<int> cnt{ 0 };
+    for (int i = 0; i < 4; ++i) {
+        threads.emplace_back([&]() {
+            ++cnt; ++cnt; ++cnt; ++cnt;
+            CHECK(cnt == 16);
+        });
+    }
+    for (auto& t : threads) { t.join(); }
+    REQUIRE(cnt == 16);
+```
+
+
+_This limitation is highly unlikely to be lifted before Catch 2 is released._
+
+### Process isolation in a test
+Catch does not support running tests in isolated (forked) processes. While this might in the future, the fact that Windows does not support forking and only allows full-on process creation and the desire to keep code as similar as possible across platforms, mean that this is likely to take significant development time, that is not currently available.
+
+### Running multiple tests in parallel
+Catch's test execution is strictly serial. If you find yourself with a test suite that takes too long to run and you want to make it parallel, there are 2 feasible solutions
+ * You can split your tests into multiple binaries and then run these binaries in parallel.
+ * You can have Catch list contained test cases and then run the same test binary multiple times in parallel, passing each instance list of test cases it should run.
+
+Both of these solutions have their problems, but should let you wring parallelism out of your test suite.
+
+## 3rd party bugs
+This section outlines known bugs in 3rd party components (this means compilers, standard libraries, standard runtimes).
+
+### Visual Studio 2017 -- raw string literal in assert fails to compile
+There is a known bug in Visual Studio 2017 (VC 15), that causes compilation error when preprocessor attempts to stringize a raw string literal (`#` preprocessor is applied to it). This snippet is sufficient to trigger the compilation error:
+```cpp
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+TEST_CASE("test") {
+    CHECK(std::string(R"("\)") == "\"\\");
+}
+```
+
+Catch provides a workaround, it is possible to disable stringification of original expressions by defining `CATCH_CONFIG_DISABLE_STRINGIFICATION`:
+```cpp
+#define CATCH_CONFIG_FAST_COMPILE
+#define CATCH_CONFIG_DISABLE_STRINGIFICATION
+#include "catch.hpp"
+
+TEST_CASE("test") {
+    CHECK(std::string(R"("\)") == "\"\\");
+}
+```
+
+_Do note that this changes the output somewhat_
+```
+catchwork\test1.cpp(6):
+PASSED:
+  CHECK( Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION )
+with expansion:
+  ""\" == ""\"
+```
+
+
+### Visual Studio 2013 -- do-while loop withing range based for fails to compile (C2059)
+There is a known bug in Visual Studio 2013 (VC 12), that causes compilation error if range based for is followed by an assertion macro, without enclosing the block in braces. This snippet is sufficient to trigger the error
+```cpp
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+TEST_CASE("Syntax error with VC12") {
+    for ( auto x : { 1 , 2, 3 } )
+        REQUIRE( x < 3.14 );
+}
+```
+An easy workaround is possible, use braces:
+```cpp
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+TEST_CASE("No longer a syntax error with VC12") {
+    for ( auto x : { 1 , 2, 3 } ) {
+        REQUIRE( x < 3.14 );
+    }
+}
+```
+
+### Visual Studio 2003 -- Syntax error caused by improperly expanded `__LINE__` macro
+Older version of Visual Studio can have trouble compiling Catch, not expanding the `__LINE__` macro properly when recompiling the test binary. This is caused by Edit and Continue being on.
+
+A workaround is to turn off Edit and Continue when compiling the test binary.
+
+### Clang/G++ -- skipping leaf sections after an exception
+Some versions of `libc++` and `libstdc++` (or their runtimes) have a bug with `std::uncaught_exception()` getting stuck returning `true` after rethrow, even if there are no active exceptions. One such case is this snippet, which skipped the sections "a" and "b", when compiled against `libcxxrt` from master
+```cpp
+#define CATCH_CONFIG_MAIN
+#include <catch.hpp>
+
+TEST_CASE("a") {
+    CHECK_THROWS(throw 3);
+}
+
+TEST_CASE("b") {
+    int i = 0;
+    SECTION("a") { i = 1; }
+    SECTION("b") { i = 2; }
+    CHECK(i > 0);
+}
+```
+
+If you are seeing a problem like this, i.e. a weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library.

+ 82 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/logging.md

@@ -0,0 +1,82 @@
+# Logging macros
+
+Additional messages can be logged during a test case. Note that the messages are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example:
+
+```cpp
+TEST_CASE("Foo") {
+    INFO("Test case start");
+    for (int i = 0; i < 2; ++i) {
+        INFO("The number is " << i);
+        CHECK(i == 0);
+    }
+}
+
+TEST_CASE("Bar") {
+    INFO("Test case start");
+    for (int i = 0; i < 2; ++i) {
+        INFO("The number is " << i);
+        CHECK(i == i);
+    }
+    CHECK(false);
+}
+```
+When the `CHECK` fails in the "Foo" test case, then two messages will be printed.
+```
+Test case start
+The number is 1
+```
+When the last `CHECK` fails in the "Bar" test case, then only one message will be printed: `Test case start`.
+
+
+## Streaming macros
+
+All these macros allow heterogenous sequences of values to be streaming using the insertion operator (```<<```) in the same way that std::ostream, std::cout, etc support it.
+
+E.g.:
+```c++
+INFO( "The number is " << i );
+```
+
+(Note that there is no initial ```<<``` - instead the insertion sequence is placed in parentheses.)
+These macros come in three forms:
+
+**INFO(** _message expression_ **)**
+
+The message is logged to a buffer, but only reported with the next assertion that is logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.
+
+**WARN(** _message expression_ **)**
+
+The message is always reported but does not fail the test.
+
+**FAIL(** _message expression_ **)**
+
+The message is reported and the test case fails.
+
+**FAIL_CHECK(** _message expression_ **)**
+
+AS `FAIL`, but does not abort the test
+
+## Quickly capture a variable value
+
+**CAPTURE(** _expression_ **)**
+
+Sometimes you just want to log the name and value of a variable. While you can easily do this with the INFO macro, above, as a convenience the CAPTURE macro handles the stringising of the variable name for you (actually it works with any expression, not just variables).
+
+E.g.
+```c++
+CAPTURE( theAnswer );
+```
+
+This would log something like:
+
+<pre>"theAnswer := 42"</pre>
+
+## Deprecated macros
+
+**SCOPED_INFO and SCOPED_CAPTURE**
+
+These macros are now deprecated and are just aliases for INFO and CAPTURE (which were not previously scoped).
+
+---
+
+[Home](Readme.md)

+ 104 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/matchers.md

@@ -0,0 +1,104 @@
+# Matchers
+
+Matchers are an alternative way to do assertions which are easily extensible and composable.
+This makes them well suited to use with more complex types (such as collections) or your own custom types.
+Matchers were first popularised by the [Hamcrest](https://en.wikipedia.org/wiki/Hamcrest) family of frameworks.
+
+## In use
+
+Matchers are introduced with the `REQUIRE_THAT` or `CHECK_THAT` macros, which take two arguments.
+The first argument is the thing (object or value) under test. The second part is a match _expression_,
+which consists of either a single matcher or one or more matchers combined using `&&`, `||` or `!` operators.
+
+For example, to assert that a string ends with a certain substring:
+ 
+ ```c++
+using Catch::Matchers::EndsWith; // or Catch::EndsWith
+std::string str = getStringFromSomewhere();
+REQUIRE_THAT( str, EndsWith( "as a service" ) ); 
+ ```
+
+The matcher objects can take multiple arguments, allowing more fine tuning.
+The built-in string matchers, for example, take a second argument specifying whether the comparison is
+case sensitive or not:
+
+```c++
+REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) ); 
+ ```
+
+And matchers can be combined:
+
+```c++
+REQUIRE_THAT( str, 
+    EndsWith( "as a service" ) || 
+    (StartsWith( "Big data" ) && !Contains( "web scale" ) ) ); 
+```
+
+## Built in matchers
+Currently Catch has some string matchers and some vector matchers. They are in the `Catch::Matchers` and `Catch` namespaces.
+The string matchers are `StartsWith`, `EndsWith`, `Contains` and `Equals`. Each of them also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive).
+The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector.
+
+
+## Custom matchers
+It's easy to provide your own matchers to extend Catch or just to work with your own types.
+
+You need to provide two things: 
+1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested.
+The constructor takes and stores any arguments needed (e.g. something to compare against) and you must
+override two methods: `match()` and `describe()`. 
+2. A simple builder function. This is what is actually called from the test code and allows overloading.
+
+Here's an example for asserting that an integer falls within a given range
+(note that it is all inline for the sake of keeping the example short):
+
+```c++
+// The matcher class
+class IntRange : public Catch::MatcherBase<int> {
+    int m_begin, m_end;
+public:
+    IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {}
+
+    // Performs the test for this matcher
+    virtual bool match( int const& i ) const override {
+        return i >= m_begin && i <= m_end;
+    }
+
+    // Produces a string describing what this matcher does. It should
+    // include any provided data (the begin/ end in this case) and
+    // be written as if it were stating a fact (in the output it will be
+    // preceded by the value under test).
+    virtual std::string describe() const {
+        std::ostringstream ss;
+        ss << "is between " << m_begin << " and " << m_end;
+        return ss.str();
+    }
+};
+
+// The builder function
+inline IntRange IsBetween( int begin, int end ) {
+    return IntRange( begin, end );
+}
+
+// ...
+
+// Usage
+TEST_CASE("Integers are within a range")
+{
+    CHECK_THAT( 3, IsBetween( 1, 10 ) );
+    CHECK_THAT( 100, IsBetween( 1, 10 ) );
+}
+```
+
+Running this test gives the following in the console:
+ 
+```
+/**/TestFile.cpp:123: FAILED:
+  CHECK_THAT( 100, IsBetween( 1, 10 ) )
+with expansion:
+  100 is between 1 and 10
+```
+
+---
+
+[Home](Readme.md)

+ 86 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/opensource-users.md

@@ -0,0 +1,86 @@
+# Open Source projects using Catch
+
+Catch is great for open source. With its [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution 
+it's easy to just drop the header into your project and start writing tests - what's not to like?
+
+As a result Catch is now being used in many Open Source projects, including some quite well known ones.
+This page is an attempt to track those projects. Obviously it can never be complete.
+This effort largely relies on the maintainers of the projects themselves updating this page and submitting a PR
+(or, if you prefer contact one of the maintainers of Catch directly, use the 
+[forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum)), or raise an [issue](https://github.com/philsquared/Catch/issues) to let us know).
+Of course users of those projects might want to update this page too. That's fine - as long you're confident the project maintainers won't mind.
+If you're an Open Source project maintainer and see your project listed here but would rather it wasn't - 
+just let us know via any of the previously mentioned means - although I'm sure there won't be many who feel that way.
+ 
+Listing a project here does not imply endorsement and the plan is to keep these ordered alphabetically to avoid an implication of relative importance.
+
+## Libraries & Frameworks
+
+### [Azmq](https://github.com/zeromq/azmq)
+Boost Asio style bindings for ZeroMQ
+
+### [ChakraCore](https://github.com/Microsoft/ChakraCore)
+The core part of the Chakra Javascript engine that powers Microsoft Edge
+
+### [ChaiScript](https://github.com/ChaiScript/ChaiScript)
+A, header-only, embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development techniques
+
+### [Clara](https://github.com/philsquared/Clara)
+A, single-header-only, type-safe, command line parser - which also prints formatted usage strings.
+
+### [Couchbase-lite-core](https://github.com/couchbase/couchbase-lite-core)
+The next-generation core storage and query engine for Couchbase Lite/
+
+### [JSON for Modern C++](https://github.com/nlohmann/json)
+A, single-header, JSON parsing library that takes advantage of what C++ has to offer.
+
+### [MNMLSTC Core](https://github.com/mnmlstc/core)
+a small and easy to use C++11 library that adds a functionality set that will be available in C++14 and later, as well as some useful additions
+
+### [nanodbc](https://github.com/lexicalunit/nanodbc/)
+A small C++ library wrapper for the native C ODBC API.
+
+### [Nonius](https://github.com/libnonius/nonius)
+A header-only framework for benchmarking small snippets of C++ code.
+
+### [SOCI](https://github.com/SOCI/soci)
+The C++ Database Access Library
+
+### [polymorphic_value](https://github.com/jbcoe/polymorphic_value)
+A polymorphic value-type for C++
+
+### [Ppconsul](https://github.com/oliora/ppconsul)
+A C++ client library for Consul. Consul is a distributed tool for discovering and configuring services in your infrastructure
+
+### [Reactive-Extensions/ RxCpp](https://github.com/Reactive-Extensions/RxCpp)
+A library of algorithms for values-distributed-in-time
+
+### [TextFlowCpp](https://github.com/philsquared/textflowcpp)
+A small, single-header-only, library for wrapping and composing columns of text
+
+### [Trompeloeil](https://github.com/rollbear/trompeloeil)
+A thread safe header only mocking framework for C++14
+
+### [args](https://github.com/Taywee/args)
+A simple header-only C++ argument parser library.
+
+## Applications & Tools
+
+### [ArangoDB](https://github.com/arangodb/arangodb)
+ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values.
+
+### [Giada - Your Hardcore Loop Machine](https://github.com/monocasual/giada)
+Minimal, open-source and cross-platform audio tool for live music production.
+
+### [MAME](https://github.com/mamedev/mame)
+MAME originally stood for Multiple Arcade Machine Emulator
+
+### [Newsbeuter](https://github.com/akrennmair/newsbeuter)
+Newsbeuter is an open-source RSS/Atom feed reader for text terminals.
+
+### [Standardese](https://github.com/foonathan/standardese)
+Standardese aims to be a nextgen Doxygen
+
+---
+
+[Home](Readme.md)

+ 72 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/own-main.md

@@ -0,0 +1,72 @@
+# Supplying main() yourself
+
+The easiest way to use Catch is to let it supply ```main()``` for you and handle configuring itself from the command line.
+
+This is achieved by writing ```#define CATCH_CONFIG_MAIN``` before the ```#include "catch.hpp"``` in *exactly one* source file.
+
+Sometimes, though, you need to write your own version of main(). You can do this by writing ```#define CATCH_CONFIG_RUNNER``` instead. Now you are free to write ```main()``` as normal and call into Catch yourself manually.
+
+You now have a lot of flexibility - but here are three recipes to get your started:
+
+## Let Catch take full control of args and config
+
+If you just need to have code that executes before and/ or after Catch this is the simplest option.
+
+```c++
+#define CATCH_CONFIG_RUNNER
+#include "catch.hpp"
+
+int main( int argc, char* argv[] )
+{
+  // global setup...
+
+  int result = Catch::Session().run( argc, argv );
+
+  // global clean-up...
+
+  return ( result < 0xff ? result : 0xff );
+}
+```
+
+## Amending the config
+
+If you still want Catch to process the command line, but you want to programatically tweak the config, you can do so in one of two ways:
+
+```c++
+#define CATCH_CONFIG_RUNNER
+#include "catch.hpp"
+
+int main( int argc, char* argv[] )
+{
+  Catch::Session session; // There must be exactly one instance
+
+  // writing to session.configData() here sets defaults
+  // this is the preferred way to set them
+
+  int returnCode = session.applyCommandLine( argc, argv );
+  if( returnCode != 0 ) // Indicates a command line error
+  	return returnCode;
+
+  // writing to session.configData() or session.Config() here 
+  // overrides command line args
+  // only do this if you know you need to
+
+  int numFailed = session.run();
+  // Note that on unices only the lower 8 bits are usually used, clamping
+  // the return value to 255 prevents false negative when some multiple
+  // of 256 tests has failed
+  return ( numFailed < 0xff ? numFailed : 0xff );
+}
+```
+
+Take a look at the definitions of Config and ConfigData to see what you can do with them.
+
+To take full control of the config simply omit the call to ```applyCommandLine()```.
+
+## Adding your own command line options
+
+Catch embeds a powerful command line parser which you can also use to parse your own options out. This capability is still in active development but will be documented here when it is ready.
+
+---
+
+[Home](Readme.md)

+ 365 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/release-notes.md

@@ -0,0 +1,365 @@
+# 1.12.2
+### Fixes
+* Fixed missing <cassert> include
+
+# 1.12.1
+
+### Fixes
+* Fixed deprecation warning in `ScopedMessage::~ScopedMessage`
+* All uses of `min` or `max` identifiers are now wrapped in parentheses
+  * This avoids problems when Windows headers define `min` and `max` macros
+
+
+
+# 1.12.0
+
+### Fixes
+* Fixed compilation for strict C++98 mode (ie not gnu++98) and older compilers (#1103)
+* `INFO` messages are included in the `xml` reporter output even without `-s` specified.
+
+
+
+# 1.11.0
+
+### Fixes
+* The original expression in `REQUIRE_FALSE( expr )` is now reporter properly as `!( expr )` (#1051)
+  * Previously the parentheses were missing and `x != y` would be expanded as `!x != x`
+* `Approx::Margin` is now inclusive (#952)
+  * Previously it was meant and documented as inclusive, but the check itself wasn't
+  * This means that `REQUIRE( 0.25f == Approx( 0.0f ).margin( 0.25f ) )` passes, instead of fails
+* `RandomNumberGenerator::result_type` is now unsigned (#1050)
+
+### Improvements
+* `__JETBRAINS_IDE__` macro handling is now CLion version specific (#1017)
+  * When CLion 2017.3 or newer is detected, `__COUNTER__` is used instead of
+* TeamCity reporter now explicitly flushes output stream after each report (#1057)
+  * On some platforms, output from redirected streams would show up only after the tests finished running
+* `ParseAndAddCatchTests` now can add test files as dependency to CMake configuration
+  * This means you do not have to manually rerun CMake configuration step to detect new tests
+
+
+# 1.10.0
+
+### Fixes
+* Evaluation layer has been rewritten (backported from Catch 2)
+  * The new layer is much simpler and fixes some issues (#981)
+* Implemented workaround for VS 2017 raw string literal stringification bug (#995)
+* Fixed interaction between `[!shouldfail]` and `[!mayfail]` tags and sections
+  * Previously sections with failing assertions would be marked as failed, not failed-but-ok
+
+### Improvements
+* Added [libidentify](https://github.com/janwilmans/LibIdentify) support
+* Added "wait-for-keypress" option
+
+
+# 1.9.7
+
+### Fixes
+* Various warnings from clang-tidy, Resharper-C++ and PVS Studio have been addressed (#957)
+* Dynamically generated sections are now properly reported (#963)
+* Writes to `std::clog` are redirected for reporters (#989)
+  * Previously only `std::cerr` writes were redirected
+  * Interleaved writes to `std::cerr` and `std::clog` are combined properly
+* Assertions failed before signals/structured exceptions fails test case are properly reported as failed (#990)
+
+### Improvements
+* Catch's runtime overhead has been decreased further (#940)
+* Added support for IBM i ILE c++ compiler (#976)
+  * This means that AS/400 is now supported.
+* The default reporter can be configured at compile time (#978)
+  * That is, the reporter used if no reporter is explicitly specified
+
+### Other
+* `ParseAndAddCatchTests` cmake script has couple new customization options
+
+
+
+# 1.9.6
+
+### Improvements
+* Catch's runtime overhead has been significantly decreased (#937, #939)
+* Added `--list-extra-info` cli option (#934).
+  * It lists all tests together with extra information, ie filename, line number and description.
+
+
+
+# 1.9.5
+
+### Fixes
+* Truthy expressions are now reconstructed properly, not as booleans (#914)
+* Various warnings are no longer erroneously suppressed in test files (files that include `catch.hpp`, but do not define `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`) (#871)
+* Catch no longer fails to link when main is compiled as C++, but linked against Objective-C (#855)
+* Fixed incorrect gcc version detection when deciding to use `__COUNTER__` (#928)
+  * Previously any GCC with minor version less than 3 would be incorrectly classified as not supporting `__COUNTER__`.
+* Suppressed C4996 warning caused by upcoming updated to MSVC 2017, marking `std::uncaught_exception` as deprecated. (#927)
+
+### Improvements
+* CMake integration script now incorporates debug messages and registers tests in an improved way (#911)
+* Various documentation improvements
+
+
+
+# 1.9.4
+
+### Fixes
+* `CATCH_FAIL` macro no longer causes compilation error without variadic macro support
+* `INFO` messages are no longer cleared after being reported once
+
+### Improvements and minor changes
+* Catch now uses `wmain` when compiled under Windows and `UNICODE` is defined.
+  * Note that Catch still officially supports only ASCII
+
+# 1.9.3
+
+### Fixes
+* Completed the fix for (lack of) uint64_t in earlier Visual Studios
+
+# 1.9.2
+
+### Improvements and minor changes
+* All of `Approx`'s member functions now accept strong typedefs in C++11 mode (#888)
+  * Previously `Approx::scale`, `Approx::epsilon`, `Approx::margin` and `Approx::operator()` didn't.
+
+
+### Fixes
+* POSIX signals are now disabled by default under QNX (#889)
+  * QNX does not support current enough (2001) POSIX specification
+* JUnit no longer counts exceptions as failures if given test case is marked as ok to fail.
+* `Catch::Option` should now have its storage properly aligned.
+* Catch no longer attempts to define `uint64_t` on windows (#862)
+  * This was causing trouble when compiled under Cygwin
+
+### Other
+* Catch is now compiled under MSVC 2017 using `std:c++latest` (C++17 mode) in CI
+* We now provide cmake script that autoregisters Catch tests into ctest.
+  * See `contrib` folder.
+
+
+# 1.9.1
+
+### Fixes
+* Unexpected exceptions are no longer ignored by default (#885, #887)
+
+
+# 1.9.0
+
+
+### Improvements and minor changes
+* Catch no longer attempts to ensure the exception type passed by user in `REQUIRE_THROWS_AS` is a constant reference.
+  * It was causing trouble when `REQUIRE_THROWS_AS` was used inside templated functions
+  * This actually reverts changes made in v1.7.2
+* Catch's `Version` struct should no longer be double freed when multiple instances of Catch tests are loaded into single program (#858)
+  * It is now a static variable in an inline function instead of being an `extern`ed struct.
+* Attempt to register invalid tag or tag alias now throws instead of calling `exit()`.
+  * Because this happen before entering main, it still aborts execution
+  * Further improvements to this are coming
+* `CATCH_CONFIG_FAST_COMPILE` now speeds-up compilation of `REQUIRE*` assertions by further ~15%.
+  * The trade-off is disabling translation of unexpected exceptions into text.
+* When Catch is compiled using C++11, `Approx` is now constructible with anything that can be explicitly converted to `double`.
+* Captured messages are now printed on unexpected exceptions
+
+### Fixes:
+* Clang's `-Wexit-time-destructors` should be suppressed for Catch's internals
+* GCC's `-Wparentheses` is now suppressed for all TU's that include `catch.hpp`.
+  * This is functionally a revert of changes made in 1.8.0, where we tried using `_Pragma` based suppression. This should have kept the suppression local to Catch's assertions, but bugs in GCC's handling of `_Pragma`s in C++ mode meant that it did not always work.
+* You can now tell Catch to use C++11-based check when checking whether a type can be streamed to output.
+  * This fixes cases when an unstreamable type has streamable private base (#877)
+  * [Details can be found in documentation](configuration.md#catch_config_cpp11_stream_insertable_check)
+
+
+### Other notes:
+* We have added VS 2017 to our CI
+* Work on Catch 2 should start soon
+
+
+
+# 1.8.2
+
+
+### Improvements and minor changes
+* TAP reporter now behaves as if `-s` was always set
+  * This should be more consistent with the protocol desired behaviour.
+* Compact reporter now obeys `-d yes` argument (#780)
+  * The format is "XXX.123 s: <section-name>" (3 decimal places are always present).
+  * Before it did not report the durations at all.
+* XML reporter now behaves the same way as Console reporter in regards to `INFO`
+  * This means it reports `INFO` messages on success, if output on success (`-s`) is enabled.
+  * Previously it only reported `INFO` messages on failure.
+* `CAPTURE(expr)` now stringifies `expr` in the same way assertion macros do (#639)
+* Listeners are now finally [documented](event-listeners.md).
+  * Listeners provide a way to hook into events generated by running your tests, including start and end of run, every test case, every section and every assertion.
+
+
+### Fixes:
+* Catch no longer attempts to reconstruct expression that led to a fatal error  (#810)
+  * This fixes possible signal/SEH loop when processing expressions, where the signal was triggered by expression decomposition.
+* Fixed (C4265) missing virtual destructor warning in Matchers (#844)
+* `std::string`s are now taken by `const&` everywhere (#842).
+  * Previously some places were taking them by-value.
+* Catch should no longer change errno (#835).
+  * This was caused by libstdc++ bug that we now work around.
+* Catch now provides `FAIL_CHECK( ... )` macro (#765).
+  * Same as `FAIL( ... )`, but does not abort the test.
+* Functions like `fabs`, `tolower`, `memset`, `isalnum` are now used with `std::` qualification (#543).
+* Clara no longer assumes first argument (binary name) is always present (#729)
+  * If it is missing, empty string is used as default.
+* Clara no longer reads 1 character past argument string (#830)
+* Regression in Objective-C bindings (Matchers) fixed (#854)
+
+
+### Other notes:
+* We have added VS 2013 and 2015 to our CI
+* Catch Classic (1.x.x) now contains its own, forked, version of Clara (the argument parser).
+
+
+
+# 1.8.1
+
+### Fixes
+
+Cygwin issue with `gettimeofday` - `#define` was not early enough
+
+# 1.8.0
+
+### New features/ minor changes
+
+* Matchers have new, simpler (and documented) interface.
+  * Catch provides string and vector matchers.
+  * For details see [Matchers documentation](matchers.md).
+* Changed console reporter test duration reporting format (#322)
+  * Old format: `Some simple comparisons between doubles completed in 0.000123s`
+  * New format: `xxx.123s: Some simple comparisons between doubles` _(There will always be exactly 3 decimal places)_
+* Added opt-in leak detection under MSVC + Windows (#439)
+  * Enable it by compiling Catch's main with `CATCH_CONFIG_WINDOWS_CRTDBG`
+* Introduced new compile-time flag, `CATCH_CONFIG_FAST_COMPILE`, trading features for compilation speed.
+  * Moves debug breaks out of tests and into implementation, speeding up test compilation time (~10% on linux).
+  * _More changes are coming_
+* Added [TAP (Test Anything Protocol)](https://testanything.org/) and [Automake](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html#Log-files-generation-and-test-results-recording) reporters.
+  * These are not present in the default single-include header and need to be downloaded from GitHub separately.
+  * For details see [documentation about integrating with build systems](build-systems.md).
+*  XML reporter now reports filename as part of the `Section` and `TestCase` tags.
+* `Approx` now supports an optional margin of absolute error
+  * It has also received [new documentation](assertions.md).
+
+### Fixes
+* Silenced C4312 ("conversion from int to 'ClassName *") warnings in the evaluate layer.
+* Fixed C4512 ("assignment operator could not be generated") warnings under VS2013.
+* Cygwin compatibility fixes
+  * Signal handling is no longer compiled by default.
+  * Usage of `gettimeofday` inside Catch should no longer cause compilation errors.
+* Improved `-Wparentheses` supression for gcc (#674)
+  * When compiled with gcc 4.8 or newer, the supression is localized to assertions only
+  * Otherwise it is supressed for the whole TU
+* Fixed test spec parser issue (with escapes in multiple names)
+
+### Other
+* Various documentation fixes and improvements
+
+
+# 1.7.2
+
+### Fixes and minor improvements
+Xml:
+
+(technically the first two are breaking changes but are also fixes and arguably break few if any people)
+* C-escape control characters instead of XML encoding them (which requires XML 1.1)
+* Revert XML output to XML 1.0
+* Can provide stylesheet references by extending the XML reporter
+* Added description and tags attribites to XML Reporter
+* Tags are closed and the stream flushed more eagerly to avoid stdout interpolation
+
+
+Other:
+* `REQUIRE_THROWS_AS` now catches exception by `const&` and reports expected type
+* In `SECTION`s the file/ line is now of the `SECTION`. not the `TEST_CASE`
+* Added std:: qualification to some functions from C stdlib
+* Removed use of RTTI (`dynamic_cast`) that had crept back in
+* Silenced a few more warnings in different circumstances
+* Travis improvements
+
+# 1.7.1
+
+### Fixes:
+* Fixed inconsistency in defining `NOMINMAX` and `WIN32_LEAN_AND_MEAN` inside `catch.hpp`.
+* Fixed SEH-related compilation error under older MinGW compilers, by making Windows SEH handling opt-in for compilers other than MSVC.
+  * For specifics, look into the [documentation](configuration.md).
+* Fixed compilation error under MinGW caused by improper compiler detection.
+* Fixed XML reporter sometimes leaving an empty output file when a test ends with signal/structured exception.
+* Fixed XML reporter not reporting captured stdout/stderr.
+* Fixed possible infinite recursion in Windows SEH.
+* Fixed possible compilation error caused by Catch's operator overloads being ambiguous in regards to user-defined templated operators.
+
+## 1.7.0
+
+### Features/ Changes:
+* Catch now runs significantly faster for passing tests
+  * Microbenchmark focused on Catch's overhead went from ~3.4s to ~0.7s.
+  * Real world test using [JSON for Modern C++](https://github.com/nlohmann/json)'s test suite went from ~6m 25s to ~4m 14s.
+* Catch can now run specific sections within test cases.
+  * For now the support is only basic (no wildcards or tags), for details see the [documentation](command-line.md).
+* Catch now supports SEH on Windows as well as signals on Linux.
+  * After receiving a signal, Catch reports failing assertion and then passes the signal onto the previous handler.
+* Approx can be used to compare values against strong typedefs (available in C++11 mode only).
+  * Strong typedefs mean types that are explicitly convertible to double.
+* CHECK macro no longer stops executing section if an exception happens.
+* Certain characters (space, tab, etc) are now pretty printed.
+  * This means that a `char c = ' '; REQUIRE(c == '\t');` would be printed as `' ' == '\t'`, instead of ` == 9`.
+
+### Fixes:
+* Text formatting no longer attempts to access out-of-bounds characters under certain conditions.
+* THROW family of assertions no longer trigger `-Wunused-value` on expressions containing explicit cast.
+* Breaking into debugger under OS X works again and no longer required `DEBUG` to be defined.
+* Compilation no longer breaks under certain compiler if a lambda is used inside assertion macro.
+
+### Other:
+* Catch's CMakeLists now defines install command.
+* Catch's CMakeLists now generates projects with warnings enabled.
+
+
+## 1.6.1
+
+### Features/ Changes:
+* Catch now supports breaking into debugger on Linux
+
+### Fixes:
+* Generators no longer leak memory (generators are still unsupported in general)
+* JUnit reporter now reports UTC timestamps, instead of "tbd"
+* `CHECK_THAT` macro is now properly defined as `CATCH_CHECK_THAT` when using `CATCH_` prefixed macros
+
+### Other:
+* Types with overloaded `&&` operator are no longer evaluated twice when used in an assertion macro.
+* The use of `__COUNTER__` is supressed when Catch is parsed by CLion
+  * This change is not active when compiling a binary
+* Approval tests can now be run on Windows
+* CMake will now warn if a file is present in the `include` folder but not is not enumerated as part of the project
+* Catch now defines `NOMINMAX` and `WIN32_LEAN_AND_MEAN` before including `windows.h`
+  * This can be disabled if needed, see [documentation](configuration.md) for details.
+
+
+## 1.6.0
+
+### Cmake/ projects:
+* Moved CMakeLists.txt to root, made it friendlier for CLion and generating XCode and VS projects, and removed the manually maintained XCode and VS projects.
+
+### Features/ Changes:
+* Approx now supports `>=` and `<=`
+* Can now use `\` to escape chars in test names on command line
+* Standardize C++11 feature toggles
+
+### Fixes:
+* Blue shell colour
+* Missing argument to `CATCH_CHECK_THROWS`
+* Don't encode extended ASCII in XML
+* use `std::shuffle` on more compilers (fixes deprecation warning/error)
+* Use `__COUNTER__` more consistently (where available)
+
+### Other:
+* Tweaks and changes to scripts - particularly for Approval test - to make them more portable
+
+
+# Older versions
+Release notes were not maintained prior to v1.6.0, but you should be able to work them out from the Git history
+
+---
+
+[Home](Readme.md)

+ 63 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/release-process.md

@@ -0,0 +1,63 @@
+# How to release
+
+When enough changes have accumulated, it is time to release new version of Catch. This document describes the proces in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `scripts/` directory.
+
+## Neccessary steps
+
+These steps are neccessary and have to be performed before each new release. They serve to make sure that the new release is correct and linked-to from the standard places.
+
+
+### Approval testing
+
+Catch's releases are primarily validated against output from previous release, stored in `projects/SelfTest/Baselines`. To validate current sources, build the SelfTest binary and pass it to the `approvalTests.py` script: `approvalTests.py <path/to/SelfTest>`.
+
+There should be no differences, as Approval tests should be updated when changes to Catch are made, but if there are, then they need to be manually reviewed and either approved (using `approve.py`) or Catch requires other fixes.
+
+
+### Incrementing version number
+
+Catch uses a variant of [semantic versioning](http://semver.org/), with breaking API changes (and thus major version increments) being very rare. Thus, the release will usually increment the patch version, when it only contains couple of bugfixes, or minor version, when it contains new functionality, or larger changes in implementation of current functionality. 
+
+After deciding which part of version number should be incremented, you can use one of the `*Release.py` scripts to perform the required changes to Catch.
+
+
+### Generate updated single-include header
+
+After updating version number, regenerate single-include header using `generateSingleHeader.py`.
+
+
+### Release notes
+
+Once a release is ready, release notes need to be written. They should summarize changes done since last release. For rough idea of expected notes see previous releases. Once written, release notes should be placed in `docs/release-notes.md`.
+
+
+### Commit and push update to GitHub
+
+After version number is incremented, single-include header is regenerated and release notes are updated, changes should be commited and pushed to GitHub. 
+
+
+### Release on GitHub
+
+After pushing changes to GitHub, GitHub release *needs* to be created. Tag version and release title should be same as the new version, description should contain the release notes for the current release. Single header version of `catch.hpp` *needs* to be attached as a binary, as that is where the official download link links to. Preferably it should use linux line endings.
+
+## Optional steps
+
+The following steps are optional, and do not have to be performed when releasing new version of Catch. However, they are *should* happen, but they can happen the next day without losing anything significant.
+
+
+### vcpkg update
+
+Catch is maintaining its own port in Microsoft's package manager [vcpkg](https://github.com/Microsoft/vcpkg). This means that when new version of Catch is released, it should be posted there as well. `updateVcpkgPackage.py` can do a lot of neccessary work for you, it creates a branch and commits neccessary changes. You should review these changes, push and open a PR against vcpkg's upstream.
+
+Note that the script assumes you have your fork of vcpkg checked out in a directory next to the directory where you have checked out Catch, like so:
+```
+GitHub
+    Catch
+    vcpkg
+```
+
+
+### Wandbox update
+
+Recently we also included a link to wandbox with preloaded Catch on the main page. Strictly speaking it is unneccessary to update this after every release, Catch usually does not change that much between versions, but it should be kept up to date anyway.
+

+ 45 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/reporters.md

@@ -0,0 +1,45 @@
+# Reporters
+
+Catch has a modular reporting system and comes bundled with a handful of useful reporters built in.
+You can also write your own reporters.
+
+## Using different reporters
+
+The reporter to use can easily be controlled from the command line.
+To specify a reporter use [`-r` or `--reporter`](command-line.md#choosing-a-reporter-to-use), followed by the name of the reporter, e.g.:
+
+```
+-r xml
+```
+
+If you don't specify a reporter then the console reporter is used by default.
+There are four reporters built in to the single include:
+
+* `console` writes as lines of text, formatted to a typical terminal width, with colours if a capable terminal is detected.
+* `compact` similar to `console` but optimised for minimal output - each entry on one line
+* `junit` writes xml that corresponds to Ant's [junitreport](http://help.catchsoftware.com/display/ET/JUnit+Format) target. Useful for build systems that understand Junit. If you are using Jenkins with Catch 1.x, you can improve quality of output by applying changes in [#923](https://github.com/philsquared/Catch/pull/923).
+Because of the way the junit format is structured the run must complete before anything is written. 
+* `xml` writes an xml format tailored to Catch. Unlike `junit` this is a streaming format so results are delivered progressively.
+
+There are a few additional reporters, for specific build systems, in the Catch repository (in `include\reporters`) which you can `#include` in your project if you would like to make use of them.
+Do this in one source file - typically the same one you have `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`.
+
+* `teamcity` writes the native, streaming, format that [TeamCity](https://www.jetbrains.com/teamcity/) understands.
+Use this when building as part of a TeamCity build to see results as they happen.
+* `tap` writes in the TAP ([Test Anything Protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol)) format.
+* `automake` writes in a format that correspond to [automake  .trs](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html) files
+
+You see what reporters are available from the command line by running with `--list-reporters`.
+
+By default all these reports are written to stdout, but can be redirected to a file with [`-o` or `--out`](command-line.md#sending-output-to-a-file)
+
+## Writing your own reporter
+
+You can write your own custom reporter and register it with Catch.
+At time of writing the interface is subject to some changes so is not, yet, documented here.
+If you are determined you shouldn't have too much trouble working it out from the existing implementations -
+but do keep in mind upcoming changes (these will be minor, simplifying, changes such as not needing to forward calls to the base class).
+
+---
+
+[Home](Readme.md)

+ 64 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/slow-compiles.md

@@ -0,0 +1,64 @@
+# Why do my tests take so long to compile?
+
+Several people have reported that test code written with Catch takes much longer to compile than they would expect. Why is that?
+
+Catch is implemented entirely in headers. There is a little overhead due to this - but not as much as you might think - and you can minimise it simply by organising your test code as follows:
+
+## Short answer
+Exactly one source file must ```#define``` either ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER``` before ```#include```-ing Catch. In this file *do not write any test cases*! In most cases that means this file will just contain two lines (the ```#define``` and the ```#include```).
+
+## Long answer
+
+Usually C++ code is split between a header file, containing declarations and prototypes, and an implementation file (.cpp) containing the definition, or implementation, code. Each implementation file, along with all the headers that it includes (and which those headers include, etc), is expanded into a single entity called a translation unit - which is then passed to the compiler and compiled down to an object file.
+
+But functions and methods can also be written inline in header files. The downside to this is that these definitions will then be compiled in *every* translation unit that includes the header.
+
+Because Catch is implemented *entirely* in headers you might think that the whole of Catch must be compiled into every translation unit that uses it! Actually it's not quite as bad as that. Catch mitigates this situation by effectively maintaining the traditional separation between the implementation code and declarations. Internally the implementation code is protected by ```#ifdef```s and is conditionally compiled into only one translation unit. This translation unit is that one that ```#define```s ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER```. Let's call this the main source file.
+
+As a result the main source file *does* compile the whole of Catch every time! So it makes sense to dedicate this file to *only* ```#define```-ing the identifier and ```#include```-ing Catch (and implementing the runner code, if you're doing that). Keep all your test cases in other files. This way you won't pay the recompilation cost for the whole of Catch 
+
+## Practical example
+Assume you have the `Factorial` function from the [tutorial](tutorial.md) in `factorial.cpp` (with forward declaration in `factorial.h`) and want to test it and keep the compile times down when adding new tests. Then you should have 2 files, `tests-main.cpp` and `tests-factorial.cpp`:
+
+```cpp
+// tests-main.cpp
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+```
+
+```cpp
+// tests-factorial.cpp
+#include "catch.hpp"
+
+#include "factorial.h"
+
+TEST_CASE( "Factorials are computed", "[factorial]" ) {
+    REQUIRE( Factorial(1) == 1 );
+    REQUIRE( Factorial(2) == 2 );
+    REQUIRE( Factorial(3) == 6 );
+    REQUIRE( Factorial(10) == 3628800 );
+}
+```
+
+After compiling `tests-main.cpp` once, it is enough to link it with separately compiled `tests-factorial.cpp`. This means that adding more tests to `tests-factorial.cpp`, will not result in recompiling Catch's main and the resulting compilation times will decrease substantially.
+
+```
+$ g++ tests-main.cpp -c
+$ g++ tests-main.o tests-factorial.cpp -o tests && ./tests -r compact
+Passed 1 test case with 4 assertions.
+```
+
+Now, the next time we change the file `tests-factorial.cpp` (say we add `REQUIRE( Factorial(0) == 1)`), it is enough to recompile the tests instead of recompiling main as well:
+
+```
+$ g++ tests-main.o tests-factorial.cpp -o tests && ./tests -r compact
+tests-factorial.cpp:11: failed: Factorial(0) == 1 for: 0 == 1
+Failed 1 test case, failed 1 assertion.
+```
+
+## Other possible solutions
+You can also opt to sacrifice some features in order to speed-up Catch's compilation times. For details see the [documentation on Catch's compile-time configuration](configuration.md#other-toggles).
+
+---
+
+[Home](Readme.md)

+ 88 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/test-cases-and-sections.md

@@ -0,0 +1,88 @@
+# Test cases and sections
+
+While Catch fully supports the traditional, xUnit, style of class-based fixtures containing test case methods this is not the preferred style.
+
+Instead Catch provides a powerful mechanism for nesting test case sections within a test case. For a more detailed discussion see the [tutorial](tutorial.md#test-cases-and-sections).
+
+Test cases and sections are very easy to use in practice:
+
+* **TEST_CASE(** _test name_ \[, _tags_ \] **)**
+* **SECTION(** _section name_ **)**
+
+_test name_ and _section name_ are free form, quoted, strings. The optional _tags_ argument is a quoted string containing one or more tags enclosed in square brackets. Tags are discussed below. Test names must be unique within the Catch executable.
+
+For examples see the [Tutorial](tutorial.md)
+
+## Tags
+
+Tags allow an arbitrary number of additional strings to be associated with a test case. Test cases can be selected (for running, or just for listing) by tag - or even by an expression that combines several tags. At their most basic level they provide a simple way to group several related tests together.
+
+As an example - given the following test cases:
+
+	TEST_CASE( "A", "[widget]" ) { /* ... */ }
+	TEST_CASE( "B", "[widget]" ) { /* ... */ }
+	TEST_CASE( "C", "[gadget]" ) { /* ... */ }
+	TEST_CASE( "D", "[widget][gadget]" ) { /* ... */ }
+
+The tag expression, ```"[widget]"``` selects A, B & D. ```"[gadget]"``` selects C & D. ```"[widget][gadget]"``` selects just D and ```"[widget],[gadget]"``` selects all four test cases.
+
+For more detail on command line selection see [the command line docs](command-line.md#specifying-which-tests-to-run)
+
+Tag names are not case sensitive and can contain any ASCII characters. This means that tags `[tag with spaces]` and `[I said "good day"]` are both allowed tags and can be filtered on. Escapes are not supported however and `[\]]` is not a valid tag.
+
+### Special Tags
+
+All tag names beginning with non-alphanumeric characters are reserved by Catch. Catch defines a number of "special" tags, which have meaning to the test runner itself. These special tags all begin with a symbol character. Following is a list of currently defined special tags and their meanings.
+
+* `[!hide]` or `[.]` (or, for legacy reasons, `[hide]`)	- causes test cases to be skipped from the default list (i.e. when no test cases have been explicitly selected through tag expressions or name wildcards). The hide tag is often combined with another, user, tag (for example `[.][integration]` - so all integration tests are excluded from the default run but can be run by passing `[integration]` on the command line). As a short-cut you can combine these by simply prefixing your user tag with a `.` - e.g. `[.integration]`. Because the hide tag has evolved to have several forms, all forms are added as tags if you use one of them.
+
+* `[!throws]`	- lets Catch know that this test is likely to throw an exception even if successful. This causes the test to be excluded when running with `-e` or `--nothrow`.
+
+* `[!mayfail]` - doesn't fail the test if any given assertion fails (but still reports it). This can be useful to flag a work-in-progress, or a known issue that you don't want to immediately fix but still want to track in your tests.
+
+* `[!shouldfail]` - like `[!mayfail]` but *fails* the test if it *passes*. This can be useful if you want to be notified of accidental, or third-party, fixes.
+
+* `[!nonportable]` - Indicates that behaviour may vary between platforms or compilers.
+
+* `[#<filename>]` - running with `-#` or `--filenames-as-tags` causes Catch to add the filename, prefixed with `#` (and with any extension stripped), as a tag to all contained tests, e.g. tests in testfile.cpp would all be tagged `[#testfile]`.
+
+* `[@<alias>]` - tag aliases all begin with `@` (see below).
+
+## Tag aliases
+
+Between tag expressions and wildcarded test names (as well as combinations of the two) quite complex patterns can be constructed to direct which test cases are run. If a complex pattern is used often it is convenient to be able to create an alias for the expression. This can be done, in code, using the following form:
+
+	CATCH_REGISTER_TAG_ALIAS( <alias string>, <tag expression> )
+
+Aliases must begin with the `@` character. An example of a tag alias is:
+
+	CATCH_REGISTER_TAG_ALIAS( "[@nhf]", "[failing]~[.]" )
+
+Now when `[@nhf]` is used on the command line this matches all tests that are tagged `[failing]`, but which are not also hidden.
+
+## BDD-style test cases
+
+In addition to Catch's take on the classic style of test cases, Catch supports an alternative syntax that allow tests to be written as "executable specifications" (one of the early goals of [Behaviour Driven Development](http://dannorth.net/introducing-bdd/)). This set of macros map on to ```TEST_CASE```s and ```SECTION```s, with a little internal support to make them smoother to work with.
+
+* **SCENARIO(** _scenario name_ \[, _tags_ \] **)**
+
+This macro maps onto ```TEST_CASE``` and works in the same way, except that the test case name will be prefixed by "Scenario: "
+
+* **GIVEN(** _something_ **)**
+* **WHEN(** _something_ **)**
+* **THEN(** _something_ **)**
+
+These macros map onto ```SECTION```s except that the section names are the _something_s prefixed by "given: ", "when: " or "then: " respectively.
+
+* **AND_WHEN(** _something_ **)**
+* **AND_THEN(** _something_ **)**
+
+Similar to ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```WHEN```s and ```THEN```s together.
+
+When any of these macros are used the console reporter recognises them and formats the test case header such that the Givens, Whens and Thens are aligned to aid readability.
+
+Other than the additional prefixes and the formatting in the console reporter these macros behave exactly as ```TEST_CASE```s and ```SECTION```s. As such there is nothing enforcing the correct sequencing of these macros - that's up to the programmer!
+
+---
+
+[Home](Readme.md)

+ 32 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/external/catch/docs/test-fixtures.md

@@ -0,0 +1,32 @@
+Although Catch allows you to group tests together as sections within a test case, it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure:
+
+```c++
+class UniqueTestsFixture {
+  private:
+   static int uniqueID;
+  protected:
+   DBConnection conn;
+  public:
+   UniqueTestsFixture() : conn(DBConnection::createConnection("myDB")) {
+   }
+  protected:
+   int getID() {
+     return ++uniqueID;
+   }
+ };
+
+ int UniqueTestsFixture::uniqueID = 0;
+
+ TEST_CASE_METHOD(UniqueTestsFixture, "Create Employee/No Name", "[create]") {
+   REQUIRE_THROWS(conn.executeSQL("INSERT INTO employee (id, name) VALUES (?, ?)", getID(), ""));
+ }
+ TEST_CASE_METHOD(UniqueTestsFixture, "Create Employee/Normal", "[create]") {
+   REQUIRE(conn.executeSQL("INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs"));
+ }
+```
+
+The two test cases here will create uniquely-named derived classes of UniqueTestsFixture and thus can access the `getID()` protected method and `conn` member variables. This ensures that both the test cases are able to create a DBConnection using the same method (DRY principle) and that any ID's created are unique such that the order that tests are executed does not matter.
+
+---
+
+[Home](Readme.md)

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно