Skip to content

Commit

Permalink
Set ICloneable to out T
Browse files Browse the repository at this point in the history
Add clone tests
  • Loading branch information
wherewhere committed Apr 6, 2024
1 parent 4714c48 commit 33ab62c
Show file tree
Hide file tree
Showing 18 changed files with 200 additions and 66 deletions.
17 changes: 17 additions & 0 deletions AdvancedSharpAdbClient.Tests/AdbClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,23 @@ public void GetFeatureSetTest()
Assert.Equal("stat_v2", features.LastOrDefault());
}

/// <summary>
/// Tests the <see cref="AdbClient.Clone()"/> method.
/// </summary>
[Fact]
public void CloneTest()
{
Assert.True(TestClient is ICloneable<IAdbClient>);
#if WINDOWS10_0_17763_0_OR_GREATER
Assert.True(TestClient is ICloneable<IAdbClient.IWinRT>);
#endif
AdbClient client = TestClient.Clone();
Assert.Equal(TestClient.EndPoint, client.EndPoint);
DnsEndPoint endPoint = new("localhost", 5555);
client = TestClient.Clone(endPoint);
Assert.Equal(endPoint, client.EndPoint);
}

private void RunConnectTest(Action test, string connectString)
{
string[] requests = [$"host:connect:{connectString}"];
Expand Down
14 changes: 14 additions & 0 deletions AdvancedSharpAdbClient.Tests/AdbServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,20 @@ public void StopServerTest()
Assert.Equal("host:kill", socket.Requests[0]);
}

/// <summary>
/// Tests the <see cref="AdbServer.Clone()"/> method.
/// </summary>
[Fact]
public void CloneTest()
{
DnsEndPoint endPoint = new("localhost", 5555);
Assert.True(adbServer is ICloneable<IAdbServer>);
AdbServer server = adbServer.Clone();
Assert.Equal(adbServer.EndPoint, server.EndPoint);
server = adbServer.Clone(endPoint);
Assert.Equal(endPoint, server.EndPoint);
}

/// <summary>
/// Tests the <see cref="AdbServer(EndPoint, Func{EndPoint, IAdbSocket}, Func{string, IAdbCommandLineClient})"/> method.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions AdvancedSharpAdbClient.Tests/AdbSocketTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,21 @@ public void GetShellStreamTest()
Assert.Equal(tcpSocket.OutputStream, shellStream.Inner);
}


/// <summary>
/// Tests the <see cref="AdbSocket.Clone()"/> method.
/// </summary>
[Fact]
public void CloneTest()
{
using DummyTcpSocket tcpSocket = new();
using AdbSocket adbSocket = new(tcpSocket);
Assert.True(adbSocket is ICloneable<IAdbSocket>);
using AdbSocket socket = adbSocket.Clone();
Assert.NotEqual(adbSocket.Socket, socket.Socket);
Assert.Equal(adbSocket.Connected, socket.Connected);
}

private static void RunTest(Action<IAdbSocket> test, byte[] expectedDataSent)
{
using DummyTcpSocket tcpSocket = new();
Expand Down
12 changes: 12 additions & 0 deletions AdvancedSharpAdbClient.Tests/DeviceMonitorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,5 +283,17 @@ public void AdbKilledTest()
Assert.True(Socket.DidReconnect);
Assert.True(dummyAdbServer.WasRestarted);
}

