Skip to content

Commit

Permalink
Fix unit tests and add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
borrrden committed May 26, 2017
1 parent 482c8a9 commit 1433f0e
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/Couchbase.Lite.Tests.Shared/DocumentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ public void TestRemoveKeys()
doc.Contains("name").Should().BeFalse("because that key was removed");
doc.Contains("weight").Should().BeFalse("because that key was removed");
doc.Contains("age").Should().BeFalse("because that key was removed");
doc.Contains("address").Should().BeFalse("because that key was removed");
doc.Contains("active").Should().BeFalse("because that key was removed");
doc.GetDictionary("address").Contains("city").Should().BeFalse("because that key was removed");

var address = doc.GetDictionary("address");
Expand Down
35 changes: 32 additions & 3 deletions src/Couchbase.Lite.Tests.Shared/ReplicationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
using System.Text;
using Couchbase.Lite;
using Couchbase.Lite.Sync;
using FluentAssertions;
using LiteCore;
using LiteCore.Interop;
#if !WINDOWS_UWP
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -58,15 +61,41 @@ public void TestEmptyPush()
PerformReplication(true, false);
}

private void PerformReplication(bool push, bool pull)
// TODO: Figure out what to do about the current .NET limitation
// which doesn't give any information about why a web socket failed
// to connect (https://github.com/dotnet/corefx/issues/13773)
//[Fact]
public void TestAuthenticationFailure()
{
_repl = Db.CreateReplication(new Uri("ws://localhost:4984/seekrit"));
RunReplication(false, true);
_repl.LastError.Should().BeAssignableTo<LiteCoreException>();
_repl.LastError.As<LiteCoreException>().Error.code.Should().Be(401);
_repl.LastError.As<LiteCoreException>().Error.domain.Should().Be(C4ErrorDomain.WebSocketDomain);
}

//[Fact]
public void TestAuthenticationPullHardcoded()
{
_repl = Db.CreateReplication(new Uri("ws://pupshaw:frank@localhost:4984/seekrit"));
RunReplication(false, true);
_repl.LastError.Should().BeNull("because otherwise the authentication failed");
}

private void RunReplication(bool push, bool pull)
{
_repl = Db.CreateReplication(_otherDB);
_repl.Push = push;
_repl.Pull = pull;
_repl.StatusChanged += ReplicationStatusChanged;
_waitAssert = new WaitAssert();
_repl.Start();
_waitAssert.WaitForResult(TimeSpan.FromSeconds(5));
_waitAssert.WaitForResult(TimeSpan.FromSeconds(50));
}

private void PerformReplication(bool push, bool pull)
{
_repl = Db.CreateReplication(_otherDB);
RunReplication(push, pull);
}

