Port everything to the new task system
The history of the task system changes is at https://github.com/shana/task-system (8b3880cd69) Also get rid of the separate NPath provider for cleaner code
This commit is contained in:
Родитель
fa6a7e2a8f
Коммит
782d623653
|
@ -21,9 +21,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Http-net_3_5", "
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CopyLibrariesToDevelopmentFolder", "src\packaging\CopyLibrariesToDevelopmentFolder\CopyLibrariesToDevelopmentFolder.csproj", "{44257C81-EE4A-4817-9AF4-A26C02AA6DD4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "src\UnitTests\UnitTests.csproj", "{69F13D9D-AD56-4EEC-AE10-D528EE23E1A9}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "src\tests\UnitTests\UnitTests.csproj", "{69F13D9D-AD56-4EEC-AE10-D528EE23E1A9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "src\IntegrationTests\IntegrationTests.csproj", "{1AC3F82E-AEAE-4C84-825C-207BB264FCFA}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "src\tests\IntegrationTests\IntegrationTests.csproj", "{1AC3F82E-AEAE-4C84-825C-207BB264FCFA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}"
|
||||
EndProject
|
||||
|
@ -31,7 +31,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "packaging", "packaging", "{
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CopyLibrariesToPackageProject", "src\packaging\CopyLibrariesToPackageProject\CopyLibrariesToPackageProject.csproj", "{7DEF4226-7740-457F-9199-34174C49A978}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "src\TestUtils\TestUtils.csproj", "{66A1D219-F61D-4AE4-9BD7-AAEB97276FFF}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "src\tests\TestUtils\TestUtils.csproj", "{66A1D219-F61D-4AE4-9BD7-AAEB97276FFF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaskSystem", "src\tests\TaskSystemIntegrationTests\TaskSystem.csproj", "{1A382F40-FD9E-43E1-89C1-320073F35CE9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "src\tests\TestApp\TestApp.csproj", "{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -106,6 +110,18 @@ Global
|
|||
{66A1D219-F61D-4AE4-9BD7-AAEB97276FFF}.dev|Any CPU.Build.0 = Debug|Any CPU
|
||||
{66A1D219-F61D-4AE4-9BD7-AAEB97276FFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{66A1D219-F61D-4AE4-9BD7-AAEB97276FFF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9}.dev|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9}.dev|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}.dev|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}.dev|Any CPU.Build.0 = Debug|Any CPU
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -119,5 +135,7 @@ Global
|
|||
{1AC3F82E-AEAE-4C84-825C-207BB264FCFA} = {D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}
|
||||
{7DEF4226-7740-457F-9199-34174C49A978} = {B50B646C-3B86-4BDA-9F2B-766F96608CE0}
|
||||
{66A1D219-F61D-4AE4-9BD7-AAEB97276FFF} = {D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}
|
||||
{1A382F40-FD9E-43E1-89C1-320073F35CE9} = {D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}
|
||||
{08B87D2A-8CF1-4211-B7AA-5209F00F72F8} = {D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -18,6 +18,7 @@ using System.Runtime.InteropServices;
|
|||
[assembly: InternalsVisibleTo("TestUtils", AllInternalsVisible = true)]
|
||||
[assembly: InternalsVisibleTo("UnitTests", AllInternalsVisible = true)]
|
||||
[assembly: InternalsVisibleTo("IntegrationTests", AllInternalsVisible = true)]
|
||||
[assembly: InternalsVisibleTo("TaskSystemIntegrationTests", AllInternalsVisible = true)]
|
||||
|
||||
//Required for NSubstitute
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2", AllInternalsVisible = true)]
|
||||
|
|
|
@ -40,9 +40,9 @@ namespace GitHub.Unity
|
|||
GitClient = new GitClient(Environment, ProcessManager, Platform.CredentialManager, TaskManager);
|
||||
}
|
||||
|
||||
public virtual Task Run()
|
||||
public virtual ITask Run()
|
||||
{
|
||||
Task task = null;
|
||||
ITask task = null;
|
||||
try
|
||||
{
|
||||
task = RunInternal();
|
||||
|
@ -64,8 +64,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
if (FileSystem == null)
|
||||
{
|
||||
FileSystem = new FileSystem();
|
||||
NPathFileSystemProvider.Current = FileSystem;
|
||||
FileSystem = NPath.FileSystem;
|
||||
}
|
||||
|
||||
FileSystem.SetCurrentDirectory(Environment.UnityProjectPath);
|
||||
|
@ -105,45 +104,45 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
private async Task RunInternal()
|
||||
private ITask RunInternal()
|
||||
{
|
||||
await ThreadingHelper.SwitchToThreadAsync();
|
||||
|
||||
var gitSetup = new GitSetup(Environment, FileSystem, CancellationToken);
|
||||
var expectedPath = gitSetup.GitInstallationPath;
|
||||
|
||||
var setupDone = await gitSetup.SetupIfNeeded(
|
||||
//new Progress<float>(x => logger.Trace("Percentage: {0}", x)),
|
||||
//new Progress<long>(x => logger.Trace("Remaining: {0}", x))
|
||||
);
|
||||
|
||||
if (setupDone)
|
||||
Environment.GitExecutablePath = gitSetup.GitExecutablePath;
|
||||
else
|
||||
Environment.GitExecutablePath = await LookForGitInstallationPath();
|
||||
|
||||
logger.Trace("Environment.GitExecutablePath \"{0}\" Exists:{1}", gitSetup.GitExecutablePath, gitSetup.GitExecutablePath.FileExists());
|
||||
|
||||
await RestartRepository();
|
||||
|
||||
if (Environment.IsWindows)
|
||||
return new FuncTask<ProgressReport>(TaskManager.Token, _ => new ProgressReport())
|
||||
.Then(async (s, progress) =>
|
||||
{
|
||||
string credentialHelper = null;
|
||||
var gitConfigGetTask = new GitConfigGetTask("credential.helper", GitConfigSource.Global, CancellationToken);
|
||||
var setupDone = await gitSetup.SetupIfNeeded(progress.Percentage, progress.Remaining);
|
||||
|
||||
if (setupDone)
|
||||
Environment.GitExecutablePath = gitSetup.GitExecutablePath;
|
||||
else
|
||||
Environment.GitExecutablePath = await LookForGitInstallationPath();
|
||||
|
||||
logger.Trace("Environment.GitExecutablePath \"{0}\" Exists:{1}", gitSetup.GitExecutablePath, gitSetup.GitExecutablePath.FileExists());
|
||||
|
||||
await RestartRepository();
|
||||
|
||||
|
||||
await gitConfigGetTask.Task;
|
||||
|
||||
if (string.IsNullOrEmpty(credentialHelper))
|
||||
if (Environment.IsWindows)
|
||||
{
|
||||
var gitConfigSetTask = new GitConfigSetTask("credential.helper", "wincred", GitConfigSource.Global, CancellationToken);
|
||||
string credentialHelper = null;
|
||||
var gitConfigGetTask = new GitConfigGetTask("credential.helper", GitConfigSource.Global, CancellationToken);
|
||||
|
||||
await gitConfigSetTask.Task;
|
||||
|
||||
await gitConfigGetTask.Task;
|
||||
|
||||
if (string.IsNullOrEmpty(credentialHelper))
|
||||
{
|
||||
var gitConfigSetTask = new GitConfigSetTask("credential.helper", "wincred", GitConfigSource.Global, CancellationToken);
|
||||
|
||||
await gitConfigSetTask.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<string> LookForGitInstallationPath()
|
||||
private async Task<NPath> LookForGitInstallationPath()
|
||||
{
|
||||
NPath cachedGitInstallPath = null;
|
||||
var path = SystemSettings.Get("GitInstallPath");
|
||||
|
@ -154,11 +153,11 @@ namespace GitHub.Unity
|
|||
if (cachedGitInstallPath == null ||
|
||||
!cachedGitInstallPath.DirectoryExists())
|
||||
{
|
||||
return await GitEnvironment.FindGitInstallationPath(ProcessManager);
|
||||
return await GitEnvironment.FindGitInstallationPath(ProcessManager).StartAwait();
|
||||
}
|
||||
else
|
||||
{
|
||||
return cachedGitInstallPath.ToString();
|
||||
return cachedGitInstallPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,17 +22,17 @@ namespace GitHub.Unity
|
|||
return Environment.GetEnvironmentVariable(variable);
|
||||
}
|
||||
|
||||
public string UnityApplication { get; set; }
|
||||
public string UnityAssetsPath { get; set; }
|
||||
public string UnityProjectPath { get; set; }
|
||||
public string ExtensionInstallPath { get; set; }
|
||||
public NPath UnityApplication { get; set; }
|
||||
public NPath UnityAssetsPath { get; set; }
|
||||
public NPath UnityProjectPath { get; set; }
|
||||
public NPath ExtensionInstallPath { get; set; }
|
||||
public NPath UserCachePath { get; set; }
|
||||
public NPath SystemCachePath { get; set; }
|
||||
public string Path { get { return Environment.GetEnvironmentVariable("PATH"); } }
|
||||
public NPath Path { get { return Environment.GetEnvironmentVariable("PATH").ToNPath(); } }
|
||||
public string NewLine { get { return Environment.NewLine; } }
|
||||
|
||||
private string gitExecutablePath;
|
||||
public string GitExecutablePath
|
||||
private NPath gitExecutablePath;
|
||||
public NPath GitExecutablePath
|
||||
{
|
||||
get { return gitExecutablePath; }
|
||||
set
|
||||
|
@ -43,8 +43,8 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
private string gitInstallPath;
|
||||
public string GitInstallPath
|
||||
private NPath gitInstallPath;
|
||||
public NPath GitInstallPath
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -55,11 +55,11 @@ namespace GitHub.Unity
|
|||
{
|
||||
if (IsWindows)
|
||||
{
|
||||
gitInstallPath = GitExecutablePath.ToNPath().Parent.Parent;
|
||||
gitInstallPath = GitExecutablePath.Parent.Parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
gitInstallPath = GitExecutablePath.ToNPath().Parent;
|
||||
gitInstallPath = GitExecutablePath.Parent;
|
||||
}
|
||||
logger.Trace("Setting GitInstallPath to " + gitInstallPath);
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
public string RepositoryPath { get { return Repository.LocalPath; } }
|
||||
public NPath RepositoryPath { get { return Repository.LocalPath; } }
|
||||
public IRepository Repository { get; set; }
|
||||
|
||||
public bool IsWindows { get { return OnWindows; } }
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace GitHub.Unity
|
|||
private readonly RepositoryPathConfiguration paths;
|
||||
private readonly CancellationToken cancellationToken;
|
||||
private readonly NPath[] ignoredPaths;
|
||||
private readonly AutoResetEvent autoResetEvent;
|
||||
private readonly ManualResetEventSlim pauseEvent;
|
||||
private readonly bool disableNative;
|
||||
private NativeInterface nativeInterface;
|
||||
private bool running;
|
||||
|
@ -52,11 +52,11 @@ namespace GitHub.Unity
|
|||
this.cancellationToken = cancellationToken;
|
||||
|
||||
ignoredPaths = new[] {
|
||||
platform.Environment.UnityProjectPath.ToNPath().Combine("Library"),
|
||||
platform.Environment.UnityProjectPath.ToNPath().Combine("Temp")
|
||||
platform.Environment.UnityProjectPath.Combine("Library"),
|
||||
platform.Environment.UnityProjectPath.Combine("Temp")
|
||||
};
|
||||
|
||||
autoResetEvent = new AutoResetEvent(false);
|
||||
pauseEvent = new ManualResetEventSlim();
|
||||
disableNative = !platform.Environment.IsWindows;
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,7 @@ namespace GitHub.Unity
|
|||
}
|
||||
|
||||
running = true;
|
||||
pauseEvent.Reset();
|
||||
task = Task.Factory.StartNew(WatcherLoop, cancellationToken, TaskCreationOptions.None, ThreadingHelper.TaskScheduler);
|
||||
}
|
||||
|
||||
|
@ -98,7 +99,7 @@ namespace GitHub.Unity
|
|||
Logger.Trace("Stopping watcher");
|
||||
|
||||
running = false;
|
||||
autoResetEvent.Set();
|
||||
pauseEvent.Set();
|
||||
}
|
||||
|
||||
private void WatcherLoop()
|
||||
|
@ -164,11 +165,11 @@ namespace GitHub.Unity
|
|||
|
||||
if (repositoryChanged)
|
||||
{
|
||||
Logger.Debug("RepositoryChanged");
|
||||
Logger.Trace("RepositoryChanged");
|
||||
RepositoryChanged?.Invoke();
|
||||
}
|
||||
|
||||
if (autoResetEvent.WaitOne(200))
|
||||
if (pauseEvent.Wait(200))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -270,7 +271,7 @@ namespace GitHub.Unity
|
|||
|
||||
var branch = string.Join(@"/", relativePathElements.ToArray());
|
||||
|
||||
Logger.Debug("LocalBranchChanged: {0}", branch);
|
||||
Logger.Trace("LocalBranchChanged: {0}", branch);
|
||||
LocalBranchChanged?.Invoke(branch);
|
||||
}
|
||||
else if (fileEvent.Type == EventType.DELETED)
|
||||
|
@ -290,7 +291,7 @@ namespace GitHub.Unity
|
|||
|
||||
var branch = string.Join(@"/", relativePathElements.ToArray());
|
||||
|
||||
Logger.Debug("LocalBranchDeleted: {0}", branch);
|
||||
Logger.Trace("LocalBranchDeleted: {0}", branch);
|
||||
LocalBranchDeleted?.Invoke(branch);
|
||||
}
|
||||
else if (fileEvent.Type == EventType.RENAMED)
|
||||
|
@ -314,7 +315,7 @@ namespace GitHub.Unity
|
|||
|
||||
var branch = string.Join(@"/", relativePathElements.ToArray());
|
||||
|
||||
Logger.Debug("LocalBranchCreated: {0}", branch);
|
||||
Logger.Trace("LocalBranchCreated: {0}", branch);
|
||||
LocalBranchCreated?.Invoke(branch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,6 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
static class AsyncExtensions
|
||||
{
|
||||
public static void Forget(this Task task)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
static class TaskExt
|
||||
{
|
||||
public static async Task<TEventArgs> FromEvent<TEventHandler, TEventArgs>(
|
||||
|
|
|
@ -151,6 +151,14 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
public static class ListExtensions
|
||||
{
|
||||
public static string Join<T>(this IEnumerable<T> list, string separator)
|
||||
{
|
||||
return String.Join(separator, list.Cast<string>().ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public struct StringResult
|
||||
{
|
||||
public string Chunk;
|
||||
|
|
|
@ -86,136 +86,136 @@ namespace GitHub.Unity
|
|||
public ITask<GitStatus?> Status(IOutputProcessor<GitStatus?> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitStatusTask(new GitObjectFactory(environment), cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<List<GitLogEntry>> Log(BaseOutputListProcessor<GitLogEntry> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitLogTask(new GitObjectFactory(environment), cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> GetConfig(string key, GitConfigSource configSource, IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitConfigGetTask(key, configSource, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> SetConfig(string key, string value, GitConfigSource configSource, IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitConfigSetTask(key, value, configSource, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<List<GitLock>> ListLocks(bool local, BaseOutputListProcessor<GitLock> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitListLocksTask(new GitObjectFactory(environment), local, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Pull(string remote, string branch, IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitPullTask(remote, branch, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Push(string remote, string branch,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitPushTask(remote, branch, true, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Fetch(string remote,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitFetchTask(remote, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> SwitchBranch(string branch,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitSwitchBranchesTask(branch, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> DeleteBranch(string branch, bool deleteUnmerged = false,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitBranchDeleteTask(branch, deleteUnmerged, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> CreateBranch(string branch, string baseBranch,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitBranchCreateTask(branch, baseBranch, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> RemoteAdd(string remote, string url,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitRemoteAddTask(remote, url, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> RemoteRemove(string remote,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitRemoteRemoveTask(remote, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> RemoteChange(string remote, string url,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitRemoteChangeTask(remote, url, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Commit(string message, string body,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitCommitTask(message, body, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Add(List<string> files,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitAddTask(files, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Remove(List<string> files,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitRemoveFromIndexTask(files, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> AddAndCommit(List<string> files, string message, string body,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return Add(files)
|
||||
.ContinueWith(new GitCommitTask(body, message, cancellationToken)
|
||||
.ConfigureGitProcess(processManager));
|
||||
.Then(new GitCommitTask(body, message, cancellationToken)
|
||||
.Configure(processManager));
|
||||
}
|
||||
|
||||
public ITask<string> Lock(string file,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitLockTask(file, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
public ITask<string> Unlock(string file, bool force,
|
||||
IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
{
|
||||
return new GitUnlockTask(file, force, cancellationToken, processor, dependsOn)
|
||||
.ConfigureGitProcess(processManager);
|
||||
.Configure(processManager);
|
||||
}
|
||||
|
||||
protected static ILogging Logger { get; } = Logging.GetLogger<GitClient>();
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
<Compile Include="NewTaskSystem\TaskCanceledExceptions.cs" />
|
||||
<Compile Include="NewTaskSystem\TaskExtensions.cs" />
|
||||
<Compile Include="NewTaskSystem\TaskManager.cs" />
|
||||
<Compile Include="ProgressReport.cs" />
|
||||
<Compile Include="Tasks\Simple\GitLfsInstallTask.cs" />
|
||||
<Compile Include="Extensions\GitStatusExtensions.cs" />
|
||||
<Compile Include="Tasks\Simple\GitRemoteChangeTask.cs" />
|
||||
|
@ -151,7 +152,6 @@
|
|||
<Compile Include="Process\GitRemoteFunction.cs" />
|
||||
<Compile Include="Process\IProcessManager.cs" />
|
||||
<Compile Include="IO\NiceIO.cs" />
|
||||
<Compile Include="IO\NPathFileSystemProvider.cs" />
|
||||
<Compile Include="Process\RemoteListOutputProcessor.cs" />
|
||||
<Compile Include="IRepository.cs" />
|
||||
<Compile Include="ISettings.cs" />
|
||||
|
@ -176,7 +176,6 @@
|
|||
<Compile Include="SimpleJson.cs" />
|
||||
<Compile Include="Tasks\CachedTask.cs" />
|
||||
<Compile Include="Git\FailureSeverity.cs" />
|
||||
<Compile Include="Tasks\Simple\FindGitTask.cs" />
|
||||
<Compile Include="Tasks\Simple\GitAddTask.cs" />
|
||||
<Compile Include="Git\GitBranch.cs" />
|
||||
<Compile Include="Tasks\Simple\GitBranchCreateTask.cs" />
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
public static class Guard
|
||||
class PropertyOrFieldNullException : Exception
|
||||
{
|
||||
public PropertyOrFieldNullException(string message) : base(message)
|
||||
{ }
|
||||
}
|
||||
|
||||
static class Guard
|
||||
{
|
||||
public static void PropertyOrFieldNotNull(object value, string name)
|
||||
{
|
||||
if (value != null) return;
|
||||
string message = String.Format(CultureInfo.InvariantCulture, $"Property or Field {name} is null");
|
||||
throw new PropertyOrFieldNullException(message);
|
||||
}
|
||||
|
||||
public static void ArgumentNotNull(object value, string name)
|
||||
{
|
||||
if (value != null) return;
|
||||
|
@ -76,4 +88,4 @@ namespace GitHub.Unity
|
|||
|
||||
public static bool InUnitTestRunner { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,19 +8,19 @@ namespace GitHub.Unity
|
|||
string GetEnvironmentVariable(string v);
|
||||
string GetSpecialFolder(Environment.SpecialFolder folder);
|
||||
|
||||
string Path { get; }
|
||||
NPath Path { get; }
|
||||
string NewLine { get; }
|
||||
string GitExecutablePath { get; set; }
|
||||
NPath GitExecutablePath { get; set; }
|
||||
bool IsWindows { get; }
|
||||
bool IsLinux { get; }
|
||||
bool IsMac { get; }
|
||||
string UnityApplication { get; set; }
|
||||
string UnityAssetsPath { get; set; }
|
||||
string UnityProjectPath { get; set; }
|
||||
string ExtensionInstallPath { get; set; }
|
||||
NPath UnityApplication { get; set; }
|
||||
NPath UnityAssetsPath { get; set; }
|
||||
NPath UnityProjectPath { get; set; }
|
||||
NPath ExtensionInstallPath { get; set; }
|
||||
NPath UserCachePath { get; set; }
|
||||
string RepositoryPath { get; }
|
||||
string GitInstallPath { get; }
|
||||
NPath RepositoryPath { get; }
|
||||
NPath GitInstallPath { get; }
|
||||
IRepository Repository { get; set; }
|
||||
NPath SystemCachePath { get; set; }
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
public static string FindCommonPath(IEnumerable<string> paths)
|
||||
{
|
||||
var pathsArray = paths.Select(s => s.ToNPath().Parent).ToArray();
|
||||
var pathsArray = paths.Where(s => s != null).Select(s => s.ToNPath().Parent).ToArray();
|
||||
var maxDepth = pathsArray.Max(path => path.Depth);
|
||||
var deepestPath = pathsArray.First(path => path.Depth == maxDepth);
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
using GitHub.Unity;
|
||||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
class NPathFileSystemProvider
|
||||
{
|
||||
public static IFileSystem Current { get; set; }
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -38,7 +37,10 @@ NiceIO
|
|||
GitHub.Unity
|
||||
#endif
|
||||
{
|
||||
public class NPath : IEquatable<NPath>, IComparable
|
||||
#if NICEIO
|
||||
public
|
||||
#endif
|
||||
class NPath : IEquatable<NPath>, IComparable
|
||||
{
|
||||
private static StringComparison? pathStringComparison;
|
||||
private static StringComparison PathStringComparison
|
||||
|
@ -46,7 +48,7 @@ GitHub.Unity
|
|||
get
|
||||
{
|
||||
if (!pathStringComparison.HasValue)
|
||||
pathStringComparison = IsLinux() ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
||||
pathStringComparison = IsLinux ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
||||
return pathStringComparison.Value;
|
||||
}
|
||||
}
|
||||
|
@ -219,7 +221,7 @@ GitHub.Unity
|
|||
ThrowIfRoot();
|
||||
|
||||
var newElements = (string[])_elements.Clone();
|
||||
newElements[newElements.Length - 1] = NPathFileSystemProvider.Current.ChangeExtension(_elements[_elements.Length - 1], WithDot(extension));
|
||||
newElements[newElements.Length - 1] = FileSystem.ChangeExtension(_elements[_elements.Length - 1], WithDot(extension));
|
||||
if (extension == string.Empty)
|
||||
newElements[newElements.Length - 1] = newElements[newElements.Length - 1].TrimEnd('.');
|
||||
return new NPath(newElements, _isRelative, _driveLetter);
|
||||
|
@ -245,7 +247,7 @@ GitHub.Unity
|
|||
|
||||
public string FileNameWithoutExtension
|
||||
{
|
||||
get { return NPathFileSystemProvider.Current.GetFileNameWithoutExtension(FileName); }
|
||||
get { return FileSystem.GetFileNameWithoutExtension(FileName); }
|
||||
}
|
||||
|
||||
public IEnumerable<string> Elements
|
||||
|
@ -275,7 +277,7 @@ GitHub.Unity
|
|||
|
||||
public bool DirectoryExists(NPath append)
|
||||
{
|
||||
return NPathFileSystemProvider.Current.DirectoryExists(Combine(append).ToString());
|
||||
return FileSystem.DirectoryExists(Combine(append).ToString());
|
||||
}
|
||||
|
||||
public bool FileExists(string append = "")
|
||||
|
@ -285,7 +287,7 @@ GitHub.Unity
|
|||
|
||||
public bool FileExists(NPath append)
|
||||
{
|
||||
return NPathFileSystemProvider.Current.FileExists(Combine(append).ToString());
|
||||
return FileSystem.FileExists(Combine(append).ToString());
|
||||
}
|
||||
|
||||
public string ExtensionWithDot
|
||||
|
@ -346,6 +348,13 @@ GitHub.Unity
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static implicit operator string(NPath path)
|
||||
{
|
||||
if (path == null)
|
||||
return null;
|
||||
return path.ToString();
|
||||
}
|
||||
|
||||
static char Slash(SlashMode slashMode)
|
||||
{
|
||||
switch (slashMode)
|
||||
|
@ -355,9 +364,7 @@ GitHub.Unity
|
|||
case SlashMode.Forward:
|
||||
return '/';
|
||||
default:
|
||||
if (NPathFileSystemProvider.Current != null)
|
||||
return NPathFileSystemProvider.Current.DirectorySeparatorChar;
|
||||
return Path.DirectorySeparatorChar;
|
||||
return FileSystem.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,9 +372,7 @@ GitHub.Unity
|
|||
{
|
||||
get
|
||||
{
|
||||
if (NPathFileSystemProvider.Current != null)
|
||||
return NPathFileSystemProvider.Current.DirectorySeparatorChar;
|
||||
return Path.DirectorySeparatorChar;
|
||||
return FileSystem.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,9 +429,9 @@ GitHub.Unity
|
|||
// Suitable nullity checks etc, of course :)
|
||||
hash = hash * 23 + _isRelative.GetHashCode();
|
||||
foreach (var element in _elements)
|
||||
hash = hash * 23 + element.GetHashCode();
|
||||
hash = hash * 23 + (IsLinux ? element : element.ToUpperInvariant()).GetHashCode();
|
||||
if (_driveLetter != null)
|
||||
hash = hash * 23 + _driveLetter.GetHashCode();
|
||||
hash = hash * 23 + (IsLinux ? _driveLetter : _driveLetter.ToUpperInvariant()).GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +476,7 @@ GitHub.Unity
|
|||
|
||||
public IEnumerable<NPath> Files(string filter, bool recurse = false)
|
||||
{
|
||||
return NPathFileSystemProvider.Current.GetFiles(ToString(), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(s => new NPath(s));
|
||||
return FileSystem.GetFiles(ToString(), filter, recurse ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly).Select(s => new NPath(s));
|
||||
}
|
||||
|
||||
public IEnumerable<NPath> Files(bool recurse = false)
|
||||
|
@ -491,7 +496,7 @@ GitHub.Unity
|
|||
|
||||
public IEnumerable<NPath> Directories(string filter, bool recurse = false)
|
||||
{
|
||||
return NPathFileSystemProvider.Current.GetDirectories(ToString(), filter, recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(s => new NPath(s));
|
||||
return FileSystem.GetDirectories(ToString(), filter, recurse ? System.IO.SearchOption.AllDirectories : System.IO.SearchOption.TopDirectoryOnly).Select(s => new NPath(s));
|
||||
}
|
||||
|
||||
public IEnumerable<NPath> Directories(bool recurse = false)
|
||||
|
@ -507,7 +512,7 @@ GitHub.Unity
|
|||
ThrowIfRelative();
|
||||
ThrowIfRoot();
|
||||
EnsureParentDirectoryExists();
|
||||
NPathFileSystemProvider.Current.WriteAllBytes(ToString(), new byte[0]);
|
||||
FileSystem.WriteAllBytes(ToString(), new byte[0]);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -530,7 +535,7 @@ GitHub.Unity
|
|||
if (IsRoot)
|
||||
throw new NotSupportedException("CreateDirectory is not supported on a root level directory because it would be dangerous:" + ToString());
|
||||
|
||||
NPathFileSystemProvider.Current.CreateDirectory(ToString());
|
||||
FileSystem.CreateDirectory(ToString());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -594,7 +599,7 @@ GitHub.Unity
|
|||
|
||||
absoluteDestination.EnsureParentDirectoryExists();
|
||||
|
||||
NPathFileSystemProvider.Current.FileCopy(ToString(), absoluteDestination.ToString(), true);
|
||||
FileSystem.FileCopy(ToString(), absoluteDestination.ToString(), true);
|
||||
return absoluteDestination;
|
||||
}
|
||||
|
||||
|
@ -617,13 +622,13 @@ GitHub.Unity
|
|||
throw new NotSupportedException("Delete is not supported on a root level directory because it would be dangerous:" + ToString());
|
||||
|
||||
if (FileExists())
|
||||
NPathFileSystemProvider.Current.FileDelete(ToString());
|
||||
FileSystem.FileDelete(ToString());
|
||||
else if (DirectoryExists())
|
||||
try
|
||||
{
|
||||
NPathFileSystemProvider.Current.DirectoryDelete(ToString(), true);
|
||||
FileSystem.DirectoryDelete(ToString(), true);
|
||||
}
|
||||
catch (IOException)
|
||||
catch (System.IO.IOException)
|
||||
{
|
||||
if (deleteMode == DeleteMode.Normal)
|
||||
throw;
|
||||
|
@ -657,7 +662,7 @@ GitHub.Unity
|
|||
Files().Delete();
|
||||
Directories().Delete();
|
||||
}
|
||||
catch (IOException)
|
||||
catch (System.IO.IOException)
|
||||
{
|
||||
if (Files(true).Any())
|
||||
throw;
|
||||
|
@ -674,7 +679,7 @@ GitHub.Unity
|
|||
var random = new Random();
|
||||
while (true)
|
||||
{
|
||||
var candidate = new NPath(NPathFileSystemProvider.Current.GetTempPath() + "/" + myprefix + "_" + random.Next());
|
||||
var candidate = new NPath(FileSystem.GetTempPath() + "/" + myprefix + "_" + random.Next());
|
||||
if (!candidate.Exists())
|
||||
return candidate.CreateDirectory();
|
||||
}
|
||||
|
@ -701,13 +706,13 @@ GitHub.Unity
|
|||
if (FileExists())
|
||||
{
|
||||
dest.EnsureParentDirectoryExists();
|
||||
NPathFileSystemProvider.Current.FileMove(ToString(), dest.ToString());
|
||||
FileSystem.FileMove(ToString(), dest.ToString());
|
||||
return dest;
|
||||
}
|
||||
|
||||
if (DirectoryExists())
|
||||
{
|
||||
NPathFileSystemProvider.Current.DirectoryMove(ToString(), dest.ToString());
|
||||
FileSystem.DirectoryMove(ToString(), dest.ToString());
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
@ -722,7 +727,7 @@ GitHub.Unity
|
|||
{
|
||||
get
|
||||
{
|
||||
return new NPath(NPathFileSystemProvider.Current.GetCurrentDirectory());
|
||||
return new NPath(FileSystem.GetCurrentDirectory());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,7 +735,7 @@ GitHub.Unity
|
|||
{
|
||||
get
|
||||
{
|
||||
if (NPathFileSystemProvider.Current.DirectorySeparatorChar == '\\')
|
||||
if (FileSystem.DirectorySeparatorChar == '\\')
|
||||
return new NPath(Environment.GetEnvironmentVariable("USERPROFILE"));
|
||||
return new NPath(Environment.GetEnvironmentVariable("HOME"));
|
||||
}
|
||||
|
@ -740,7 +745,7 @@ GitHub.Unity
|
|||
{
|
||||
get
|
||||
{
|
||||
return new NPath(NPathFileSystemProvider.Current.GetTempPath());
|
||||
return new NPath(FileSystem.GetTempPath());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,7 +788,7 @@ GitHub.Unity
|
|||
public NPath FileMustExist()
|
||||
{
|
||||
if (!FileExists())
|
||||
throw new FileNotFoundException("File was expected to exist : " + ToString());
|
||||
throw new System.IO.FileNotFoundException("File was expected to exist : " + ToString());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -791,7 +796,7 @@ GitHub.Unity
|
|||
public NPath DirectoryMustExist()
|
||||
{
|
||||
if (!DirectoryExists())
|
||||
throw new DirectoryNotFoundException("Expected directory to exist : " + ToString());
|
||||
throw new System.IO.DirectoryNotFoundException("Expected directory to exist : " + ToString());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -855,35 +860,35 @@ GitHub.Unity
|
|||
{
|
||||
ThrowIfRelative();
|
||||
EnsureParentDirectoryExists();
|
||||
NPathFileSystemProvider.Current.WriteAllText(ToString(), contents);
|
||||
FileSystem.WriteAllText(ToString(), contents);
|
||||
return this;
|
||||
}
|
||||
|
||||
public string ReadAllText()
|
||||
{
|
||||
ThrowIfRelative();
|
||||
return NPathFileSystemProvider.Current.ReadAllText(ToString());
|
||||
return FileSystem.ReadAllText(ToString());
|
||||
}
|
||||
|
||||
public NPath WriteAllLines(string[] contents)
|
||||
{
|
||||
ThrowIfRelative();
|
||||
EnsureParentDirectoryExists();
|
||||
NPathFileSystemProvider.Current.WriteAllLines(ToString(), contents);
|
||||
FileSystem.WriteAllLines(ToString(), contents);
|
||||
return this;
|
||||
}
|
||||
|
||||
public string[] ReadAllLines()
|
||||
{
|
||||
ThrowIfRelative();
|
||||
return NPathFileSystemProvider.Current.ReadAllLines(ToString());
|
||||
return FileSystem.ReadAllLines(ToString());
|
||||
}
|
||||
|
||||
public NPath WriteAllBytes(byte[] contents)
|
||||
{
|
||||
ThrowIfRelative();
|
||||
EnsureParentDirectoryExists();
|
||||
NPathFileSystemProvider.Current.WriteAllBytes(ToString(), contents);
|
||||
FileSystem.WriteAllBytes(ToString(), contents);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -907,25 +912,37 @@ GitHub.Unity
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool IsLinux()
|
||||
private static bool? isLinux;
|
||||
private static bool IsLinux
|
||||
{
|
||||
return NPathFileSystemProvider.Current.DirectoryExists("/proc");
|
||||
get
|
||||
{
|
||||
if (!isLinux.HasValue)
|
||||
isLinux = FileSystem.DirectoryExists("/proc");
|
||||
return isLinux.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator NPath(string value)
|
||||
private static IFileSystem _fileSystem;
|
||||
public static IFileSystem FileSystem
|
||||
{
|
||||
if (value == null) return null;
|
||||
|
||||
return new NPath(value);
|
||||
}
|
||||
|
||||
public static implicit operator string(NPath path)
|
||||
{
|
||||
return path?.ToString();
|
||||
get
|
||||
{
|
||||
if (_fileSystem == null)
|
||||
_fileSystem = new FileSystem();
|
||||
return _fileSystem;
|
||||
}
|
||||
set
|
||||
{
|
||||
_fileSystem = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Extensions
|
||||
#if NICEIO
|
||||
public
|
||||
#endif
|
||||
static class Extensions
|
||||
{
|
||||
public static IEnumerable<NPath> Copy(this IEnumerable<NPath> self, string dest)
|
||||
{
|
||||
|
@ -967,6 +984,8 @@ GitHub.Unity
|
|||
|
||||
public static NPath ToNPath(this string path)
|
||||
{
|
||||
if (path == null)
|
||||
return null;
|
||||
return new NPath(path);
|
||||
}
|
||||
}
|
||||
|
@ -983,4 +1002,4 @@ GitHub.Unity
|
|||
Normal,
|
||||
Soft
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace GitHub.Unity
|
|||
/// <summary>
|
||||
/// Gets the local path of the repository.
|
||||
/// </summary>
|
||||
string LocalPath { get; }
|
||||
NPath LocalPath { get; }
|
||||
bool IsGitHub { get; }
|
||||
/// <summary>
|
||||
/// Gets the current remote of the repository.
|
||||
|
|
|
@ -174,7 +174,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
logger.Warning("Archive \"{0}\" missing", archiveFilePath.ToString());
|
||||
|
||||
archiveFilePath = environment.ExtensionInstallPath.ToNPath().Combine(archiveFilePath);
|
||||
archiveFilePath = environment.ExtensionInstallPath.Combine(archiveFilePath);
|
||||
if (!archiveFilePath.FileExists())
|
||||
{
|
||||
logger.Warning("Archive \"{0}\" missing, returning", archiveFilePath.ToString());
|
||||
|
@ -236,7 +236,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
logger.Warning("Archive \"{0}\" missing", archiveFilePath.ToString());
|
||||
|
||||
archiveFilePath = environment.ExtensionInstallPath.ToNPath().Combine(archiveFilePath);
|
||||
archiveFilePath = environment.ExtensionInstallPath.Combine(archiveFilePath);
|
||||
if (!archiveFilePath.FileExists())
|
||||
{
|
||||
logger.Warning("Archive \"{0}\" missing, returning", archiveFilePath.ToString());
|
||||
|
|
|
@ -170,7 +170,7 @@ namespace GitHub.Unity
|
|||
|
||||
public LocalSettings(IEnvironment environment)
|
||||
{
|
||||
SettingsPath = environment.UnityProjectPath.ToNPath().Combine(RelativeSettingsPath);
|
||||
SettingsPath = environment.UnityProjectPath.Combine(RelativeSettingsPath);
|
||||
}
|
||||
|
||||
protected override string SettingsFileName { get { return settingsFileName; } }
|
||||
|
|
|
@ -8,101 +8,172 @@ namespace GitHub.Unity
|
|||
class ActionTask : TaskBase
|
||||
{
|
||||
protected Action<bool> Callback { get; }
|
||||
protected Action<bool, Exception> CallbackWithException { get; }
|
||||
|
||||
public ActionTask(CancellationToken token, Action<bool> action)
|
||||
: base(token)
|
||||
public ActionTask(CancellationToken token, Action<bool> action, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
Name = "ActionTask";
|
||||
}
|
||||
|
||||
public ActionTask(CancellationToken token, Action<bool> action, ITask dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public ActionTask(CancellationToken token, Action<bool, Exception> action, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
this.CallbackWithException = action;
|
||||
Name = "ActionTask<Exception>";
|
||||
}
|
||||
|
||||
public ActionTask(Task task)
|
||||
: base(task)
|
||||
{
|
||||
|
||||
Name = "ActionTask(Task)";
|
||||
}
|
||||
|
||||
protected override void Run(bool success)
|
||||
{
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
base.Run(success);
|
||||
|
||||
RaiseOnStart();
|
||||
Callback?.Invoke(success);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
Callback?.Invoke(success);
|
||||
if (CallbackWithException != null)
|
||||
{
|
||||
Exception thrown = GetThrownException();
|
||||
thrown = thrown != null ? thrown.InnerException : thrown;
|
||||
CallbackWithException?.Invoke(success, thrown);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Errors = ex.Message;
|
||||
exception = ex;
|
||||
}
|
||||
RaiseOnEnd();
|
||||
|
||||
if (!success)
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
class ActionTask<T> : TaskBase
|
||||
{
|
||||
protected Action<bool, T> Callback { get; }
|
||||
protected Action<bool, Exception, T> CallbackWithException { get; }
|
||||
|
||||
public ActionTask(CancellationToken token, Action<bool, T> action, ITask<T> dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public ActionTask(CancellationToken token, Action<bool, T> action, ITask<T> dependsOn, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
Task = new Task(() => Run(DependsOn.Successful, DependsOn.Successful ? ((ITask<T>)DependsOn).Result : default(T)),
|
||||
Token, TaskCreationOptions.None);
|
||||
Name = $"ActionTask<{typeof(T)}>";
|
||||
}
|
||||
|
||||
protected override void Run(bool success)
|
||||
public ActionTask(CancellationToken token, Action<bool, Exception, T> action, ITask<T> dependsOn, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.CallbackWithException = action;
|
||||
Task = new Task(() => Run(DependsOn.Successful, DependsOn.Successful ? ((ITask<T>)DependsOn).Result : default(T)),
|
||||
Token, TaskCreationOptions.None);
|
||||
Name = $"ActionTask<Exception, {typeof(T)}>";
|
||||
}
|
||||
|
||||
public ActionTask(Task task)
|
||||
: base(task)
|
||||
{
|
||||
Name = $"ActionTask<{typeof(T)}>(Task)";
|
||||
}
|
||||
|
||||
protected virtual void Run(bool success, T previousResult)
|
||||
{
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
base.Run(success);
|
||||
|
||||
RaiseOnStart();
|
||||
Callback?.Invoke(success, previousResult);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
Callback?.Invoke(success, previousResult);
|
||||
if (CallbackWithException != null)
|
||||
{
|
||||
Exception thrown = GetThrownException();
|
||||
thrown = thrown != null ? thrown.InnerException : thrown;
|
||||
CallbackWithException?.Invoke(success, thrown, previousResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Errors = ex.Message;
|
||||
exception = ex;
|
||||
}
|
||||
RaiseOnEnd();
|
||||
|
||||
if (!success)
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
class FuncTask<T> : TaskBase<T>
|
||||
{
|
||||
protected Func<bool, T> Callback { get; }
|
||||
protected Func<bool, Exception, T> CallbackWithException { get; }
|
||||
|
||||
public FuncTask(CancellationToken token, Func<bool, T> action)
|
||||
: base(token)
|
||||
public FuncTask(CancellationToken token, Func<bool, T> action, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
Name = $"FuncTask<{typeof(T)}>";
|
||||
}
|
||||
|
||||
public FuncTask(CancellationToken token, Func<bool, T> action, ITask dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public FuncTask(CancellationToken token, Func<bool, Exception, T> action, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
this.CallbackWithException = action;
|
||||
Name = $"FuncTask<Exception, {typeof(T)}>";
|
||||
}
|
||||
|
||||
public FuncTask(Task<T> task)
|
||||
: base(task)
|
||||
{
|
||||
Name = $"FuncTask<{typeof(T)}>(Task)";
|
||||
}
|
||||
|
||||
protected override T RunWithReturn(bool success)
|
||||
{
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
T result = base.RunWithReturn(success);
|
||||
|
||||
RaiseOnStart();
|
||||
|
||||
T result = default(T);
|
||||
result = Callback(success);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
if (Callback != null)
|
||||
{
|
||||
result = Callback(success);
|
||||
}
|
||||
else if (CallbackWithException != null)
|
||||
{
|
||||
Exception thrown = GetThrownException();
|
||||
thrown = thrown != null ? thrown.InnerException : thrown;
|
||||
result = CallbackWithException(success, thrown);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Errors = ex.Message;
|
||||
exception = ex;
|
||||
}
|
||||
RaiseOnEnd();
|
||||
|
||||
if (!success)
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -111,88 +182,174 @@ namespace GitHub.Unity
|
|||
class FuncTask<T, TResult> : TaskBase<T, TResult>
|
||||
{
|
||||
protected Func<bool, T, TResult> Callback { get; }
|
||||
protected Func<bool, Exception, T, TResult> CallbackWithException { get; }
|
||||
|
||||
public FuncTask(CancellationToken token, Func<bool, T, TResult> action, ITask<T> dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public FuncTask(CancellationToken token, Func<bool, T, TResult> action, ITask<T> dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
Name = $"FuncTask<{typeof(T)}, {typeof(TResult)}>";
|
||||
}
|
||||
|
||||
public FuncTask(CancellationToken token, Func<bool, Exception, T, TResult> action, ITask<T> dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.CallbackWithException = action;
|
||||
Name = $"FuncTask<{typeof(T)}, Exception, {typeof(TResult)}>";
|
||||
}
|
||||
|
||||
|
||||
public FuncTask(Task<TResult> task)
|
||||
: base(task)
|
||||
{
|
||||
Name = $"FuncTask<{typeof(T)}, {typeof(TResult)}>(Task)";
|
||||
}
|
||||
|
||||
protected override TResult RunWithData(bool success, T previousResult)
|
||||
{
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
var result = base.RunWithData(success, previousResult);
|
||||
|
||||
RaiseOnStart();
|
||||
var result = Callback(success, previousResult);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
if (Callback != null)
|
||||
{
|
||||
result = Callback(success, previousResult);
|
||||
}
|
||||
else if (CallbackWithException != null)
|
||||
{
|
||||
Exception thrown = GetThrownException();
|
||||
thrown = thrown != null ? thrown.InnerException : thrown;
|
||||
result = CallbackWithException(success, thrown, previousResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Errors = ex.Message;
|
||||
exception = ex;
|
||||
}
|
||||
RaiseOnEnd();
|
||||
|
||||
if (!success)
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class FuncListTask<T> : ListTaskBase<List<T>, T>
|
||||
class FuncListTask<T> : DataTaskBase<T, List<T>>
|
||||
{
|
||||
protected Func<bool, List<T>> Callback { get; }
|
||||
protected Func<bool, Exception, List<T>> CallbackWithException { get; }
|
||||
|
||||
public FuncListTask(CancellationToken token, Func<bool, List<T>> action)
|
||||
: base(token)
|
||||
public FuncListTask(CancellationToken token, Func<bool, List<T>> action, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
}
|
||||
|
||||
public FuncListTask(CancellationToken token, Func<bool, List<T>> action, ITask dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public FuncListTask(CancellationToken token, Func<bool, Exception, List<T>> action, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
this.CallbackWithException = action;
|
||||
}
|
||||
|
||||
public FuncListTask(Task<List<T>> task)
|
||||
: base(task)
|
||||
{ }
|
||||
|
||||
protected override List<T> RunWithReturn(bool success)
|
||||
{
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
var result = base.RunWithReturn(success);
|
||||
|
||||
RaiseOnStart();
|
||||
|
||||
List<T> result = null;
|
||||
result = Callback(success);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
if (Callback != null)
|
||||
{
|
||||
result = Callback(success);
|
||||
}
|
||||
else if (CallbackWithException != null)
|
||||
{
|
||||
Exception thrown = GetThrownException();
|
||||
thrown = thrown != null ? thrown.InnerException : thrown;
|
||||
result = CallbackWithException(success, thrown);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Errors = ex.Message;
|
||||
exception = ex;
|
||||
}
|
||||
RaiseOnEnd();
|
||||
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
if (result == null)
|
||||
result = new List<T>();
|
||||
|
||||
if (!success)
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class FuncListTask<TDependentResult, TResult, TData> : ListTaskBase<TDependentResult, TResult, TData>
|
||||
class FuncListTask<T, TData, TResult> : DataTaskBase<T, TData, List<TResult>>
|
||||
{
|
||||
protected Func<bool, TDependentResult, TResult> Callback { get; }
|
||||
protected Func<bool, T, List<TResult>> Callback { get; }
|
||||
protected Func<bool, Exception, T, List<TResult>> CallbackWithException { get; }
|
||||
|
||||
public FuncListTask(CancellationToken token, Func<bool, TDependentResult, TResult> action, ITask<TDependentResult> dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public FuncListTask(CancellationToken token, Func<bool, T, List<TResult>> action, ITask<T> dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.Callback = action;
|
||||
}
|
||||
|
||||
protected override TResult RunWithData(bool success, TDependentResult previousResult)
|
||||
public FuncListTask(CancellationToken token, Func<bool, Exception, T, List<TResult>> action, ITask<T> dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
Guard.ArgumentNotNull(action, "action");
|
||||
this.CallbackWithException = action;
|
||||
}
|
||||
|
||||
public FuncListTask(Task<List<TResult>> task)
|
||||
: base(task)
|
||||
{ }
|
||||
|
||||
protected override List<TResult> RunWithData(bool success, T previousResult)
|
||||
{
|
||||
var result = base.RunWithData(success, previousResult);
|
||||
|
||||
RaiseOnStart();
|
||||
TResult result = default(TResult);
|
||||
result = Callback(success, previousResult);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
if (Callback != null)
|
||||
{
|
||||
result = Callback(success, previousResult);
|
||||
}
|
||||
else if (CallbackWithException != null)
|
||||
{
|
||||
Exception thrown = GetThrownException();
|
||||
thrown = thrown != null ? thrown.InnerException : thrown;
|
||||
result = CallbackWithException(success, thrown, previousResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Errors = ex.Message;
|
||||
exception = ex;
|
||||
}
|
||||
RaiseOnEnd();
|
||||
|
||||
if (!success)
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace GitHub.Unity
|
|||
event Action<T> OnEntry;
|
||||
}
|
||||
|
||||
interface IOutputProcessor<T, TData> : IOutputProcessor<T>
|
||||
interface IOutputProcessor<TData, T> : IOutputProcessor<T>
|
||||
{
|
||||
new event Action<TData> OnEntry;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace GitHub.Unity
|
|||
protected ILogging Logger { get { return logger = logger ?? Logging.GetLogger(GetType()); } }
|
||||
}
|
||||
|
||||
abstract class BaseOutputProcessor<T, TData> : BaseOutputProcessor<T>, IOutputProcessor<T, TData>
|
||||
abstract class BaseOutputProcessor<TData, T> : BaseOutputProcessor<T>, IOutputProcessor<TData, T>
|
||||
{
|
||||
public new event Action<TData> OnEntry;
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
abstract class BaseOutputListProcessor<T> : BaseOutputProcessor<List<T>, T>
|
||||
abstract class BaseOutputListProcessor<T> : BaseOutputProcessor<T, List<T>>
|
||||
{
|
||||
protected override void RaiseOnEntry(T entry)
|
||||
{
|
||||
|
@ -114,4 +114,16 @@ namespace GitHub.Unity
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class FirstLineIsPathOutputProcessor : FirstResultOutputProcessor<NPath>
|
||||
{
|
||||
protected override bool ProcessLine(string line, out NPath result)
|
||||
{
|
||||
result = null;
|
||||
if (String.IsNullOrEmpty(line))
|
||||
return false;
|
||||
result = line.ToNPath();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@ namespace GitHub.Unity
|
|||
static TaskSchedulerExcludingThread()
|
||||
{
|
||||
executeEntryMethod = typeof(Task).GetMethod("ExecuteEntry", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
}
|
||||
|
||||
public TaskSchedulerExcludingThread(int threadToExclude)
|
||||
{
|
||||
ThreadToExclude = threadToExclude;
|
||||
|
@ -29,7 +29,6 @@ namespace GitHub.Unity
|
|||
|
||||
private static bool ExecuteEntry(Task task, bool flag)
|
||||
{
|
||||
|
||||
return (bool)executeEntryMethod.Invoke(task, new object[] { flag });
|
||||
}
|
||||
|
||||
|
@ -125,9 +124,10 @@ namespace GitHub.Unity
|
|||
exclusiveTaskScheduler = new ConcurrentExclusiveTaskScheduler(this, new Queue<Task>(), 1);
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
public async Task Wait()
|
||||
{
|
||||
taskExecuting?.Wait();
|
||||
if (taskExecuting != null)
|
||||
await taskExecuting;
|
||||
}
|
||||
|
||||
/// <summary>Notifies the interleave that new work has arrived to be processed.</summary>
|
||||
|
@ -139,7 +139,7 @@ namespace GitHub.Unity
|
|||
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
// Otherwise, run the processor. Store the task and then start it to ensure that
|
||||
// Otherwise, run the processor. Store the task and then start it to ensure that
|
||||
// the assignment happens before the body of the task runs.
|
||||
taskExecuting = new Task(ConcurrentExclusiveInterleaveProcessor, CancellationToken.None, TaskCreationOptions.None);
|
||||
taskExecuting.Start(parallelOptions.TaskScheduler);
|
||||
|
@ -168,8 +168,8 @@ namespace GitHub.Unity
|
|||
exclusiveTaskScheduler.ExecuteTask(task);
|
||||
// Just because we executed the task doesn't mean it's "complete",
|
||||
// if it has child tasks that have not yet completed
|
||||
// and will complete later asynchronously. To account for this,
|
||||
// if a task isn't yet completed, leave the interleave processor
|
||||
// and will complete later asynchronously. To account for this,
|
||||
// if a task isn't yet completed, leave the interleave processor
|
||||
// but leave it still in a running state. When the task completes,
|
||||
// we'll come back in and keep going. Note that the children
|
||||
// must not be scheduled to this interleave, or this will deadlock.
|
||||
|
|
|
@ -16,5 +16,6 @@ namespace GitHub.Unity
|
|||
T ScheduleExclusive<T>(T task) where T : ITask;
|
||||
T ScheduleUI<T>(T task) where T : ITask;
|
||||
void Stop();
|
||||
Task Wait();
|
||||
}
|
||||
}
|
|
@ -11,13 +11,13 @@ namespace GitHub.Unity
|
|||
{
|
||||
static class ProcessTaskExtensions
|
||||
{
|
||||
public static T ConfigureGitProcess<T>(this T task, IProcessManager processManager, bool withInput = false)
|
||||
public static T Configure<T>(this T task, IProcessManager processManager, bool withInput = false)
|
||||
where T : IProcess
|
||||
{
|
||||
return processManager.ConfigureGitProcess(task, withInput);
|
||||
return processManager.Configure(task, withInput);
|
||||
}
|
||||
|
||||
public static T Configure<T>(this T task, IProcessManager processManager, string executable, string arguments, string workingDirectory, bool withInput)
|
||||
public static T Configure<T>(this T task, IProcessManager processManager, string executable, string arguments, string workingDirectory = null, bool withInput = false)
|
||||
where T : IProcess
|
||||
{
|
||||
return processManager.Configure(task, executable, arguments, workingDirectory, withInput);
|
||||
|
@ -43,9 +43,9 @@ namespace GitHub.Unity
|
|||
void Configure(ProcessStartInfo psi, IOutputProcessor<T> processor);
|
||||
}
|
||||
|
||||
interface IProcessTask<T, TData> : ITask<T, TData>, IProcess
|
||||
interface IProcessTask<TData, T> : ITask<TData, T>, IProcess
|
||||
{
|
||||
void Configure(ProcessStartInfo psi, IOutputProcessor<T, TData> processor);
|
||||
void Configure(ProcessStartInfo psi, IOutputProcessor<TData, T> processor);
|
||||
}
|
||||
|
||||
class ProcessWrapper
|
||||
|
@ -81,7 +81,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
Process.OutputDataReceived += (s, e) =>
|
||||
{
|
||||
//logger.Trace("OutputData \"" + (e.Data == null ? "'null'" : e.Data) + "\" exited:" + process.HasExited);
|
||||
Logger.Trace("OutputData \"" + (e.Data == null ? "'null'" : e.Data) + "\"");
|
||||
|
||||
string encodedData = null;
|
||||
if (e.Data != null)
|
||||
|
@ -98,7 +98,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
if (e.Data != null)
|
||||
{
|
||||
//logger.Trace("ErrorData \"" + (e.Data == null ? "'null'" : e.Data) + "\" exited:" + process.HasExited);
|
||||
Logger.Trace("ErrorData \"" + (e.Data == null ? "'null'" : e.Data) + "\"");
|
||||
}
|
||||
|
||||
string encodedData = null;
|
||||
|
@ -165,7 +165,7 @@ namespace GitHub.Unity
|
|||
|
||||
private bool WaitForExit(int milliseconds)
|
||||
{
|
||||
//logger.Debug("WaitForExit - time: {0}ms", milliseconds);
|
||||
//Logger.Debug("WaitForExit - time: {0}ms", milliseconds);
|
||||
|
||||
// Workaround for a bug in which some data may still be processed AFTER this method returns true, thus losing the data.
|
||||
// http://connect.microsoft.com/VisualStudio/feedback/details/272125/waitforexit-and-waitforexit-int32-provide-different-and-undocumented-implementations
|
||||
|
@ -192,26 +192,14 @@ namespace GitHub.Unity
|
|||
public event Action<IProcess> OnStartProcess;
|
||||
public event Action<IProcess> OnEndProcess;
|
||||
|
||||
private string errors = null;
|
||||
private Exception thrownException = null;
|
||||
|
||||
public ProcessTask(CancellationToken token)
|
||||
: base(token)
|
||||
{
|
||||
}
|
||||
|
||||
public ProcessTask(CancellationToken token, ITask dependsOn)
|
||||
: base(token, dependsOn)
|
||||
{
|
||||
}
|
||||
|
||||
public ProcessTask(CancellationToken token, IOutputProcessor<T> outputProcessor)
|
||||
: base(token)
|
||||
{
|
||||
this.outputProcessor = outputProcessor;
|
||||
}
|
||||
|
||||
public ProcessTask(CancellationToken token, IOutputProcessor<T> outputProcessor, ITask dependsOn)
|
||||
public ProcessTask(CancellationToken token, IOutputProcessor<T> outputProcessor = null, ITask dependsOn = null)
|
||||
: base(token, dependsOn)
|
||||
{
|
||||
this.outputProcessor = outputProcessor;
|
||||
|
@ -226,7 +214,7 @@ namespace GitHub.Unity
|
|||
public ProcessTask(CancellationToken token, string arguments, IOutputProcessor<T> outputProcessor = null, ITask dependsOn = null)
|
||||
: base(token, dependsOn)
|
||||
{
|
||||
Guard.ArgumentNotNull(token, "token");
|
||||
Guard.ArgumentNotNull(token, nameof(token));
|
||||
|
||||
this.outputProcessor = outputProcessor;
|
||||
ProcessArguments = arguments;
|
||||
|
@ -237,6 +225,8 @@ namespace GitHub.Unity
|
|||
Guard.ArgumentNotNull(psi, "psi");
|
||||
|
||||
ConfigureOutputProcessor();
|
||||
|
||||
Guard.PropertyOrFieldNotNull(outputProcessor, nameof(outputProcessor));
|
||||
Process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||
ProcessName = psi.FileName;
|
||||
}
|
||||
|
@ -245,6 +235,9 @@ namespace GitHub.Unity
|
|||
{
|
||||
outputProcessor = processor ?? outputProcessor;
|
||||
ConfigureOutputProcessor();
|
||||
|
||||
Guard.PropertyOrFieldNotNull(outputProcessor, nameof(outputProcessor));
|
||||
|
||||
Process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||
ProcessName = psi.FileName;
|
||||
}
|
||||
|
@ -254,6 +247,9 @@ namespace GitHub.Unity
|
|||
Guard.ArgumentNotNull(existingProcess, "existingProcess");
|
||||
|
||||
ConfigureOutputProcessor();
|
||||
|
||||
Guard.PropertyOrFieldNotNull(outputProcessor, nameof(outputProcessor));
|
||||
|
||||
Process = existingProcess;
|
||||
ProcessName = existingProcess.StartInfo.FileName;
|
||||
}
|
||||
|
@ -281,21 +277,16 @@ namespace GitHub.Unity
|
|||
|
||||
protected override T RunWithReturn(bool success)
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
}
|
||||
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
var result = base.RunWithReturn(success);
|
||||
|
||||
wrapper = new ProcessWrapper(Process, outputProcessor,
|
||||
RaiseOnStart,
|
||||
() =>
|
||||
{
|
||||
RaiseOnEnd();
|
||||
if (errors != null)
|
||||
if (Errors != null)
|
||||
{
|
||||
OnErrorData?.Invoke(errors);
|
||||
OnErrorData?.Invoke(Errors);
|
||||
if (thrownException == null)
|
||||
throw new ProcessException(this);
|
||||
else
|
||||
|
@ -305,17 +296,19 @@ namespace GitHub.Unity
|
|||
(ex, error) =>
|
||||
{
|
||||
thrownException = ex;
|
||||
errors = error;
|
||||
Errors = error;
|
||||
},
|
||||
Token);
|
||||
|
||||
wrapper.Run();
|
||||
|
||||
if (outputProcessor != null)
|
||||
return outputProcessor.Result;
|
||||
if (typeof(T) == typeof(string))
|
||||
return (T)(object)(Process.StartInfo.CreateNoWindow ? "Process finished" : "Process running");
|
||||
return default(T);
|
||||
result = outputProcessor.Result;
|
||||
|
||||
if (result == null && typeof(T) == typeof(string))
|
||||
result = (T)(object)(Process.StartInfo.CreateNoWindow ? "Process finished" : "Process running");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Process Process { get; set; }
|
||||
|
@ -326,10 +319,9 @@ namespace GitHub.Unity
|
|||
public virtual string ProcessArguments { get; }
|
||||
}
|
||||
|
||||
class ProcessTaskWithListOutput<T> : ListTaskBase<List<T>, T>, ITask<List<T>, T>, IProcessTask<List<T>, T>
|
||||
class ProcessTaskWithListOutput<T> : DataTaskBase<T, List<T>>, IProcessTask<T, List<T>>
|
||||
{
|
||||
private IOutputProcessor<List<T>, T> outputProcessor;
|
||||
private string errors = null;
|
||||
private IOutputProcessor<T, List<T>> outputProcessor;
|
||||
private Exception thrownException = null;
|
||||
private ProcessWrapper wrapper;
|
||||
|
||||
|
@ -337,23 +329,12 @@ namespace GitHub.Unity
|
|||
public event Action<IProcess> OnStartProcess;
|
||||
public event Action<IProcess> OnEndProcess;
|
||||
|
||||
public ProcessTaskWithListOutput(CancellationToken token)
|
||||
: base(token)
|
||||
{
|
||||
}
|
||||
|
||||
public ProcessTaskWithListOutput(CancellationToken token, IOutputProcessor<List<T>, T> outputProcessor)
|
||||
: this(token)
|
||||
{
|
||||
this.outputProcessor = outputProcessor;
|
||||
}
|
||||
|
||||
public ProcessTaskWithListOutput(CancellationToken token, ITask dependsOn)
|
||||
: base(token, dependsOn)
|
||||
{
|
||||
}
|
||||
|
||||
public ProcessTaskWithListOutput(CancellationToken token, IOutputProcessor<List<T>, T> outputProcessor, ITask dependsOn)
|
||||
public ProcessTaskWithListOutput(CancellationToken token, IOutputProcessor<T, List<T>> outputProcessor = null, ITask dependsOn = null)
|
||||
: base(token, dependsOn)
|
||||
{
|
||||
this.outputProcessor = outputProcessor;
|
||||
|
@ -364,6 +345,9 @@ namespace GitHub.Unity
|
|||
Guard.ArgumentNotNull(psi, "psi");
|
||||
|
||||
ConfigureOutputProcessor();
|
||||
|
||||
Guard.PropertyOrFieldNotNull(outputProcessor, nameof(outputProcessor));
|
||||
|
||||
Process = new Process { StartInfo = psi, EnableRaisingEvents = true };
|
||||
ProcessName = psi.FileName;
|
||||
}
|
||||
|
@ -373,11 +357,12 @@ namespace GitHub.Unity
|
|||
Guard.ArgumentNotNull(existingProcess, "existingProcess");
|
||||
|
||||
ConfigureOutputProcessor();
|
||||
Guard.PropertyOrFieldNotNull(outputProcessor, nameof(outputProcessor));
|
||||
Process = existingProcess;
|
||||
ProcessName = existingProcess.StartInfo.FileName;
|
||||
}
|
||||
|
||||
public virtual void Configure(ProcessStartInfo psi, IOutputProcessor<List<T>, T> processor)
|
||||
public virtual void Configure(ProcessStartInfo psi, IOutputProcessor<T, List<T>> processor)
|
||||
{
|
||||
Guard.ArgumentNotNull(psi, "psi");
|
||||
Guard.ArgumentNotNull(processor, "processor");
|
||||
|
@ -411,21 +396,16 @@ namespace GitHub.Unity
|
|||
|
||||
protected override List<T> RunWithReturn(bool success)
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
throw DependsOn.Task.Exception.InnerException;
|
||||
}
|
||||
|
||||
Logger.Debug(String.Format("Executing id:{0} success?:{1}", Task.Id, success));
|
||||
var result = base.RunWithReturn(success);
|
||||
|
||||
wrapper = new ProcessWrapper(Process, outputProcessor,
|
||||
RaiseOnStart,
|
||||
() =>
|
||||
{
|
||||
RaiseOnEnd();
|
||||
if (errors != null)
|
||||
if (Errors != null)
|
||||
{
|
||||
OnErrorData?.Invoke(errors);
|
||||
OnErrorData?.Invoke(Errors);
|
||||
if (thrownException == null)
|
||||
throw new ProcessException(this);
|
||||
else
|
||||
|
@ -435,14 +415,16 @@ namespace GitHub.Unity
|
|||
(ex, error) =>
|
||||
{
|
||||
thrownException = ex;
|
||||
errors = error;
|
||||
Errors = error;
|
||||
},
|
||||
Token);
|
||||
wrapper.Run();
|
||||
|
||||
if (outputProcessor != null)
|
||||
return outputProcessor.Result;
|
||||
return new List<T>();
|
||||
result = outputProcessor.Result;
|
||||
if (result == null)
|
||||
result = new List<T>();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Process Process { get; set; }
|
||||
|
@ -452,4 +434,20 @@ namespace GitHub.Unity
|
|||
public virtual string ProcessName { get; protected set; }
|
||||
public virtual string ProcessArguments { get; }
|
||||
}
|
||||
|
||||
class SimpleProcessTask : ProcessTask<string>
|
||||
{
|
||||
private readonly NPath fullPathToExecutable;
|
||||
private readonly string arguments;
|
||||
|
||||
public SimpleProcessTask(NPath fullPathToExecutable, string arguments, CancellationToken token)
|
||||
: base(token, new FirstNonNullLineOutputProcessor())
|
||||
{
|
||||
this.fullPathToExecutable = fullPathToExecutable;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public override string ProcessName => fullPathToExecutable.FileName;
|
||||
public override string ProcessArguments => arguments;
|
||||
}
|
||||
}
|
|
@ -6,31 +6,30 @@ namespace GitHub.Unity
|
|||
{
|
||||
interface ITask : IAsyncResult
|
||||
{
|
||||
T ContinueWith<T>(T continuation, bool always = true) where T : ITask;
|
||||
// Continues and also sends a flag indicating whether the current task was successful or not
|
||||
ITask ContinueWith(Action<bool> continuation, bool always = true);
|
||||
// Continues and also sends a flag indicating whether the current task was successful or not
|
||||
ITask ContinueWithUI(Action<bool> continuation, bool always = true);
|
||||
T Then<T>(T continuation, bool always = false) where T : ITask;
|
||||
ITask Finally(Action<bool, Exception> continuation, TaskAffinity affinity = TaskAffinity.Concurrent);
|
||||
ITask SetDependsOn(ITask dependsOn);
|
||||
ITask Start();
|
||||
ITask Start(TaskScheduler scheduler);
|
||||
|
||||
void Wait();
|
||||
bool Wait(int milliseconds);
|
||||
bool Successful { get; }
|
||||
string Errors { get; }
|
||||
Task Task { get; }
|
||||
string Name { get; }
|
||||
TaskAffinity Affinity { get; }
|
||||
TaskAffinity Affinity { get; set; }
|
||||
CancellationToken Token { get; }
|
||||
TaskBase DependsOn { get; }
|
||||
event Action<ITask> OnStart;
|
||||
event Action<ITask> OnEnd;
|
||||
ITask SetDependsOn(ITask dependsOn);
|
||||
}
|
||||
|
||||
interface ITask<TResult> : ITask
|
||||
{
|
||||
ActionTask<TResult> ContinueWith(Action<bool, TResult> continuation, bool always = true);
|
||||
FuncTask<TResult, T> ContinueWith<T>(Func<bool, TResult, T> continuation, bool always = true);
|
||||
ActionTask<TResult> ContinueWithUI(Action<bool, TResult> continuation, bool always = true);
|
||||
FuncTask<TResult, T> ContinueWithUI<T>(Func<bool, TResult, T> continuation, bool always = true);
|
||||
ITask<TResult> Finally(Func<bool, Exception, TResult, TResult> continuation, TaskAffinity affinity = TaskAffinity.Concurrent);
|
||||
ITask Finally(Action<bool, Exception, TResult> continuation, TaskAffinity affinity = TaskAffinity.Concurrent);
|
||||
new ITask<TResult> Start();
|
||||
new ITask<TResult> Start(TaskScheduler scheduler);
|
||||
TResult Result { get; }
|
||||
new Task<TResult> Task { get; }
|
||||
|
@ -38,7 +37,7 @@ namespace GitHub.Unity
|
|||
new event Action<ITask<TResult>> OnEnd;
|
||||
}
|
||||
|
||||
interface ITask<T, TData> : ITask<T>
|
||||
interface ITask<TData, T> : ITask<T>
|
||||
{
|
||||
event Action<TData> OnData;
|
||||
}
|
||||
|
@ -46,24 +45,26 @@ namespace GitHub.Unity
|
|||
abstract class TaskBase : ITask
|
||||
{
|
||||
protected const TaskContinuationOptions runAlwaysOptions = TaskContinuationOptions.None;
|
||||
protected const TaskContinuationOptions runOnSuccessOptions = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted;
|
||||
protected const TaskContinuationOptions runOnSuccessOptions = TaskContinuationOptions.OnlyOnRanToCompletion;
|
||||
protected const TaskContinuationOptions runOnFaultOptions = TaskContinuationOptions.OnlyOnFaulted;
|
||||
|
||||
public event Action<ITask> OnStart;
|
||||
public event Action<ITask> OnEnd;
|
||||
|
||||
public TaskBase(CancellationToken token)
|
||||
: this(token, null)
|
||||
{
|
||||
}
|
||||
protected bool previousSuccess = true;
|
||||
protected Exception previousException;
|
||||
protected object previousResult;
|
||||
|
||||
public TaskBase(CancellationToken token, ITask dependsOn)
|
||||
protected TaskBase continuation;
|
||||
protected bool continuationAlways;
|
||||
|
||||
public TaskBase(CancellationToken token, ITask dependsOn = null, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(token, "token");
|
||||
|
||||
Token = token;
|
||||
DependsOn = dependsOn;
|
||||
Task = new Task(() => Run(DependsOn?.Successful ?? true), Token, TaskCreationOptions.None);
|
||||
Task = new Task(() => Run(DependsOn?.Successful ?? previousSuccess), Token, TaskCreationOptions.None);
|
||||
dependsOn?.Then(this, always);
|
||||
}
|
||||
|
||||
public TaskBase(Task task)
|
||||
|
@ -71,55 +72,124 @@ namespace GitHub.Unity
|
|||
Task = task;
|
||||
}
|
||||
|
||||
protected TaskBase()
|
||||
{ }
|
||||
|
||||
public ITask SetDependsOn(ITask dependsOn)
|
||||
{
|
||||
DependsOn = dependsOn;
|
||||
DependsOn = (TaskBase)dependsOn;
|
||||
return this;
|
||||
}
|
||||
|
||||
public T ContinueWith<T>(T continuation, bool always = true)
|
||||
public virtual T Then<T>(T continuation, bool always = false)
|
||||
where T : ITask
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
|
||||
continuation.SetDependsOn(this);
|
||||
Task.ContinueWith(_ => continuation.Start(), Token,
|
||||
always ? runAlwaysOptions : runOnSuccessOptions,
|
||||
TaskManager.GetScheduler(continuation.Affinity));
|
||||
this.continuation = (TaskBase)(object)continuation;
|
||||
this.continuationAlways = always;
|
||||
return continuation;
|
||||
}
|
||||
|
||||
public ITask ContinueWith(Action<bool> continuation, bool always = true)
|
||||
public ITask Finally(Action<bool, Exception> continuation, TaskAffinity affinity = TaskAffinity.Concurrent)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
|
||||
var ret = new ActionTask(Token, continuation, this);
|
||||
return ContinueWith(ret, always);
|
||||
var ret = new ActionTask(Token, continuation, this, true) { Affinity = affinity, Name = "Finally" };
|
||||
DependsOn?.SetFaultHandler(ret);
|
||||
ret.ContinuationIsFinally = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ITask ContinueWithUI(Action<bool> continuation, bool always = true)
|
||||
internal virtual ITask Finally<T>(T continuation)
|
||||
where T : ITask
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
var ret = new ActionTask(Token, continuation, this) { Affinity = TaskAffinity.UI };
|
||||
return ContinueWith(ret, always);
|
||||
continuation.SetDependsOn(this);
|
||||
this.continuation = (TaskBase)(object)continuation;
|
||||
this.continuationAlways = true;
|
||||
DependsOn?.SetFaultHandler((TaskBase)(object)continuation);
|
||||
return continuation;
|
||||
}
|
||||
|
||||
internal void SetFaultHandler(TaskBase handler)
|
||||
{
|
||||
Task.ContinueWith(t => handler.Start(t), Token,
|
||||
TaskContinuationOptions.OnlyOnFaulted,
|
||||
TaskManager.GetScheduler(handler.Affinity));
|
||||
DependsOn?.SetFaultHandler(handler);
|
||||
}
|
||||
|
||||
public virtual ITask Start()
|
||||
{
|
||||
TaskManager.Instance.Schedule(this);
|
||||
//Task.Start(TaskManager.GetScheduler(Affinity));
|
||||
return this;
|
||||
var depends = GetFirstDepends();
|
||||
if (depends != null)
|
||||
{
|
||||
depends.Run();
|
||||
return this;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TaskManager.Instance.Schedule(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void Run()
|
||||
{
|
||||
if (Task.Status == TaskStatus.Created)
|
||||
{
|
||||
TaskManager.Instance.Schedule(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
RunContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
protected void Start(Task task)
|
||||
{
|
||||
previousSuccess = task.Status == TaskStatus.RanToCompletion && task.Status != TaskStatus.Faulted;
|
||||
previousException = task.Exception;
|
||||
Task.Start(TaskManager.GetScheduler(Affinity));
|
||||
}
|
||||
|
||||
public virtual ITask Start(TaskScheduler scheduler)
|
||||
{
|
||||
if (DependsOn != null && DependsOn.Task.Status == TaskStatus.Created)
|
||||
DependsOn.Start();
|
||||
else
|
||||
if (Task.Status == TaskStatus.Created)
|
||||
{
|
||||
Logger.Trace(String.Format($"Starting {Affinity} {Task.Id} {Name}"));
|
||||
Task.Start(scheduler);
|
||||
}
|
||||
|
||||
RunContinuation();
|
||||
return this;
|
||||
}
|
||||
|
||||
protected virtual void RunContinuation()
|
||||
{
|
||||
if (continuation != null)
|
||||
{
|
||||
Task.ContinueWith(_ => ((TaskBase)(object)continuation).Run(), Token, continuationAlways ? runAlwaysOptions : runOnSuccessOptions,
|
||||
TaskManager.GetScheduler(continuation.Affinity));
|
||||
}
|
||||
}
|
||||
|
||||
protected TaskBase GetFirstDepends()
|
||||
{
|
||||
var depends = DependsOn;
|
||||
if (depends == null)
|
||||
return null;
|
||||
return depends.GetFirstDepends(null);
|
||||
}
|
||||
|
||||
protected TaskBase GetFirstDepends(TaskBase ret)
|
||||
{
|
||||
ret = (Task.Status == TaskStatus.Created ? this : ret);
|
||||
var depends = DependsOn;
|
||||
if (depends == null)
|
||||
return ret;
|
||||
return depends.GetFirstDepends(ret);
|
||||
}
|
||||
|
||||
public virtual void Wait()
|
||||
{
|
||||
Task.Wait(Token);
|
||||
|
@ -131,7 +201,9 @@ namespace GitHub.Unity
|
|||
}
|
||||
|
||||
protected virtual void Run(bool success)
|
||||
{ }
|
||||
{
|
||||
Logger.Trace(String.Format("Executing {0}({1}) success?:{2}", Name, Task.Id, success));
|
||||
}
|
||||
|
||||
protected virtual void RaiseOnStart()
|
||||
{
|
||||
|
@ -143,76 +215,143 @@ namespace GitHub.Unity
|
|||
OnEnd?.Invoke(this);
|
||||
}
|
||||
|
||||
protected AggregateException GetThrownException()
|
||||
{
|
||||
if (DependsOn == null)
|
||||
return null;
|
||||
|
||||
if (DependsOn.Task.Status != TaskStatus.Created && !DependsOn.Successful)
|
||||
{
|
||||
return DependsOn.Task.Exception;
|
||||
}
|
||||
return DependsOn.GetThrownException();
|
||||
}
|
||||
|
||||
public virtual bool Successful { get { return Task.Status == TaskStatus.RanToCompletion && Task.Status != TaskStatus.Faulted; } }
|
||||
public string Errors { get; protected set; }
|
||||
public Task Task { get; protected set; }
|
||||
|
||||
public bool IsCompleted { get { return (Task as IAsyncResult).IsCompleted; } }
|
||||
|
||||
public WaitHandle AsyncWaitHandle { get { return (Task as IAsyncResult).AsyncWaitHandle; } }
|
||||
|
||||
public object AsyncState { get { return (Task as IAsyncResult).AsyncState; } }
|
||||
|
||||
public bool CompletedSynchronously { get { return (Task as IAsyncResult).CompletedSynchronously; } }
|
||||
public virtual string Name { get; set; }
|
||||
public virtual TaskAffinity Affinity { get; set; }
|
||||
|
||||
private ILogging logger;
|
||||
protected ILogging Logger { get { return logger = logger ?? Logging.GetLogger(GetType()); } }
|
||||
protected ITask DependsOn { get; private set; }
|
||||
protected CancellationToken Token { get; }
|
||||
public TaskBase DependsOn { get; private set; }
|
||||
public CancellationToken Token { get; }
|
||||
internal TaskBase Continuation => continuation;
|
||||
internal bool ContinuationAlways => continuationAlways;
|
||||
internal bool ContinuationIsFinally { get; set; }
|
||||
}
|
||||
|
||||
abstract class TaskBase<TResult> : TaskBase, ITask<TResult>
|
||||
{
|
||||
public TaskBase(CancellationToken token, ITask dependsOn = null)
|
||||
: base(token, dependsOn)
|
||||
private DeferredContinuation deferred;
|
||||
protected class DeferredContinuation
|
||||
{
|
||||
Task = new Task<TResult>(() => RunWithReturn(DependsOn?.Successful ?? true), Token, TaskCreationOptions.None);
|
||||
public bool Always;
|
||||
public Func<TResult, ITask> GetContinueWith;
|
||||
}
|
||||
|
||||
public ActionTask<TResult> ContinueWith(Action<bool, TResult> continuation, bool always = true)
|
||||
protected DeferredContinuation GetDeferred()
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
var ret = new ActionTask<TResult>(Token, continuation, this);
|
||||
return ContinueWith(ret, always);
|
||||
return deferred;
|
||||
}
|
||||
|
||||
public FuncTask<TResult, T> ContinueWith<T>(Func<bool, TResult, T> continuation, bool always = true)
|
||||
public TaskBase(CancellationToken token, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
var ret = new FuncTask<TResult, T>(Token, continuation, this);
|
||||
return ContinueWith(ret, always);
|
||||
Task = new Task<TResult>(() =>
|
||||
{
|
||||
var ret = RunWithReturn(DependsOn?.Successful ?? previousSuccess);
|
||||
AdjustNextTask(ret);
|
||||
return ret;
|
||||
}, Token, TaskCreationOptions.None);
|
||||
}
|
||||
|
||||
public ActionTask<TResult> ContinueWithUI(Action<bool, TResult> continuation, bool always = true)
|
||||
protected void AdjustNextTask(TResult ret)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
var ret = new ActionTask<TResult>(Token, continuation, this) { Affinity = TaskAffinity.UI };
|
||||
return ContinueWith(ret, always);
|
||||
var def = GetDeferred();
|
||||
if (def != null)
|
||||
{
|
||||
var next = def.GetContinueWith(ret);
|
||||
var cont = continuation.Continuation;
|
||||
if (cont != null)
|
||||
{
|
||||
if (cont.ContinuationIsFinally)
|
||||
{
|
||||
((TaskBase)next).Finally(cont);
|
||||
}
|
||||
else
|
||||
{
|
||||
next.Then(cont, cont.ContinuationAlways);
|
||||
}
|
||||
}
|
||||
continuation.Then(next, continuationAlways);
|
||||
}
|
||||
}
|
||||
|
||||
public FuncTask<TResult, T> ContinueWithUI<T>(Func<bool, TResult, T> continuation, bool always = true)
|
||||
public TaskBase(Task<TResult> task)
|
||||
{
|
||||
Task = task;
|
||||
}
|
||||
|
||||
public override T Then<T>(T continuation, bool always = false)
|
||||
{
|
||||
deferred = null;
|
||||
return base.Then<T>(continuation, always);
|
||||
}
|
||||
|
||||
public ITask<T> ThenIf<T>(Func<TResult, ITask<T>> continueWith, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continueWith, "continueWith");
|
||||
var ret = new FuncTask<TResult, T>(Token, (s, d) => default(T), this, always);
|
||||
deferred = new DeferredContinuation { Always = always, GetContinueWith = d => continueWith(d) };
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ITask<TResult> Finally(Func<bool, Exception, TResult, TResult> continuation, TaskAffinity affinity = TaskAffinity.Concurrent)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
var ret = new FuncTask<TResult, T>(Token, continuation, this) { Affinity = TaskAffinity.UI };
|
||||
return ContinueWith(ret, always);
|
||||
var ret = new FuncTask<TResult, TResult>(Token, continuation, this, true) { Affinity = affinity, Name = "Finally" };
|
||||
ret.ContinuationIsFinally = true;
|
||||
DependsOn?.SetFaultHandler(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ITask Finally(Action<bool, Exception, TResult> continuation, TaskAffinity affinity = TaskAffinity.Concurrent)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
var ret = new ActionTask<TResult>(Token, continuation, this, true) { Affinity = affinity, Name = "Finally" };
|
||||
ret.ContinuationIsFinally = true;
|
||||
DependsOn?.SetFaultHandler(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public new ITask<TResult> Start()
|
||||
{
|
||||
Task.Start(TaskManager.GetScheduler(Affinity));
|
||||
base.Start();
|
||||
return this;
|
||||
}
|
||||
|
||||
public new ITask<TResult> Start(TaskScheduler scheduler)
|
||||
{
|
||||
Task.Start(scheduler);
|
||||
base.Start(scheduler);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected override void RunContinuation()
|
||||
{
|
||||
//if (deferred == null)
|
||||
//{
|
||||
base.RunContinuation();
|
||||
//}
|
||||
}
|
||||
|
||||
protected virtual TResult RunWithReturn(bool success)
|
||||
{
|
||||
base.Run(success);
|
||||
return default(TResult);
|
||||
}
|
||||
|
||||
|
@ -228,25 +367,37 @@ namespace GitHub.Unity
|
|||
|
||||
abstract class TaskBase<T, TResult> : TaskBase<TResult>
|
||||
{
|
||||
public TaskBase(CancellationToken token, ITask<T> dependsOn)
|
||||
: base(token, dependsOn)
|
||||
public TaskBase(CancellationToken token, ITask<T> dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always)
|
||||
{
|
||||
Task = new Task<TResult>(() => RunWithData(dependsOn.Successful, dependsOn.Result), Token, TaskCreationOptions.None);
|
||||
Task = new Task<TResult>(() =>
|
||||
{
|
||||
var ret = RunWithData(DependsOn?.Successful ?? previousSuccess, DependsOn.Successful ? ((ITask<T>)DependsOn).Result : default(T));
|
||||
AdjustNextTask(ret);
|
||||
return ret;
|
||||
},
|
||||
Token, TaskCreationOptions.None);
|
||||
}
|
||||
|
||||
public TaskBase(Task<TResult> task)
|
||||
: base(task)
|
||||
{ }
|
||||
|
||||
protected virtual TResult RunWithData(bool success, T previousResult)
|
||||
{
|
||||
base.Run(success);
|
||||
return default(TResult);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ListTaskBase<TResult, TData> : TaskBase<TResult>, ITask<TResult, TData>
|
||||
abstract class DataTaskBase<TData, TResult> : TaskBase<TResult>, ITask<TData, TResult>
|
||||
{
|
||||
public ListTaskBase(CancellationToken token)
|
||||
: base(token) { }
|
||||
public DataTaskBase(CancellationToken token, ITask dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always) { }
|
||||
|
||||
public ListTaskBase(CancellationToken token, ITask dependsOn)
|
||||
: base(token, dependsOn) { }
|
||||
public DataTaskBase(Task<TResult> task)
|
||||
: base(task)
|
||||
{ }
|
||||
|
||||
public event Action<TData> OnData;
|
||||
protected void RaiseOnData(TData data)
|
||||
|
@ -255,10 +406,14 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
abstract class ListTaskBase<T, TResult, TData> : TaskBase<T, TResult>, ITask<TResult, TData>
|
||||
abstract class DataTaskBase<T, TData, TResult> : TaskBase<T, TResult>, ITask<TData, TResult>
|
||||
{
|
||||
public ListTaskBase(CancellationToken token, ITask<T> dependsOn)
|
||||
: base(token, dependsOn) { }
|
||||
public DataTaskBase(CancellationToken token, ITask<T> dependsOn = null, bool always = false)
|
||||
: base(token, dependsOn, always) { }
|
||||
|
||||
public DataTaskBase(Task<TResult> task)
|
||||
: base(task)
|
||||
{ }
|
||||
|
||||
public event Action<TData> OnData;
|
||||
protected void RaiseOnData(TData data)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -7,7 +6,25 @@ namespace GitHub.Unity
|
|||
{
|
||||
static class TaskExtensions
|
||||
{
|
||||
public static async Task<T> Catch<T>(this Task<T> source, Func<Exception, T> handler = null)
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "task")]
|
||||
public static void Forget(this Task task)
|
||||
{
|
||||
}
|
||||
|
||||
public static async Task SafeAwait(this Task source, Action<Exception> handler = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await source;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (handler != null)
|
||||
handler(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<T> SafeAwait<T>(this Task<T> source, Func<Exception, T> handler = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -21,11 +38,11 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
public static async Task Catch(this Task source, Action<Exception> handler = null)
|
||||
public static async Task StartAwait(this ITask source, Action<Exception> handler = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await source;
|
||||
await source.Start().Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -34,37 +51,24 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
public static async Task<T> StartAwait<T>(this ITask<T> source, Func<Exception, T> handler = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await source.Start().Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (handler != null)
|
||||
return handler(ex);
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "task")]
|
||||
public static void Forget(this Task task)
|
||||
public static void Forget(this ITask task)
|
||||
{
|
||||
}
|
||||
|
||||
public static async Task<ITask<T>> Catch<T>(this ITask<T> source, Func<ITask<T>, Exception, ITask<T>> handler = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await source.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (handler != null)
|
||||
return handler(source, ex);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
public static async Task<ITask> Catch(this ITask source, Action<ITask, Exception> handler = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await source.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (handler != null)
|
||||
handler(source, ex);
|
||||
}
|
||||
return source;
|
||||
task.Task.Forget();
|
||||
}
|
||||
|
||||
//http://stackoverflow.com/a/29491927
|
||||
|
@ -95,5 +99,103 @@ namespace GitHub.Unity
|
|||
});
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never end a chain with Catch, always use Finally instead
|
||||
/// Catch sets a direct continuation on the previous task, which means await is not going to be
|
||||
/// waiting for it to finish before returning (catch will run faster this way).
|
||||
/// Always end a chain with Finally and use Catch statements in the middle of the chain.
|
||||
/// </summary>
|
||||
public static T Catch<T>(this T task, Action<Exception> handler)
|
||||
where T : ITask
|
||||
{
|
||||
Guard.ArgumentNotNull(handler, "handler");
|
||||
task.Task.ContinueWith(t =>
|
||||
{
|
||||
handler(t.Exception is AggregateException ? t.Exception.InnerException : t.Exception);
|
||||
},
|
||||
task.Token,
|
||||
TaskContinuationOptions.OnlyOnFaulted,
|
||||
TaskManager.GetScheduler(task.Affinity));
|
||||
task.DependsOn?.Catch(handler);
|
||||
return task;
|
||||
}
|
||||
|
||||
public static ITask Then(this ITask task, Action<bool> continuation, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new ActionTask(task.Token, continuation, task, always) { Name = "Then" };
|
||||
}
|
||||
|
||||
public static ActionTask<T> Then<T>(this ITask<T> task, Action<bool, T> continuation, TaskAffinity affinity = TaskAffinity.Concurrent, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new ActionTask<T>(task.Token, continuation, task, always) { Affinity = affinity, Name = $"Then<{typeof(T)}>" };
|
||||
}
|
||||
|
||||
public static FuncTask<TResult, T> Then<TResult, T>(this ITask<TResult> task, Func<bool, TResult, T> continuation, TaskAffinity affinity = TaskAffinity.Concurrent, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new FuncTask<TResult, T>(task.Token, continuation, task) { Affinity = affinity, Name = $"Then<{typeof(TResult)}, {typeof(T)}>" };
|
||||
}
|
||||
|
||||
public static FuncTask<T> Then<T>(this ITask task, Func<bool, T> continuation, TaskAffinity affinity = TaskAffinity.Concurrent, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new FuncTask<T>(task.Token, continuation, task) { Affinity = affinity, Name = $"Then<{typeof(T)}>" };
|
||||
}
|
||||
|
||||
public static ITask<T> ThenAsync<T>(this ITask task, ITask<T> continuation, bool always = false)
|
||||
{
|
||||
return task.Then(continuation, always);
|
||||
}
|
||||
|
||||
public static FuncTask<TResult, T> ThenInUI<TResult, T>(this ITask<TResult> task, Func<bool, TResult, T> continuation, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new FuncTask<TResult, T>(task.Token, continuation, task) { Affinity = TaskAffinity.UI, Name = $"ThenInUI<{typeof(TResult)}, {typeof(T)}>" };
|
||||
}
|
||||
|
||||
public static ActionTask<T> ThenInUI<T>(this ITask<T> task, Action<bool, T> continuation, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new ActionTask<T>(task.Token, continuation, task) { Affinity = TaskAffinity.UI, Name = $"ThenInUI<{typeof(T)}>" };
|
||||
}
|
||||
|
||||
|
||||
public static ITask ThenInUI(this ITask task, ITask continuation)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
continuation.Affinity = TaskAffinity.UI;
|
||||
return continuation;
|
||||
}
|
||||
|
||||
public static ITask ThenInUI(this ITask task, Action<bool> continuation, bool always = false)
|
||||
{
|
||||
Guard.ArgumentNotNull(continuation, "continuation");
|
||||
return new ActionTask(task.Token, continuation, task, always) { Affinity = TaskAffinity.UI, Name = "ThenInUI" };
|
||||
}
|
||||
|
||||
public static T FinallyInUI<T>(this T task, Action<bool, Exception> continuation)
|
||||
where T : ITask
|
||||
{
|
||||
return (T)task.Finally(continuation, TaskAffinity.UI);
|
||||
}
|
||||
|
||||
public static ITask<T> FinallyInUI<T>(this ITask<T> task, Func<bool, Exception, T, T> continuation)
|
||||
{
|
||||
return task.Finally(continuation, TaskAffinity.UI);
|
||||
}
|
||||
|
||||
public static ITask FinallyInUI<T>(this ITask<T> task, Action<bool, Exception, T> continuation)
|
||||
{
|
||||
return task.Finally(continuation, TaskAffinity.UI);
|
||||
}
|
||||
|
||||
public static ITask<T> ThenAsync<T>(this ITask task, Task<T> continuation, TaskAffinity affinity = TaskAffinity.Concurrent, bool always = false)
|
||||
{
|
||||
var cont = new FuncTask<T>(continuation) { Affinity = affinity, Name = $"ThenAsync<{typeof(T)}>" };
|
||||
return task.Then(cont, always);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
class TaskManager : ITaskManager
|
||||
{
|
||||
private static readonly ILogging logger = Logging.GetLogger<ProcessManager>();
|
||||
private static readonly ILogging logger = Logging.GetLogger<TaskManager>();
|
||||
|
||||
private readonly CancellationTokenSource cts;
|
||||
private readonly ConcurrentExclusiveInterleave manager;
|
||||
|
@ -34,7 +34,12 @@ namespace GitHub.Unity
|
|||
public void Stop()
|
||||
{
|
||||
cts.Cancel();
|
||||
manager.Wait();
|
||||
Wait();
|
||||
}
|
||||
|
||||
public Task Wait()
|
||||
{
|
||||
return manager.Wait();
|
||||
}
|
||||
|
||||
public static TaskScheduler GetScheduler(TaskAffinity affinity)
|
||||
|
@ -100,13 +105,12 @@ namespace GitHub.Unity
|
|||
{
|
||||
task.Task.ContinueWith(tt =>
|
||||
{
|
||||
logger.Error(tt.Exception.InnerException, String.Format("Exception on ui thread: {0} {1}", tt.Id, task.Name));
|
||||
logger.Trace(tt.Exception.InnerException, String.Format("Exception on ui thread: {0} {1}", tt.Id, task.Name));
|
||||
},
|
||||
cts.Token,
|
||||
TaskContinuationOptions.OnlyOnFaulted, UIScheduler
|
||||
TaskContinuationOptions.OnlyOnFaulted, ConcurrentScheduler
|
||||
);
|
||||
}
|
||||
logger.Trace(String.Format("Schedule {0} {1}", "UI", task.Task.Id));
|
||||
return (T)task.Start(UIScheduler);
|
||||
}
|
||||
|
||||
|
@ -123,13 +127,12 @@ namespace GitHub.Unity
|
|||
{
|
||||
task.Task.ContinueWith(tt =>
|
||||
{
|
||||
logger.Error(tt.Exception.InnerException, String.Format("Exception on exclusive thread: {0} {1}", tt.Id, task.Name));
|
||||
logger.Trace(tt.Exception.InnerException, String.Format("Exception on exclusive thread: {0} {1}", tt.Id, task.Name));
|
||||
},
|
||||
cts.Token,
|
||||
TaskContinuationOptions.OnlyOnFaulted, UIScheduler
|
||||
TaskContinuationOptions.OnlyOnFaulted, ConcurrentScheduler
|
||||
);
|
||||
}
|
||||
logger.Trace(String.Format("Schedule {0} {1}", "Exclusive", task.Task.Id));
|
||||
return (T)task.Start(manager.ExclusiveTaskScheduler);
|
||||
}
|
||||
|
||||
|
@ -146,13 +149,12 @@ namespace GitHub.Unity
|
|||
{
|
||||
task.Task.ContinueWith(tt =>
|
||||
{
|
||||
logger.Error(tt.Exception.InnerException, String.Format("Exception on concurrent thread: {0} {1}", tt.Id, task.Name));
|
||||
logger.Trace(tt.Exception.InnerException, String.Format("Exception on concurrent thread: {0} {1}", tt.Id, task.Name));
|
||||
},
|
||||
cts.Token,
|
||||
TaskContinuationOptions.OnlyOnFaulted, UIScheduler
|
||||
TaskContinuationOptions.OnlyOnFaulted, ConcurrentScheduler
|
||||
);
|
||||
}
|
||||
logger.Trace(String.Format("Schedule {0} {1}", "Concurrent", task.Task.Id));
|
||||
return (T)task.Start(manager.ConcurrentTaskScheduler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
app = String.Format("credential-{0} ", credHelper);
|
||||
task = new ProcessTask<string>(taskManager.Token, app, new SimpleOutputProcessor())
|
||||
.ConfigureGitProcess(processManager, true);
|
||||
.Configure(processManager, true);
|
||||
}
|
||||
|
||||
task.OnStart += t =>
|
||||
|
|
|
@ -2,8 +2,6 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
|
@ -25,9 +23,10 @@ namespace GitHub.Unity
|
|||
return FileSystem.FileExists(path);
|
||||
}
|
||||
|
||||
public virtual async Task<string> FindGitInstallationPath(IProcessManager processManager)
|
||||
public virtual ITask<NPath> FindGitInstallationPath(IProcessManager processManager)
|
||||
{
|
||||
return await LookForGitInPath(processManager);
|
||||
return new ProcessTask<NPath>(TaskManager.Instance.Token, new FirstLineIsPathOutputProcessor())
|
||||
.Configure(processManager, Environment.IsWindows ? "where" : "which", "git", null, false);
|
||||
}
|
||||
|
||||
public abstract string GetExecutableExtension();
|
||||
|
@ -144,13 +143,5 @@ namespace GitHub.Unity
|
|||
|
||||
psi.WorkingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
protected async Task<string> LookForGitInPath(IProcessManager processManager)
|
||||
{
|
||||
return await new ProcessTask<string>(CancellationToken.None, Environment.IsWindows ? "where" : "which")
|
||||
.ConfigureGitProcess(processManager)
|
||||
.Task;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
interface IProcessEnvironment
|
||||
{
|
||||
Task<string> FindGitInstallationPath(IProcessManager processManager);
|
||||
ITask<NPath> FindGitInstallationPath(IProcessManager processManager);
|
||||
|
||||
string GetExecutableExtension();
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ namespace GitHub.Unity
|
|||
{
|
||||
}
|
||||
|
||||
public override Task<string> FindGitInstallationPath(IProcessManager processManager)
|
||||
public override ITask<NPath> FindGitInstallationPath(IProcessManager processManager)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GitExecutablePath))
|
||||
return TaskEx.FromResult(Environment.GitExecutablePath);
|
||||
return new FuncTask<NPath>(TaskEx.FromResult(Environment.GitExecutablePath));
|
||||
|
||||
return base.FindGitInstallationPath(processManager); ;
|
||||
}
|
||||
|
|
|
@ -11,27 +11,30 @@ namespace GitHub.Unity
|
|||
{
|
||||
}
|
||||
|
||||
public override async Task<string> FindGitInstallationPath(IProcessManager processManager)
|
||||
public override ITask<NPath> FindGitInstallationPath(IProcessManager processManager)
|
||||
{
|
||||
Logger.Trace("Looking for Git Installation folder");
|
||||
|
||||
//if (!String.IsNullOrEmpty(Environment.GitExecutablePath))
|
||||
// return Environment.GitExecutablePath;
|
||||
|
||||
var path = LookForPortableGit();
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
path = await base.FindGitInstallationPath(processManager);
|
||||
}
|
||||
|
||||
Logger.Trace("Git Installation folder {0} discovered: '{1}'", path == null ? "not" : "", path);
|
||||
return path;
|
||||
return new FuncTask<NPath>(TaskManager.Instance.Token, _ =>
|
||||
{
|
||||
Logger.Trace("Looking for Git Installation folder");
|
||||
return LookForPortableGit();
|
||||
})
|
||||
.ThenIf(path =>
|
||||
{
|
||||
if (path == null)
|
||||
return base.FindGitInstallationPath(processManager);
|
||||
else
|
||||
return new FuncTask<NPath>(TaskEx.FromResult(path));
|
||||
})
|
||||
.Finally((s, e, path) =>
|
||||
{
|
||||
Logger.Trace("Git Installation folder {0} discovered: '{1}'", path == null ? "not" : "", path);
|
||||
return path;
|
||||
});
|
||||
}
|
||||
|
||||
private string LookForPortableGit()
|
||||
private NPath LookForPortableGit()
|
||||
{
|
||||
NPath path = "/usr/local/bin/git";
|
||||
NPath path = "/usr/local/bin/git".ToNPath();
|
||||
if (path.FileExists())
|
||||
return path;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
GitEnvironment = new LinuxGitEnvironment(environment, filesystem);
|
||||
localAppData = Environment.GetSpecialFolder(System.Environment.SpecialFolder.LocalApplicationData).ToNPath();
|
||||
commonAppData = "/usr/local/share/";
|
||||
commonAppData = "/usr/local/share/".ToNPath();
|
||||
}
|
||||
|
||||
Environment.UserCachePath = localAppData.Combine(ApplicationInfo.ApplicationName);
|
||||
|
|
|
@ -13,44 +13,45 @@ namespace GitHub.Unity
|
|||
{
|
||||
}
|
||||
|
||||
public override async Task<string> FindGitInstallationPath(IProcessManager processManager)
|
||||
public override ITask<NPath> FindGitInstallationPath(IProcessManager processManager)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Environment.GitExecutablePath))
|
||||
return Environment.GitExecutablePath;
|
||||
return new FuncTask<NPath>(TaskEx.FromResult(Environment.GitExecutablePath));
|
||||
|
||||
var path = LookForPortableGit();
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
path = await base.FindGitInstallationPath(processManager);
|
||||
}
|
||||
|
||||
Logger.Trace("Git Installation folder {0} discovered: '{1}'", path == null ? "not" : "", path);
|
||||
return path;
|
||||
return new FuncTask<NPath>(TaskManager.Instance.Token, _ =>
|
||||
{
|
||||
Logger.Trace("Looking for Git Installation folder");
|
||||
return LookForPortableGit();
|
||||
})
|
||||
.ThenIf(path =>
|
||||
{
|
||||
if (path == null)
|
||||
return base.FindGitInstallationPath(processManager);
|
||||
else
|
||||
return new FuncTask<NPath>(TaskEx.FromResult(path));
|
||||
})
|
||||
.Finally((s, e, path) =>
|
||||
{
|
||||
Logger.Trace("Git Installation folder {0} discovered: '{1}'", path == null ? "not" : "", path);
|
||||
return path;
|
||||
});
|
||||
}
|
||||
|
||||
private string LookForPortableGit()
|
||||
private NPath LookForPortableGit()
|
||||
{
|
||||
var localAppDataPath = Environment.GetSpecialFolder(System.Environment.SpecialFolder.LocalApplicationData);
|
||||
var gitHubLocalAppDataPath = System.IO.Path.Combine(localAppDataPath, ApplicationInfo.ApplicationName);
|
||||
|
||||
var searchPath = System.IO.Path.Combine(gitHubLocalAppDataPath, "PortableGit_");
|
||||
|
||||
if (!FileSystem.DirectoryExists(gitHubLocalAppDataPath))
|
||||
var gitHubLocalAppDataPath = Environment.UserCachePath;
|
||||
if (!gitHubLocalAppDataPath.DirectoryExists())
|
||||
return null;
|
||||
|
||||
var directories = FileSystem.GetDirectories(gitHubLocalAppDataPath).ToArray();
|
||||
var searchPath = "PortableGit_";
|
||||
|
||||
var portableGitPath = directories.Where(s => {
|
||||
var startsWith = s.StartsWith(searchPath, StringComparison.InvariantCultureIgnoreCase);
|
||||
return startsWith;
|
||||
}).Select(s => System.IO.Path.Combine(gitHubLocalAppDataPath, s)).ToArray().FirstOrDefault();
|
||||
var portableGitPath = gitHubLocalAppDataPath.Directories()
|
||||
.Where(s => s.FileName.StartsWith(searchPath, StringComparison.InvariantCultureIgnoreCase))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (portableGitPath != null)
|
||||
{
|
||||
portableGitPath = System.IO.Path.Combine(gitHubLocalAppDataPath, portableGitPath);
|
||||
portableGitPath = System.IO.Path.Combine(portableGitPath, "cmd");
|
||||
portableGitPath = System.IO.Path.Combine(portableGitPath, "git.exe");
|
||||
portableGitPath = portableGitPath.Combine("cmd", $"git{GetExecutableExtension()}");
|
||||
}
|
||||
|
||||
return portableGitPath;
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
interface IProcessManager
|
||||
{
|
||||
T ConfigureGitProcess<T>(T processTask, bool withInput = false) where T : IProcess;
|
||||
T Configure<T>(T processTask, bool withInput = false) where T : IProcess;
|
||||
T Configure<T>(T processTask, string executableFileName, string arguments, string workingDirectory = null, bool withInput = false)
|
||||
where T : IProcess;
|
||||
IProcess Reconnect(IProcess processTask, int i);
|
||||
|
|
|
@ -27,9 +27,12 @@ namespace GitHub.Unity
|
|||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public T ConfigureGitProcess<T>(T processTask, bool withInput = false) where T : IProcess
|
||||
public T Configure<T>(T processTask, bool withInput = false) where T : IProcess
|
||||
{
|
||||
return Configure(processTask, environment.GitExecutablePath, processTask.ProcessArguments, environment.RepositoryPath, withInput);
|
||||
return Configure(processTask,
|
||||
processTask.ProcessName != null ? processTask.ProcessName : environment.GitExecutablePath,
|
||||
processTask.ProcessArguments,
|
||||
environment.RepositoryPath, withInput);
|
||||
}
|
||||
|
||||
public T Configure<T>(T processTask, string executableFileName, string arguments, string workingDirectory = null, bool withInput = false)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
using Rackspace.Threading;
|
||||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
class ProgressReport
|
||||
{
|
||||
public Progress<float> Percentage = new Progress<float>();
|
||||
public Progress<long> Remaining = new Progress<long>();
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ namespace GitHub.Unity
|
|||
/// <param name="name">The repository name.</param>
|
||||
/// <param name="cloneUrl">The repository's clone URL.</param>
|
||||
/// <param name="localPath"></param>
|
||||
public Repository(IGitClient gitClient, IRepositoryManager repositoryManager, string name, UriString cloneUrl, string localPath)
|
||||
public Repository(IGitClient gitClient, IRepositoryManager repositoryManager, string name, UriString cloneUrl, NPath localPath)
|
||||
{
|
||||
Guard.ArgumentNotNull(repositoryManager, nameof(repositoryManager));
|
||||
Guard.ArgumentNotNullOrWhiteSpace(name, nameof(name));
|
||||
|
@ -157,7 +157,7 @@ namespace GitHub.Unity
|
|||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 17 * 23 + (Name?.GetHashCode() ?? 0) * 23 + (Owner?.GetHashCode() ?? 0) * 23 + (LocalPath?.TrimEnd('\\').ToUpperInvariant().GetHashCode() ?? 0);
|
||||
return 17 * 23 + (Name?.GetHashCode() ?? 0) * 23 + (Owner?.GetHashCode() ?? 0) * 23 + (LocalPath?.GetHashCode() ?? 0);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
|
@ -181,7 +181,7 @@ namespace GitHub.Unity
|
|||
String.Equals(Name, other.Name) &&
|
||||
String.Equals(Owner, other.Owner) &&
|
||||
String.Equals(CloneUrl, other.CloneUrl) &&
|
||||
String.Equals(LocalPath?.TrimEnd('\\'), other.LocalPath?.TrimEnd('\\'), StringComparison.CurrentCultureIgnoreCase);
|
||||
object.Equals(LocalPath, other.LocalPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -208,7 +208,7 @@ namespace GitHub.Unity
|
|||
|
||||
public string Name { get; private set; }
|
||||
public UriString CloneUrl { get; private set; }
|
||||
public string LocalPath { get; private set; }
|
||||
public NPath LocalPath { get; private set; }
|
||||
public string Owner => CloneUrl?.Owner ?? string.Empty;
|
||||
public bool IsGitHub { get { return CloneUrl != ""; } }
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace GitHub.Unity
|
|||
|
||||
var initTask = new GitInitTask(token);
|
||||
|
||||
var unityYamlMergeExec = Environment.UnityApplication.ToNPath().Parent.Combine("Tools", "UnityYAMLMerge");
|
||||
var unityYamlMergeExec = Environment.UnityApplication.Parent.Combine("Tools", "UnityYAMLMerge");
|
||||
var yamlMergeCommand = string.Format(@"'{0}' merge -p ""$BASE"" ""$REMOTE"" ""$LOCAL"" ""$MERGED""", unityYamlMergeExec);
|
||||
var yaml1 = new GitConfigSetTask("merge.unityyamlmerge.cmd", yamlMergeCommand, GitConfigSource.Local, token);
|
||||
var yaml2 = new GitConfigSetTask("merge.unityyamlmerge.trustExitCode", "false", GitConfigSource.Local, token);
|
||||
|
@ -59,13 +59,13 @@ namespace GitHub.Unity
|
|||
var commitTask = new GitCommitTask("Initial commit", null, token);
|
||||
|
||||
initTask
|
||||
.ContinueWith(yaml1)
|
||||
.ContinueWith(yaml2)
|
||||
.ContinueWith(lfsTask)
|
||||
.ContinueWith(ignoresTask)
|
||||
.ContinueWith(addTask)
|
||||
.ContinueWith(commitTask)
|
||||
.ContinueWith(_ => ApplicationManager.RestartRepository().Start(TaskManager.ConcurrentScheduler));
|
||||
.Then(yaml1)
|
||||
.Then(yaml2)
|
||||
.Then(lfsTask)
|
||||
.Then(ignoresTask)
|
||||
.Then(addTask)
|
||||
.Then(commitTask)
|
||||
.Then(_ => ApplicationManager.RestartRepository().Start(TaskManager.ConcurrentScheduler));
|
||||
initTask.Schedule(TaskManager);
|
||||
|
||||
//task.Critical = false;
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace GitHub.Unity
|
|||
DotGitPath =
|
||||
DotGitPath.ReadAllLines()
|
||||
.Where(x => x.StartsWith("gitdir:"))
|
||||
.Select(x => x.Substring(7).Trim())
|
||||
.Select(x => x.Substring(7).Trim().ToNPath())
|
||||
.First();
|
||||
}
|
||||
|
||||
|
@ -198,28 +198,26 @@ namespace GitHub.Unity
|
|||
{
|
||||
var add = GitClient.Add(files);
|
||||
add.OnStart += t => IsBusy = true;
|
||||
var task = add.ContinueWith(GitClient.Commit(message, body))
|
||||
.ContinueWith(_ => IsBusy = false);
|
||||
add.Schedule(taskManager);
|
||||
return task;
|
||||
return add.Then(GitClient.Commit(message, body))
|
||||
.Then(_ => IsBusy = false);
|
||||
}
|
||||
|
||||
public ITask Fetch(string remote)
|
||||
{
|
||||
var task = GitClient.Fetch(remote);
|
||||
return HookupHandlers(task).Schedule(taskManager);
|
||||
return HookupHandlers(task);
|
||||
}
|
||||
|
||||
public ITask Pull(string remote, string branch)
|
||||
{
|
||||
var task = GitClient.Pull(remote, branch);
|
||||
return HookupHandlers(task, true).Schedule(taskManager);
|
||||
return HookupHandlers(task, true);
|
||||
}
|
||||
|
||||
public ITask Push(string remote, string branch)
|
||||
{
|
||||
var task = GitClient.Push(remote, branch);
|
||||
return HookupHandlers(task).Schedule(taskManager);
|
||||
return HookupHandlers(task);
|
||||
}
|
||||
|
||||
public ITask RemoteAdd(string remote, string url)
|
||||
|
@ -228,9 +226,9 @@ namespace GitHub.Unity
|
|||
HookupHandlers(task);
|
||||
if (!platform.Environment.IsWindows)
|
||||
{
|
||||
task.ContinueWith(_ => OnConfigChanged());
|
||||
task.Then(_ => OnConfigChanged());
|
||||
}
|
||||
return task.Schedule(taskManager);
|
||||
return task;
|
||||
}
|
||||
|
||||
public ITask RemoteRemove(string remote)
|
||||
|
@ -239,40 +237,40 @@ namespace GitHub.Unity
|
|||
HookupHandlers(task);
|
||||
if (!platform.Environment.IsWindows)
|
||||
{
|
||||
task.ContinueWith(_ => OnConfigChanged());
|
||||
task.Then(_ => OnConfigChanged());
|
||||
}
|
||||
return task.Schedule(taskManager);
|
||||
return task;
|
||||
}
|
||||
|
||||
public ITask RemoteChange(string remote, string url)
|
||||
{
|
||||
var task = GitClient.RemoteChange(remote, url);
|
||||
return HookupHandlers(task).Schedule(taskManager);
|
||||
return HookupHandlers(task);
|
||||
}
|
||||
|
||||
public ITask SwitchBranch(string branch)
|
||||
{
|
||||
var task = GitClient.SwitchBranch(branch);
|
||||
return HookupHandlers(task, true).Schedule(taskManager);
|
||||
return HookupHandlers(task, true);
|
||||
}
|
||||
|
||||
public ITask DeleteBranch(string branch, bool deleteUnmerged = false)
|
||||
{
|
||||
var task = GitClient.DeleteBranch(branch, deleteUnmerged);
|
||||
return HookupHandlers(task).Schedule(taskManager);
|
||||
return HookupHandlers(task);
|
||||
}
|
||||
|
||||
public ITask CreateBranch(string branch, string baseBranch)
|
||||
{
|
||||
var task = GitClient.CreateBranch(branch, baseBranch);
|
||||
return HookupHandlers(task).Schedule(taskManager);
|
||||
return HookupHandlers(task);
|
||||
}
|
||||
|
||||
public ITask ListLocks(bool local)
|
||||
{
|
||||
var task = GitClient
|
||||
.ListLocks(local)
|
||||
.ContinueWith((s, t) =>
|
||||
.Then((s, t) =>
|
||||
{
|
||||
if (locks == null || !locks.SequenceEqual(t))
|
||||
{
|
||||
|
@ -281,13 +279,13 @@ namespace GitHub.Unity
|
|||
OnLocksUpdated(locks);
|
||||
}
|
||||
});
|
||||
return HookupHandlers(task).Schedule(taskManager);
|
||||
return HookupHandlers(task);
|
||||
}
|
||||
|
||||
public ITask LockFile(string file)
|
||||
{
|
||||
var task = GitClient.Lock(file);
|
||||
HookupHandlers(task).Schedule(taskManager);
|
||||
HookupHandlers(task);
|
||||
ListLocks(false);
|
||||
return task;
|
||||
}
|
||||
|
@ -355,7 +353,7 @@ namespace GitHub.Unity
|
|||
Logger.Trace("Starting OnRepositoryUpdatedHandler");
|
||||
|
||||
var task = GitClient.Status()
|
||||
.ContinueWith((success, data) =>
|
||||
.Finally((success, ex, data) =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
|
@ -363,7 +361,7 @@ namespace GitHub.Unity
|
|||
}
|
||||
Logger.Trace("Ending OnRepositoryUpdatedHandler");
|
||||
});
|
||||
HookupHandlers(task).Schedule(taskManager);
|
||||
HookupHandlers(task).Start();
|
||||
}
|
||||
|
||||
private void OnConfigChanged()
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
using System.Threading;
|
||||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
class FindGitTask : ProcessTask<string>
|
||||
{
|
||||
private readonly IEnvironment environment;
|
||||
|
||||
public FindGitTask(IEnvironment environment,
|
||||
CancellationToken token, IOutputProcessor<string> processor = null, ITask dependsOn = null)
|
||||
: base(token, processor ?? new FirstNonNullLineOutputProcessor(), dependsOn)
|
||||
{
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public override string Name { get { return "find git"; } }
|
||||
public override string ProcessName { get { return environment.IsWindows ? "where" : "which"; } }
|
||||
public override string ProcessArguments { get { return "git"; } }
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ namespace GitHub.Unity
|
|||
{
|
||||
private readonly string value;
|
||||
private readonly string arguments;
|
||||
private bool done = false;
|
||||
private string label;
|
||||
|
||||
public GitConfigSetTask(string key, string value, GitConfigSource configSource,
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using GitHub.Unity;
|
||||
using NUnit.Framework;
|
||||
using Rackspace.Threading;
|
||||
using TestUtils;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
class GitSetupTests : BaseGitRepoTest
|
||||
{
|
||||
[Test, Category("DoNotRunOnAppVeyor")]
|
||||
public void InstallGit()
|
||||
{
|
||||
var environmentPath = NPath.CreateTempDirectory("integration-test-environment");
|
||||
var environment = new IntegrationTestEnvironment(SolutionDirectory, environmentPath);
|
||||
var gitSetup = new GitSetup(environment, FileSystem, CancellationToken.None);
|
||||
var expectedPath = gitSetup.GitInstallationPath;
|
||||
|
||||
var setupDone = false;
|
||||
var percent = -1f;
|
||||
gitSetup.GitExecutablePath.FileExists().Should().BeFalse();
|
||||
|
||||
setupDone = gitSetup.SetupIfNeeded(percentage: new Progress<float>(x => percent = x)).Result;
|
||||
|
||||
if (environment.IsWindows)
|
||||
{
|
||||
environment.GitExecutablePath = gitSetup.GitExecutablePath;
|
||||
|
||||
setupDone.Should().BeTrue ();
|
||||
percent.Should().Be (1);
|
||||
|
||||
Logger.Trace ("Expected GitExecutablePath: {0}", gitSetup.GitExecutablePath);
|
||||
gitSetup.GitExecutablePath.FileExists ().Should ().BeTrue ();
|
||||
|
||||
var gitLfsDestinationPath = gitSetup.GitInstallationPath;
|
||||
gitLfsDestinationPath = gitLfsDestinationPath.Combine("mingw32");
|
||||
|
||||
gitLfsDestinationPath = gitLfsDestinationPath.Combine("libexec", "git-core", "git-lfs.exe");
|
||||
gitLfsDestinationPath.FileExists().Should().BeTrue();
|
||||
|
||||
var calculateMd5 = NPathFileSystemProvider.Current.CalculateMD5(gitLfsDestinationPath);
|
||||
GitInstaller.GitLfsExecutableMD5.Should ().Be (calculateMd5);
|
||||
|
||||
setupDone = gitSetup.SetupIfNeeded (percentage: new Progress<float> (x => percent = x)).Result;
|
||||
setupDone.Should ().BeFalse ();
|
||||
}
|
||||
else
|
||||
{
|
||||
environment.GitExecutablePath = "/usr/local/bin/git";
|
||||
setupDone.Should().BeFalse ();
|
||||
}
|
||||
|
||||
var platform = new Platform(environment, FileSystem);
|
||||
var gitEnvironment = platform.GitEnvironment;
|
||||
var processManager = new ProcessManager(environment, gitEnvironment);
|
||||
|
||||
var gitBranches = processManager.GetGitBranches(TestRepoMasterDirtyUnsynchronized, environment.GitExecutablePath).Result;
|
||||
|
||||
gitBranches.Should().BeEquivalentTo(
|
||||
new GitBranch("master", "origin/master: behind 1", true),
|
||||
new GitBranch("feature/document", "origin/feature/document", false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using NSubstitute.Core;
|
||||
|
||||
namespace TestUtils
|
||||
{
|
||||
class CreateEnvironmentOptions
|
||||
{
|
||||
public const string DefaultExtensionFolder = @"c:\GitHubUnity\ExtensionFolder";
|
||||
public const string DefaultUserProfilePath = @"c:\GitHubUnity\UserProfile";
|
||||
public const string DefaultUnityProjectPathAndRepositoryPath = @"c:\GitHubUnity\UnityProject";
|
||||
|
||||
public string Extensionfolder { get; set; } = DefaultExtensionFolder;
|
||||
public string UserProfilePath { get; set; } = DefaultUserProfilePath;
|
||||
public string UnityProjectPath { get; set; } = DefaultUnityProjectPathAndRepositoryPath;
|
||||
public string RepositoryPath { get; set; } = DefaultUnityProjectPathAndRepositoryPath;
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
Environment = environment;
|
||||
FileSystem = fileSystem;
|
||||
NPathFileSystemProvider.Current = FileSystem;
|
||||
NPath.FileSystem = FileSystem;
|
||||
Platform = platform;
|
||||
ProcessManager = processManager;
|
||||
}
|
||||
|
@ -38,12 +38,12 @@ namespace GitHub.Unity
|
|||
Initialize();
|
||||
}
|
||||
|
||||
public override Task Run()
|
||||
public override ITask Run()
|
||||
{
|
||||
Utility.Initialize();
|
||||
|
||||
return base.Run()
|
||||
.ContinueWith(_ =>
|
||||
.ThenInUI(_ =>
|
||||
{
|
||||
Utility.Run();
|
||||
|
||||
|
@ -54,14 +54,14 @@ namespace GitHub.Unity
|
|||
view.Initialize(this);
|
||||
|
||||
//logger.Debug("Application Restarted");
|
||||
}, UIScheduler);
|
||||
}).Start();
|
||||
}
|
||||
|
||||
|
||||
protected override void InitializeEnvironment()
|
||||
{
|
||||
FileSystem = new FileSystem();
|
||||
NPathFileSystemProvider.Current = FileSystem;
|
||||
NPath.FileSystem = FileSystem;
|
||||
|
||||
Environment = new DefaultEnvironment();
|
||||
|
||||
|
@ -72,10 +72,10 @@ namespace GitHub.Unity
|
|||
var assetsPath = Application.dataPath.ToNPath();
|
||||
var projectPath = assetsPath.Parent;
|
||||
|
||||
Environment.UnityApplication = EditorApplication.applicationPath;
|
||||
Environment.UnityApplication = EditorApplication.applicationPath.ToNPath();
|
||||
|
||||
Environment.UnityAssetsPath = assetsPath.ToString(SlashMode.Forward);
|
||||
Environment.UnityProjectPath = projectPath.ToString(SlashMode.Forward);
|
||||
Environment.UnityAssetsPath = assetsPath;
|
||||
Environment.UnityProjectPath = projectPath;
|
||||
|
||||
base.InitializeEnvironment();
|
||||
}
|
||||
|
@ -132,17 +132,17 @@ namespace GitHub.Unity
|
|||
}
|
||||
}
|
||||
|
||||
private string DetermineInstallationPath()
|
||||
private NPath DetermineInstallationPath()
|
||||
{
|
||||
// Juggling to find out where we got installed
|
||||
var shim = ScriptableObject.CreateInstance<RunLocationShim>();
|
||||
var script = MonoScript.FromScriptableObject(shim);
|
||||
string ret = String.Empty;
|
||||
NPath ret = null;
|
||||
|
||||
if (script != null)
|
||||
{
|
||||
var scriptPath = AssetDatabase.GetAssetPath(script).ToNPath();
|
||||
ret = scriptPath.Parent.ToString(SlashMode.Forward);
|
||||
ret = scriptPath.Parent;
|
||||
}
|
||||
ScriptableObject.DestroyImmediate(shim);
|
||||
return ret;
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace GitHub.Unity
|
|||
public string TriggerText { get; private set; }
|
||||
}
|
||||
|
||||
class EvaluateProjectConfigurationTask : ListTaskBase<List<ProjectConfigurationIssue>, ProjectConfigurationIssue>
|
||||
class EvaluateProjectConfigurationTask : DataTaskBase<ProjectConfigurationIssue, List<ProjectConfigurationIssue>>
|
||||
{
|
||||
private const string GitIgnoreFilePattern = ".gitignore";
|
||||
private const string VCSPropertyName = "m_ExternalVersionControlSupport";
|
||||
|
|
|
@ -512,7 +512,7 @@ namespace GitHub.Unity
|
|||
if (createBranch)
|
||||
{
|
||||
GitClient.CreateBranch(newBranchName, selectedNode.Name)
|
||||
.ContinueWithUI(success => { if (success) Refresh(); });
|
||||
.FinallyInUI((success, e) => { if (success) Refresh(); });
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
|
@ -617,7 +617,7 @@ namespace GitHub.Unity
|
|||
ConfirmSwitchCancel))
|
||||
{
|
||||
GitClient.SwitchBranch(node.Name)
|
||||
.ContinueWithUI(success =>
|
||||
.FinallyInUI((success, e) =>
|
||||
{
|
||||
if (success)
|
||||
Refresh();
|
||||
|
@ -632,7 +632,7 @@ namespace GitHub.Unity
|
|||
else if (node.Type == NodeType.RemoteBranch)
|
||||
{
|
||||
GitClient.CreateBranch(selectedNode.Name.Substring(selectedNode.Name.IndexOf('/') + 1), selectedNode.Name)
|
||||
.ContinueWithUI(success =>
|
||||
.FinallyInUI((success, e) =>
|
||||
{
|
||||
if (success)
|
||||
Refresh();
|
||||
|
|
|
@ -190,8 +190,8 @@ namespace GitHub.Unity
|
|||
busy = true;
|
||||
|
||||
GitClient.Commit(commitMessage, commitBody)
|
||||
.ContinueWith(GitClient.Status())
|
||||
.ContinueWithUI(_ => busy = false);
|
||||
.Then(GitClient.Status())
|
||||
.FinallyInUI((_, __) => busy = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ namespace GitHub.Unity
|
|||
{
|
||||
var entryPath = entries[index].Path.ToNPath();
|
||||
if (entryPath.IsChildOf(tree.Path))
|
||||
entryPath = entryPath.RelativeTo(tree.Path);
|
||||
entryPath = entryPath.RelativeTo(tree.Path.ToNPath());
|
||||
|
||||
var node = new FileTreeNode(entryPath, stateChangeCallback) { Target = entryCommitTargets[index] };
|
||||
if (!string.IsNullOrEmpty(entries[index].ProjectPath))
|
||||
|
|
|
@ -127,7 +127,7 @@ namespace GitHub.Unity
|
|||
private void RefreshLog()
|
||||
{
|
||||
GitClient.Log()
|
||||
.ContinueWithUI((success, log) => { if (success) OnLogUpdate(log); });
|
||||
.ThenInUI((success, log) => { if (success) OnLogUpdate(log); });
|
||||
}
|
||||
|
||||
public override void Refresh()
|
||||
|
@ -648,7 +648,7 @@ namespace GitHub.Unity
|
|||
var remote = Repository.CurrentRemote.HasValue ? Repository.CurrentRemote.Value.Name : String.Empty;
|
||||
Repository.Pull()
|
||||
// we need the error propagated from the original git command to handle things appropriately
|
||||
.ContinueWith(success =>
|
||||
.Then(success =>
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
|
@ -656,8 +656,8 @@ namespace GitHub.Unity
|
|||
// whether pull triggered a merge or a rebase, and abort the operation accordingly
|
||||
// (either git rebase --abort or git merge --abort)
|
||||
}
|
||||
})
|
||||
.ContinueWithUI(success => {
|
||||
}, true)
|
||||
.FinallyInUI((success, e) => {
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog(Localization.PullActionTitle,
|
||||
|
@ -670,15 +670,17 @@ namespace GitHub.Unity
|
|||
Localization.PullFailureDescription,
|
||||
Localization.Cancel);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void Push()
|
||||
{
|
||||
var remote = Repository.CurrentRemote.HasValue ? Repository.CurrentRemote.Value.Name : String.Empty;
|
||||
Repository.Push()
|
||||
.ContinueWithUI(success => {
|
||||
Repository
|
||||
.Push()
|
||||
.FinallyInUI((success, e) => {
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog(Localization.PushActionTitle,
|
||||
|
@ -691,7 +693,8 @@ namespace GitHub.Unity
|
|||
Localization.PushFailureDescription,
|
||||
Localization.Cancel);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Start();
|
||||
}
|
||||
|
||||
void drawTimelineRectAroundIconRect(Rect parentRect, Rect iconRect)
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace GitHub.Unity
|
|||
if (locks == null)
|
||||
return false;
|
||||
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID());
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID()).ToNPath();
|
||||
NPath repositoryPath = EntryPoint.Environment.GetRepositoryPath(assetPath);
|
||||
|
||||
var alreadyLocked = locks.Any(x =>
|
||||
|
@ -69,16 +69,18 @@ namespace GitHub.Unity
|
|||
isBusy = true;
|
||||
var selected = Selection.activeObject;
|
||||
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID());
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID()).ToNPath();
|
||||
NPath repositoryPath = EntryPoint.Environment.GetRepositoryPath(assetPath);
|
||||
|
||||
repository.RequestLock(repositoryPath)
|
||||
.ContinueWith(new ActionTask(EntryPoint.AppManager.CancellationToken, _ =>
|
||||
repository
|
||||
.RequestLock(repositoryPath)
|
||||
.ThenInUI(new ActionTask(EntryPoint.AppManager.CancellationToken, _ =>
|
||||
{
|
||||
isBusy = false;
|
||||
Selection.activeGameObject = null;
|
||||
EditorApplication.RepaintProjectWindow();
|
||||
}));
|
||||
}))
|
||||
.Start();
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Release lock", true, 1000)]
|
||||
|
@ -95,7 +97,7 @@ namespace GitHub.Unity
|
|||
if (locks == null || locks.Count == 0)
|
||||
return false;
|
||||
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID());
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID()).ToNPath();
|
||||
NPath repositoryPath = EntryPoint.Environment.GetRepositoryPath(assetPath);
|
||||
|
||||
var isLocked = locks.Any(x => repositoryPath == x.Path);
|
||||
|
@ -108,16 +110,18 @@ namespace GitHub.Unity
|
|||
isBusy = true;
|
||||
var selected = Selection.activeObject;
|
||||
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID());
|
||||
NPath assetPath = AssetDatabase.GetAssetPath(selected.GetInstanceID()).ToNPath();
|
||||
NPath repositoryPath = EntryPoint.Environment.GetRepositoryPath(assetPath);
|
||||
|
||||
repository.ReleaseLock(repositoryPath, false)
|
||||
.ContinueWith(new ActionTask(EntryPoint.AppManager.CancellationToken, _ =>
|
||||
repository
|
||||
.ReleaseLock(repositoryPath, false)
|
||||
.ThenInUI(new ActionTask(EntryPoint.AppManager.CancellationToken, _ =>
|
||||
{
|
||||
isBusy = false;
|
||||
Selection.activeGameObject = null;
|
||||
EditorApplication.RepaintProjectWindow();
|
||||
}));
|
||||
}))
|
||||
.Start();
|
||||
}
|
||||
public static void Run()
|
||||
{
|
||||
|
@ -159,7 +163,7 @@ namespace GitHub.Unity
|
|||
guidsLocks.Clear();
|
||||
foreach (var lck in locks)
|
||||
{
|
||||
NPath repositoryPath = lck.Path;
|
||||
NPath repositoryPath = lck.Path.ToNPath();
|
||||
NPath assetPath = EntryPoint.Environment.GetAssetPath(repositoryPath);
|
||||
|
||||
var g = AssetDatabase.AssetPathToGUID(assetPath);
|
||||
|
|
|
@ -127,7 +127,7 @@ namespace GitHub.Unity
|
|||
lockedFiles = new List<GitLock>();
|
||||
OnLocksUpdate(Repository.CurrentLocks);
|
||||
Repository.OnLocksUpdated += RunLocksUpdateOnMainThread;
|
||||
Repository.ListLocks();
|
||||
Repository.ListLocks().Start();
|
||||
|
||||
gitName = Repository.User.Name;
|
||||
gitEmail = Repository.User.Email;
|
||||
|
@ -227,11 +227,11 @@ namespace GitHub.Unity
|
|||
if (needsSaving)
|
||||
{
|
||||
GitClient.SetConfig("user.name", gitName, GitConfigSource.User)
|
||||
.ContinueWith((success, value) => { if (success) Repository.User.Name = value; })
|
||||
.ContinueWith(
|
||||
.Then((success, value) => { if (success) Repository.User.Name = value; })
|
||||
.Then(
|
||||
GitClient.SetConfig("user.email", gitEmail, GitConfigSource.User)
|
||||
.ContinueWith((success, value) => { if (success) Repository.User.Email = value; }))
|
||||
.ContinueWithUI(_ => busy = false);
|
||||
.Then((success, value) => { if (success) Repository.User.Email = value; }))
|
||||
.FinallyInUI((_, __) => busy = false);
|
||||
busy = true;
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +255,8 @@ namespace GitHub.Unity
|
|||
if (needsSaving)
|
||||
{
|
||||
Repository.SetupRemote(repositoryRemoteName, repositoryRemoteUrl)
|
||||
.ContinueWithUI(_ => busy = false, true);
|
||||
.FinallyInUI((_, __) => busy = false)
|
||||
.Start();
|
||||
}
|
||||
else
|
||||
busy = false;
|
||||
|
@ -583,7 +584,7 @@ namespace GitHub.Unity
|
|||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("Unlock"))
|
||||
{
|
||||
Repository.ReleaseLock(lck.Path, false);
|
||||
Repository.ReleaseLock(lck.Path, false).Start();
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
@ -603,7 +604,7 @@ namespace GitHub.Unity
|
|||
|
||||
GUI.enabled = !busy;
|
||||
|
||||
var gitExecPath = EntryPoint.Environment.GitExecutablePath;
|
||||
var gitExecPath = EntryPoint.Environment.GitExecutablePath.ToString();
|
||||
// Install path field
|
||||
EditorGUI.BeginChangeCheck();
|
||||
{
|
||||
|
@ -617,7 +618,7 @@ namespace GitHub.Unity
|
|||
}
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EntryPoint.Environment.GitExecutablePath = gitExecPath;
|
||||
EntryPoint.Environment.GitExecutablePath = gitExecPath.ToNPath();
|
||||
}
|
||||
|
||||
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
|
||||
|
@ -627,8 +628,9 @@ namespace GitHub.Unity
|
|||
// Find button - for attempting to locate a new install
|
||||
if (GUILayout.Button(GitInstallFindButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
var task = new FindGitTask(EntryPoint.Environment, Manager.CancellationToken)
|
||||
.ContinueWithUI((success, path) =>
|
||||
var task = new ProcessTask<NPath>(Manager.CancellationToken, new FirstLineIsPathOutputProcessor())
|
||||
.Configure(Manager.ProcessManager, Manager.Environment.IsWindows ? "where" : "which", "git")
|
||||
.FinallyInUI((success, ex, path) =>
|
||||
{
|
||||
if (success && !string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
|
|
@ -243,7 +243,7 @@ namespace GitHub.Unity
|
|||
private void SignOut(object obj)
|
||||
{
|
||||
var task = new ActionTask(EntryPoint.CredentialManager.Delete(EntryPoint.CredentialManager.CachedCredentials.Host))
|
||||
.ContinueWith(s =>
|
||||
.Then(s =>
|
||||
{
|
||||
if (s)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace IntegrationTests
|
|||
NPath commonParent = longestPath;
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var cp = commonParent.GetCommonParent(path);
|
||||
var cp = commonParent.GetCommonParent(path.ToNPath());
|
||||
if (cp != null)
|
||||
commonParent = cp;
|
||||
else
|
||||
|
@ -37,7 +37,7 @@ namespace IntegrationTests
|
|||
public void CommonParentTest()
|
||||
{
|
||||
var filesystem = new FileSystem(TestRepoMasterDirtyUnsynchronized);
|
||||
NPathFileSystemProvider.Current = filesystem;
|
||||
NPath.FileSystem = filesystem;
|
||||
var environment = new DefaultEnvironment();
|
||||
|
||||
var ret = FindCommonPath(new string[]
|
|
@ -1,29 +1,21 @@
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using GitHub.Unity;
|
||||
using NSubstitute;
|
||||
using TestUtils;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
class BaseGitEnvironmentTest : BaseGitRepoTest
|
||||
{
|
||||
protected void InitializeEnvironment(NPath repoPath, bool enableEnvironmentTrace = false)
|
||||
protected IEnvironment InitializeEnvironment(NPath repoPath, NPath environmentPath = null, bool enableEnvironmentTrace = false)
|
||||
{
|
||||
TaskManager = new TaskManager();
|
||||
var sc = new ThreadSynchronizationContext(TaskManager.Token);
|
||||
TaskManager.UIScheduler = new SynchronizationContextTaskScheduler(sc);
|
||||
|
||||
Environment = new IntegrationTestEnvironment(SolutionDirectory, enableTrace: enableEnvironmentTrace) {
|
||||
Environment = new IntegrationTestEnvironment(SolutionDirectory, environmentPath, enableEnvironmentTrace) {
|
||||
RepositoryPath = repoPath,
|
||||
UnityProjectPath = repoPath
|
||||
};
|
||||
|
||||
var gitSetup = new GitSetup(Environment, FileSystem, CancellationToken.None);
|
||||
gitSetup.SetupIfNeeded().Wait();
|
||||
|
||||
Environment.GitExecutablePath = gitSetup.GitExecutablePath;
|
||||
|
||||
FileSystem.SetCurrentDirectory(repoPath);
|
||||
|
||||
Platform = new Platform(Environment, FileSystem);
|
||||
|
@ -33,7 +25,6 @@ namespace IntegrationTests
|
|||
Platform.Initialize(ProcessManager, TaskManager);
|
||||
|
||||
Environment.UnityProjectPath = repoPath;
|
||||
Environment.GitExecutablePath = GitEnvironment.FindGitInstallationPath(ProcessManager).Result;
|
||||
|
||||
GitClient = new GitClient(Environment, ProcessManager, Platform.CredentialManager, TaskManager);
|
||||
|
||||
|
@ -51,7 +42,7 @@ namespace IntegrationTests
|
|||
DotGitPath =
|
||||
DotGitPath.ReadAllLines()
|
||||
.Where(x => x.StartsWith("gitdir:"))
|
||||
.Select(x => x.Substring(7).Trim())
|
||||
.Select(x => x.Substring(7).Trim().ToNPath())
|
||||
.First();
|
||||
}
|
||||
|
||||
|
@ -60,6 +51,17 @@ namespace IntegrationTests
|
|||
DotGitIndex = DotGitPath.Combine("index");
|
||||
DotGitHead = DotGitPath.Combine("HEAD");
|
||||
DotGitConfig = DotGitPath.Combine("config");
|
||||
return Environment;
|
||||
}
|
||||
|
||||
protected IEnvironment Initialize(NPath repoPath, NPath environmentPath = null, bool enableEnvironmentTrace = false)
|
||||
{
|
||||
InitializeEnvironment(repoPath, environmentPath, enableEnvironmentTrace);
|
||||
var gitSetup = new GitSetup(Environment, FileSystem, TaskManager.Token);
|
||||
gitSetup.SetupIfNeeded().Wait();
|
||||
|
||||
Environment.GitExecutablePath = gitSetup.GitExecutablePath;
|
||||
return Environment;
|
||||
}
|
||||
|
||||
protected override void OnTearDown()
|
|
@ -1,8 +1,6 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using GitHub.Unity;
|
||||
using Ionic.Zip;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
|
@ -17,10 +15,7 @@ namespace IntegrationTests
|
|||
TestRepoMasterCleanSynchronized = TestBasePath.Combine("IOTestsRepo", "IOTestsRepo_master_clean_sync");
|
||||
TestRepoMasterDirtyUnsynchronized = TestBasePath.Combine("IOTestsRepo", "IOTestsRepo_master_dirty_unsync");
|
||||
|
||||
using (var zipFile = new ZipFile(TestZipFilePath))
|
||||
{
|
||||
zipFile.ExtractAll(TestBasePath.ToString(), ExtractExistingFileAction.OverwriteSilently);
|
||||
}
|
||||
ZipHelper.ExtractZipFile(TestZipFilePath, TestBasePath.ToString(), CancellationToken.None);
|
||||
}
|
||||
|
||||
protected NPath TestRepoMasterCleanSynchronized { get; private set; }
|
|
@ -3,6 +3,7 @@ using FluentAssertions;
|
|||
using NUnit.Framework;
|
||||
using GitHub.Unity;
|
||||
using NCrunch.Framework;
|
||||
using System.Threading;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
|
@ -13,7 +14,7 @@ namespace IntegrationTests
|
|||
protected ILogging Logger { get; private set; }
|
||||
protected IFileSystem FileSystem { get; private set; }
|
||||
protected TestUtils.SubstituteFactory Factory { get; set; }
|
||||
protected static string SolutionDirectory => TestContext.CurrentContext.TestDirectory;
|
||||
protected static NPath SolutionDirectory => TestContext.CurrentContext.TestDirectory.ToNPath();
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void TestFixtureSetUp()
|
||||
|
@ -30,11 +31,8 @@ namespace IntegrationTests
|
|||
|
||||
protected virtual void OnSetup()
|
||||
{
|
||||
FileSystem = new FileSystem(TestBasePath);
|
||||
|
||||
NPathFileSystemProvider.Current.Should().BeNull("Test should run in isolation");
|
||||
NPathFileSystemProvider.Current = FileSystem;
|
||||
|
||||
FileSystem = new FileSystem();
|
||||
NPath.FileSystem = FileSystem;
|
||||
TestBasePath = NPath.CreateTempDirectory("integration-tests");
|
||||
FileSystem.SetCurrentDirectory(TestBasePath);
|
||||
}
|
||||
|
@ -47,18 +45,25 @@ namespace IntegrationTests
|
|||
|
||||
protected virtual void OnTearDown()
|
||||
{
|
||||
try
|
||||
TaskManager.Instance?.Stop();
|
||||
Logger.Debug("Deleting TestBasePath: {0}", TestBasePath.ToString());
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
Logger.Debug("Deleting TestBasePath: {0}", TestBasePath.ToString());
|
||||
TestBasePath.Delete();
|
||||
try
|
||||
{
|
||||
TestBasePath.Delete();
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (TestBasePath.Exists())
|
||||
Logger.Warning("Error deleting TestBasePath: {0}", TestBasePath.ToString());
|
||||
}
|
||||
|
||||
FileSystem = null;
|
||||
NPathFileSystemProvider.Current = null;
|
||||
NPath.FileSystem = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using GitHub.Unity;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using TestUtils;
|
||||
using TestUtils.Events;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
class RepositoryManagerTests : BaseGitEnvironmentTest
|
||||
{
|
||||
[Test]
|
||||
public void ShouldDetectFileChanges()
|
||||
public async Task ShouldDetectFileChanges()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -41,6 +41,11 @@ namespace IntegrationTests
|
|||
var foobarTxt = TestRepoMasterCleanSynchronized.Combine("foobar.txt");
|
||||
foobarTxt.WriteAllText("foobar");
|
||||
|
||||
await TaskManager.Wait();
|
||||
// give the fs watcher a bit of time to catch up
|
||||
await TaskEx.Delay(200);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnRepositoryChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
||||
Logger.Trace("Continue test");
|
||||
|
@ -59,9 +64,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldAddAndCommitFiles()
|
||||
public async Task ShouldAddAndCommitFiles()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -102,6 +107,7 @@ namespace IntegrationTests
|
|||
|
||||
var testDocumentTxt = TestRepoMasterCleanSynchronized.Combine("Assets", "TestDocument.txt");
|
||||
testDocumentTxt.WriteAllText("foobar");
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnRepositoryChanged.WaitOne(TimeSpan.FromSeconds(5000)).Should().BeTrue();
|
||||
|
||||
|
@ -123,7 +129,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.CommitFiles(new List<string>() { "Assets\\TestDocument.txt", "foobar.txt" }, "IntegrationTest Commit", string.Empty);
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.CommitFiles(new List<string>() { "Assets\\TestDocument.txt", "foobar.txt" }, "IntegrationTest Commit", string.Empty).Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnActiveBranchChanged.WaitOne(TimeSpan.FromSeconds(5)).Should().BeTrue();
|
||||
|
||||
|
@ -139,9 +146,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectBranchChange()
|
||||
public async Task ShouldDetectBranchChange()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -159,10 +166,15 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.SwitchBranch("feature/document");
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.SwitchBranch("feature/document").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
// give the fs watcher a bit of time to catch up
|
||||
await TaskEx.Delay(100);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnRepositoryChanged.WaitOne(TimeSpan.FromSeconds(3)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnActiveBranchChanged.WaitOne(TimeSpan.FromSeconds(3)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnRepositoryChanged.WaitOne(TimeSpan.FromSeconds(3)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnHeadChanged.WaitOne(TimeSpan.FromSeconds(3)).Should().BeTrue();
|
||||
|
||||
Logger.Trace("Continue test");
|
||||
|
@ -180,9 +192,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectBranchDelete()
|
||||
public async Task ShouldDetectBranchDelete()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -191,7 +203,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.DeleteBranch("feature/document", true);
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.DeleteBranch("feature/document", true).Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnLocalBranchListChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnRemoteOrTrackingChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -210,9 +223,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectBranchCreate()
|
||||
public async Task ShouldDetectBranchCreate()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -221,7 +234,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.CreateBranch("feature/document2", "feature/document");
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.CreateBranch("feature/document2", "feature/document").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnLocalBranchListChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
||||
|
@ -241,7 +255,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.CreateBranch("feature2/document2", "feature/document");
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.CreateBranch("feature2/document2", "feature/document").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnLocalBranchListChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
||||
|
@ -260,9 +275,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectChangesToRemotes()
|
||||
public async Task ShouldDetectChangesToRemotes()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -271,7 +286,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.RemoteRemove("origin").Wait();
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.RemoteRemove("origin").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnActiveBranchChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnActiveRemoteChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -293,7 +309,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.RemoteAdd("origin", "https://github.com/EvilStanleyGoldman/IOTestsRepo.git").Wait();
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.RemoteAdd("origin", "https://github.com/EvilStanleyGoldman/IOTestsRepo.git").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnActiveRemoteChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnRemoteOrTrackingChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -311,10 +328,10 @@ namespace IntegrationTests
|
|||
repositoryManagerListener.DidNotReceive().OnLocksUpdated(Args.EnumerableGitLock);
|
||||
}
|
||||
|
||||
[Test, Ignore("Failing on CI, needs fixing")]
|
||||
public void ShouldDetectGitPull()
|
||||
[Test]
|
||||
public async Task ShouldDetectGitPull()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -332,7 +349,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.Pull("origin", "master").Wait();
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.Pull("origin", "master").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnRepositoryChanged.WaitOne(TimeSpan.FromSeconds(7)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnActiveBranchChanged.WaitOne(TimeSpan.FromSeconds(7)).Should().BeTrue();
|
||||
|
@ -357,10 +375,10 @@ namespace IntegrationTests
|
|||
repositoryManagerListener.DidNotReceive().OnLocksUpdated(Args.EnumerableGitLock);
|
||||
}
|
||||
|
||||
[Test, Ignore("Failing on CI, needs fixing")]
|
||||
public void ShouldDetectGitFetch()
|
||||
[Test]
|
||||
public async Task ShouldDetectGitFetch()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanUnsynchronized);
|
||||
Initialize(TestRepoMasterCleanUnsynchronized);
|
||||
|
||||
var managerAutoResetEvent = new RepositoryManagerAutoResetEvent();
|
||||
|
||||
|
@ -369,7 +387,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
RepositoryManager.Fetch("origin").Wait();
|
||||
Assert.DoesNotThrow(async () => await RepositoryManager.Fetch("origin").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
managerAutoResetEvent.OnRemoteBranchListChanged.WaitOne(TimeSpan.FromSeconds(3)).Should().BeTrue();
|
||||
managerAutoResetEvent.OnRemoteBranchListChanged.WaitOne(TimeSpan.FromSeconds(3)).Should().BeTrue();
|
|
@ -5,6 +5,7 @@ using GitHub.Unity;
|
|||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using TestUtils;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
|
@ -13,9 +14,9 @@ namespace IntegrationTests
|
|||
private const int ThreadSleepTimeout = 2000;
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectFileChanges()
|
||||
public async Task ShouldDetectFileChanges()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanSynchronized);
|
||||
|
||||
|
@ -34,6 +35,7 @@ namespace IntegrationTests
|
|||
Logger.Trace("Issuing Changes");
|
||||
|
||||
foobarTxt.WriteAllText("foobar");
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.IndexChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
watcherAutoResetEvent.RepositoryChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -58,9 +60,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectBranchChange()
|
||||
public async Task ShouldDetectBranchChange()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanSynchronized);
|
||||
|
||||
|
@ -76,7 +78,8 @@ namespace IntegrationTests
|
|||
{
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
SwitchBranch("feature/document");
|
||||
Assert.DoesNotThrow(async () => await GitClient.SwitchBranch("feature/document").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.HeadChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
watcherAutoResetEvent.IndexChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -102,9 +105,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectBranchDelete()
|
||||
public async Task ShouldDetectBranchDelete()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanSynchronized);
|
||||
|
||||
|
@ -120,7 +123,8 @@ namespace IntegrationTests
|
|||
{
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
DeleteBranch("feature/document");
|
||||
Assert.DoesNotThrow(async () => await GitClient.DeleteBranch("feature/document", true).Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.ConfigChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
watcherAutoResetEvent.LocalBranchDeleted.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -145,9 +149,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectBranchCreate()
|
||||
public async Task ShouldDetectBranchCreate()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanSynchronized);
|
||||
|
||||
|
@ -163,7 +167,8 @@ namespace IntegrationTests
|
|||
{
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
CreateBranch("feature/document2", "feature/document");
|
||||
Assert.DoesNotThrow(async () => await GitClient.CreateBranch("feature/document2", "feature/document").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.LocalBranchCreated.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
||||
|
@ -184,7 +189,8 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
CreateBranch("feature2/document2", "feature/document");
|
||||
Assert.DoesNotThrow(async () => await GitClient.CreateBranch("feature2/document2", "feature/document").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.LocalBranchCreated.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
||||
|
@ -210,9 +216,9 @@ namespace IntegrationTests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldDetectChangesToRemotes()
|
||||
public async Task ShouldDetectChangesToRemotes()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanSynchronized);
|
||||
|
||||
|
@ -228,7 +234,8 @@ namespace IntegrationTests
|
|||
{
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
GitRemoteRemove("origin");
|
||||
Assert.DoesNotThrow(async () => await GitClient.RemoteRemove("origin").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.ConfigChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
watcherAutoResetEvent.RemoteBranchDeleted.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -253,7 +260,11 @@ namespace IntegrationTests
|
|||
|
||||
Logger.Trace("Issuing 2nd Command");
|
||||
|
||||
GitRemoteAdd("origin", "https://github.com/EvilStanleyGoldman/IOTestsRepo.git");
|
||||
Assert.DoesNotThrow(async () => await GitClient.RemoteAdd("origin", "https://github.com/EvilStanleyGoldman/IOTestsRepo.git").Start().Task);
|
||||
// give the fs watcher a bit of time to catch up
|
||||
await TaskEx.Delay(500);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.ConfigChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
||||
Logger.Trace("Continue 2nd test");
|
||||
|
@ -275,10 +286,10 @@ namespace IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
[Test, Ignore("Failing on CI, needs fixing")]
|
||||
public void ShouldDetectGitPull()
|
||||
[Test]
|
||||
public async Task ShouldDetectGitPull()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanSynchronized);
|
||||
|
||||
|
@ -294,7 +305,8 @@ namespace IntegrationTests
|
|||
{
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
GitPull("origin", "master");
|
||||
Assert.DoesNotThrow(async () => await GitClient.Pull("origin", "master").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.IndexChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
watcherAutoResetEvent.LocalBranchChanged.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
|
@ -319,10 +331,10 @@ namespace IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
[Test, Ignore("Failing on CI, needs fixing")]
|
||||
public void ShouldDetectGitFetch()
|
||||
[Test]
|
||||
public async Task ShouldDetectGitFetch()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanUnsynchronized);
|
||||
Initialize(TestRepoMasterCleanUnsynchronized);
|
||||
|
||||
var repositoryWatcher = CreateRepositoryWatcher(TestRepoMasterCleanUnsynchronized);
|
||||
|
||||
|
@ -338,7 +350,8 @@ namespace IntegrationTests
|
|||
{
|
||||
Logger.Trace("Issuing Command");
|
||||
|
||||
GitFetch("origin");
|
||||
Assert.DoesNotThrow(async () => await GitClient.Fetch("origin").Start().Task);
|
||||
await TaskManager.Wait();
|
||||
|
||||
watcherAutoResetEvent.RemoteBranchCreated.WaitOne(TimeSpan.FromSeconds(2)).Should().BeTrue();
|
||||
watcherAutoResetEvent.RemoteBranchCreated.WaitOne(TimeSpan.FromSeconds(2));
|
||||
|
@ -363,69 +376,6 @@ namespace IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
protected void SwitchBranch(string branch)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.SwitchBranch(branch)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
protected void GitPull(string remote, string branch)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.Pull(remote, branch)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
protected void GitFetch(string remote)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.Fetch(remote)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
protected void DeleteBranch(string branch)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.DeleteBranch(branch)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
protected void GitRemoteAdd(string remote, string url)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.RemoteAdd(remote, url)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
protected void GitRemoteRemove(string remote)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.RemoteRemove(remote)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
protected void CreateBranch(string branch, string baseBranch)
|
||||
{
|
||||
var evt = new ManualResetEventSlim(false);
|
||||
GitClient.CreateBranch(branch, baseBranch)
|
||||
.ContinueWithUI(_ => evt.Set());
|
||||
var completed = evt.Wait(100);
|
||||
completed.Should().BeTrue();
|
||||
}
|
||||
|
||||
private RepositoryWatcher CreateRepositoryWatcher(NPath path)
|
||||
{
|
||||
var paths = new RepositoryPathConfiguration(path);
|
|
@ -0,0 +1,67 @@
|
|||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using GitHub.Unity;
|
||||
using NUnit.Framework;
|
||||
using Rackspace.Threading;
|
||||
using TestUtils;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
class GitSetupTests : BaseGitEnvironmentTest
|
||||
{
|
||||
[Test, Category("DoNotRunOnAppVeyor")]
|
||||
public async Task InstallGit()
|
||||
{
|
||||
var environmentPath = NPath.CreateTempDirectory("integration-test-environment");
|
||||
var environment = InitializeEnvironment(TestRepoMasterDirtyUnsynchronized, environmentPath);
|
||||
|
||||
var gitSetup = new GitSetup(environment, FileSystem, TaskManager.Token);
|
||||
var expectedPath = gitSetup.GitInstallationPath;
|
||||
|
||||
var setupDone = false;
|
||||
var percent = -1f;
|
||||
gitSetup.GitExecutablePath.FileExists().Should().BeFalse();
|
||||
|
||||
setupDone = await gitSetup.SetupIfNeeded(percentage: new Progress<float>(x => percent = x));
|
||||
|
||||
if (environment.IsWindows)
|
||||
{
|
||||
environment.GitExecutablePath = gitSetup.GitExecutablePath;
|
||||
|
||||
setupDone.Should().BeTrue();
|
||||
percent.Should().Be(1);
|
||||
|
||||
Logger.Trace("Expected GitExecutablePath: {0}", gitSetup.GitExecutablePath);
|
||||
gitSetup.GitExecutablePath.FileExists().Should().BeTrue();
|
||||
|
||||
var gitLfsDestinationPath = gitSetup.GitInstallationPath;
|
||||
gitLfsDestinationPath = gitLfsDestinationPath.Combine("mingw32");
|
||||
|
||||
gitLfsDestinationPath = gitLfsDestinationPath.Combine("libexec", "git-core", "git-lfs.exe");
|
||||
gitLfsDestinationPath.FileExists().Should().BeTrue();
|
||||
|
||||
var calculateMd5 = NPath.FileSystem.CalculateMD5(gitLfsDestinationPath);
|
||||
GitInstaller.GitLfsExecutableMD5.Should().Be(calculateMd5);
|
||||
|
||||
setupDone = await gitSetup.SetupIfNeeded(percentage: new Progress<float>(x => percent = x));
|
||||
setupDone.Should().BeFalse();
|
||||
}
|
||||
else
|
||||
{
|
||||
environment.GitExecutablePath = "/usr/local/bin/git".ToNPath();
|
||||
setupDone.Should().BeFalse();
|
||||
}
|
||||
|
||||
var platform = new Platform(environment, FileSystem);
|
||||
var gitEnvironment = platform.GitEnvironment;
|
||||
var processManager = new ProcessManager(environment, gitEnvironment);
|
||||
|
||||
var gitBranches = await processManager.GetGitBranches(TestRepoMasterDirtyUnsynchronized, environment.GitExecutablePath);
|
||||
|
||||
gitBranches.Should().BeEquivalentTo(
|
||||
new GitBranch("master", "origin/master: behind 1", true),
|
||||
new GitBranch("feature/document", "origin/feature/document", false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,11 +8,11 @@ namespace IntegrationTests
|
|||
private static readonly ILogging logger = Logging.GetLogger<IntegrationTestEnvironment>();
|
||||
private readonly bool enableTrace;
|
||||
|
||||
private readonly string extensionInstallPath;
|
||||
private readonly NPath extensionInstallPath;
|
||||
private readonly NPath integrationTestEnvironmentPath;
|
||||
|
||||
private DefaultEnvironment defaultEnvironment;
|
||||
private string unityProjectPath;
|
||||
private NPath unityProjectPath;
|
||||
|
||||
public IntegrationTestEnvironment(NPath solutionDirectory, NPath environmentPath = null,
|
||||
bool enableTrace = false)
|
||||
|
@ -65,10 +65,10 @@ namespace IntegrationTests
|
|||
|
||||
public string UserProfilePath => integrationTestEnvironmentPath.CreateDirectory("user-profile-path");
|
||||
|
||||
public string Path => Environment.GetEnvironmentVariable("PATH");
|
||||
public NPath Path => Environment.GetEnvironmentVariable("PATH").ToNPath();
|
||||
public string NewLine => Environment.NewLine;
|
||||
|
||||
public string GitExecutablePath
|
||||
public NPath GitExecutablePath
|
||||
{
|
||||
get { return defaultEnvironment.GitExecutablePath; }
|
||||
set
|
||||
|
@ -85,11 +85,11 @@ namespace IntegrationTests
|
|||
public bool IsLinux => defaultEnvironment.IsLinux;
|
||||
public bool IsMac => defaultEnvironment.IsMac;
|
||||
|
||||
public string UnityApplication { get; set; }
|
||||
public NPath UnityApplication { get; set; }
|
||||
|
||||
public string UnityAssetsPath { get; set; }
|
||||
public NPath UnityAssetsPath { get; set; }
|
||||
|
||||
public string UnityProjectPath
|
||||
public NPath UnityProjectPath
|
||||
{
|
||||
get { return unityProjectPath; }
|
||||
set
|
||||
|
@ -102,7 +102,7 @@ namespace IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
public string ExtensionInstallPath
|
||||
public NPath ExtensionInstallPath
|
||||
{
|
||||
get { return extensionInstallPath; }
|
||||
set {}
|
||||
|
@ -111,9 +111,9 @@ namespace IntegrationTests
|
|||
public NPath UserCachePath { get; set; }
|
||||
public NPath SystemCachePath { get; set; }
|
||||
|
||||
public string RepositoryPath { get; set; }
|
||||
public NPath RepositoryPath { get; set; }
|
||||
|
||||
public string GitInstallPath
|
||||
public NPath GitInstallPath
|
||||
{
|
||||
get { return defaultEnvironment.GitInstallPath; }
|
||||
}
|
|
@ -10,7 +10,7 @@ namespace IntegrationTests
|
|||
[Test]
|
||||
public void FindRepoRootTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var repositoryLocator = new RepositoryLocator(Environment.UnityProjectPath);
|
||||
|
|
@ -32,19 +32,19 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AsyncBridge.Net35, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b3b1c0202c0d6a87, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\AsyncBridge.Net35.0.2.0\lib\net35-Client\AsyncBridge.Net35.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetZip, Version=1.10.1.0, Culture=neutral, PublicKeyToken=6583c7c814667745, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\AsyncBridge.Net35.0.2.0\lib\net35-Client\AsyncBridge.Net35.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\FluentAssertions.2.2.0.0\lib\net35\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=2.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\5.5\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NCrunch.Framework, Version=3.3.0.6, Culture=neutral, PublicKeyToken=01d101bf6f3e0aea, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NCrunch.Framework.3.3.0.6\lib\NCrunch.Framework.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\NCrunch.Framework.3.3.0.6\lib\NCrunch.Framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NSubstitute, Version=1.10.0.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca, processorArchitecture=MSIL">
|
||||
|
@ -87,11 +87,11 @@
|
|||
<Compile Include="ThreadSynchronizationContext.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GitHub.Api\GitHub.Api.csproj">
|
||||
<ProjectReference Include="$(SolutionDir)\src\\GitHub.Api\GitHub.Api.csproj">
|
||||
<Project>{b389adaf-62cc-486e-85b4-2d8b078df763}</Project>
|
||||
<Name>GitHub.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Logging\GitHub.Logging.csproj">
|
||||
<ProjectReference Include="$(SolutionDir)\src\\GitHub.Logging\GitHub.Logging.csproj">
|
||||
<Project>{bb6a8eda-15d8-471b-a6ed-ee551e0b3ba0}</Project>
|
||||
<Name>GitHub.Logging</Name>
|
||||
</ProjectReference>
|
||||
|
@ -99,7 +99,7 @@
|
|||
<Project>{66a1d219-f61d-4ae4-9bd7-aaeb97276fff}</Project>
|
||||
<Name>TestUtils</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\UnityExtension\Assets\Editor\GitHub.Unity\GitHub.Unity.csproj">
|
||||
<ProjectReference Include="$(SolutionDir)\src\\UnityExtension\Assets\Editor\GitHub.Unity\GitHub.Unity.csproj">
|
||||
<Project>{add7a18b-dd2a-4c22-a2c1-488964eff30a}</Project>
|
||||
<Name>GitHub.Unity</Name>
|
||||
</ProjectReference>
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using GitHub.Unity;
|
||||
using TestUtils;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace IntegrationTests
|
|||
[Test]
|
||||
public void BranchListTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanUnsynchronized);
|
||||
Initialize(TestRepoMasterCleanUnsynchronized);
|
||||
|
||||
var gitBranches = ProcessManager.GetGitBranches(TestRepoMasterCleanUnsynchronized).Result;
|
||||
|
||||
|
@ -26,13 +26,11 @@ namespace IntegrationTests
|
|||
[Test]
|
||||
public void LogEntriesTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanUnsynchronized);
|
||||
Initialize(TestRepoMasterCleanUnsynchronized);
|
||||
|
||||
var logEntries =
|
||||
ProcessManager
|
||||
.GetGitLogEntries(TestRepoMasterCleanUnsynchronized, Environment, FileSystem, GitEnvironment, 2)
|
||||
.Result
|
||||
.ToArray();
|
||||
List<GitLogEntry> logEntries = null;
|
||||
Assert.DoesNotThrow(async () => logEntries = await ProcessManager
|
||||
.GetGitLogEntries(TestRepoMasterCleanUnsynchronized, Environment, FileSystem, GitEnvironment, 2));
|
||||
|
||||
logEntries.AssertEqual(new[]
|
||||
{
|
||||
|
@ -78,7 +76,7 @@ namespace IntegrationTests
|
|||
[Test, Category("DoNotRunOnAppVeyor")]
|
||||
public void RussianLogEntriesTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanUnsynchronizedRussianLanguage);
|
||||
Initialize(TestRepoMasterCleanUnsynchronizedRussianLanguage);
|
||||
|
||||
var logEntries =
|
||||
ProcessManager
|
||||
|
@ -112,7 +110,7 @@ namespace IntegrationTests
|
|||
[Test]
|
||||
public void RemoteListTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var gitRemotes = ProcessManager.GetGitRemoteEntries(TestRepoMasterCleanSynchronized).Result;
|
||||
|
||||
|
@ -128,7 +126,7 @@ namespace IntegrationTests
|
|||
[Test]
|
||||
public void StatusTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterDirtyUnsynchronized);
|
||||
Initialize(TestRepoMasterDirtyUnsynchronized);
|
||||
|
||||
var gitStatus = ProcessManager
|
||||
.GetGitStatus(TestRepoMasterDirtyUnsynchronized, Environment, FileSystem, GitEnvironment)
|
||||
|
@ -162,7 +160,7 @@ namespace IntegrationTests
|
|||
[Test]
|
||||
public void CredentialHelperGetTest()
|
||||
{
|
||||
InitializeEnvironment(TestRepoMasterCleanSynchronized);
|
||||
Initialize(TestRepoMasterCleanSynchronized);
|
||||
|
||||
var s = ProcessManager.GetGitCreds(TestRepoMasterCleanSynchronized, Environment, FileSystem, GitEnvironment);
|
||||
s.Should().NotBeNull();
|
|
@ -8,16 +8,14 @@ namespace IntegrationTests
|
|||
{
|
||||
static class ProcessManagerExtensions
|
||||
{
|
||||
static NPath defaultGitPath = "git".ToNPath();
|
||||
|
||||
public static async Task<IEnumerable<GitBranch>> GetGitBranches(this IProcessManager processManager,
|
||||
string workingDirectory,
|
||||
string gitPath = null)
|
||||
NPath workingDirectory,
|
||||
NPath gitPath = null)
|
||||
{
|
||||
var processor = new BranchListOutputProcessor();
|
||||
NPath path = null;
|
||||
if (gitPath == null)
|
||||
path = "git";
|
||||
else
|
||||
path = gitPath;
|
||||
NPath path = gitPath ?? defaultGitPath;
|
||||
|
||||
var results = await new ProcessTaskWithListOutput<GitBranch>(CancellationToken.None, processor)
|
||||
.Configure(processManager, path, "branch -vv", workingDirectory, false)
|
||||
|
@ -27,10 +25,11 @@ namespace IntegrationTests
|
|||
return results;
|
||||
}
|
||||
|
||||
public static async Task<List<GitLogEntry>> GetGitLogEntries(this IProcessManager processManager,
|
||||
string workingDirectory,
|
||||
public static Task<List<GitLogEntry>> GetGitLogEntries(this IProcessManager processManager,
|
||||
NPath workingDirectory,
|
||||
IEnvironment environment, IFileSystem filesystem, IProcessEnvironment gitEnvironment,
|
||||
int? logCount = null, string gitPath = null)
|
||||
int? logCount = null,
|
||||
NPath gitPath = null)
|
||||
{
|
||||
var gitStatusEntryFactory = new GitObjectFactory(environment);
|
||||
|
||||
|
@ -43,33 +42,23 @@ namespace IntegrationTests
|
|||
logNameStatus = logNameStatus + " -" + logCount.Value;
|
||||
}
|
||||
|
||||
NPath path = null;
|
||||
if (gitPath == null)
|
||||
path = "git";
|
||||
else
|
||||
path = gitPath;
|
||||
NPath path = gitPath ?? defaultGitPath;
|
||||
|
||||
var results = await new ProcessTaskWithListOutput<GitLogEntry>(CancellationToken.None, processor)
|
||||
return new ProcessTaskWithListOutput<GitLogEntry>(CancellationToken.None, processor)
|
||||
.Configure(processManager, path, logNameStatus, workingDirectory, false)
|
||||
.Start()
|
||||
.Task;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static async Task<GitStatus> GetGitStatus(this IProcessManager processManager,
|
||||
string workingDirectory,
|
||||
IEnvironment environment, IFileSystem filesystem, IProcessEnvironment gitEnvironment,
|
||||
string gitPath = null)
|
||||
NPath gitPath = null)
|
||||
{
|
||||
var gitStatusEntryFactory = new GitObjectFactory(environment);
|
||||
var processor = new StatusOutputProcessor(gitStatusEntryFactory);
|
||||
|
||||
NPath path = null;
|
||||
if (gitPath == null)
|
||||
path = "git";
|
||||
else
|
||||
path = gitPath;
|
||||
NPath path = gitPath ?? defaultGitPath;
|
||||
|
||||
var results = await new ProcessTask<GitStatus?>(CancellationToken.None, processor)
|
||||
.Configure(processManager, path, "status -b -u --porcelain", workingDirectory, false)
|
||||
|
@ -81,15 +70,11 @@ namespace IntegrationTests
|
|||
|
||||
public static async Task<List<GitRemote>> GetGitRemoteEntries(this IProcessManager processManager,
|
||||
string workingDirectory,
|
||||
string gitPath = null)
|
||||
NPath gitPath = null)
|
||||
{
|
||||
var processor = new RemoteListOutputProcessor();
|
||||
|
||||
NPath path = null;
|
||||
if (gitPath == null)
|
||||
path = "git";
|
||||
else
|
||||
path = gitPath;
|
||||
NPath path = gitPath ?? defaultGitPath;
|
||||
|
||||
var results = await new ProcessTaskWithListOutput<GitRemote>(CancellationToken.None, processor)
|
||||
.Configure(processManager, path, "remote -v", workingDirectory, false)
|
||||
|
@ -101,15 +86,11 @@ namespace IntegrationTests
|
|||
public static async Task<string> GetGitCreds(this IProcessManager processManager,
|
||||
string workingDirectory,
|
||||
IEnvironment environment, IFileSystem filesystem, IProcessEnvironment gitEnvironment,
|
||||
string gitPath = null)
|
||||
NPath gitPath = null)
|
||||
{
|
||||
var processor = new FirstNonNullLineOutputProcessor();
|
||||
|
||||
NPath path = null;
|
||||
if (gitPath == null)
|
||||
path = "git";
|
||||
else
|
||||
path = gitPath;
|
||||
NPath path = gitPath ?? defaultGitPath;
|
||||
|
||||
var task = new ProcessTask<string>(CancellationToken.None, processor)
|
||||
.Configure(processManager, path, "credential-wincred get", workingDirectory, true);
|
|
@ -18,7 +18,7 @@ namespace IntegrationTests
|
|||
Logging.LoggerFactory = context => new MultipleLogAdapter(() => new ConsoleLogAdapter(context),
|
||||
() => new FileLogAdapter(fileLog, context));
|
||||
|
||||
Logging.Trace("Logging to \"{0}\"", fileLog);
|
||||
Logging.Debug("Logging to \"{0}\"", fileLog);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ namespace GitHub.Unity
|
|||
count++;
|
||||
if (current - secondStart > TimeSpan.TicksPerMillisecond * 1000)
|
||||
{
|
||||
Console.WriteLine(String.Format("FPS {0}", count));
|
||||
//Console.WriteLine(String.Format("FPS {0}", count));
|
||||
count = 0;
|
||||
secondStart = current;
|
||||
}
|
||||
|
@ -71,7 +71,11 @@ namespace GitHub.Unity
|
|||
long waitTime = (current + ticksPerFrame - lastTime) / TimeSpan.TicksPerMillisecond;
|
||||
if (waitTime > 0 && waitTime < int.MaxValue)
|
||||
{
|
||||
wait.Wait((int)waitTime, token);
|
||||
try
|
||||
{
|
||||
wait.Wait((int)waitTime, token);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +89,7 @@ namespace GitHub.Unity
|
|||
}
|
||||
if (queue.TryDequeue(out data))
|
||||
{
|
||||
Logging.GetLogger<ThreadSynchronizationContext>().Trace($"Running {data.Id} on main thread");
|
||||
data.Run();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("IntegrationTests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("IntegrationTests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1a382f40-fd9e-43e1-89c1-320073f35ce9")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("IntegrationTests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("IntegrationTests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1a382f40-fd9e-43e1-89c1-320073f35ce9")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,73 @@
|
|||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// File: SynchronizationContextTaskScheduler.cs
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace System.Threading.Tasks.Schedulers
|
||||
{
|
||||
/// <summary>Provides a task scheduler that targets a specific SynchronizationContext.</summary>
|
||||
public sealed class SynchronizationContextTaskScheduler : TaskScheduler
|
||||
{
|
||||
/// <summary>The queue of tasks to execute, maintained for debugging purposes.</summary>
|
||||
private readonly ConcurrentQueue<Task> _tasks;
|
||||
/// <summary>The target context under which to execute the queued tasks.</summary>
|
||||
private readonly SynchronizationContext _context;
|
||||
|
||||
/// <summary>Initializes an instance of the SynchronizationContextTaskScheduler class.</summary>
|
||||
public SynchronizationContextTaskScheduler() :
|
||||
this(SynchronizationContext.Current)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of the SynchronizationContextTaskScheduler class
|
||||
/// with the specified SynchronizationContext.
|
||||
/// </summary>
|
||||
/// <param name="context">The SynchronizationContext under which to execute tasks.</param>
|
||||
public SynchronizationContextTaskScheduler(SynchronizationContext context)
|
||||
{
|
||||
if (context == null) throw new ArgumentNullException("context");
|
||||
_context = context;
|
||||
_tasks = new ConcurrentQueue<Task>();
|
||||
}
|
||||
|
||||
/// <summary>Queues a task to the scheduler for execution on the I/O ThreadPool.</summary>
|
||||
/// <param name="task">The Task to queue.</param>
|
||||
protected override void QueueTask(Task task)
|
||||
{
|
||||
_tasks.Enqueue(task);
|
||||
_context.Post(delegate
|
||||
{
|
||||
Task nextTask;
|
||||
if (_tasks.TryDequeue(out nextTask)) TryExecuteTask(nextTask);
|
||||
}, null);
|
||||
}
|
||||
|
||||
/// <summary>Tries to execute a task on the current thread.</summary>
|
||||
/// <param name="task">The task to be executed.</param>
|
||||
/// <param name="taskWasPreviouslyQueued">Ignored.</param>
|
||||
/// <returns>Whether the task could be executed.</returns>
|
||||
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
|
||||
{
|
||||
return _context == SynchronizationContext.Current && TryExecuteTask(task);
|
||||
}
|
||||
|
||||
/// <summary>Gets an enumerable of tasks queued to the scheduler.</summary>
|
||||
/// <returns>An enumerable of tasks queued to the scheduler.</returns>
|
||||
protected override IEnumerable<Task> GetScheduledTasks()
|
||||
{
|
||||
return _tasks.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
|
||||
public override int MaximumConcurrencyLevel { get { return 1; } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{1A382F40-FD9E-43E1-89C1-320073F35CE9}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>IntegrationTests</RootNamespace>
|
||||
<AssemblyName>TaskSystemIntegrationTests</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AsyncBridge.Net35, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b3b1c0202c0d6a87, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\AsyncBridge.Net35.0.2.0\lib\net35-Client\AsyncBridge.Net35.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NSubstitute, Version=1.10.0.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\NSubstitute.1.10.0.0\lib\net35\NSubstitute.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Rackspace.Threading, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bb62785d398726f0, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Rackspace.Threading.2.0.0-alpha001\lib\net35-client\Rackspace.Threading.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Threading, Version=1.0.2856.102, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\TaskParallelLibrary.1.0.2856.0\lib\Net35\System.Threading.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="SynchronizationContextTaskScheduler.cs" />
|
||||
<Compile Include="Tests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ThreadSynchronizationContext.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TestApp\TestApp.csproj">
|
||||
<Project>{08b87d2a-8cf1-4211-b7aa-5209f00f72f8}</Project>
|
||||
<Name>TestApp</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\GitHub.Api\GitHub.Api.csproj">
|
||||
<Project>{B389ADAF-62CC-486E-85B4-2D8B078DF763}</Project>
|
||||
<Name>GitHub.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\GitHub.Logging\GitHub.Logging.csproj">
|
||||
<Project>{bb6a8eda-15d8-471b-a6ed-ee551e0b3ba0}</Project>
|
||||
<Name>GitHub.Logging</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,635 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using System.Threading;
|
||||
using GitHub.Unity;
|
||||
using System.Threading.Tasks.Schedulers;
|
||||
using System.IO;
|
||||
using NSubstitute;
|
||||
|
||||
namespace IntegrationTests
|
||||
{
|
||||
class BaseTest
|
||||
{
|
||||
protected ITaskManager TaskManager { get; set; }
|
||||
protected IProcessManager ProcessManager { get; set; }
|
||||
protected NPath TestBasePath { get; private set; }
|
||||
protected IFileSystem FileSystem { get; private set; }
|
||||
protected CancellationToken Token => TaskManager.Token;
|
||||
protected NPath TestApp => System.Reflection.Assembly.GetExecutingAssembly().Location.ToNPath().Combine("TestApp.exe");
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
Logging.LoggerFactory = context => new ConsoleLogAdapter(context);
|
||||
|
||||
TaskManager = new TaskManager();
|
||||
var syncContext = new ThreadSynchronizationContext(Token);
|
||||
TaskManager.UIScheduler = new SynchronizationContextTaskScheduler(syncContext);
|
||||
|
||||
|
||||
FileSystem = new FileSystem();
|
||||
NPath.FileSystem = FileSystem;
|
||||
TestBasePath = NPath.CreateTempDirectory("integration-tests");
|
||||
FileSystem.SetCurrentDirectory(TestBasePath);
|
||||
|
||||
var env = new DefaultEnvironment();
|
||||
var repo = Substitute.For<IRepository>();
|
||||
repo.LocalPath.Returns(TestBasePath);
|
||||
env.Repository = repo;
|
||||
|
||||
var platform = new Platform(env, FileSystem);
|
||||
ProcessManager = new ProcessManager(env, platform.GitEnvironment, Token);
|
||||
var processEnv = platform.GitEnvironment;
|
||||
var path = processEnv.FindGitInstallationPath(ProcessManager).Start().Result;
|
||||
env.GitExecutablePath = path;
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void OneTimeTearDown()
|
||||
{
|
||||
TaskManager?.Stop();
|
||||
try
|
||||
{
|
||||
TestBasePath.DeleteIfExists();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class ProcessTaskTests : BaseTest
|
||||
{
|
||||
[Test]
|
||||
public async Task ProcessReturningErrorThrowsException()
|
||||
{
|
||||
var success = false;
|
||||
Exception thrown = null;
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name" };
|
||||
|
||||
var task = new SimpleProcessTask(TestApp, @"-s 100 -d ""one name""", Token).Configure(ProcessManager)
|
||||
.Catch(ex => thrown = ex)
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(new SimpleProcessTask(TestApp, @"-e kaboom -r -1", Token).Configure(ProcessManager))
|
||||
.Catch(ex => thrown = ex)
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Finally((s, e) => success = s);
|
||||
|
||||
await task.StartAwait();
|
||||
|
||||
Assert.IsFalse(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNotNull(thrown);
|
||||
Assert.AreEqual("kaboom", thrown.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class SchedulerTests : BaseTest
|
||||
{
|
||||
private ActionTask GetTask(TaskAffinity affinity, int id, Action<int> body, ActionTask dependsOn = null)
|
||||
{
|
||||
return new ActionTask(Token, _ => body(id), dependsOn) { Affinity = affinity };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This exemplifies that running a bunch of tasks that don't depend on anything on the concurrent (default) scheduler
|
||||
/// run in any order
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConcurrentSchedulerDoesNotGuaranteeOrdering()
|
||||
{
|
||||
var runningOrder = new List<int>();
|
||||
var rand = new Randomizer();
|
||||
var tasks = new List<ActionTask>();
|
||||
for (int i = 1; i < 11; i++)
|
||||
{
|
||||
tasks.Add(GetTask(TaskAffinity.Concurrent, i, id => { new ManualResetEventSlim().Wait(rand.Next(100, 200)); lock (runningOrder) runningOrder.Add(id); }));
|
||||
}
|
||||
|
||||
TaskManager.Schedule(tasks.Cast<ITask>().ToArray());
|
||||
Task.WaitAll(tasks.Select(x => x.Task).ToArray());
|
||||
Console.WriteLine(String.Join(",", runningOrder.Select(x => x.ToString()).ToArray()));
|
||||
Assert.AreEqual(10, runningOrder.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This exemplifies that running a bunch of tasks that depend on other things on the concurrent (default) scheduler
|
||||
/// run in dependency order. Each group of tasks depends on a task on the previous group, so the first group
|
||||
/// runs first, then the second group of tasks, then the third. Run order within each group is not guaranteed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConcurrentSchedulerWithDependencyOrdering()
|
||||
{
|
||||
var count = 3;
|
||||
var runningOrder = new List<int>();
|
||||
var rand = new Randomizer();
|
||||
var startTasks = new List<ActionTask>();
|
||||
var i = 1;
|
||||
for (var start = i; i < start + count; i++)
|
||||
{
|
||||
startTasks.Add(GetTask(TaskAffinity.Concurrent, i,
|
||||
id => { new ManualResetEventSlim().Wait(rand.Next(100, 200)); lock (runningOrder) runningOrder.Add(id); }));
|
||||
}
|
||||
|
||||
var midTasks = new List<ActionTask>();
|
||||
for (var start = i; i < start + count; i++)
|
||||
{
|
||||
midTasks.Add(GetTask(TaskAffinity.Concurrent, i,
|
||||
id => { new ManualResetEventSlim().Wait(rand.Next(100, 200)); lock (runningOrder) runningOrder.Add(id); },
|
||||
startTasks[i - 4]));
|
||||
}
|
||||
|
||||
var endTasks = new List<ActionTask>();
|
||||
for (var start = i; i < start + count; i++)
|
||||
{
|
||||
endTasks.Add(GetTask(TaskAffinity.Concurrent, i,
|
||||
id => { new ManualResetEventSlim().Wait(rand.Next(100, 200)); lock (runningOrder) runningOrder.Add(id); },
|
||||
midTasks[i - 7]));
|
||||
}
|
||||
|
||||
foreach (var t in endTasks)
|
||||
t.Start();
|
||||
Task.WaitAll(endTasks.Select(x => x.Task).ToArray());
|
||||
|
||||
CollectionAssert.AreEquivalent(Enumerable.Range(1, 3), runningOrder.Take(3));
|
||||
CollectionAssert.AreEquivalent(Enumerable.Range(4, 3), runningOrder.Skip(3).Take(3));
|
||||
CollectionAssert.AreEquivalent(Enumerable.Range(7, 3), runningOrder.Skip(6).Take(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExclusiveSchedulerGuaranteesOrdering()
|
||||
{
|
||||
var runningOrder = new List<int>();
|
||||
var tasks = new List<ActionTask>();
|
||||
var rand = new Randomizer();
|
||||
for (int i = 1; i < 11; i++)
|
||||
{
|
||||
tasks.Add(GetTask(TaskAffinity.Exclusive, i, id => { new ManualResetEventSlim().Wait(rand.Next(100, 200)); lock (runningOrder) runningOrder.Add(id); }));
|
||||
}
|
||||
|
||||
TaskManager.Schedule(tasks.Cast<ITask>().ToArray());
|
||||
Task.WaitAll(tasks.Select(x => x.Task).ToArray());
|
||||
Assert.AreEqual(Enumerable.Range(1, 10), runningOrder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UISchedulerGuaranteesOrdering()
|
||||
{
|
||||
var runningOrder = new List<int>();
|
||||
var tasks = new List<ActionTask>();
|
||||
var rand = new Randomizer();
|
||||
for (int i = 1; i < 11; i++)
|
||||
{
|
||||
tasks.Add(GetTask(TaskAffinity.UI, i, id => { new ManualResetEventSlim().Wait(rand.Next(100, 200)); lock (runningOrder) runningOrder.Add(id); }));
|
||||
}
|
||||
|
||||
TaskManager.Schedule(tasks.Cast<ITask>().ToArray());
|
||||
Task.WaitAll(tasks.Select(x => x.Task).ToArray());
|
||||
Assert.AreEqual(Enumerable.Range(1, 10), runningOrder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async void NonUITasksAlwaysRunOnDifferentThreadFromUITasks()
|
||||
{
|
||||
var output = new Dictionary<int, int>();
|
||||
var tasks = new List<ITask>();
|
||||
var seed = Randomizer.RandomSeed;
|
||||
var rand = new Randomizer(seed);
|
||||
|
||||
var uiThread = 0;
|
||||
await new ActionTask(Token, _ => uiThread = Thread.CurrentThread.ManagedThreadId) { Affinity = TaskAffinity.UI }.StartAwait();
|
||||
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
tasks.Add(GetTask(i % 2 == 0 ? TaskAffinity.Concurrent : TaskAffinity.Exclusive, i,
|
||||
id => { lock (output) output.Add(id, Thread.CurrentThread.ManagedThreadId); })
|
||||
.Start());
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks.Select(x => x.Task).ToArray());
|
||||
CollectionAssert.DoesNotContain(output.Values, uiThread);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async void ChainingOnDifferentSchedulers()
|
||||
{
|
||||
var output = new Dictionary<int, KeyValuePair<int, int>>();
|
||||
var tasks = new List<ITask>();
|
||||
var seed = Randomizer.RandomSeed;
|
||||
var rand = new Randomizer(seed);
|
||||
|
||||
var uiThread = 0;
|
||||
await new ActionTask(Token, _ => uiThread = Thread.CurrentThread.ManagedThreadId) { Affinity = TaskAffinity.UI }.StartAwait();
|
||||
|
||||
for (int i = 1; i < 100; i++)
|
||||
{
|
||||
tasks.Add(
|
||||
GetTask(TaskAffinity.UI, i,
|
||||
id => { lock (output) output.Add(id, KeyValuePair.Create(Thread.CurrentThread.ManagedThreadId, -1)); })
|
||||
.Then(
|
||||
GetTask(i % 2 == 0 ? TaskAffinity.Concurrent : TaskAffinity.Exclusive, i,
|
||||
id => { lock (output) output[id] = KeyValuePair.Create(output[id].Key, Thread.CurrentThread.ManagedThreadId); })
|
||||
).
|
||||
Start());
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks.Select(x => x.Task).ToArray());
|
||||
Console.WriteLine(String.Join(",", output.Select(x => x.Key.ToString()).ToArray()));
|
||||
foreach (var t in output)
|
||||
{
|
||||
Assert.AreEqual(uiThread, t.Value.Key, $"Task {t.Key} pass 1 should have been on ui thread {uiThread} but ran instead on {t.Value.Key}");
|
||||
Assert.AreNotEqual(t.Value.Key, t.Value.Value, $"Task {t.Key} pass 2 should not have been on ui thread {uiThread}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class Chains : BaseTest
|
||||
{
|
||||
[Test]
|
||||
public async Task ThrowingInterruptsTaskChainButAlwaysRunsFinallyAndCatch()
|
||||
{
|
||||
var success = false;
|
||||
string thrown = "";
|
||||
Exception finallyException = null;
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name" };
|
||||
|
||||
var task =
|
||||
new FuncTask<string>(Token, _ => "one name") { Affinity = TaskAffinity.UI }
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(_ => { throw new Exception("an exception"); })
|
||||
.Catch(ex => thrown = ex.Message)
|
||||
.Then(new FuncTask<string>(Token, _ => "another name") { Affinity = TaskAffinity.Exclusive })
|
||||
.ThenInUI((s, d) => output.Add(d))
|
||||
.Finally((s, e) =>
|
||||
{
|
||||
success = s;
|
||||
finallyException = e;
|
||||
});
|
||||
|
||||
await task.StartAwait();
|
||||
|
||||
Assert.IsFalse(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNotNull(finallyException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FinallyReportsException()
|
||||
{
|
||||
var success = false;
|
||||
Exception finallyException = null;
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name" };
|
||||
|
||||
var task =
|
||||
new FuncTask<string>(Token, _ => "one name") { Affinity = TaskAffinity.UI }
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(_ => { throw new Exception("an exception"); })
|
||||
.Then(new FuncTask<string>(Token, _ => "another name") { Affinity = TaskAffinity.Exclusive })
|
||||
.ThenInUI((s, d) => output.Add(d))
|
||||
.Finally((s, e) =>
|
||||
{
|
||||
success = s;
|
||||
finallyException = e;
|
||||
});
|
||||
|
||||
await task.StartAwait();
|
||||
|
||||
Assert.IsFalse(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNotNull(finallyException);
|
||||
Assert.AreEqual("an exception", finallyException.Message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CatchAlwaysRunsBeforeFinally()
|
||||
{
|
||||
var success = false;
|
||||
Exception exception = null;
|
||||
Exception finallyException = null;
|
||||
var runOrder = new List<string>();
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name" };
|
||||
|
||||
var task =
|
||||
new FuncTask<string>(Token, _ => "one name") { Affinity = TaskAffinity.UI }
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(_ => { throw new Exception("an exception"); })
|
||||
.Then(new FuncTask<string>(Token, _ => "another name") { Affinity = TaskAffinity.Exclusive })
|
||||
.Then((s, d) =>
|
||||
{
|
||||
output.Add(d);
|
||||
return "done";
|
||||
})
|
||||
.Catch(ex =>
|
||||
{
|
||||
Thread.Sleep(300);
|
||||
lock (runOrder)
|
||||
{
|
||||
exception = ex;
|
||||
runOrder.Add("catch");
|
||||
}
|
||||
})
|
||||
.Finally((s, e, d) =>
|
||||
{
|
||||
Thread.Sleep(300);
|
||||
lock (runOrder)
|
||||
{
|
||||
success = s;
|
||||
finallyException = e;
|
||||
runOrder.Add("finally");
|
||||
}
|
||||
return d;
|
||||
});
|
||||
|
||||
await task.StartAwait();
|
||||
|
||||
Assert.IsFalse(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNotNull(exception);
|
||||
Assert.IsNotNull(finallyException);
|
||||
Assert.AreEqual("an exception", exception.Message);
|
||||
Assert.AreEqual("an exception", finallyException.Message);
|
||||
CollectionAssert.AreEqual(new List<string> { "catch", "finally" }, runOrder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DoNotUseCatchAtTheEndOfAChain()
|
||||
{
|
||||
var success = false;
|
||||
Exception exception = null;
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name" };
|
||||
|
||||
var task =
|
||||
new FuncTask<string>(Token, _ => "one name") { Affinity = TaskAffinity.UI }
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(_ => { throw new Exception("an exception"); })
|
||||
.Then(new FuncTask<string>(Token, _ => "another name") { Affinity = TaskAffinity.Exclusive })
|
||||
.ThenInUI((s, d) => output.Add(d))
|
||||
.Finally((_, __) => { })
|
||||
.Catch(ex => { Thread.Sleep(20); exception = ex; });
|
||||
|
||||
await task.StartAwait();
|
||||
|
||||
Assert.IsFalse(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNull(exception);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FinallyCanReturnData()
|
||||
{
|
||||
var success = false;
|
||||
Exception exception = null;
|
||||
Exception finallyException = null;
|
||||
var runOrder = new List<string>();
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name", "another name", "done" };
|
||||
|
||||
var task =
|
||||
new FuncTask<string>(Token, _ => "one name") { Affinity = TaskAffinity.UI }
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(new FuncTask<string>(Token, _ => "another name") { Affinity = TaskAffinity.Exclusive })
|
||||
.Then((s, d) =>
|
||||
{
|
||||
output.Add(d);
|
||||
return "done";
|
||||
})
|
||||
.Catch(ex =>
|
||||
{
|
||||
lock (runOrder)
|
||||
{
|
||||
exception = ex;
|
||||
runOrder.Add("catch");
|
||||
}
|
||||
})
|
||||
.Finally((s, e, d) =>
|
||||
{
|
||||
lock (runOrder)
|
||||
{
|
||||
success = s;
|
||||
output.Add(d);
|
||||
finallyException = e;
|
||||
runOrder.Add("finally");
|
||||
}
|
||||
return d;
|
||||
});
|
||||
|
||||
var ret = await task.StartAwait();
|
||||
Assert.AreEqual("done", ret);
|
||||
Assert.IsTrue(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNull(exception);
|
||||
Assert.IsNull(finallyException);
|
||||
CollectionAssert.AreEqual(new List<string> { "finally" }, runOrder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConditionalChaining()
|
||||
{
|
||||
var success = false;
|
||||
Exception exception = null;
|
||||
Exception finallyException = null;
|
||||
var runOrder = new List<string>();
|
||||
var output = new List<string>();
|
||||
var bools = new List<bool>();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
bools.Add(i % 2 == 0);
|
||||
}
|
||||
var expectedOutput = bools.SelectMany(x => new List<string> { x.ToString().ToLower(), x ? "something" : "nothing" }).ToList();
|
||||
|
||||
var tasks = new List<ITask>();
|
||||
foreach (var b in bools)
|
||||
{
|
||||
var task =
|
||||
new FuncTask<bool>(Token, _ => b)
|
||||
.ThenIf(go =>
|
||||
{
|
||||
output.Add(go.ToString().ToLower());
|
||||
if (go)
|
||||
return new FuncTask<string>(Token, _ => "something");
|
||||
else
|
||||
return new FuncTask<string>(Token, _ => "nothing");
|
||||
})
|
||||
.Finally((s, e, d) =>
|
||||
{
|
||||
lock (runOrder)
|
||||
{
|
||||
success = s;
|
||||
output.Add(d);
|
||||
finallyException = e;
|
||||
}
|
||||
return d;
|
||||
});
|
||||
tasks.Add(task.Start());
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks.Select(x => x.Task).ToArray());
|
||||
|
||||
Assert.IsTrue(success);
|
||||
CollectionAssert.AreEquivalent(expectedOutput, output);
|
||||
Assert.IsNull(exception);
|
||||
Assert.IsNull(finallyException);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FinallyCanAlsoNotReturnData()
|
||||
{
|
||||
var success = false;
|
||||
Exception exception = null;
|
||||
Exception finallyException = null;
|
||||
var runOrder = new List<string>();
|
||||
var output = new List<string>();
|
||||
var expectedOutput = new List<string> { "one name", "another name", "done" };
|
||||
|
||||
var task =
|
||||
new FuncTask<string>(Token, _ => "one name") { Affinity = TaskAffinity.UI }
|
||||
.Then((s, d) => output.Add(d))
|
||||
.Then(new FuncTask<string>(Token, _ => "another name") { Affinity = TaskAffinity.Exclusive })
|
||||
.Then((s, d) =>
|
||||
{
|
||||
output.Add(d);
|
||||
return "done";
|
||||
})
|
||||
.Finally((s, e, d) =>
|
||||
{
|
||||
lock (runOrder)
|
||||
{
|
||||
success = s;
|
||||
output.Add(d);
|
||||
finallyException = e;
|
||||
runOrder.Add("finally");
|
||||
}
|
||||
});
|
||||
|
||||
await task.StartAwait();
|
||||
|
||||
Assert.IsTrue(success);
|
||||
CollectionAssert.AreEqual(expectedOutput, output);
|
||||
Assert.IsNull(exception);
|
||||
Assert.IsNull(finallyException);
|
||||
CollectionAssert.AreEqual(new List<string> { "finally" }, runOrder);
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class Exceptions : BaseTest
|
||||
{
|
||||
[Test]
|
||||
public async Task StartAndEndAreAlwaysRaised()
|
||||
{
|
||||
var runOrder = new List<string>();
|
||||
var task = new ActionTask(Token, _ => { throw new Exception(); })
|
||||
.Finally((s, d) => { });
|
||||
task.OnStart += _ => runOrder.Add("start");
|
||||
task.OnEnd += _ => runOrder.Add("end");
|
||||
await task.Start().Task;
|
||||
CollectionAssert.AreEqual(new string[] { "start", "end" }, runOrder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public async Task ExceptionPropagatesOutIfNoFinally()
|
||||
{
|
||||
var runOrder = new List<string>();
|
||||
var task = new ActionTask(Token, _ => { throw new InvalidOperationException(); })
|
||||
.Catch(_ => { });
|
||||
await task.Start().Task;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task StartAwaitSafelyAwaits()
|
||||
{
|
||||
var runOrder = new List<string>();
|
||||
var task = new ActionTask(Token, _ => { throw new InvalidOperationException(); })
|
||||
.Catch(_ => { });
|
||||
await task.StartAwait();
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class TaskToActionTask : BaseTest
|
||||
{
|
||||
[Test]
|
||||
public async Task CanWrapATask()
|
||||
{
|
||||
var uiThread = 0;
|
||||
await new ActionTask(Token, _ => uiThread = Thread.CurrentThread.ManagedThreadId) { Affinity = TaskAffinity.UI }.StartAwait();
|
||||
|
||||
var runOrder = new List<string>();
|
||||
var task = new Task(() => runOrder.Add($"ran {Thread.CurrentThread.ManagedThreadId}"));
|
||||
var act = new ActionTask(task) { Affinity = TaskAffinity.UI };
|
||||
await act.StartAwait();
|
||||
CollectionAssert.AreEqual(new string[] { $"ran {uiThread}" }, runOrder);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task AsyncBodies()
|
||||
{
|
||||
var uiThread = 0;
|
||||
await new ActionTask(Token, _ => uiThread = Thread.CurrentThread.ManagedThreadId) { Affinity = TaskAffinity.UI }.StartAwait();
|
||||
|
||||
var runOrder = new List<string>();
|
||||
var act = new ActionTask(Token, _ => runOrder.Add($"ran 1"))
|
||||
.ThenAsync(new Task<int>(() =>
|
||||
{
|
||||
runOrder.Add($"ran 2");
|
||||
return 10;
|
||||
}))
|
||||
|
||||
.ThenAsync(new FuncTask<int, int>(Token, (s, n) =>
|
||||
{
|
||||
runOrder.Add($"ran 3");
|
||||
return n * 2;
|
||||
}))
|
||||
;
|
||||
var ret = await act.StartAwait();
|
||||
CollectionAssert.AreEqual(new string[] { "ran 1", "ran 2", "ran 3" }, runOrder);
|
||||
Assert.AreEqual(20, ret);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Inlining()
|
||||
{
|
||||
var runOrder = new List<string>();
|
||||
var act = new ActionTask(Token, _ => runOrder.Add($"started"))
|
||||
.ThenAsync(TaskEx.FromResult(1), TaskAffinity.Exclusive)
|
||||
.Then((_, n) => n + 1)
|
||||
.Then((_, n) => runOrder.Add(n.ToString()))
|
||||
.ThenAsync(TaskEx.FromResult(20f), TaskAffinity.Exclusive)
|
||||
.Then((_, n) => n + 1)
|
||||
.Then((_, n) => runOrder.Add(n.ToString()))
|
||||
.Finally((_, t) => runOrder.Add("done"))
|
||||
;
|
||||
await act.StartAwait();
|
||||
CollectionAssert.AreEqual(new string[] { "started", "2", "21", "done" }, runOrder);
|
||||
}
|
||||
}
|
||||
|
||||
static class KeyValuePair
|
||||
{
|
||||
public static KeyValuePair<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value)
|
||||
{
|
||||
return new KeyValuePair<TKey, TValue>(key, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Unity
|
||||
{
|
||||
class ThreadSynchronizationContext : SynchronizationContext
|
||||
{
|
||||
private readonly CancellationToken token;
|
||||
private readonly ConcurrentQueue<PostData> queue = new ConcurrentQueue<PostData>();
|
||||
private readonly ConcurrentQueue<PostData> priorityQueue = new ConcurrentQueue<PostData>();
|
||||
private readonly JobSignal jobSignal = new JobSignal();
|
||||
private long jobId;
|
||||
private readonly Task task;
|
||||
private int threadId;
|
||||
|
||||
public ThreadSynchronizationContext(CancellationToken token)
|
||||
{
|
||||
this.token = token;
|
||||
task = new Task(Start, token, TaskCreationOptions.LongRunning);
|
||||
task.Start();
|
||||
}
|
||||
|
||||
public override void Post(SendOrPostCallback d, object state)
|
||||
{
|
||||
queue.Enqueue(new PostData { Callback = d, State = state });
|
||||
}
|
||||
|
||||
public override void Send(SendOrPostCallback d, object state)
|
||||
{
|
||||
if (Thread.CurrentThread.ManagedThreadId == threadId)
|
||||
{
|
||||
d(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = Interlocked.Increment(ref jobId);
|
||||
priorityQueue.Enqueue(new PostData { Id = id, Callback = d, State = state });
|
||||
Wait(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void Wait(long id)
|
||||
{
|
||||
jobSignal.Wait(id, token);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
threadId = Thread.CurrentThread.ManagedThreadId;
|
||||
var lastTime = DateTime.Now.Ticks;
|
||||
var wait = new ManualResetEventSlim(false);
|
||||
var ticksPerFrame = TimeSpan.TicksPerMillisecond * 10;
|
||||
var count = 0;
|
||||
var secondStart = DateTime.Now.Ticks;
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
var current = DateTime.Now.Ticks;
|
||||
var elapsed = current - lastTime;
|
||||
count++;
|
||||
if (current - secondStart > TimeSpan.TicksPerMillisecond * 1000)
|
||||
{
|
||||
//Console.WriteLine(String.Format("FPS {0}", count));
|
||||
count = 0;
|
||||
secondStart = current;
|
||||
}
|
||||
Pump();
|
||||
lastTime = DateTime.Now.Ticks;
|
||||
long waitTime = (current + ticksPerFrame - lastTime) / TimeSpan.TicksPerMillisecond;
|
||||
if (waitTime > 0 && waitTime < int.MaxValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
wait.Wait((int)waitTime, token);
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Pump()
|
||||
{
|
||||
PostData data;
|
||||
if (priorityQueue.TryDequeue(out data))
|
||||
{
|
||||
data.Run();
|
||||
}
|
||||
if (queue.TryDequeue(out data))
|
||||
{
|
||||
Logging.GetLogger<ThreadSynchronizationContext>().Trace($"Running {data.Id} on main thread");
|
||||
data.Run();
|
||||
}
|
||||
}
|
||||
struct PostData
|
||||
{
|
||||
public long Id;
|
||||
public SendOrPostCallback Callback;
|
||||
public object State;
|
||||
public void Run()
|
||||
{
|
||||
Callback(State);
|
||||
}
|
||||
}
|
||||
|
||||
class JobSignal : ManualResetEventSlim
|
||||
{
|
||||
private readonly HashSet<long> signaledIds = new HashSet<long>();
|
||||
|
||||
public void Set(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
signaledIds.Add(id);
|
||||
}
|
||||
catch { } // it's already on the list
|
||||
Set();
|
||||
Reset();
|
||||
}
|
||||
|
||||
public bool Wait(long id, CancellationToken token)
|
||||
{
|
||||
bool signaled = false;
|
||||
do
|
||||
{
|
||||
|
||||
signaled = signaledIds.Contains(id);
|
||||
if (signaled)
|
||||
break;
|
||||
Wait(token);
|
||||
}
|
||||
while (!token.IsCancellationRequested && !signaled);
|
||||
return signaled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AsyncBridge.Net35" version="0.2.0" targetFramework="net35" />
|
||||
<package id="NSubstitute" version="1.10.0.0" targetFramework="net35" />
|
||||
<package id="NUnit" version="2.6.4" targetFramework="net35" />
|
||||
<package id="NUnitTestAdapter" version="2.1.1" targetFramework="net35" />
|
||||
<package id="Rackspace.Threading" version="2.0.0-alpha001" targetFramework="net35" />
|
||||
<package id="TaskParallelLibrary" version="1.0.2856.0" targetFramework="net452" />
|
||||
</packages>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
|
||||
<supportedRuntime version="v2.0.50727"/></startup>
|
||||
</configuration>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using Mono.Options;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
int retCode = 0;
|
||||
string ret = String.Empty;
|
||||
string error = String.Empty;
|
||||
|
||||
int sleepms = 0;
|
||||
var p = new OptionSet();
|
||||
p = p
|
||||
.Add("r=", (int v) => retCode = v)
|
||||
.Add("d=|data=", v => ret = v)
|
||||
.Add("e=|error=", v => error = v)
|
||||
.Add("f=|file=", v => ret = File.ReadAllText(v))
|
||||
.Add("ef=|errorFile=", v => error = File.ReadAllText(v))
|
||||
.Add("s=|sleep=", (int v) => sleepms = v)
|
||||
.Add("h|help", v => p.WriteOptionDescriptions(Console.Out));
|
||||
|
||||
p.Parse(args);
|
||||
|
||||
if (sleepms > 0)
|
||||
Thread.Sleep(sleepms);
|
||||
|
||||
if (!String.IsNullOrEmpty(ret))
|
||||
Console.WriteLine(ret);
|
||||
if (!String.IsNullOrEmpty(error))
|
||||
Console.Error.WriteLine(error);
|
||||
return retCode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("TestApp")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("TestApp")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("08b87d2a-8cf1-4211-b7aa-5209f00f72f8")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{08B87D2A-8CF1-4211-B7AA-5209F00F72F8}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>TestApp</RootNamespace>
|
||||
<AssemblyName>TestApp</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Mono.Options-PCL.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -34,7 +34,8 @@ namespace TestUtils.Events
|
|||
|
||||
static class RepositoryManagerListenerExtensions
|
||||
{
|
||||
public static void AttachListener(this IRepositoryManagerListener listener, IRepositoryManager repositoryManager, RepositoryManagerAutoResetEvent managerAutoResetEvent = null, bool trace = false)
|
||||
public static void AttachListener(this IRepositoryManagerListener listener, IRepositoryManager repositoryManager,
|
||||
RepositoryManagerAutoResetEvent managerAutoResetEvent = null, bool trace = false)
|
||||
{
|
||||
var logger = trace ? Logging.GetLogger<IRepositoryManagerListener>() : null;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using GitHub.Unity;
|
||||
using NSubstitute.Core;
|
||||
|
||||
namespace TestUtils
|
||||
{
|
||||
class CreateEnvironmentOptions
|
||||
{
|
||||
public const string DefaultExtensionFolder = @"c:\GitHubUnity\ExtensionFolder";
|
||||
public const string DefaultUserProfilePath = @"c:\GitHubUnity\UserProfile";
|
||||
public const string DefaultUnityProjectPathAndRepositoryPath = @"c:\GitHubUnity\UnityProject";
|
||||
|
||||
public NPath Extensionfolder { get; set; } = DefaultExtensionFolder.ToNPath();
|
||||
public NPath UserProfilePath { get; set; } = DefaultUserProfilePath.ToNPath();
|
||||
public NPath UnityProjectPath { get; set; } = DefaultUnityProjectPathAndRepositoryPath.ToNPath();
|
||||
public NPath RepositoryPath { get; set; } = DefaultUnityProjectPathAndRepositoryPath.ToNPath();
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ namespace TestUtils
|
|||
{
|
||||
createEnvironmentOptions = createEnvironmentOptions ?? new CreateEnvironmentOptions();
|
||||
|
||||
var userPath = createEnvironmentOptions.UserProfilePath.ToNPath();
|
||||
var userPath = createEnvironmentOptions.UserProfilePath;
|
||||
var localAppData = userPath.Parent.Combine("LocalAppData").ToString();
|
||||
var appData = userPath.Parent.Combine("AppData").ToString();
|
||||
|
|
@ -68,15 +68,15 @@
|
|||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GitHub.Api\GitHub.Api.csproj">
|
||||
<ProjectReference Include="..\..\GitHub.Api\GitHub.Api.csproj">
|
||||
<Project>{b389adaf-62cc-486e-85b4-2d8b078df763}</Project>
|
||||
<Name>GitHub.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Logging\GitHub.Logging.csproj">
|
||||
<ProjectReference Include="..\..\GitHub.Logging\GitHub.Logging.csproj">
|
||||
<Project>{bb6a8eda-15d8-471b-a6ed-ee551e0b3ba0}</Project>
|
||||
<Name>GitHub.Logging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\UnityExtension\Assets\Editor\GitHub.Unity\GitHub.Unity.csproj">
|
||||
<ProjectReference Include="..\..\UnityExtension\Assets\Editor\GitHub.Unity\GitHub.Unity.csproj">
|
||||
<Project>{add7a18b-dd2a-4c22-a2c1-488964eff30a}</Project>
|
||||
<Name>GitHub.Unity</Name>
|
||||
</ProjectReference>
|
|
@ -17,13 +17,13 @@ namespace UnitTests
|
|||
public void TestSetup()
|
||||
{
|
||||
var fileSystem = SubstituteFactory.CreateFileSystem(new CreateFileSystemOptions());
|
||||
NPathFileSystemProvider.Current = fileSystem;
|
||||
NPath.FileSystem = fileSystem;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TestTearDown()
|
||||
{
|
||||
NPathFileSystemProvider.Current = null;
|
||||
NPath.FileSystem = null;
|
||||
}
|
||||
|
||||
[Test, Sequential]
|
||||
|
@ -34,11 +34,11 @@ namespace UnitTests
|
|||
[Values(@"test.txt", "UnityProject/test.txt", "test.txt")]string expected)
|
||||
{
|
||||
var environment = Substitute.For<IEnvironment>();
|
||||
environment.RepositoryPath.Returns(repositoryPath);
|
||||
environment.UnityProjectPath.Returns(projectPath);
|
||||
environment.RepositoryPath.Returns(repositoryPath.ToNPath());
|
||||
environment.UnityProjectPath.Returns(projectPath.ToNPath());
|
||||
|
||||
NPath nExpected = expected;
|
||||
var repositoryFilePath = environment.GetRepositoryPath(path);
|
||||
NPath nExpected = expected.ToNPath();
|
||||
var repositoryFilePath = environment.GetRepositoryPath(path.ToNPath());
|
||||
repositoryFilePath.Should().Be(nExpected);
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,10 @@ namespace UnitTests
|
|||
[Values(@"test.txt")]string path)
|
||||
{
|
||||
var environment = Substitute.For<IEnvironment>();
|
||||
environment.RepositoryPath.Returns(repositoryPath);
|
||||
environment.UnityProjectPath.Returns(projectPath);
|
||||
environment.RepositoryPath.Returns(repositoryPath.ToNPath());
|
||||
environment.UnityProjectPath.Returns(projectPath.ToNPath());
|
||||
|
||||
var repositoryFilePath = environment.GetRepositoryPath(path);
|
||||
var repositoryFilePath = environment.GetRepositoryPath(path.ToNPath());
|
||||
}
|
||||
|
||||
[Test, Sequential]
|
||||
|
@ -64,11 +64,11 @@ namespace UnitTests
|
|||
[Values("test.txt", "Assets/test.txt", "test.txt")] string expected)
|
||||
{
|
||||
var environment = Substitute.For<IEnvironment>();
|
||||
environment.RepositoryPath.Returns(repositoryPath);
|
||||
environment.UnityProjectPath.Returns(projectPath);
|
||||
environment.RepositoryPath.Returns(repositoryPath.ToNPath());
|
||||
environment.UnityProjectPath.Returns(projectPath.ToNPath());
|
||||
|
||||
NPath nExpected = expected;
|
||||
var repositoryFilePath = environment.GetAssetPath(path);
|
||||
NPath nExpected = expected.ToNPath();
|
||||
var repositoryFilePath = environment.GetAssetPath(path.ToNPath());
|
||||
repositoryFilePath.Should().Be(nExpected);
|
||||
}
|
||||
|
||||
|
@ -80,10 +80,10 @@ namespace UnitTests
|
|||
[Values("test.txt")] string path)
|
||||
{
|
||||
var environment = Substitute.For<IEnvironment>();
|
||||
environment.RepositoryPath.Returns(repositoryPath);
|
||||
environment.UnityProjectPath.Returns(projectPath);
|
||||
environment.RepositoryPath.Returns(repositoryPath.ToNPath());
|
||||
environment.UnityProjectPath.Returns(projectPath.ToNPath());
|
||||
|
||||
var repositoryFilePath = environment.GetAssetPath(path);
|
||||
var repositoryFilePath = environment.GetAssetPath(path.ToNPath());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,18 +8,6 @@ namespace UnitTests
|
|||
[TestFixture]
|
||||
public class BranchListOutputProcessorTests
|
||||
{
|
||||
//[Test]
|
||||
public void IntegrationTest_MonoRepo()
|
||||
{
|
||||
var filesystem = new FileSystem();
|
||||
var environment = new DefaultEnvironment();
|
||||
environment.UnityProjectPath = @"D:\code\github\UnityInternal\src\UnityExtension";
|
||||
var gitEnvironment = new WindowsGitEnvironment(environment, filesystem);
|
||||
var fact = new GitObjectFactory(environment);
|
||||
var pm = new ProcessManager(environment, gitEnvironment);
|
||||
var results = pm.GetGitBranches(@"D:\code\github\UnityInternal");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldProcessOutput()
|
||||
{
|
|
@ -17,15 +17,13 @@ namespace UnitTests
|
|||
var substituteFactory = new TestUtils.SubstituteFactory();
|
||||
var fileSystem = substituteFactory.CreateFileSystem(new CreateFileSystemOptions() { });
|
||||
|
||||
NPathFileSystemProvider.Current.Should().BeNull();
|
||||
NPathFileSystemProvider.Current = fileSystem;
|
||||
NPath.FileSystem = fileSystem;
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void TestFixtureTearDown()
|
||||
{
|
||||
NPathFileSystemProvider.Current.Should().NotBeNull();
|
||||
NPathFileSystemProvider.Current = null;
|
||||
NPath.FileSystem = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -37,7 +35,7 @@ namespace UnitTests
|
|||
item.ShouldThrow<InvalidOperationException>();
|
||||
|
||||
item = () => { FileSystemHelpers.FindCommonPath(new List<string> { null }); };
|
||||
item.ShouldThrow<ArgumentNullException>();
|
||||
item.ShouldThrow<InvalidOperationException>();
|
||||
|
||||
item = () => { FileSystemHelpers.FindCommonPath(new List<string> { "" }); };
|
||||
item.ShouldThrow<InvalidOperationException>();
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче