Skip to content

Commit 1cc041e

Browse files
committed
Fix resuming downloads and add download tests
1 parent a310fb7 commit 1cc041e

25 files changed

+319
-65
lines changed

.yamato/test.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,6 @@ test_production_trigger:
115115
{% endfor %}
116116
{% endfor %}
117117
triggers:
118-
pull_requests:
119-
- targets:
120-
only:
121-
- "master"
122-
- "/v\\d+\\.\\d+.*/"
123-
- "/release.*/v\\d+\\.\\d+.*/"
124-
branches:
125-
only:
126-
- "master"
127-
- "/v\\d+\\.\\d+.*/"
128-
- "/release.*/v\\d+\\.\\d+.*/"
129118
tags:
130119
only:
131-
- "/.*/"
120+
- "/release.*/v\\d+\\.\\d+.*/"

Unity.Editor.Tasks.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBase", "tests\Helpers\T
1515
EndProject
1616
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helper.CommandLine", "tests\Helpers\Helper.CommandLine\Helper.CommandLine.csproj", "{9A8F0974-A7AE-433B-8C49-5DBD30B0E479}"
1717
EndProject
18+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Helper.WebServer", "tests\Helpers\Helper.WebServer\Helper.WebServer.csproj", "{F946F77C-25DD-44CE-AB8A-5E23528AC70F}"
19+
EndProject
1820
Global
1921
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2022
Debug|Any CPU = Debug|Any CPU
@@ -37,6 +39,10 @@ Global
3739
{9A8F0974-A7AE-433B-8C49-5DBD30B0E479}.Debug|Any CPU.Build.0 = Debug|Any CPU
3840
{9A8F0974-A7AE-433B-8C49-5DBD30B0E479}.Release|Any CPU.ActiveCfg = Release|Any CPU
3941
{9A8F0974-A7AE-433B-8C49-5DBD30B0E479}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{F946F77C-25DD-44CE-AB8A-5E23528AC70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{F946F77C-25DD-44CE-AB8A-5E23528AC70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{F946F77C-25DD-44CE-AB8A-5E23528AC70F}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{F946F77C-25DD-44CE-AB8A-5E23528AC70F}.Release|Any CPU.Build.0 = Release|Any CPU
4046
EndGlobalSection
4147
GlobalSection(SolutionProperties) = preSolution
4248
HideSolutionNode = FALSE
@@ -45,6 +51,7 @@ Global
4551
{452FEC2C-09AD-4FB5-965C-29D1FB872A4E} = {13E9D517-C9FB-4DBE-B4B2-10748698E27E}
4652
{93F4BC16-1A50-4DB1-8FA3-78FCDB54B365} = {93CD1CE7-1B0E-47A0-ADC3-F30702D65605}
4753
{9A8F0974-A7AE-433B-8C49-5DBD30B0E479} = {93CD1CE7-1B0E-47A0-ADC3-F30702D65605}
54+
{F946F77C-25DD-44CE-AB8A-5E23528AC70F} = {93CD1CE7-1B0E-47A0-ADC3-F30702D65605}
4855
EndGlobalSection
4956
GlobalSection(ExtensibilityGlobals) = postSolution
5057
SolutionGuid = {11F9BFE4-27A0-4CCF-A3C1-3683D75126A6}

src/com.unity.editor.tasks/Editor/Extensions/PathExtensions.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99

1010
namespace Unity.Editor.Tasks.Extensions
1111
{
12-
public static class PathExtensions
12+
using Internal.IO;
13+
14+
internal static class PathExtensions
1315
{
14-
public static string ToMD5(this string path)
16+
public static string ToMD5(this SPath path)
1517
{
1618
byte[] computeHash;
1719
using (var hash = System.Security.Cryptography.MD5.Create())
1820
{
19-
using (var stream = File.OpenRead(path))
21+
using (var stream = path.OpenRead())
2022
{
2123
computeHash = hash.ComputeHash(stream);
2224
}
@@ -25,12 +27,12 @@ public static string ToMD5(this string path)
2527
return BitConverter.ToString(computeHash).Replace("-", string.Empty).ToLower();
2628
}
2729

28-
public static string ToSha256(this string path)
30+
public static string ToSha256(this SPath path)
2931
{
3032
byte[] computeHash;
3133
using (var hash = System.Security.Cryptography.SHA256.Create())
3234
{
33-
using (var stream = File.OpenRead(path))
35+
using (var stream = path.OpenRead())
3436
{
3537
computeHash = hash.ComputeHash(stream);
3638
}
@@ -39,5 +41,4 @@ public static string ToSha256(this string path)
3941
return BitConverter.ToString(computeHash).Replace("-", string.Empty).ToLower();
4042
}
4143
}
42-
4344
}

src/com.unity.editor.tasks/Editor/Helpers/Utils.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
namespace Unity.Editor.Tasks.Helpers
1212
{
13+
using Internal.IO;
14+
1315
public static class Utils
1416
{
1517
public static bool Copy(Stream source,
@@ -93,9 +95,9 @@ public static bool VerifyFileIntegrity(string file, string hash)
9395
return false;
9496
string actual;
9597
if (hash.Length == 32)
96-
actual = file.ToMD5();
98+
actual = file.ToSPath().ToMD5();
9799
else
98-
actual = file.ToSha256();
100+
actual = file.ToSPath().ToSha256();
99101
return hash.Equals(actual, StringComparison.InvariantCultureIgnoreCase);
100102
}
101103
}

src/com.unity.editor.tasks/Editor/IO/SimpleIO.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,7 @@ public Stream OpenWrite(string path, FileMode mode)
16931693
{
16941694
if (!Path.IsPathRooted(path))
16951695
throw new ArgumentException("OpenWrite requires a rooted path", nameof(path));
1696-
return new FileStream(path, mode);
1696+
return new FileStream(path, mode, FileAccess.Write);
16971697
}
16981698

16991699
public string CurrentDirectory

src/com.unity.editor.tasks/Editor/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
using System.Runtime.CompilerServices;
1010

1111
[assembly: InternalsVisibleTo("Tasks.Tests")]
12+
[assembly: InternalsVisibleTo("Helper.WebServer")]
1213
[assembly: InternalsVisibleTo("com.unity.editor.tasks.tests")]

src/com.unity.editor.tasks/Editor/Tasks/DownloadTask.cs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Unity.Editor.Tasks
1313
{
14+
using Internal.IO;
1415
using Logging;
1516
using Unity.Editor.Tasks.Helpers;
1617

@@ -31,7 +32,7 @@ public DownloadTask(
3132
RetryCount = retryCount;
3233
Url = url;
3334
Filename = string.IsNullOrEmpty(filename) ? url.Filename : filename;
34-
TargetDirectory = targetDirectory;
35+
this.targetDirectory = targetDirectory.ToSPath();
3536
Name = $"Download {Url}";
3637
Message = Filename;
3738
}
@@ -73,8 +74,8 @@ protected virtual string RunDownload(bool success)
7374
Exception exception = null;
7475
var attempts = 0;
7576
bool result = false;
76-
var partialFile = Path.Combine(TargetDirectory, Filename + ".partial");
77-
Directory.CreateDirectory(TargetDirectory);
77+
var partialFile = targetDirectory.Combine(Filename + ".partial");
78+
targetDirectory.EnsureDirectoryExists();
7879
do
7980
{
8081
exception = null;
@@ -86,7 +87,7 @@ protected virtual string RunDownload(bool success)
8687
{
8788
Logger.Trace($"Download of {Url} to {Destination} Attempt {attempts + 1} of {RetryCount + 1}");
8889

89-
using (var destinationStream = File.OpenWrite(partialFile))
90+
using (var destinationStream = partialFile.OpenWrite(FileMode.Append))
9091
{
9192
result = Downloader.Download(Logger, Url, destinationStream,
9293
(value, total) => {
@@ -97,7 +98,7 @@ protected virtual string RunDownload(bool success)
9798

9899
if (result)
99100
{
100-
File.Move(partialFile, Destination);
101+
partialFile.Move(Destination);
101102
}
102103
}
103104
catch (Exception ex)
@@ -118,11 +119,12 @@ protected virtual string RunDownload(bool success)
118119

119120
public UriString Url { get; }
120121

121-
public string TargetDirectory { get; }
122+
private SPath targetDirectory;
123+
public string TargetDirectory => targetDirectory.ToString();
122124

123125
public string Filename { get; }
124126

125-
public string Destination => Path.Combine(TargetDirectory, Filename);
127+
public string Destination => targetDirectory.Combine(Filename).ToString();
126128

127129
protected int RetryCount { get; }
128130
}
@@ -170,10 +172,25 @@ public DownloadData(UriString url, string file)
170172

171173
public class Downloader : TaskQueue<string, DownloadData>
172174
{
175+
/// <summary>
176+
/// Called for every queued download task when it finishes.
177+
/// </summary>
173178
public event Action<UriString, string> OnDownloadComplete;
179+
/// <summary>
180+
/// Called for every queued download task when it fails.
181+
/// </summary>
174182
public event Action<UriString, Exception> OnDownloadFailed;
183+
/// <summary>
184+
/// Called for every queued download task when it starts.
185+
/// </summary>
175186
public event Action<UriString> OnDownloadStart;
176187

188+
/// <summary>
189+
/// TaskQueue of DownloaderTask objects that can download multiple
190+
/// things in parallel.
191+
/// </summary>
192+
/// <param name="taskManager"></param>
193+
/// <param name="token"></param>
177194
public Downloader(ITaskManager taskManager, CancellationToken token = default)
178195
: base(taskManager, t => {
179196
var dt = t as DownloadTask;

src/com.unity.editor.tasks/Tests/Editor/BaseTest_Shared.cs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
namespace BaseTests
1313
{
14+
using SpoiledCat.Tests.TestWebServer;
1415
using System.Threading;
16+
using Unity.Editor.Tasks.Helpers;
1517
using Unity.Editor.Tasks.Internal.IO;
1618

1719
internal class TestData : IDisposable
@@ -24,12 +26,18 @@ internal class TestData : IDisposable
2426
public readonly IEnvironment Environment;
2527
public readonly IProcessManager ProcessManager;
2628
private readonly CancellationTokenSource cts;
29+
public readonly SPath SourceDirectory;
30+
#if NUNIT
31+
public readonly HttpServer HttpServer;
32+
#endif
33+
2734

28-
public TestData(string testName, ILogging logger)
35+
public TestData(string testName, ILogging logger, bool withHttpServer = false)
2936
{
3037
TestName = testName;
3138
Logger = logger;
3239
Watch = new Stopwatch();
40+
SourceDirectory = TestContext.CurrentContext.TestDirectory.ToSPath();
3341
TestPath = SPath.CreateTempDirectory(testName);
3442
TaskManager = new TaskManager();
3543
cts = CancellationTokenSource.CreateLinkedTokenSource(TaskManager.Token);
@@ -49,6 +57,19 @@ public TestData(string testName, ILogging logger)
4957
InitializeEnvironment();
5058
ProcessManager = new ProcessManager(Environment);
5159

60+
#if NUNIT
61+
if (withHttpServer)
62+
{
63+
var filesToServePath = SourceDirectory.Combine("files");
64+
HttpServer = new HttpServer(filesToServePath, 0);
65+
var started = new ManualResetEventSlim();
66+
var task = TaskManager.With(HttpServer.Start, TaskAffinity.None);
67+
task.OnStart += _ => started.Set();
68+
task.Start();
69+
started.Wait();
70+
}
71+
#endif
72+
5273
Logger.Trace($"START {testName}");
5374
Watch.Start();
5475
}
@@ -84,6 +105,16 @@ private void InitializeEnvironment()
84105
public void Dispose()
85106
{
86107
Watch.Stop();
108+
#if NUNIT
109+
try
110+
{
111+
if (HttpServer != null)
112+
{
113+
HttpServer.Stop();
114+
}
115+
}
116+
catch { }
117+
#endif
87118

88119
ProcessManager.Dispose();
89120
if (SynchronizationContext.Current is IMainThreadSynchronizationContext ourContext)
@@ -98,11 +129,9 @@ public void Dispose()
98129

99130
public partial class BaseTest
100131
{
101-
protected const int Timeout = 30000;
132+
protected const int Timeout = 3000;
102133
protected const int RandomSeed = 120938;
103134

104-
105-
106135
protected void StartTrackTime(Stopwatch watch, ILogging logger, string message = "")
107136
{
108137
if (!string.IsNullOrEmpty(message))
@@ -181,6 +210,12 @@ public static void MatchesUnsorted<T>(this IEnumerable<T> actual, IEnumerable<T>
181210
public static void Matches(this string actual, string expected) => Assert.AreEqual(expected, actual);
182211
public static void Matches(this int actual, int expected) => Assert.AreEqual(expected, actual);
183212
public static void Matches(this SPath actual, SPath expected) => Assert.AreEqual(expected, actual);
213+
214+
public static UriString FixPort(this UriString url, int port)
215+
{
216+
var uri = url.ToUri();
217+
return UriString.TryParse(new UriBuilder(uri.Scheme, uri.Host, port, uri.PathAndQuery).Uri.ToString());
218+
}
184219
}
185220

186221
static class KeyValuePair

tests/Helpers/Helper.WebServer/Helper.WebServer.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
</None>
1010
</ItemGroup>
1111
<ItemGroup>
12-
<ProjectReference Include="..\..\..\src\com.spoiledcat.logging\Editor\SpoiledCat.Unity.Logging.csproj" />
13-
<ProjectReference Include="..\..\..\src\com.spoiledcat.simplejson\Editor\SpoiledCat.Unity.SimpleJson.csproj" />
12+
<ProjectReference Include="..\..\..\src\com.unity.editor.tasks\Editor\Unity.Editor.Tasks.csproj" />
1413
</ItemGroup>
1514
</Project>

tests/Helpers/Helper.WebServer/HttpServer.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99

1010
namespace SpoiledCat.Tests.TestWebServer
1111
{
12-
using System.Xml;
13-
using Logging;
14-
using Json;
12+
using Unity.Editor.Tasks.Logging;
1513

1614
public class HttpServer
1715
{
@@ -39,7 +37,7 @@ public class HttpServer
3937
/// <param name="port">Port of the server.</param>
4038
public HttpServer(string path = null, int port = 0)
4139
{
42-
if (String.IsNullOrEmpty(path) || !Directory.Exists(path))
40+
if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
4341
{
4442
path = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), "files");
4543
}
@@ -92,31 +90,6 @@ private void Process(HttpListenerContext context)
9290
{
9391
Logger.Info("Handling request {0}", context.Request.Url.AbsolutePath);
9492

95-
if (context.Request.Url.AbsolutePath == "/api/usage/unity")
96-
{
97-
var streamReader = new StreamReader(context.Request.InputStream);
98-
string body = null;
99-
using (streamReader)
100-
{
101-
body = streamReader.ReadToEnd();
102-
}
103-
104-
Logger.Info(body);
105-
106-
var json = new { result = "Cool unity usage" }.ToJson();
107-
context.Response.StatusCode = (int)HttpStatusCode.OK;
108-
context.Response.ContentLength64 = json.Length;
109-
110-
string mime;
111-
context.Response.ContentType = mimeTypeMappings.TryGetValue(".json", out mime)
112-
? mime
113-
: "application/octet-stream";
114-
Utils.Copy(new MemoryStream(Encoding.UTF8.GetBytes(json)), context.Response.OutputStream, json.Length);
115-
context.Response.OutputStream.Flush();
116-
context.Response.Close();
117-
return;
118-
}
119-
12093
var filename = context.Request.Url.AbsolutePath;
12194
filename = filename.TrimStart('/');
12295
filename = filename.Replace('/', Path.DirectorySeparatorChar);
@@ -139,6 +112,7 @@ private void Process(HttpListenerContext context)
139112

140113
context.Response.AddHeader("Date", DateTime.Now.ToString("r"));
141114
context.Response.AddHeader("Last-Modified", File.GetLastWriteTime(filename).ToString("r"));
115+
context.Response.AddHeader("Accept-Ranges", "bytes");
142116

143117
using (var input = new FileStream(filename, FileMode.Open))
144118
{
@@ -168,7 +142,7 @@ private void Process(HttpListenerContext context)
168142
if (input.CanSeek && (input.Length > start) && (end <= input.Length))
169143
{
170144
context.Response.StatusCode = (int)HttpStatusCode.PartialContent;
171-
context.Response.Headers.Add("Content-Range", $"{start}-{end}/{input.Length}");
145+
context.Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{input.Length}");
172146
input.Seek(start, SeekOrigin.Current);
173147
}
174148
else

0 commit comments

Comments
 (0)