private void ReplicationStatusChanged(object sender, ReplicationStatusChangedEventArgs e)
Expand Down
3 changes: 2 additions & 1 deletion src/Couchbase.Lite/API/Database/DocumentChangedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
namespace Couchbase.Lite
{
/// <summary>
/// The arguments for the <see cref="Database.DocumentChanged"/> event
/// The arguments for the <see cref="Database.AddDocumentChangedListener(string, EventHandler{DocumentChangedEventArgs})"/>
/// event
/// </summary>
public sealed class DocumentChangedEventArgs : EventArgs
{
Expand Down
6 changes: 5 additions & 1 deletion src/Couchbase.Lite/API/Document/DictionaryObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,11 @@ private void SetValue(string key, object value, bool isChange)
/// <inheritdoc />
public override bool Contains(string key)
{
return (_dict.ContainsKey(key) && !ReferenceEquals(_dict[key], RemovedValue)) || base.Contains(key);
if (_dict.ContainsKey(key)) {
return !ReferenceEquals(_dict[key], RemovedValue);
}

return base.Contains(key);
}

/// <inheritdoc />
Expand Down
58 changes: 56 additions & 2 deletions src/Couchbase.Lite/API/IQueryRow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
// limitations under the License.
//
using System;
using Couchbase.Lite.Internal.Serialization;
using LiteCore.Interop;

namespace Couchbase.Lite
{
Expand Down Expand Up @@ -54,18 +52,74 @@ public interface IQueryRow

#region Public Methods

/// <summary>
/// Gets the <see cref="Boolean"/> value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
bool GetBoolean(int index);

/// <summary>
/// Gets the <see cref="DateTimeOffset"/> value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
DateTimeOffset GetDate(int index);

/// <summary>
/// Gets the <see cref="Double"/> value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
double GetDouble(int index);

/// <summary>
/// Gets the <see cref="Int32"/> value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
int GetInt(int index);

/// <summary>
/// Gets the <see cref="Int64"/> value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
long GetLong(int index);

/// <summary>
/// Gets the value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
object GetObject(int index);

/// <summary>
/// Gets the <see cref="String"/> value of the nth selected
/// value of the query row (in order of what was specified in the
/// SELECT portion of the query)
/// </summary>
/// <param name="index">The index of the element to retrieve in terms
/// of the SELECT query</param>
/// <returns>The value at the index</returns>
string GetString(int index);

#endregion
Expand Down
9 changes: 9 additions & 0 deletions src/Couchbase.Lite/API/Query/ILiveQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,21 @@

namespace Couchbase.Lite.Query
{
/// <summary>
/// Arguments for the <see cref="ILiveQuery.Changed" /> event
/// </summary>
public sealed class LiveQueryChangedEventArgs : EventArgs
{
#region Properties

/// <summary>
/// Gets the updated rows of the query
/// </summary>
public IReadOnlyList<IQueryRow> Rows { get; }

/// <summary>
/// Gets the error that occurred, if any
/// </summary>
public Exception Error { get; }

#endregion
Expand Down
7 changes: 7 additions & 0 deletions src/Couchbase.Lite/API/Sync/IReplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// limitations under the License.
//
using System;
using System.Collections.Generic;

namespace Couchbase.Lite.Sync
{
Expand Down Expand Up @@ -61,6 +62,12 @@ public interface IReplication : IDisposable
/// </summary>
Exception LastError { get; }

///// <summary>
///// Get or set options affecting replication (See <see cref="ReplicationOptionKeys" />
///// for a list of keys)
///// </summary>
//IDictionary<string, object> Options { get; set; }

/// <summary>
/// Gets the remote <see cref="Database"/> being replicated to, if this is
/// a local replication.
Expand Down
30 changes: 30 additions & 0 deletions src/Couchbase.Lite/API/Sync/ReplicationOptionKeys.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// ReplicationOptionKeys.cs
//
// Author:
// Jim Borden <[email protected]>
//
// Copyright (c) 2017 Couchbase, Inc All rights reserved.
//
// 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.
//

namespace Couchbase.Lite.Sync
{
internal struct ReplicationOptionKeys
{
public static readonly string AuthOption = "auth";
public static readonly string AuthUsername = "username";
public static readonly string AuthPassword = "password";
}
}
25 changes: 15 additions & 10 deletions src/Couchbase.Lite/Sync/Replication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// limitations under the License.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

Expand Down Expand Up @@ -55,6 +56,7 @@ internal sealed unsafe class Replication : IReplication

public bool Continuous { get; set; }
public Database Database { get; }
public IDictionary<string, object> Options { get; set; }
public Database OtherDatabase { get; }
public bool Pull { get; set; }
public bool Push { get; set; }
Expand Down Expand Up @@ -207,12 +209,6 @@ public void Start()
dbNameStr = RemoteUrl.Segments.Last().TrimEnd('/');
}

var database = Database as Database;
var otherDatabase = OtherDatabase as Database;
if (database == null) {
throw new NotSupportedException("Custom IDatabase not supported in Replication");
}

C4Error err;
using (var scheme = new C4String(RemoteUrl?.Scheme))
using (var host = new C4String(RemoteUrl?.Host))
Expand All @@ -229,17 +225,26 @@ public void Start()
path = path.AsC4Slice()
};

var options = Options ?? new Dictionary<string, object>();
var userComponents = RemoteUrl?.UserInfo?.Split(':');
if (userComponents?.Length == 2 && !options.ContainsKey(ReplicationOptionKeys.AuthOption)) {
var auth = new Dictionary<string, object>(2);
auth[ReplicationOptionKeys.AuthUsername] = userComponents[0];
auth[ReplicationOptionKeys.AuthPassword] = userComponents[1];
options[ReplicationOptionKeys.AuthOption] = auth;
}

_callback = new ReplicatorStateChangedCallback(StatusChangedCallback, this);

var otherDb = otherDatabase == null ? null : otherDatabase.c4db;
_repl = Native.c4repl_new(database.c4db, addr, dbNameStr, otherDb, Mkmode(Push, Continuous),
Mkmode(Pull, Continuous), null, _callback, &err);
var otherDb = OtherDatabase == null ? null : OtherDatabase.c4db;
_repl = Native.c4repl_new(Database.c4db, addr, dbNameStr, otherDb, Mkmode(Push, Continuous),
Mkmode(Pull, Continuous), options, _callback, &err);
}

C4ReplicatorStatus status;
if (_repl != null) {
status = Native.c4repl_getStatus(_repl);
database.ActiveReplications.Add(this);
Database.ActiveReplications.Add(this);
} else {
status = new C4ReplicatorStatus {
level = C4ReplicatorActivityLevel.Stopped,
Expand Down
7 changes: 5 additions & 2 deletions src/Couchbase.Lite/Sync/WebSocketTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private static void DoCompleteReceive(C4Socket* socket, ulong bytecount)
socketWrapper.CompletedReceive(bytecount);
}

private static void DoOpen(C4Socket* socket, C4Address* address)
private static void DoOpen(C4Socket* socket, C4Address* address, C4Slice options)
{
var builder = new UriBuilder {
Host = address->hostname.CreateString(),
Expand All @@ -88,7 +88,10 @@ private static void DoOpen(C4Socket* socket, C4Address* address)
return;
}

var socketWrapper = new WebSocketWrapper(uri, socket);
var opts =
FLSliceExtensions.ToObject(NativeRaw.FLValue_FromTrustedData((FLSlice) options)) as
IReadOnlyDictionary<string, object>;
var socketWrapper = new WebSocketWrapper(uri, socket, opts);
var id = Interlocked.Increment(ref _NextID);
socket->nativeHandle = (void*)id;
Sockets[id] = socketWrapper;
Expand Down
21 changes: 19 additions & 2 deletions src/Couchbase.Lite/Sync/WebSocketWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;

using Couchbase.Lite.Logging;
using Couchbase.Lite.Support;
using Couchbase.Lite.Util;
using LiteCore.Interop;

namespace Couchbase.Lite.Sync
Expand All @@ -50,6 +52,7 @@ internal sealed unsafe class WebSocketWrapper
private readonly SerialQueue _queue = new SerialQueue();
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1, 1);
private readonly ManualResetEventSlim _connected = new ManualResetEventSlim();
private readonly IReadOnlyDictionary<string, object> _options;

private readonly C4Socket* _socket;
private readonly Uri _url;
Expand All @@ -66,11 +69,12 @@ internal sealed unsafe class WebSocketWrapper

#region Constructors

public WebSocketWrapper(Uri url, C4Socket* socket)
public WebSocketWrapper(Uri url, C4Socket* socket, IReadOnlyDictionary<string, object> options)
{
WebSocket.Options.AddSubProtocol("BLIP");
_socket = socket;
_url = url;
_options = options;
}

#endregion
Expand Down Expand Up @@ -116,6 +120,7 @@ public void Start()
{
_queue.DispatchAsync(() =>
{
SetupAuth();
var cts = new CancellationTokenSource();
cts.CancelAfter(ConnectTimeout);
WebSocket.ConnectAsync(_url, cts.Token).ContinueWith(t =>
Expand Down Expand Up @@ -148,7 +153,7 @@ public void Write(byte[] data)
_connected.Wait();
var cts = new CancellationTokenSource();
cts.CancelAfter(IdleTimeout);
_mutex.Wait();
_mutex.Wait(cts.Token);
WebSocket.SendAsync(new ArraySegment<byte>(data), WebSocketMessageType.Binary, true, cts.Token)
.ContinueWith(t =>
{
Expand Down Expand Up @@ -231,6 +236,18 @@ private void Receive()

}

private void SetupAuth()
{
var auth = _options?.Get(ReplicationOptionKeys.AuthOption) as IDictionary<string, object>;
if (auth != null) {
var username = auth.GetCast<string>(ReplicationOptionKeys.AuthUsername);
var password = auth.GetCast<string>(ReplicationOptionKeys.AuthPassword);
if (username != null && password != null) {
WebSocket.Options.Credentials = new NetworkCredential(username, password);
}
}
}

#endregion
}
}
Loading

0 comments on commit 1433f0e

Please sign in to comment.