Skip to content

Commit f6c72e6

Browse files
authored
Refactor output providers (#80)
1 parent 52ebb3c commit f6c72e6

File tree

16 files changed

+398
-252
lines changed

16 files changed

+398
-252
lines changed

resources/nuget/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ public interface ISpeedTestService
3232
{
3333
public Task<IServer[]> GetServersAsync(CancellationToken cancellationToken = default);
3434

35-
public Task<ServerLatencyResult> GetServerLatencyAsync(IServer server, CancellationToken cancellationToken = default);
35+
public Task<ServerLatencyResult> GetServerLatencyAsync(IServer server, CancellationToken cancellationToken = default);
36+
public Task<ServerLatencyResult> GetServerLatencyAsync(string serverUrl, CancellationToken cancellationToken = default);
3637
public Task<ServerLatencyResult> GetFastestServerByLatencyAsync(IServer[] servers, CancellationToken cancellationToken = default);
3738

3839
public Task<SpeedTestResult> GetDownloadSpeedAsync(IServer server, CancellationToken cancellationToken = default);
Lines changed: 12 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
using System;
2-
using System.Text.Json;
3-
using System.Text.Json.Serialization;
4-
using ByteSizeLib;
5-
using Humanizer;
61
using NetPace.Core;
2+
using NetPace.Console.ConsoleWriters;
73
using Spectre.Console.Extensions;
84

95
namespace NetPace.Console.Commands;
@@ -12,6 +8,14 @@ public sealed class SpeedTestCommand(IAnsiConsole console, ISpeedTestService spe
128
{
139
protected override async Task<int> ExecuteAsync(CommandContext context, SpeedTestCommandSettings settings, CancellationToken cancellationToken)
1410
{
11+
IConsoleWriter writer = settings switch
12+
{
13+
{ CSV: true } => new CSVConsoleWriter(),
14+
{ Json: true } or { JsonPretty: true } => new JsonConsoleWriter(),
15+
{ Verbosity: Verbosity.Minimal } => new MinimalConsoleWriter(),
16+
_ => new DefaultConsoleWriter()
17+
};
18+
1519
if (settings.Loop)
1620
{
1721
// Run continuously.
@@ -21,7 +25,7 @@ protected override async Task<int> ExecuteAsync(CommandContext context, SpeedTes
2125
try
2226
{
2327
// Run the speed test.
24-
await internalExecuteAsync(includeCSVHeader: firstLoop, settings, cancellationToken);
28+
await writer.PerformSpeedTestAsync(initialSpeedTest: firstLoop, console, clock, speedTestClient, settings, cancellationToken);
2529
}
2630
catch (TaskCanceledException)
2731
{
@@ -58,7 +62,7 @@ protected override async Task<int> ExecuteAsync(CommandContext context, SpeedTes
5862
try
5963
{
6064
// Run the speed test.
61-
await internalExecuteAsync(includeCSVHeader: (i == 0), settings, cancellationToken);
65+
await writer.PerformSpeedTestAsync(initialSpeedTest: (i == 0), console, clock, speedTestClient, settings, cancellationToken);
6266
}
6367
catch (TaskCanceledException)
6468
{
@@ -91,7 +95,7 @@ protected override async Task<int> ExecuteAsync(CommandContext context, SpeedTes
9195
try
9296
{
9397
// Run the speed test.
94-
await internalExecuteAsync(includeCSVHeader: true, settings, cancellationToken);
98+
await writer.PerformSpeedTestAsync(initialSpeedTest: true, console, clock, speedTestClient, settings, cancellationToken);
9599
}
96100
catch (TaskCanceledException)
97101
{
@@ -106,234 +110,4 @@ protected override async Task<int> ExecuteAsync(CommandContext context, SpeedTes
106110

107111
return 0;
108112
}
109-
110-
private async Task internalExecuteAsync(bool includeCSVHeader, SpeedTestCommandSettings settings, CancellationToken cancellationToken)
111-
{
112-
ServerLatencyResult fastest;
113-
114-
if (string.IsNullOrEmpty(settings.ServerUrl))
115-
{
116-
// Get the fastest speed test server.
117-
var servers = await speedTestClient.GetServersAsync(cancellationToken);
118-
fastest = await speedTestClient.GetFastestServerByLatencyAsync(servers, cancellationToken);
119-
}
120-
else
121-
{
122-
// User specified speed test server.
123-
var server = new Core.Clients.Ookla.Server() { Sponsor = "(Unknown)", Url = settings.ServerUrl };
124-
fastest = await speedTestClient.GetServerLatencyAsync(server, cancellationToken);
125-
}
126-
127-
128-
if (!settings.CSV && !settings.Json && !settings.JsonPretty && ((settings.Verbosity & (Verbosity.Normal | Verbosity.Debug)) != 0))
129-
{
130-
console.WriteLine("");
131-
console.WriteLine($"{fastest.Server.Sponsor}", new Style(foreground: Color.Yellow, decoration: Decoration.Bold));
132-
console.WriteLine($"{fastest.Server.Url}");
133-
134-
if (!console.Profile.Capabilities.Interactive)
135-
{
136-
// Add an extra line given the live widget will not appear.
137-
console.WriteLine("");
138-
}
139-
}
140-
141-
142-
// Perform speed test
143-
var (downloadResult, uploadResult) = await PerformSpeedTestAsync(fastest.Server, settings, cancellationToken);
144-
145-
146-
// CSV output overrides the display options below
147-
if (settings.CSV)
148-
{
149-
// Always including the timestamp in the CSV output seems reasonable
150-
settings.IncludeTimestamp = true;
151-
152-
if (settings.CSVHeaderUnits)
153-
{
154-
var downloadFormattedParts = downloadResult.GetSpeedStringParts(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale);
155-
var uploadFormattedParts = uploadResult.GetSpeedStringParts(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale);
156-
157-
// Header row.
158-
if (includeCSVHeader)
159-
{
160-
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
161-
{
162-
settings.IncludeTimestamp ? "Timestamp" : null,
163-
"Latency (ms)",
164-
!settings.NoDownload ? $"Download ({downloadFormattedParts.unit})" : null,
165-
!settings.NoUpload ? $"Upload ({uploadFormattedParts.unit})" : null
166-
}.Where(s => !string.IsNullOrEmpty(s))));
167-
}
168-
169-
// Data row.
170-
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
171-
{
172-
settings.IncludeTimestamp ? clock.Now.ToString(settings.DateTimeFormat) : null,
173-
$"{fastest.Latency}",
174-
!settings.NoDownload ? downloadFormattedParts.speed : null,
175-
!settings.NoUpload ? uploadFormattedParts.speed : null
176-
}.Where(s => !string.IsNullOrEmpty(s))));
177-
}
178-
else
179-
{
180-
// Header row.
181-
if (includeCSVHeader)
182-
{
183-
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
184-
{
185-
settings.IncludeTimestamp ? "Timestamp" : null,
186-
"Latency",
187-
!settings.NoDownload ? "Download" : null,
188-
!settings.NoUpload ? "Upload" : null
189-
}.Where(s => !string.IsNullOrEmpty(s))));
190-
}
191-
192-
// Data row.
193-
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
194-
{
195-
settings.IncludeTimestamp ? clock.Now.ToString(settings.DateTimeFormat) : null,
196-
$"{fastest.Latency} ms",
197-
!settings.NoDownload ? downloadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale) : null,
198-
!settings.NoUpload ? uploadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale) : null
199-
}.Where(s => !string.IsNullOrEmpty(s))));
200-
}
201-
}
202-
// Json output overrides the display options below
203-
else if (settings.Json || settings.JsonPretty)
204-
{
205-
var downloadFormatted = !settings.NoDownload ? downloadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale) : null;
206-
var uploadFormatted = !settings.NoUpload ? uploadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale) : null;
207-
208-
var jsonResult = new JsonResult
209-
{
210-
ServerLocation = fastest.Server.Location,
211-
ServerSponsor = fastest.Server.Sponsor,
212-
ServerUrl = fastest.Server.Url,
213-
Timestamp = clock.Now.ToString(settings.DateTimeFormat),
214-
Latency = $"{fastest.Latency} ms",
215-
DownloadSpeed = downloadFormatted!,
216-
UploadSpeed = uploadFormatted!
217-
};
218-
219-
var options = new JsonSerializerOptions { WriteIndented = settings.JsonPretty, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
220-
string jsonString = JsonSerializer.Serialize(jsonResult, options);
221-
222-
console.WriteLine(jsonString);
223-
}
224-
else
225-
{
226-
if ((settings.Verbosity & Verbosity.Debug) != 0)
227-
{
228-
// Display detailed diagnostics
229-
ByteSize size; TimeSpan elapsed;
230-
231-
if (!settings.NoDownload)
232-
{
233-
size = ByteSize.FromBytes(downloadResult.BytesProcessed);
234-
elapsed = TimeSpan.FromMilliseconds(downloadResult.ElapsedMilliseconds);
235-
console.WriteLine($"{size} downloaded in {elapsed.Humanize()}");
236-
}
237-
if (!settings.NoUpload)
238-
{
239-
size = ByteSize.FromBytes(uploadResult.BytesProcessed);
240-
elapsed = TimeSpan.FromMilliseconds(uploadResult.ElapsedMilliseconds);
241-
console.WriteLine($"{size} uploaded in {elapsed.Humanize()}");
242-
}
243-
244-
if (!(settings.NoDownload && settings.NoUpload))
245-
{
246-
console.WriteLine("");
247-
}
248-
}
249-
250-
if ((settings.NoDownload && settings.NoUpload) && ((settings.Verbosity & (Verbosity.Normal | Verbosity.Debug)) != 0) &&
251-
console.Profile.Capabilities.Interactive)
252-
{
253-
// Latency only test: Add an extra blank line for formatting.
254-
console.WriteLine("");
255-
}
256-
257-
258-
// Display speed test result
259-
console.WriteLine(string.Join(", ", new[]
260-
{
261-
settings.IncludeTimestamp ? clock.Now.ToString(settings.DateTimeFormat) : null,
262-
$"Latency: {fastest.Latency} ms",
263-
!settings.NoDownload ? $"Download: {downloadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale)}" : null,
264-
!settings.NoUpload ? $"Upload: {uploadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale)}" : null
265-
}.Where(s => !string.IsNullOrEmpty(s))));
266-
267-
268-
if ((settings.Verbosity & (Verbosity.Normal | Verbosity.Debug)) != 0)
269-
{
270-
console.WriteLine("\nTry 'NetPace --help' for more information.");
271-
}
272-
}
273-
}
274-
275-
private async Task<(SpeedTestResult downloadResult, SpeedTestResult uploadResult)> PerformSpeedTestAsync(IServer server, SpeedTestCommandSettings settings, CancellationToken cancellationToken)
276-
{
277-
var downloadResult = new SpeedTestResult();
278-
var uploadResult = new SpeedTestResult();
279-
280-
281-
if (settings.NoDownload && settings.NoUpload)
282-
{
283-
// Latency only test - so just return
284-
return (downloadResult, uploadResult);
285-
}
286-
287-
288-
if (settings.CSV || settings.Json || settings.JsonPretty || ((settings.Verbosity & Verbosity.Minimal) != 0))
289-
{
290-
// No progress is reported
291-
if (!settings.NoDownload) downloadResult = await speedTestClient.GetDownloadSpeedAsync(server, settings.DownloadSizeMb, cancellationToken);
292-
if (!settings.NoUpload) uploadResult = await speedTestClient.GetUploadSpeedAsync(server, settings.UploadSizeMb, cancellationToken);
293-
}
294-
else
295-
{
296-
// Graphical progress bar
297-
await console.Progress()
298-
.AutoClear(false)
299-
.Columns(
300-
[
301-
new TaskDescriptionColumn(),
302-
new ProgressBarColumn(),
303-
new PercentageColumn(),
304-
])
305-
.StartAsync(async progress =>
306-
{
307-
ProgressTask? downloadProgress = null; ProgressTask? uploadProgress = null;
308-
309-
// Create the progress bars
310-
if (!settings.NoDownload)
311-
{
312-
downloadProgress = progress.AddTask("Downloading", autoStart: true, maxValue: 100);
313-
}
314-
if (!settings.NoUpload)
315-
{
316-
uploadProgress = progress.AddTask("Uploading", autoStart: true, maxValue: 100);
317-
}
318-
319-
// Perform the speed tests and show progress
320-
if (!settings.NoDownload)
321-
{
322-
downloadResult = await speedTestClient.GetDownloadSpeedAsync(server, settings.DownloadSizeMb, (SpeedTestProgress progress) =>
323-
{
324-
downloadProgress!.Value = progress.PercentageComplete;
325-
}, cancellationToken);
326-
}
327-
if (!settings.NoUpload)
328-
{
329-
uploadResult = await speedTestClient.GetUploadSpeedAsync(server, settings.UploadSizeMb, (SpeedTestProgress progress) =>
330-
{
331-
uploadProgress!.Value = progress.PercentageComplete;
332-
}, cancellationToken);
333-
}
334-
});
335-
}
336-
337-
return (downloadResult, uploadResult);
338-
}
339113
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using NetPace.Core;
2+
3+
namespace NetPace.Console.ConsoleWriters;
4+
5+
public sealed class CSVConsoleWriter : IConsoleWriter
6+
{
7+
public async Task PerformSpeedTestAsync(bool initialSpeedTest, IAnsiConsole console, IClock clock, ISpeedTestService speedTestClient, SpeedTestCommandSettings settings, CancellationToken cancellationToken)
8+
{
9+
ServerLatencyResult fastest;
10+
11+
if (string.IsNullOrEmpty(settings.ServerUrl))
12+
{
13+
// Get the fastest speed test server.
14+
var servers = await speedTestClient.GetServersAsync(cancellationToken);
15+
fastest = await speedTestClient.GetFastestServerByLatencyAsync(servers, cancellationToken);
16+
}
17+
else
18+
{
19+
// User specified speed test server.
20+
fastest = await speedTestClient.GetServerLatencyAsync(settings.ServerUrl, cancellationToken);
21+
}
22+
23+
24+
var downloadResult = new SpeedTestResult();
25+
var uploadResult = new SpeedTestResult();
26+
27+
// Perform speed test.
28+
if (!settings.NoDownload) downloadResult = await speedTestClient.GetDownloadSpeedAsync(fastest.Server, settings.DownloadSizeMb, cancellationToken);
29+
if (!settings.NoUpload) uploadResult = await speedTestClient.GetUploadSpeedAsync(fastest.Server, settings.UploadSizeMb, cancellationToken);
30+
31+
32+
// Display speed test result.
33+
if (settings.CSVHeaderUnits)
34+
{
35+
var downloadFormattedParts = downloadResult.GetSpeedStringParts(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale);
36+
var uploadFormattedParts = uploadResult.GetSpeedStringParts(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale);
37+
38+
// Header row.
39+
if (initialSpeedTest)
40+
{
41+
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
42+
{
43+
"Timestamp",
44+
"Latency (ms)",
45+
!settings.NoDownload ? $"Download ({downloadFormattedParts.unit})" : null,
46+
!settings.NoUpload ? $"Upload ({uploadFormattedParts.unit})" : null
47+
}.Where(s => !string.IsNullOrEmpty(s))));
48+
}
49+
50+
// Data row.
51+
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
52+
{
53+
clock.Now.ToString(settings.DateTimeFormat),
54+
$"{fastest.Latency}",
55+
!settings.NoDownload ? downloadFormattedParts.speed : null,
56+
!settings.NoUpload ? uploadFormattedParts.speed : null
57+
}.Where(s => !string.IsNullOrEmpty(s))));
58+
}
59+
else
60+
{
61+
// Header row.
62+
if (initialSpeedTest)
63+
{
64+
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
65+
{
66+
"Timestamp",
67+
"Latency",
68+
!settings.NoDownload ? "Download" : null,
69+
!settings.NoUpload ? "Upload" : null
70+
}.Where(s => !string.IsNullOrEmpty(s))));
71+
}
72+
73+
// Data row.
74+
console.WriteLine(string.Join(settings.CSVDelimiter, new[]
75+
{
76+
clock.Now.ToString(settings.DateTimeFormat),
77+
$"{fastest.Latency} ms",
78+
!settings.NoDownload ? downloadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale) : null,
79+
!settings.NoUpload ? uploadResult.GetSpeedString(settings.SpeedUnit, settings.SpeedUnitSystem, settings.SpeedScale) : null
80+
}.Where(s => !string.IsNullOrEmpty(s))));
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)