/// <summary>
/// Tests the <see cref="DeviceMonitor.Clone()"/> method.
/// </summary>
[Fact]
public void CloneTest()
{
using DeviceMonitor deviceMonitor = new(Socket);
Assert.True(deviceMonitor is ICloneable<IDeviceMonitor>);
using DeviceMonitor monitor = deviceMonitor.Clone();
Assert.NotEqual(deviceMonitor.Socket, monitor.Socket);
}
}
}
32 changes: 23 additions & 9 deletions AdvancedSharpAdbClient.Tests/Dummys/DummyAdbSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace AdvancedSharpAdbClient.Tests
/// <summary>
/// A mock implementation of the <see cref="IAdbSocket"/> class.
/// </summary>
internal class DummyAdbSocket : IDummyAdbSocket
internal class DummyAdbSocket : IDummyAdbSocket, ICloneable<DummyAdbSocket>
{
/// <summary>
/// Use this message to cause <see cref="ReadString"/> and <see cref="ReadStringAsync(CancellationToken)"/> to throw
Expand All @@ -21,21 +21,21 @@ internal class DummyAdbSocket : IDummyAdbSocket

public DummyAdbSocket() => IsConnected = true;

public Queue<AdbResponse> Responses { get; } = new Queue<AdbResponse>();
public Queue<AdbResponse> Responses { get; init; } = new Queue<AdbResponse>();

public Queue<SyncCommand> SyncResponses { get; } = new Queue<SyncCommand>();
public Queue<SyncCommand> SyncResponses { get; init; } = new Queue<SyncCommand>();

public Queue<byte[]> SyncDataReceived { get; } = new Queue<byte[]>();
public Queue<byte[]> SyncDataReceived { get; init; } = new Queue<byte[]>();

public Queue<byte[]> SyncDataSent { get; } = new Queue<byte[]>();
public Queue<byte[]> SyncDataSent { get; init; } = new Queue<byte[]>();

public Queue<string> ResponseMessages { get; } = new Queue<string>();
public Queue<string> ResponseMessages { get; init; } = new Queue<string>();

public List<string> Requests { get; } = [];
public List<string> Requests { get; init; } = [];

public List<(SyncCommand, string)> SyncRequests { get; } = [];
public List<(SyncCommand, string)> SyncRequests { get; init; } = [];

public Queue<Stream> ShellStreams { get; } = new Queue<Stream>();
public Queue<Stream> ShellStreams { get; init; } = new Queue<Stream>();

public bool IsConnected { get; set; }

Expand Down Expand Up @@ -295,5 +295,19 @@ public async Task ReconnectAsync(bool isForce, CancellationToken cancellationTok
await Task.Yield();
DidReconnect = true;
}

public DummyAdbSocket Clone() => new()
{
Responses = Responses,
SyncResponses = SyncResponses,
SyncDataReceived = SyncDataReceived,
SyncDataSent = SyncDataSent,
ResponseMessages = ResponseMessages,
Requests = Requests,
SyncRequests = SyncRequests,
ShellStreams = ShellStreams
};

object ICloneable.Clone() => Clone();
}
}
14 changes: 13 additions & 1 deletion AdvancedSharpAdbClient.Tests/Dummys/DummyTcpSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace AdvancedSharpAdbClient.Tests
/// <summary>
/// A mock implementation of the <see cref="ITcpSocket"/> class.
/// </summary>
internal class DummyTcpSocket : ITcpSocket
internal class DummyTcpSocket : ITcpSocket, ICloneable<DummyTcpSocket>
{
/// <summary>
/// The stream from which the <see cref="DummyTcpSocket"/> reads.
Expand Down Expand Up @@ -113,5 +113,17 @@ public async ValueTask<int> SendAsync(ReadOnlyMemory<byte> buffer, SocketFlags s
}

public byte[] GetBytesSent() => OutputStream.ToArray();

public DummyTcpSocket Clone()
{
DummyTcpSocket socket = new()
{
Connected = true,
ReceiveBufferSize = ReceiveBufferSize
};
return socket;
}

object ICloneable.Clone() => Clone();
}
}
4 changes: 4 additions & 0 deletions AdvancedSharpAdbClient.Tests/Dummys/TracingAdbSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ namespace AdvancedSharpAdbClient.Tests
{
internal class TracingAdbSocket(EndPoint endPoint) : AdbSocket(endPoint), IDummyAdbSocket
{
private readonly EndPoint endPoint = endPoint;

public bool DoDispose { get; set; }

public Queue<AdbResponse> Responses { get; } = new Queue<AdbResponse>();
Expand Down Expand Up @@ -178,5 +180,7 @@ public override async Task<string> ReadStringAsync(CancellationToken cancellatio
Reconnect(isForce);
DidReconnect = true;
}

public override AdbSocket Clone() => new TracingAdbSocket(endPoint);
}
}
30 changes: 30 additions & 0 deletions AdvancedSharpAdbClient.Tests/SyncServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,5 +210,35 @@ public void IsProcessingTest()
Assert.True(service.IsOutdate);
});
}

/// <summary>
/// Tests the <see cref="SyncService.Clone()"/> method.
/// </summary>
[Fact]
public void CloneTest()
{
DummyAdbSocket socket = new()
{
Requests =
{
"host:transport:169.254.109.177:5555",
"sync:",
"host:transport:169.254.109.177:5555",
"sync:"
}
};
socket.Responses.Enqueue(AdbResponse.OK);
socket.Responses.Enqueue(AdbResponse.OK);
socket.Responses.Enqueue(AdbResponse.OK);
socket.Responses.Enqueue(AdbResponse.OK);
using SyncService syncService = new(socket, Device);
Assert.True(syncService is ICloneable<ISyncService>);
#if WINDOWS10_0_17763_0_OR_GREATER
Assert.True(syncService is ICloneable<ISyncService.IWinRT>);
#endif
using SyncService service = syncService.Clone();
Assert.NotEqual(syncService.Socket, service.Socket);
Assert.Equal(syncService.Device, service.Device);
}
}
}
15 changes: 15 additions & 0 deletions AdvancedSharpAdbClient.Tests/TcpSocketTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,20 @@ public void CreateUnsupportedSocketTest()
using TcpSocket socket = new();
_ = Assert.Throws<NotSupportedException>(() => socket.Connect(new CustomEndPoint()));
}

