mirror of
https://github.com/qvacua/vimr.git
synced 2024-09-11 17:15:34 +03:00
Fix MsgpackRpc Neovim example
This commit is contained in:
parent
a8be0f282c
commit
2b7afdd5e1
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4B0225EF224AAE260052362B"
|
||||
BuildableName = "MinimalNvimViewDemo.app"
|
||||
BlueprintName = "MinimalNvimViewDemo"
|
||||
ReferencedContainer = "container:NvimViewSupport.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4B0225EF224AAE260052362B"
|
||||
BuildableName = "MinimalNvimViewDemo.app"
|
||||
BlueprintName = "MinimalNvimViewDemo"
|
||||
ReferencedContainer = "container:NvimViewSupport.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4B0225EF224AAE260052362B"
|
||||
BuildableName = "MinimalNvimViewDemo.app"
|
||||
BlueprintName = "MinimalNvimViewDemo"
|
||||
ReferencedContainer = "container:NvimViewSupport.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -1,65 +0,0 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Nimble
|
||||
import RxBlocking
|
||||
import RxPack
|
||||
import RxSwift
|
||||
import XCTest
|
||||
|
||||
@testable import RxPack
|
||||
|
||||
class RxMessagePortTest: XCTestCase {
|
||||
let disposeBag = DisposeBag()
|
||||
|
||||
let serverName = "com.qvacua.RxPack.RxMessagePortTest.server.\(UUID().uuidString)"
|
||||
let server = RxMessagePortServer(queueQos: .default)
|
||||
let client = RxMessagePortClient(queueQos: .default)
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
self.server
|
||||
.stream
|
||||
.subscribe(onNext: nil)
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
_ = try! self.server
|
||||
.run(as: self.serverName)
|
||||
.andThen(self.client.connect(to: self.serverName))
|
||||
.toBlocking()
|
||||
.first()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
|
||||
_ = try! self.client
|
||||
.stop()
|
||||
.andThen(self.server.stop())
|
||||
.toBlocking()
|
||||
.first()
|
||||
}
|
||||
|
||||
func testSth() {
|
||||
self.server
|
||||
.syncReplyBody = { msgid, inputData in data("response-to-\(msgid)-\(str(inputData))") }
|
||||
|
||||
var response = try! self.client
|
||||
.send(msgid: 0, data: data("first-msg"), expectsReply: true)
|
||||
.toBlocking()
|
||||
.first()!
|
||||
expect(str(response)).to(equal("response-to-0-first-msg"))
|
||||
|
||||
response = try! self.client
|
||||
.send(msgid: 1, data: data("second-msg"), expectsReply: true)
|
||||
.toBlocking()
|
||||
.first()!
|
||||
expect(str(response)).to(equal("response-to-1-second-msg"))
|
||||
}
|
||||
}
|
||||
|
||||
private func str(_ data: Data?) -> String { String(data: data!, encoding: .utf8)! }
|
||||
private func data(_ str: String) -> Data { str.data(using: .utf8)! }
|
@ -1,85 +1,148 @@
|
||||
/// Tae Won Ha - http://taewon.de - @hataewon
|
||||
/// See LICENSE
|
||||
|
||||
import Nimble
|
||||
import RxBlocking
|
||||
import RxPack
|
||||
import RxSwift
|
||||
import XCTest
|
||||
|
||||
extension PrimitiveSequenceType {
|
||||
func waitCompletion() throws {
|
||||
_ = try self.primitiveSequence.toBlocking().first()
|
||||
}
|
||||
}
|
||||
|
||||
private func delayingCompletable() -> Completable {
|
||||
Single.just(0)
|
||||
.delay(.milliseconds(10), scheduler: MainScheduler.instance)
|
||||
.asCompletable()
|
||||
}
|
||||
|
||||
/// No real test, just a sample code to see that it works with Neovim: Execute
|
||||
///
|
||||
/// ```bash
|
||||
/// NVIM_LISTEN_ADDRESS=/tmp/nvim.sock nvim --headless $SOMEFILE
|
||||
/// ```
|
||||
///
|
||||
/// in Terminal and rename xtestExample() to testExample() to run.
|
||||
class RxMsgpackRpcNeovimExample: XCTestCase {
|
||||
let connection = RxMsgpackRpc(queueQos: .default)
|
||||
let rpc = RxMsgpackRpc(queueQos: .default)
|
||||
let disposeBag = DisposeBag()
|
||||
var proc: Process!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
self.connection.stream
|
||||
.subscribe(
|
||||
onNext: { msg in
|
||||
switch msg {
|
||||
case let .notification(method, params):
|
||||
print("NOTIFICATION: \(method): array of \(params.count) elements")
|
||||
case let .error(value, msg):
|
||||
print("ERROR: \(msg) with \(value)")
|
||||
default:
|
||||
print("???")
|
||||
}
|
||||
},
|
||||
onError: { print("ERROR: \($0)") },
|
||||
onCompleted: { print("COMPLETED!") }
|
||||
)
|
||||
.disposed(by: self.disposeBag)
|
||||
self.rpc.stream.subscribe(
|
||||
onNext: { msg in
|
||||
switch msg {
|
||||
case let .notification(method, params):
|
||||
print("NOTIFICATION: \(method): array of \(params.count) elements")
|
||||
case let .error(value, msg):
|
||||
print("ERROR: \(msg) with \(value)")
|
||||
case let .response(msgid, error, result):
|
||||
print("RESPONSE: \(msgid), \(error), \(result)")
|
||||
default:
|
||||
fail("Unknown msg type from rpc")
|
||||
}
|
||||
},
|
||||
onError: { print("ERROR: \($0)") },
|
||||
onCompleted: { print("COMPLETED!") }
|
||||
)
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
_ = try! self.connection.run(at: "/tmp/nvim.sock").toBlocking().first()
|
||||
self.proc = self.neovimProcess()
|
||||
let inPipe = self.proc.standardInput as! Pipe
|
||||
let outPipe = self.proc.standardOutput as! Pipe
|
||||
let errorPipe = self.proc.standardError as! Pipe
|
||||
try! self.proc.run()
|
||||
try! self.rpc.run(inPipe: inPipe, outPipe: outPipe, errorPipe: errorPipe).waitCompletion()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
|
||||
_ = try! self.connection
|
||||
.request(
|
||||
method: "nvim_command", params: [.string("q!")],
|
||||
expectsReturnValue: false
|
||||
)
|
||||
.toBlocking()
|
||||
.first()
|
||||
try! self.rpc.request(
|
||||
method: "nvim_command", params: [.string("q!")],
|
||||
expectsReturnValue: false
|
||||
).waitCompletion()
|
||||
|
||||
_ = try! self.connection.stop().toBlocking().first()
|
||||
try! self.rpc.stop().waitCompletion()
|
||||
self.proc.waitUntilExit()
|
||||
}
|
||||
|
||||
func xtestExample() {
|
||||
let lineCount = try! self.connection
|
||||
.request(
|
||||
method: "nvim_buf_line_count",
|
||||
params: [.int(0)],
|
||||
expectsReturnValue: true
|
||||
)
|
||||
.toBlocking()
|
||||
.first()
|
||||
|
||||
print(lineCount!)
|
||||
func testExample() throws {
|
||||
try self.rpc.request(
|
||||
method: "nvim_ui_attach",
|
||||
params: [80, 24, [:]],
|
||||
expectsReturnValue: false
|
||||
).waitCompletion()
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "mm:ss.SSS"
|
||||
for i in 0...100 {
|
||||
let date = Date()
|
||||
let response = try! self.connection
|
||||
let response = try self.rpc
|
||||
.request(
|
||||
method: "nvim_command_output",
|
||||
params: [.string("echo '\(i) \(formatter.string(from: date))'")],
|
||||
expectsReturnValue: true
|
||||
)
|
||||
.toBlocking()
|
||||
.first()
|
||||
.toBlocking().first()!
|
||||
|
||||
print(response!)
|
||||
Swift.print(response)
|
||||
}
|
||||
|
||||
let testFileUrl: URL = FileManager.default
|
||||
.homeDirectoryForCurrentUser.appending(components: "test/big.swift")
|
||||
guard FileManager.default.fileExists(atPath: testFileUrl.path) else {
|
||||
try self.rpc.request(method: "nvim_ui_detach", params: [], expectsReturnValue: false)
|
||||
.waitCompletion()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try self.rpc.request(
|
||||
method: "nvim_command",
|
||||
params: [.string("e \(testFileUrl.path)")],
|
||||
expectsReturnValue: false
|
||||
).waitCompletion()
|
||||
|
||||
let lineCount = try self.rpc.request(
|
||||
method: "nvim_buf_line_count",
|
||||
params: [.int(0)],
|
||||
expectsReturnValue: true
|
||||
)
|
||||
.toBlocking().first()!
|
||||
Swift.print("Line count of \(testFileUrl): \(lineCount)")
|
||||
|
||||
let repeatCount = 200
|
||||
for _ in 0...repeatCount {
|
||||
try self.rpc
|
||||
.request(method: "nvim_input", params: ["<PageDown>"], expectsReturnValue: false)
|
||||
.waitCompletion()
|
||||
try delayingCompletable().waitCompletion()
|
||||
}
|
||||
for _ in 0...repeatCount {
|
||||
try self.rpc
|
||||
.request(method: "nvim_input", params: ["<PageUp>"], expectsReturnValue: false)
|
||||
.waitCompletion()
|
||||
try delayingCompletable().waitCompletion()
|
||||
}
|
||||
|
||||
Thread.sleep(forTimeInterval: 1)
|
||||
|
||||
try self.rpc.request(method: "nvim_ui_detach", params: [], expectsReturnValue: false)
|
||||
.waitCompletion()
|
||||
}
|
||||
|
||||
private func neovimProcess() -> Process {
|
||||
let inPipe = Pipe()
|
||||
let outPipe = Pipe()
|
||||
let errorPipe = Pipe()
|
||||
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: "/opt/homebrew/bin/nvim")
|
||||
process.standardInput = inPipe
|
||||
process.standardError = errorPipe
|
||||
process.standardOutput = outPipe
|
||||
process.arguments = ["--embed"]
|
||||
|
||||
return process
|
||||
}
|
||||
}
|
||||
|
@ -7,347 +7,346 @@ import Nimble
|
||||
import RxBlocking
|
||||
import RxPack
|
||||
import RxSwift
|
||||
import Socket
|
||||
import XCTest
|
||||
|
||||
class RxMsgpackRpcrTests: XCTestCase {
|
||||
typealias Value = RxMsgpackRpc.Value
|
||||
typealias Message = RxMsgpackRpc.Message
|
||||
typealias MessageType = RxMsgpackRpc.MessageType
|
||||
|
||||
var testFinished = false
|
||||
let testFinishedCond = NSCondition()
|
||||
|
||||
var serverReady = false
|
||||
let serverReadyCond = NSCondition()
|
||||
|
||||
var server: TestServer!
|
||||
var clientSocket: Socket!
|
||||
let msgpackRpc = RxMsgpackRpc(queueQos: .default)
|
||||
|
||||
var requestsFromClient = [[Value]]()
|
||||
let responseScheduler = ConcurrentDispatchQueueScheduler(qos: .default)
|
||||
let disposeBag = DisposeBag()
|
||||
|
||||
let uuid = UUID().uuidString
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
self.server = TestServer(
|
||||
path: FileManager.default
|
||||
.temporaryDirectory
|
||||
.appendingPathComponent("\(self.uuid).sock")
|
||||
.path
|
||||
)
|
||||
|
||||
self.server.queue.async {
|
||||
self.server.waitForClientAndStartReading()
|
||||
self.clientSocket = self.server.connectedSocket
|
||||
|
||||
self.serverReadyCond.lock()
|
||||
self.serverReady = true
|
||||
self.serverReadyCond.signal()
|
||||
self.serverReadyCond.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
_ = try! self.msgpackRpc.stop().toBlocking().first()
|
||||
self.server.shutdownServer()
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testResponsesFromServer() {
|
||||
self.runClient(readBufferSize: Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE)
|
||||
|
||||
var beginAssertion = false
|
||||
let beginAssertionCond = NSCondition()
|
||||
|
||||
self.assertMsgsFromClient { data, _ in
|
||||
// WARNING:
|
||||
// - Sometimes, the first and the second message get coalesced to one read operation by the
|
||||
// server.
|
||||
// - Sometimes, second message arrives first.
|
||||
let requests = try! unpackAll(data)
|
||||
.map(\.arrayValue!)
|
||||
.sorted { left, right in left[2].stringValue! < right[2].stringValue! }
|
||||
|
||||
for request in requests {
|
||||
expect(request).to(haveCount(4))
|
||||
self.requestsFromClient.append(request)
|
||||
|
||||
if request[2].stringValue! == "2-second-request" {
|
||||
beginAssertionCond.lock()
|
||||
beginAssertion = true
|
||||
beginAssertionCond.signal()
|
||||
beginAssertionCond.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var responseCount = 0
|
||||
self.sendRequests {
|
||||
self.msgpackRpc
|
||||
.request(method: "1-first-request", params: [.uint(123)], expectsReturnValue: true)
|
||||
.subscribe(on: self.responseScheduler)
|
||||
.subscribe(onSuccess: { response in
|
||||
expect(response.error).to(equal(.nil))
|
||||
expect(response.result).to(equal(.float(0.321)))
|
||||
responseCount += 1
|
||||
|
||||
self.signalEndOfTest()
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
self.msgpackRpc
|
||||
.request(method: "2-second-request", params: [.uint(321)], expectsReturnValue: true)
|
||||
.subscribe(on: self.responseScheduler)
|
||||
.subscribe(onSuccess: { response in
|
||||
expect(response.error).to(equal(.nil))
|
||||
expect(response.result).to(equal(.float(0.123)))
|
||||
responseCount += 1
|
||||
})
|
||||
.disposed(by: self.disposeBag)
|
||||
|
||||
beginAssertionCond.lock()
|
||||
while beginAssertion == false {
|
||||
beginAssertionCond.wait(until: Date(timeIntervalSinceNow: 2 * 60))
|
||||
}
|
||||
beginAssertionCond.unlock()
|
||||
|
||||
let request1 = self.requestsFromClient[0]
|
||||
expect(request1[0].uint64Value).to(equal(MessageType.request.rawValue))
|
||||
expect(request1[2].stringValue).to(equal("1-first-request"))
|
||||
expect(request1[3].arrayValue).to(equal([.uint(123)]))
|
||||
|
||||
let request2 = self.requestsFromClient[1]
|
||||
expect(request2[0].uint64Value).to(equal(MessageType.request.rawValue))
|
||||
expect(request2[2].stringValue).to(equal("2-second-request"))
|
||||
expect(request2[3].arrayValue).to(equal([.uint(321)]))
|
||||
|
||||
try! self.clientSocket
|
||||
.write(from: self.dataForResponse(msgid: 1, error: .nil, params: .float(0.123)))
|
||||
|
||||
try! self.clientSocket
|
||||
.write(from: self.dataForResponse(msgid: 0, error: .nil, params: .float(0.321)))
|
||||
}
|
||||
|
||||
expect(responseCount).to(equal(2))
|
||||
}
|
||||
|
||||
func testNotificationsFromServer() {
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
let msgs = try! self.msgpackRpc.stream.toBlocking().toArray()
|
||||
expect(msgs).to(haveCount(2))
|
||||
|
||||
let (method1, params1) = self.notification(from: msgs[0])
|
||||
let (method2, params2) = self.notification(from: msgs[1])
|
||||
|
||||
expect(method1).to(equal("first-msg"))
|
||||
expect(params1).to(haveCount(2))
|
||||
expect(params1[0].uintValue).to(equal(321))
|
||||
expect(params1[1].dataValue).to(haveCount(321))
|
||||
|
||||
expect(method2).to(equal("second-msg"))
|
||||
expect(params2).to(haveCount(2))
|
||||
expect(params2[0].dataValue).to(haveCount(123))
|
||||
expect(params2[1].floatValue).to(equal(0.123))
|
||||
|
||||
self.signalEndOfTest()
|
||||
}
|
||||
|
||||
self.runClient(readBufferSize: Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE)
|
||||
|
||||
self.sendRequests {
|
||||
let data1 = dataForNotification(
|
||||
method: "first-msg",
|
||||
params: [.uint(321), .binary(.random(ofCount: 321))]
|
||||
)
|
||||
let data2 = dataForNotification(
|
||||
method: "second-msg",
|
||||
params: [.binary(.random(ofCount: 123)), .float(0.123)]
|
||||
)
|
||||
|
||||
try! self.clientSocket.write(from: data1)
|
||||
try! self.clientSocket.write(from: data2)
|
||||
|
||||
self.server.shutdownServer()
|
||||
}
|
||||
}
|
||||
|
||||
func testPartialRequestFromServer() {
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
let msgs = try! self.msgpackRpc.stream.toBlocking().toArray()
|
||||
expect(msgs).to(haveCount(2))
|
||||
|
||||
let (method1, params1) = self.notification(from: msgs[0])
|
||||
let (method2, params2) = self.notification(from: msgs[1])
|
||||
|
||||
expect(method1).to(equal("first-msg"))
|
||||
expect(params1).to(haveCount(2))
|
||||
expect(params1[0].uintValue).to(equal(321))
|
||||
expect(params1[1].dataValue).to(haveCount(321))
|
||||
|
||||
expect(method2).to(equal("second-msg"))
|
||||
expect(params2).to(haveCount(2))
|
||||
expect(params2[0].dataValue).to(haveCount(123))
|
||||
expect(params2[1].floatValue).to(equal(0.123))
|
||||
|
||||
self.signalEndOfTest()
|
||||
}
|
||||
|
||||
self.runClient(readBufferSize: Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE)
|
||||
self.sendRequests {
|
||||
let data1 = dataForNotification(
|
||||
method: "first-msg",
|
||||
params: [.uint(321), .binary(.random(ofCount: 321))]
|
||||
)
|
||||
let data2 = dataForNotification(
|
||||
method: "second-msg",
|
||||
params: [.binary(.random(ofCount: 123)), .float(0.123)]
|
||||
)
|
||||
|
||||
var msg1 = data1
|
||||
msg1.append(data2[..<100])
|
||||
let msg2 = data2[100...]
|
||||
|
||||
try! self.clientSocket.write(from: msg1)
|
||||
try! self.clientSocket.write(from: msg2)
|
||||
|
||||
self.server.shutdownServer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RxMsgpackRpcrTests {
|
||||
func dataForResponse(msgid: UInt64, error: Value, params: Value) -> Data {
|
||||
pack(.array([
|
||||
.uint(MessageType.response.rawValue),
|
||||
.uint(msgid),
|
||||
error,
|
||||
params,
|
||||
]))
|
||||
}
|
||||
|
||||
func dataForNotification(method: String, params: [Value]) -> Data {
|
||||
pack(.array([
|
||||
.uint(MessageType.notification.rawValue),
|
||||
.string(method),
|
||||
.array(params),
|
||||
]))
|
||||
}
|
||||
|
||||
func assertMsgsFromClient(assertFn: @escaping TestServer.DataReadCallback) {
|
||||
self.server.dataReadCallback = assertFn
|
||||
}
|
||||
|
||||
func runClient(readBufferSize: Int) {
|
||||
usleep(500)
|
||||
_ = try! self.msgpackRpc
|
||||
.run(at: self.server.path, readBufferSize: readBufferSize)
|
||||
.toBlocking()
|
||||
.first()
|
||||
|
||||
self.serverReadyCond.lock()
|
||||
while self.serverReady == false {
|
||||
self.serverReadyCond.wait(until: Date(timeIntervalSinceNow: 2 * 60))
|
||||
}
|
||||
self.serverReadyCond.unlock()
|
||||
}
|
||||
|
||||
func sendRequests(requestFn: () -> Void) {
|
||||
requestFn()
|
||||
self.waitForAssertions()
|
||||
}
|
||||
|
||||
func signalEndOfTest() {
|
||||
self.testFinishedCond.lock()
|
||||
self.testFinished = true
|
||||
self.testFinishedCond.signal()
|
||||
self.testFinishedCond.unlock()
|
||||
}
|
||||
|
||||
func waitForAssertions() {
|
||||
var signalled = false
|
||||
self.testFinishedCond.lock()
|
||||
while self.testFinished == false {
|
||||
signalled = self.testFinishedCond.wait(until: Date(timeIntervalSinceNow: 2 * 60))
|
||||
}
|
||||
expect(signalled).to(beTrue())
|
||||
self.testFinishedCond.unlock()
|
||||
}
|
||||
|
||||
func notification(from msg: Message) -> (method: String, params: [Value]) {
|
||||
guard case let .notification(method, params) = msg else {
|
||||
preconditionFailure("\(msg) is not a notification")
|
||||
}
|
||||
|
||||
return (method: method, params: params)
|
||||
}
|
||||
}
|
||||
|
||||
/// Modified version of the example in the README of https://github.com/Kitura/BlueSocket.
|
||||
/// It only supports one connection. Everywhere ! and no error handling whatsoever.
|
||||
class TestServer {
|
||||
typealias DataReadCallback = (Data, Int) -> Void
|
||||
|
||||
let queue = DispatchQueue(label: "com.qvacua.RxPack.TestServer")
|
||||
var connectedSocket: Socket!
|
||||
|
||||
var path: String
|
||||
var readBufferSize: Int = 1024 * 1024
|
||||
var dataReadCallback: DataReadCallback = { _, _ in }
|
||||
|
||||
private var listenSocket: Socket!
|
||||
|
||||
init(path: String) { self.path = path }
|
||||
deinit { self.shutdownServer() }
|
||||
|
||||
func waitForClientAndStartReading() {
|
||||
self.listenSocket = try! Socket.create(family: .unix)
|
||||
|
||||
try! self.listenSocket.listen(on: self.path)
|
||||
let newSocket = try! self.listenSocket.acceptClientConnection()
|
||||
self.connectedSocket = newSocket
|
||||
|
||||
self.readData(from: newSocket)
|
||||
}
|
||||
|
||||
func readData(from socket: Socket) {
|
||||
self.queue.async { [unowned self] in
|
||||
var shouldKeepRunning = true
|
||||
|
||||
var readData = Data(capacity: self.readBufferSize)
|
||||
repeat {
|
||||
guard let bytesRead = try? socket.read(into: &readData) else {
|
||||
shouldKeepRunning = false
|
||||
break
|
||||
}
|
||||
|
||||
if bytesRead > 0 {
|
||||
self.dataReadCallback(readData, bytesRead)
|
||||
} else {
|
||||
shouldKeepRunning = false
|
||||
break
|
||||
}
|
||||
|
||||
readData.count = 0
|
||||
} while shouldKeepRunning
|
||||
|
||||
socket.close()
|
||||
}
|
||||
}
|
||||
|
||||
func shutdownServer() {
|
||||
self.connectedSocket?.close()
|
||||
self.listenSocket?.close()
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
static func random(ofCount count: Int) -> Data {
|
||||
Data((0..<count).map { _ in UInt8.random(in: UInt8.min...UInt8.max) })
|
||||
}
|
||||
}
|
||||
// class RxMsgpackRpcrTests: XCTestCase {
|
||||
// typealias Value = RxMsgpackRpc.Value
|
||||
// typealias Message = RxMsgpackRpc.Message
|
||||
// typealias MessageType = RxMsgpackRpc.MessageType
|
||||
//
|
||||
// var testFinished = false
|
||||
// let testFinishedCond = NSCondition()
|
||||
//
|
||||
// var serverReady = false
|
||||
// let serverReadyCond = NSCondition()
|
||||
//
|
||||
// var server: TestServer!
|
||||
// var clientSocket: Socket!
|
||||
// let msgpackRpc = RxMsgpackRpc(queueQos: .default)
|
||||
//
|
||||
// var requestsFromClient = [[Value]]()
|
||||
// let responseScheduler = ConcurrentDispatchQueueScheduler(qos: .default)
|
||||
// let disposeBag = DisposeBag()
|
||||
//
|
||||
// let uuid = UUID().uuidString
|
||||
//
|
||||
// override func setUp() {
|
||||
// super.setUp()
|
||||
//
|
||||
// self.server = TestServer(
|
||||
// path: FileManager.default
|
||||
// .temporaryDirectory
|
||||
// .appendingPathComponent("\(self.uuid).sock")
|
||||
// .path
|
||||
// )
|
||||
//
|
||||
// self.server.queue.async {
|
||||
// self.server.waitForClientAndStartReading()
|
||||
// self.clientSocket = self.server.connectedSocket
|
||||
//
|
||||
// self.serverReadyCond.lock()
|
||||
// self.serverReady = true
|
||||
// self.serverReadyCond.signal()
|
||||
// self.serverReadyCond.unlock()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override func tearDown() {
|
||||
// _ = try! self.msgpackRpc.stop().toBlocking().first()
|
||||
// self.server.shutdownServer()
|
||||
//
|
||||
// super.tearDown()
|
||||
// }
|
||||
//
|
||||
// func testResponsesFromServer() {
|
||||
// self.runClient(readBufferSize: Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE)
|
||||
//
|
||||
// var beginAssertion = false
|
||||
// let beginAssertionCond = NSCondition()
|
||||
//
|
||||
// self.assertMsgsFromClient { data, _ in
|
||||
// // WARNING:
|
||||
// // - Sometimes, the first and the second message get coalesced to one read operation by the
|
||||
// // server.
|
||||
// // - Sometimes, second message arrives first.
|
||||
// let requests = try! unpackAll(data)
|
||||
// .map(\.arrayValue!)
|
||||
// .sorted { left, right in left[2].stringValue! < right[2].stringValue! }
|
||||
//
|
||||
// for request in requests {
|
||||
// expect(request).to(haveCount(4))
|
||||
// self.requestsFromClient.append(request)
|
||||
//
|
||||
// if request[2].stringValue! == "2-second-request" {
|
||||
// beginAssertionCond.lock()
|
||||
// beginAssertion = true
|
||||
// beginAssertionCond.signal()
|
||||
// beginAssertionCond.unlock()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var responseCount = 0
|
||||
// self.sendRequests {
|
||||
// self.msgpackRpc
|
||||
// .request(method: "1-first-request", params: [.uint(123)], expectsReturnValue: true)
|
||||
// .subscribe(on: self.responseScheduler)
|
||||
// .subscribe(onSuccess: { response in
|
||||
// expect(response.error).to(equal(.nil))
|
||||
// expect(response.result).to(equal(.float(0.321)))
|
||||
// responseCount += 1
|
||||
//
|
||||
// self.signalEndOfTest()
|
||||
// })
|
||||
// .disposed(by: self.disposeBag)
|
||||
//
|
||||
// self.msgpackRpc
|
||||
// .request(method: "2-second-request", params: [.uint(321)], expectsReturnValue: true)
|
||||
// .subscribe(on: self.responseScheduler)
|
||||
// .subscribe(onSuccess: { response in
|
||||
// expect(response.error).to(equal(.nil))
|
||||
// expect(response.result).to(equal(.float(0.123)))
|
||||
// responseCount += 1
|
||||
// })
|
||||
// .disposed(by: self.disposeBag)
|
||||
//
|
||||
// beginAssertionCond.lock()
|
||||
// while beginAssertion == false {
|
||||
// beginAssertionCond.wait(until: Date(timeIntervalSinceNow: 2 * 60))
|
||||
// }
|
||||
// beginAssertionCond.unlock()
|
||||
//
|
||||
// let request1 = self.requestsFromClient[0]
|
||||
// expect(request1[0].uint64Value).to(equal(MessageType.request.rawValue))
|
||||
// expect(request1[2].stringValue).to(equal("1-first-request"))
|
||||
// expect(request1[3].arrayValue).to(equal([.uint(123)]))
|
||||
//
|
||||
// let request2 = self.requestsFromClient[1]
|
||||
// expect(request2[0].uint64Value).to(equal(MessageType.request.rawValue))
|
||||
// expect(request2[2].stringValue).to(equal("2-second-request"))
|
||||
// expect(request2[3].arrayValue).to(equal([.uint(321)]))
|
||||
//
|
||||
// try! self.clientSocket
|
||||
// .write(from: self.dataForResponse(msgid: 1, error: .nil, params: .float(0.123)))
|
||||
//
|
||||
// try! self.clientSocket
|
||||
// .write(from: self.dataForResponse(msgid: 0, error: .nil, params: .float(0.321)))
|
||||
// }
|
||||
//
|
||||
// expect(responseCount).to(equal(2))
|
||||
// }
|
||||
//
|
||||
// func testNotificationsFromServer() {
|
||||
// DispatchQueue.global(qos: .default).async {
|
||||
// let msgs = try! self.msgpackRpc.stream.toBlocking().toArray()
|
||||
// expect(msgs).to(haveCount(2))
|
||||
//
|
||||
// let (method1, params1) = self.notification(from: msgs[0])
|
||||
// let (method2, params2) = self.notification(from: msgs[1])
|
||||
//
|
||||
// expect(method1).to(equal("first-msg"))
|
||||
// expect(params1).to(haveCount(2))
|
||||
// expect(params1[0].uintValue).to(equal(321))
|
||||
// expect(params1[1].dataValue).to(haveCount(321))
|
||||
//
|
||||
// expect(method2).to(equal("second-msg"))
|
||||
// expect(params2).to(haveCount(2))
|
||||
// expect(params2[0].dataValue).to(haveCount(123))
|
||||
// expect(params2[1].floatValue).to(equal(0.123))
|
||||
//
|
||||
// self.signalEndOfTest()
|
||||
// }
|
||||
//
|
||||
// self.runClient(readBufferSize: Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE)
|
||||
//
|
||||
// self.sendRequests {
|
||||
// let data1 = dataForNotification(
|
||||
// method: "first-msg",
|
||||
// params: [.uint(321), .binary(.random(ofCount: 321))]
|
||||
// )
|
||||
// let data2 = dataForNotification(
|
||||
// method: "second-msg",
|
||||
// params: [.binary(.random(ofCount: 123)), .float(0.123)]
|
||||
// )
|
||||
//
|
||||
// try! self.clientSocket.write(from: data1)
|
||||
// try! self.clientSocket.write(from: data2)
|
||||
//
|
||||
// self.server.shutdownServer()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func testPartialRequestFromServer() {
|
||||
// DispatchQueue.global(qos: .default).async {
|
||||
// let msgs = try! self.msgpackRpc.stream.toBlocking().toArray()
|
||||
// expect(msgs).to(haveCount(2))
|
||||
//
|
||||
// let (method1, params1) = self.notification(from: msgs[0])
|
||||
// let (method2, params2) = self.notification(from: msgs[1])
|
||||
//
|
||||
// expect(method1).to(equal("first-msg"))
|
||||
// expect(params1).to(haveCount(2))
|
||||
// expect(params1[0].uintValue).to(equal(321))
|
||||
// expect(params1[1].dataValue).to(haveCount(321))
|
||||
//
|
||||
// expect(method2).to(equal("second-msg"))
|
||||
// expect(params2).to(haveCount(2))
|
||||
// expect(params2[0].dataValue).to(haveCount(123))
|
||||
// expect(params2[1].floatValue).to(equal(0.123))
|
||||
//
|
||||
// self.signalEndOfTest()
|
||||
// }
|
||||
//
|
||||
// self.runClient(readBufferSize: Socket.SOCKET_MINIMUM_READ_BUFFER_SIZE)
|
||||
// self.sendRequests {
|
||||
// let data1 = dataForNotification(
|
||||
// method: "first-msg",
|
||||
// params: [.uint(321), .binary(.random(ofCount: 321))]
|
||||
// )
|
||||
// let data2 = dataForNotification(
|
||||
// method: "second-msg",
|
||||
// params: [.binary(.random(ofCount: 123)), .float(0.123)]
|
||||
// )
|
||||
//
|
||||
// var msg1 = data1
|
||||
// msg1.append(data2[..<100])
|
||||
// let msg2 = data2[100...]
|
||||
//
|
||||
// try! self.clientSocket.write(from: msg1)
|
||||
// try! self.clientSocket.write(from: msg2)
|
||||
//
|
||||
// self.server.shutdownServer()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// extension RxMsgpackRpcrTests {
|
||||
// func dataForResponse(msgid: UInt64, error: Value, params: Value) -> Data {
|
||||
// pack(.array([
|
||||
// .uint(MessageType.response.rawValue),
|
||||
// .uint(msgid),
|
||||
// error,
|
||||
// params,
|
||||
// ]))
|
||||
// }
|
||||
//
|
||||
// func dataForNotification(method: String, params: [Value]) -> Data {
|
||||
// pack(.array([
|
||||
// .uint(MessageType.notification.rawValue),
|
||||
// .string(method),
|
||||
// .array(params),
|
||||
// ]))
|
||||
// }
|
||||
//
|
||||
// func assertMsgsFromClient(assertFn: @escaping TestServer.DataReadCallback) {
|
||||
// self.server.dataReadCallback = assertFn
|
||||
// }
|
||||
//
|
||||
// func runClient(readBufferSize: Int) {
|
||||
// usleep(500)
|
||||
// _ = try! self.msgpackRpc
|
||||
// .run(at: self.server.path, readBufferSize: readBufferSize)
|
||||
// .toBlocking()
|
||||
// .first()
|
||||
//
|
||||
// self.serverReadyCond.lock()
|
||||
// while self.serverReady == false {
|
||||
// self.serverReadyCond.wait(until: Date(timeIntervalSinceNow: 2 * 60))
|
||||
// }
|
||||
// self.serverReadyCond.unlock()
|
||||
// }
|
||||
//
|
||||
// func sendRequests(requestFn: () -> Void) {
|
||||
// requestFn()
|
||||
// self.waitForAssertions()
|
||||
// }
|
||||
//
|
||||
// func signalEndOfTest() {
|
||||
// self.testFinishedCond.lock()
|
||||
// self.testFinished = true
|
||||
// self.testFinishedCond.signal()
|
||||
// self.testFinishedCond.unlock()
|
||||
// }
|
||||
//
|
||||
// func waitForAssertions() {
|
||||
// var signalled = false
|
||||
// self.testFinishedCond.lock()
|
||||
// while self.testFinished == false {
|
||||
// signalled = self.testFinishedCond.wait(until: Date(timeIntervalSinceNow: 2 * 60))
|
||||
// }
|
||||
// expect(signalled).to(beTrue())
|
||||
// self.testFinishedCond.unlock()
|
||||
// }
|
||||
//
|
||||
// func notification(from msg: Message) -> (method: String, params: [Value]) {
|
||||
// guard case let .notification(method, params) = msg else {
|
||||
// preconditionFailure("\(msg) is not a notification")
|
||||
// }
|
||||
//
|
||||
// return (method: method, params: params)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
///// Modified version of the example in the README of https://github.com/Kitura/BlueSocket.
|
||||
///// It only supports one connection. Everywhere ! and no error handling whatsoever.
|
||||
// class TestServer {
|
||||
// typealias DataReadCallback = (Data, Int) -> Void
|
||||
//
|
||||
// let queue = DispatchQueue(label: "com.qvacua.RxPack.TestServer")
|
||||
// var connectedSocket: Socket!
|
||||
//
|
||||
// var path: String
|
||||
// var readBufferSize: Int = 1024 * 1024
|
||||
// var dataReadCallback: DataReadCallback = { _, _ in }
|
||||
//
|
||||
// private var listenSocket: Socket!
|
||||
//
|
||||
// init(path: String) { self.path = path }
|
||||
// deinit { self.shutdownServer() }
|
||||
//
|
||||
// func waitForClientAndStartReading() {
|
||||
// self.listenSocket = try! Socket.create(family: .unix)
|
||||
//
|
||||
// try! self.listenSocket.listen(on: self.path)
|
||||
// let newSocket = try! self.listenSocket.acceptClientConnection()
|
||||
// self.connectedSocket = newSocket
|
||||
//
|
||||
// self.readData(from: newSocket)
|
||||
// }
|
||||
//
|
||||
// func readData(from socket: Socket) {
|
||||
// self.queue.async { [unowned self] in
|
||||
// var shouldKeepRunning = true
|
||||
//
|
||||
// var readData = Data(capacity: self.readBufferSize)
|
||||
// repeat {
|
||||
// guard let bytesRead = try? socket.read(into: &readData) else {
|
||||
// shouldKeepRunning = false
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// if bytesRead > 0 {
|
||||
// self.dataReadCallback(readData, bytesRead)
|
||||
// } else {
|
||||
// shouldKeepRunning = false
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// readData.count = 0
|
||||
// } while shouldKeepRunning
|
||||
//
|
||||
// socket.close()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func shutdownServer() {
|
||||
// self.connectedSocket?.close()
|
||||
// self.listenSocket?.close()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// extension Data {
|
||||
// static func random(ofCount count: Int) -> Data {
|
||||
// Data((0..<count).map { _ in UInt8.random(in: UInt8.min...UInt8.max) })
|
||||
// }
|
||||
// }
|
||||
|
@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4BEBA5041CFF374B00673FDF"
|
||||
BuildableName = "VimR.app"
|
||||
BlueprintName = "VimR"
|
||||
ReferencedContainer = "container:VimR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4BEBA5041CFF374B00673FDF"
|
||||
BuildableName = "VimR.app"
|
||||
BlueprintName = "VimR"
|
||||
ReferencedContainer = "container:VimR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<LocationScenarioReference
|
||||
identifier = "com.apple.dt.IDEFoundation.CurrentLocationScenarioIdentifier"
|
||||
referenceType = "1">
|
||||
</LocationScenarioReference>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4BEBA5041CFF374B00673FDF"
|
||||
BuildableName = "VimR.app"
|
||||
BlueprintName = "VimR"
|
||||
ReferencedContainer = "container:VimR.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
Loading…
Reference in New Issue
Block a user