/// <summary>
/// Tests the <see cref="TcpSocket.Clone()"/> method.
/// </summary>
[Fact]
public void CloneTest()
{
using TcpSocket tcpSocket = new();
Assert.True(tcpSocket is ICloneable<ITcpSocket>);
Assert.Throws<ArgumentNullException>(tcpSocket.Clone);
tcpSocket.Connect(new DnsEndPoint("www.bing.com", 80));
using TcpSocket socket = tcpSocket.Clone();
Assert.Equal(tcpSocket.EndPoint, socket.EndPoint);
Assert.True(socket.Connected);
}
}
}
18 changes: 6 additions & 12 deletions AdvancedSharpAdbClient/AdbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ namespace AdvancedSharpAdbClient
/// <para><seealso href="https://github.com/android/platform_system_core/blob/master/adb/adb.c">adb.c</seealso></para>
/// </remarks>
[DebuggerDisplay($"{nameof(AdbClient)} \\{{ {nameof(EndPoint)} = {{{nameof(EndPoint)}}} }}")]
public partial class AdbClient : IAdbClient, ICloneable<IAdbClient>, ICloneable
public partial class AdbClient : IAdbClient, ICloneable<AdbClient>, ICloneable
#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
, IAdbClient.IWinRT, ICloneable<IAdbClient.IWinRT>
, IAdbClient.IWinRT
#endif
{
/// <summary>
Expand Down Expand Up @@ -1133,20 +1133,14 @@ public IEnumerable<string> GetFeatureSet(DeviceData device)
public override string ToString() => $"The {nameof(AdbClient)} communicate with adb server at {EndPoint}";

/// <summary>
/// Creates a new <see cref="IAdbClient"/> object that is a copy of the current instance with new <see cref="EndPoint"/>.
/// Creates a new <see cref="AdbClient"/> object that is a copy of the current instance with new <see cref="EndPoint"/>.
/// </summary>
/// <param name="endPoint">The new <see cref="EndPoint"/> to use.</param>
/// <returns>A new <see cref="IAdbClient"/> object that is a copy of this instance with new <see cref="EndPoint"/>.</returns>
public virtual IAdbClient Clone(EndPoint endPoint) => new AdbClient(endPoint, AdbSocketFactory);
/// <returns>A new <see cref="AdbClient"/> object that is a copy of this instance with new <see cref="EndPoint"/>.</returns>
public virtual AdbClient Clone(EndPoint endPoint) => new(endPoint, AdbSocketFactory);

/// <inheritdoc/>
public IAdbClient Clone() => Clone(EndPoint);

#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
/// <inheritdoc/>
IAdbClient.IWinRT ICloneable<IAdbClient.IWinRT>.Clone() => Clone(EndPoint) is IAdbClient.IWinRT client ? client
: throw new NotSupportedException($"The {nameof(Clone)} method does not return a {nameof(IAdbClient.IWinRT)} object.");
#endif
public AdbClient Clone() => Clone(EndPoint);

/// <inheritdoc/>
object ICloneable.Clone() => Clone();
Expand Down
10 changes: 5 additions & 5 deletions AdvancedSharpAdbClient/AdbServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace AdvancedSharpAdbClient
/// between clients and devices.</para>
/// </summary>
[DebuggerDisplay($"{nameof(AdbServer)} \\{{ {nameof(EndPoint)} = {{{nameof(EndPoint)}}}, {nameof(CachedAdbPath)} = {{{nameof(CachedAdbPath)}}} }}")]
public partial class AdbServer : IAdbServer, ICloneable<IAdbServer>, ICloneable
public partial class AdbServer : IAdbServer, ICloneable<AdbServer>, ICloneable
{
/// <summary>
/// The minimum version of <c>adb.exe</c> that is supported by this library.
Expand Down Expand Up @@ -274,14 +274,14 @@ public AdbServerStatus GetStatus()
public override string ToString() => $"The {nameof(AdbServer)} communicate with adb at {EndPoint}";

/// <summary>
/// Creates a new <see cref="IAdbServer"/> object that is a copy of the current instance with new <see cref="EndPoint"/>.
/// Creates a new <see cref="AdbServer"/> object that is a copy of the current instance with new <see cref="EndPoint"/>.
/// </summary>
/// <param name="endPoint">The new <see cref="EndPoint"/> to use.</param>
/// <returns>A new <see cref="IAdbServer"/> object that is a copy of this instance with new <see cref="EndPoint"/>.</returns>
public virtual IAdbServer Clone(EndPoint endPoint) => new AdbServer(endPoint, AdbSocketFactory, AdbCommandLineClientFactory);
/// <returns>A new <see cref="AdbServer"/> object that is a copy of this instance with new <see cref="EndPoint"/>.</returns>
public virtual AdbServer Clone(EndPoint endPoint) => new(endPoint, AdbSocketFactory, AdbCommandLineClientFactory);

/// <inheritdoc/>
public IAdbServer Clone() => Clone(EndPoint);
public AdbServer Clone() => Clone(EndPoint);

/// <inheritdoc/>
object ICloneable.Clone() => Clone(EndPoint);
Expand Down
17 changes: 6 additions & 11 deletions AdvancedSharpAdbClient/AdbSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace AdvancedSharpAdbClient
/// <param name="socket">The <see cref="ITcpSocket"/> at which the Android Debug Bridge is listening for clients.</param>
/// <param name="logger">The logger to use when logging.</param>
[DebuggerDisplay($"{nameof(AdbSocket)} \\{{ {nameof(Connected)} = {{{nameof(Connected)}}}, {nameof(Socket)} = {{{nameof(Socket)}}} }}")]
public partial class AdbSocket(ITcpSocket socket, ILogger<AdbSocket>? logger = null) : IAdbSocket, ICloneable<IAdbSocket>, ICloneable
public partial class AdbSocket(ITcpSocket socket, ILogger<AdbSocket>? logger = null) : IAdbSocket, ICloneable<AdbSocket>, ICloneable
{
/// <summary>
/// The logger to use when logging messages.
Expand Down Expand Up @@ -75,7 +75,7 @@ public AdbSocket(string host, int port, ILogger<AdbSocket>? logger = null)
/// <summary>
/// Gets the underlying TCP socket that manages the connection with the ADB server.
/// </summary>
public ITcpSocket Socket { get; init; } = socket ?? throw new ArgumentNullException(nameof(socket));
public ITcpSocket Socket { get; } = socket ?? throw new ArgumentNullException(nameof(socket));

/// <summary>
/// Determines whether the specified reply is okay.
Expand Down Expand Up @@ -560,15 +560,10 @@ public override string ToString() =>
public void Close() => Socket.Dispose();

/// <inheritdoc/>
public virtual IAdbSocket Clone()
{
if (Socket is not ICloneable<ITcpSocket> cloneable)
{
throw new NotSupportedException($"{Socket.GetType()} does not support cloning.");
}
ITcpSocket socket = cloneable.Clone();
return new AdbSocket(socket, logger);
}
public virtual AdbSocket Clone() =>
Socket is ICloneable<ITcpSocket> cloneable
? new AdbSocket(cloneable.Clone(), logger)
: throw new NotSupportedException($"{Socket.GetType()} does not support cloning.");

/// <inheritdoc/>
object ICloneable.Clone() => Clone();
Expand Down
24 changes: 21 additions & 3 deletions AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,33 @@ namespace AdvancedSharpAdbClient.DeviceCommands
[DebuggerDisplay($"{{{nameof(ToString)}(),nq}}")]
public partial record class DeviceClient(IAdbClient AdbClient, DeviceData Device)
{
/// <summary>
/// The <see cref="IAdbClient"/> to use when communicating with the device.
/// </summary>
private IAdbClient adbClient = AdbClient ?? throw new ArgumentNullException(nameof(AdbClient));

/// <summary>
/// The device on which to process command.
/// </summary>
private DeviceData device = DeviceData.EnsureDevice(ref Device);

/// <summary>
/// Gets the <see cref="IAdbClient"/> to use when communicating with the device.
/// </summary>
public IAdbClient AdbClient { get; init; } = AdbClient ?? throw new ArgumentNullException(nameof(AdbClient));
public IAdbClient AdbClient
{
get => adbClient;
init => adbClient = value ?? throw new ArgumentNullException(nameof(AdbClient));
}

/// <summary>
/// Gets the device.
/// Gets the device on which to process command.
/// </summary>
public DeviceData Device { get; init; } = DeviceData.EnsureDevice(ref Device);
public DeviceData Device
{
get => device;
init => device = DeviceData.EnsureDevice(ref value);
}

/// <summary>
/// Gets the current device screen snapshot.
Expand Down
Loading

0 comments on commit 33ab62c

Please sign in to comment.