From 6fd69fd0e3dc89e36073e5fb27bcebb5feeffd84 Mon Sep 17 00:00:00 2001 From: Andreia Gaita Date: Fri, 18 Sep 2015 16:20:13 +0200 Subject: [PATCH 1/4] Add support for testing code that uses LibGit2Sharp Testing RepositoryModel creation via the path constructor implies hitting libgit2sharp codepaths, and libgit2sharp doesn't expose nice interfaces for testing (why??). So, move things around and add a GitService that replaces the extension methods that depend on libgit2sharp, so we can at least mock at a higher level. The things that require this GitService are not created via mef, so expose it via the Services static class. --- src/DesignTimeStyleHelper/MainWindow.xaml.cs | 2 +- .../Services/RepositoryPublishService.cs | 2 +- .../SimpleRepositoryModelExtensions.cs | 6 +- src/GitHub.Exports/Extensions/VSExtensions.cs | 10 +++ src/GitHub.Exports/GitHub.Exports.csproj | 1 + .../Models/SimpleRepositoryModel.cs | 16 ++--- src/GitHub.Exports/Services/GitService.cs | 66 +++++++++++++++++++ src/GitHub.Exports/Services/Services.cs | 36 ++++------ src/GitHub.Exports/Services/VSServices.cs | 23 ++++--- ...arisonTests.cs => RepositoryModelTests.cs} | 43 ++++++++++-- src/UnitTests/Helpers/TestBaseClass.cs | 26 +++++++- src/UnitTests/Substitutes.cs | 34 ++++------ src/UnitTests/UnitTests.csproj | 2 +- 13 files changed, 189 insertions(+), 78 deletions(-) create mode 100644 src/GitHub.Exports/Services/GitService.cs rename src/UnitTests/GitHub.App/Models/{ComparisonTests.cs => RepositoryModelTests.cs} (69%) diff --git a/src/DesignTimeStyleHelper/MainWindow.xaml.cs b/src/DesignTimeStyleHelper/MainWindow.xaml.cs index 8bc353bd6c..9ca122416f 100644 --- a/src/DesignTimeStyleHelper/MainWindow.xaml.cs +++ b/src/DesignTimeStyleHelper/MainWindow.xaml.cs @@ -3,7 +3,7 @@ using GitHub.SampleData; using GitHub.Services; using GitHub.UI; -using GitHub.VisualStudio; +using GitHub.Extensions; using GitHub.Models; namespace DesignTimeStyleHelper diff --git a/src/GitHub.App/Services/RepositoryPublishService.cs b/src/GitHub.App/Services/RepositoryPublishService.cs index 1e44859f25..2f7edcefe6 100644 --- a/src/GitHub.App/Services/RepositoryPublishService.cs +++ b/src/GitHub.App/Services/RepositoryPublishService.cs @@ -13,7 +13,7 @@ namespace GitHub.Services public class RepositoryPublishService : IRepositoryPublishService { readonly IGitClient gitClient; - readonly Repository activeRepository; + readonly IRepository activeRepository; [ImportingConstructor] public RepositoryPublishService(IGitClient gitClient, IVSServices services) diff --git a/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs b/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs index 918d747218..68ed82339b 100644 --- a/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs +++ b/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs @@ -6,6 +6,8 @@ namespace GitHub.Extensions { + using Services; + using VisualStudio; public static class SimpleRepositoryModelExtensions { /// @@ -15,12 +17,12 @@ public static ISimpleRepositoryModel ToModel(this IGitRepositoryInfo repo) { if (repo == null) return null; - return SimpleRepositoryModel.Create(repo.RepositoryPath); + return new SimpleRepositoryModel(repo.RepositoryPath); } public static bool HasCommits(this ISimpleRepositoryModel repository) { - var repo = GitHelpers.GetRepoFromPath(repository.LocalPath); + var repo = Services.IGitService.GetRepo(repository.LocalPath); return repo?.Commits.Any() ?? false; } diff --git a/src/GitHub.Exports/Extensions/VSExtensions.cs b/src/GitHub.Exports/Extensions/VSExtensions.cs index d1b7e80859..e9bc4dbc1f 100644 --- a/src/GitHub.Exports/Extensions/VSExtensions.cs +++ b/src/GitHub.Exports/Extensions/VSExtensions.cs @@ -5,6 +5,8 @@ namespace GitHub.Extensions { + using VisualStudio; + public static class VSExtensions { public static T TryGetService(this IServiceProvider serviceProvider) where T : class @@ -36,6 +38,14 @@ public static T GetService(this IServiceProvider serviceProvider) return (T)serviceProvider.GetService(typeof(T)); } + public static T GetExportedValue(this IServiceProvider serviceProvider) + { + var ui = serviceProvider as IUIProvider; + if (ui != null) + return ui.GetService(); + return Services.ComponentModel.DefaultExportProvider.GetExportedValue(); + } + public static ITeamExplorerSection GetSection(this IServiceProvider serviceProvider, Guid section) { return serviceProvider?.GetService()?.GetSection(section); diff --git a/src/GitHub.Exports/GitHub.Exports.csproj b/src/GitHub.Exports/GitHub.Exports.csproj index ac9059889f..9f37bb637f 100644 --- a/src/GitHub.Exports/GitHub.Exports.csproj +++ b/src/GitHub.Exports/GitHub.Exports.csproj @@ -111,6 +111,7 @@ + diff --git a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs index 4ec5ddbe83..c7785fcca6 100644 --- a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs +++ b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs @@ -1,5 +1,6 @@ using GitHub.Extensions; using GitHub.Primitives; +using GitHub.Services; using GitHub.UI; using GitHub.VisualStudio; using GitHub.VisualStudio.Helpers; @@ -10,6 +11,8 @@ namespace GitHub.Models { + using VisualStudio; + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SimpleRepositoryModel : NotificationAwareObject, ISimpleRepositoryModel, INotifyPropertySource, IEquatable { @@ -28,7 +31,7 @@ public SimpleRepositoryModel(string path) var dir = new DirectoryInfo(path); if (!dir.Exists) throw new ArgumentException("Path does not exist", nameof(path)); - var uri = GitHelpers.GetRepoFromPath(path)?.GetUri(); + var uri = Services.IGitService.GetUri(path); var name = uri?.NameWithOwner; if (name == null) name = dir.Name; @@ -38,15 +41,6 @@ public SimpleRepositoryModel(string path) Icon = Octicon.repo; } - public static ISimpleRepositoryModel Create(string path) - { - if (path == null) - return null; - if (!Directory.Exists(path)) - return null; - return new SimpleRepositoryModel(path); - } - public void SetIcon(bool isPrivate, bool isFork) { Icon = isPrivate @@ -60,7 +54,7 @@ public void Refresh() { if (LocalPath == null) return; - var uri = GitHelpers.GetRepoFromPath(LocalPath)?.GetUri(); + var uri = Services.IGitService.GetUri(LocalPath); if (CloneUrl != uri) CloneUrl = uri; } diff --git a/src/GitHub.Exports/Services/GitService.cs b/src/GitHub.Exports/Services/GitService.cs new file mode 100644 index 0000000000..31043087b2 --- /dev/null +++ b/src/GitHub.Exports/Services/GitService.cs @@ -0,0 +1,66 @@ +using GitHub.Primitives; +using LibGit2Sharp; +using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GitHub.Services +{ + public interface IGitService + { + UriString GetUri(string path); + UriString GetUri(IRepository repo); + UriString GetUri(IGitRepositoryInfo repoInfo); + IRepository GetRepo(IGitRepositoryInfo repoInfo); + IRepository GetRepo(string path); + } + + [Export(typeof(IVSServices))] + [PartCreationPolicy(CreationPolicy.Shared)] + public class GitService : IGitService + { + public UriString GetUri(IRepository repo) + { + return UriString.ToUriString(GetUriFromRepository(repo)?.ToRepositoryUrl()); + } + + public UriString GetUri(string path) + { + return GetUri(GetRepo(path)); + } + + public UriString GetUri(IGitRepositoryInfo repoInfo) + { + return GetUri(GetRepo(repoInfo)); + } + + public IRepository GetRepo(IGitRepositoryInfo repoInfo) + { + var repoPath = Repository.Discover(repoInfo?.RepositoryPath); + if (repoPath == null) + return null; + return new Repository(repoPath); + } + + public IRepository GetRepo(string path) + { + var repoPath = Repository.Discover(path); + if (repoPath == null) + return null; + return new Repository(repoPath); + } + + internal static UriString GetUriFromRepository(IRepository repo) + { + return repo + ?.Network + .Remotes + .FirstOrDefault(x => x.Name.Equals("origin", StringComparison.Ordinal)) + ?.Url; + } + } +} diff --git a/src/GitHub.Exports/Services/Services.cs b/src/GitHub.Exports/Services/Services.cs index 0034f5f597..5c2eff06f4 100644 --- a/src/GitHub.Exports/Services/Services.cs +++ b/src/GitHub.Exports/Services/Services.cs @@ -5,7 +5,6 @@ using LibGit2Sharp; using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; -using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using GitHub.Info; using GitHub.Primitives; @@ -13,16 +12,16 @@ namespace GitHub.VisualStudio { + public static class Services { public static IServiceProvider PackageServiceProvider { get; set; } /// - /// Three ways of getting a service. First, trying the passed-in , - /// then , then - /// If the passed-in provider returns null, try PackageServiceProvider or Package, returning the fetched value - /// regardless of whether it's null or not. Package.GetGlobalService is never called if PackageServiceProvider is set. - /// This is on purpose, to support easy unit testing outside VS. + /// Two ways of getting a service. First, trying the passed-in , + /// then + /// If the passed-in provider returns null, try PackageServiceProvider, returning the fetched value + /// regardless of whether it's null or not. /// /// /// @@ -35,9 +34,7 @@ static Ret GetGlobalService(IServiceProvider provider = null) where T : ret = provider.GetService(typeof(T)) as Ret; if (ret != null) return ret; - if (PackageServiceProvider != null) - return PackageServiceProvider.GetService(typeof(T)) as Ret; - return Package.GetGlobalService(typeof(T)) as Ret; + return PackageServiceProvider.GetService(typeof(T)) as Ret; } public static IComponentModel ComponentModel @@ -101,18 +98,6 @@ public static IVsSolution GetSolution(this IServiceProvider provider) return GetGlobalService(provider); } - public static T GetExportedValue(this IServiceProvider serviceProvider) - { - var ui = serviceProvider as IUIProvider; - if (ui != null) - return ui.GetService(); - else - { - var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel)); - return componentModel.DefaultExportProvider.GetExportedValue(); - } - } - public static UriString GetRepoUrlFromSolution(IVsSolution solution) { string solutionDir, solutionFile, userFile; @@ -125,11 +110,11 @@ public static UriString GetRepoUrlFromSolution(IVsSolution solution) return null; using (var repo = new Repository(repoPath)) { - return repo.GetUri(); + return GetUri(repo); } } - public static Repository GetRepoFromSolution(this IVsSolution solution) + public static IRepository GetRepoFromSolution(this IVsSolution solution) { string solutionDir, solutionFile, userFile; if (!ErrorHandler.Succeeded(solution.GetSolutionInfo(out solutionDir, out solutionFile, out userFile))) @@ -141,5 +126,10 @@ public static Repository GetRepoFromSolution(this IVsSolution solution) return null; return new Repository(repoPath); } + static UriString GetUri(IRepository repo) + { + return UriString.ToUriString(GitService.GetUriFromRepository(repo)?.ToRepositoryUrl()); + } + public static IGitService IGitService { get { return PackageServiceProvider.GetService(); } } } } diff --git a/src/GitHub.Exports/Services/VSServices.cs b/src/GitHub.Exports/Services/VSServices.cs index c90fa6c41e..3a1ab2cbae 100644 --- a/src/GitHub.Exports/Services/VSServices.cs +++ b/src/GitHub.Exports/Services/VSServices.cs @@ -21,7 +21,7 @@ public interface IVSServices string GetLocalClonePathFromGitProvider(); void Clone(string cloneUrl, string clonePath, bool recurseSubmodules); string GetActiveRepoPath(); - LibGit2Sharp.Repository GetActiveRepo(); + LibGit2Sharp.IRepository GetActiveRepo(); IEnumerable GetKnownRepositories(); string SetDefaultProjectPath(string path); @@ -73,11 +73,12 @@ public void Clone(string cloneUrl, string clonePath, bool recurseSubmodules) gitExt.Clone(cloneUrl, clonePath, recurseSubmodules ? CloneOptions.RecurseSubmodule : CloneOptions.None); } - public LibGit2Sharp.Repository GetActiveRepo() + public LibGit2Sharp.IRepository GetActiveRepo() { var gitExt = serviceProvider.GetService(); + var gitService = serviceProvider.GetService(); if (gitExt.ActiveRepositories.Count > 0) - return gitExt.ActiveRepositories.First().GetRepoFromIGit(); + return gitService.GetRepo(gitExt.ActiveRepositories.First()); return serviceProvider.GetSolution().GetRepoFromSolution(); } @@ -117,16 +118,18 @@ static IEnumerable PokeTheRegistryForRepositoryList() { using (var subkey = key.OpenSubKey(x)) { - var path = subkey?.GetValue("Path") as string; - if (path != null) + try { - var uri = GitHelpers.GetRepoFromPath(path)?.GetUri(); - var name = uri?.NameWithOwner; - if (name != null) - return new SimpleRepositoryModel(name, uri, path); + var path = subkey?.GetValue("Path") as string; + if (path != null) + return new SimpleRepositoryModel(path); } + catch (Exception ex) + { + VsOutputLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "Error loading the repository from the registry '{0}'", ex)); + } + return null; } - return null; }) .Where(x => x != null) .ToList(); diff --git a/src/UnitTests/GitHub.App/Models/ComparisonTests.cs b/src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs similarity index 69% rename from src/UnitTests/GitHub.App/Models/ComparisonTests.cs rename to src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs index 98e1bd00c1..6dc3a7e0d3 100644 --- a/src/UnitTests/GitHub.App/Models/ComparisonTests.cs +++ b/src/UnitTests/GitHub.App/Models/RepositoryModelTests.cs @@ -1,17 +1,15 @@ using GitHub.Models; using GitHub.Primitives; +using GitHub.Services; +using GitHub.VisualStudio; +using LibGit2Sharp; using NSubstitute; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using UnitTests; using Xunit; -public class ComparisonTests +public class RepositoryModelTests { - public class RepositoryModelTests : TestBaseClass + public class ComparisonTests : TestBaseClass { [Theory] [InlineData("a name", "https://github.com/github/VisualStudio", null, "a name", "https://github.com/github/VisualStudio", null)] @@ -55,6 +53,37 @@ public void DifferentContentEqualsFalse(string name1, string url1, string name2, } } + public class PathConstructorTests : TempFileBaseClass + { + [Fact] + public void NoRemoteUrl() + { + var provider = Substitutes.ServiceProvider; + Services.PackageServiceProvider = provider; + var gitservice = Substitutes.IGitService; + provider.GetService(typeof(IGitService)).Returns(gitservice); + var repo = Substitute.For(); + var path = Directory.CreateSubdirectory("repo-name"); + gitservice.GetUri(path.FullName).Returns((UriString)null); + var model = new SimpleRepositoryModel(path.FullName); + Assert.Equal("repo-name", model.Name); + } + + [Fact] + public void WithRemoteUrl() + { + var provider = Substitutes.ServiceProvider; + Services.PackageServiceProvider = provider; + var gitservice = Substitutes.IGitService; + provider.GetService(typeof(IGitService)).Returns(gitservice); + var repo = Substitute.For(); + var path = Directory.CreateSubdirectory("repo-name"); + gitservice.GetUri(path.FullName).Returns(new UriString("https://github.com/user/repo-name")); + var model = new SimpleRepositoryModel(path.FullName); + Assert.Equal("user/repo-name", model.Name); + } + } + public class HostAddressTests : TestBaseClass { [Theory] diff --git a/src/UnitTests/Helpers/TestBaseClass.cs b/src/UnitTests/Helpers/TestBaseClass.cs index 32ac06d9ea..c1aa49a638 100644 --- a/src/UnitTests/Helpers/TestBaseClass.cs +++ b/src/UnitTests/Helpers/TestBaseClass.cs @@ -1,4 +1,5 @@ using EntryExitDecoratorInterfaces; +using System.IO; /// /// This base class will get its methods called by the most-derived @@ -7,14 +8,35 @@ /// public class TestBaseClass : IEntryExitDecorator { - public void OnEntry() + public virtual void OnEntry() { // Ensure that every test has the InUnitTestRunner flag // set, so threading doesn't go nuts. Splat.ModeDetector.Current.SetInUnitTestRunner(true); } - public void OnExit() + public virtual void OnExit() { } } + +public class TempFileBaseClass : TestBaseClass +{ + public DirectoryInfo Directory { get; set; } + + public override void OnEntry() + { + var f = Path.GetTempFileName(); + var name = Path.GetFileNameWithoutExtension(f); + File.Delete(f); + Directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), name)); + Directory.Create(); + base.OnEntry(); + } + + public override void OnExit() + { + Directory.Delete(true); + base.OnExit(); + } +} \ No newline at end of file diff --git a/src/UnitTests/Substitutes.cs b/src/UnitTests/Substitutes.cs index 6418b54473..2a6de0d842 100644 --- a/src/UnitTests/Substitutes.cs +++ b/src/UnitTests/Substitutes.cs @@ -14,14 +14,8 @@ namespace UnitTests { internal static class Substitutes { - public static IGitRepositoriesExt IGitRepositoriesExt - { - get - { - var ret = Substitute.For(); - return ret; - } - } + public static IGitRepositoriesExt IGitRepositoriesExt { get { return Substitute.For(); } } + public static IGitService IGitService { get { return Substitute.For(); } } public static IVSServices IVSServices { @@ -89,27 +83,22 @@ public static IServiceProvider GetServiceProvider( { var ret = Substitute.For(); var os = OperatingSystem; - var git = IGitRepositoriesExt; var vs = IVSServices; var clone = cloneService ?? new RepositoryCloneService(os, vs); var create = creationService ?? new RepositoryCreationService(clone); - var hosts = RepositoryHosts; - var exports = ExportFactoryProvider; - var connection = Connection; - var connectionManager = ConnectionManager; - var twoFactorChallengeHandler = TwoFactorChallengeHandler; avatarProvider = avatarProvider ?? Substitute.For(); - ret.GetService(typeof(IGitRepositoriesExt)).Returns(git); + ret.GetService(typeof(IGitRepositoriesExt)).Returns(IGitRepositoriesExt); + ret.GetService(typeof(IGitService)).Returns(IGitService); ret.GetService(typeof(IVSServices)).Returns(vs); ret.GetService(typeof(IOperatingSystem)).Returns(os); ret.GetService(typeof(IRepositoryCloneService)).Returns(clone); ret.GetService(typeof(IRepositoryCreationService)).Returns(create); - ret.GetService(typeof(IRepositoryHosts)).Returns(hosts); - ret.GetService(typeof(IExportFactoryProvider)).Returns(exports); - ret.GetService(typeof(IConnection)).Returns(connection); - ret.GetService(typeof(IConnectionManager)).Returns(connectionManager); + ret.GetService(typeof(IRepositoryHosts)).Returns(RepositoryHosts); + ret.GetService(typeof(IExportFactoryProvider)).Returns(ExportFactoryProvider); + ret.GetService(typeof(IConnection)).Returns(Connection); + ret.GetService(typeof(IConnectionManager)).Returns(ConnectionManager); ret.GetService(typeof(IAvatarProvider)).Returns(avatarProvider); - ret.GetService(typeof(ITwoFactorChallengeHandler)).Returns(twoFactorChallengeHandler); + ret.GetService(typeof(ITwoFactorChallengeHandler)).Returns(TwoFactorChallengeHandler); return ret; } @@ -123,6 +112,11 @@ public static IVSServices GetVSServices(this IServiceProvider provider) return provider.GetService(typeof(IVSServices)) as IVSServices; } + public static IGitService GetGitService(this IServiceProvider provider) + { + return provider.GetService(typeof(IGitService)) as IGitService; + } + public static IOperatingSystem GetOperatingSystem(this IServiceProvider provider) { return provider.GetService(typeof(IOperatingSystem)) as IOperatingSystem; diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 1d25688796..660b00715c 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -145,7 +145,7 @@ - + From 4fb1bc6fbb3e5bf1b3305d6a7e8b061845200e3e Mon Sep 17 00:00:00 2001 From: Haacked Date: Fri, 18 Sep 2015 14:04:51 -0700 Subject: [PATCH 2/4] :art: Micro optimization create needed services The prior code always created a `IGitService` instance, but we only need to create them if there are any active repositories. Also changed to use `.Any()` because it is more expressive. --- src/GitHub.Exports/Services/VSServices.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/GitHub.Exports/Services/VSServices.cs b/src/GitHub.Exports/Services/VSServices.cs index 3a1ab2cbae..d4d18c5102 100644 --- a/src/GitHub.Exports/Services/VSServices.cs +++ b/src/GitHub.Exports/Services/VSServices.cs @@ -76,16 +76,15 @@ public void Clone(string cloneUrl, string clonePath, bool recurseSubmodules) public LibGit2Sharp.IRepository GetActiveRepo() { var gitExt = serviceProvider.GetService(); - var gitService = serviceProvider.GetService(); - if (gitExt.ActiveRepositories.Count > 0) - return gitService.GetRepo(gitExt.ActiveRepositories.First()); - return serviceProvider.GetSolution().GetRepoFromSolution(); + return gitExt.ActiveRepositories.Any() + ? serviceProvider.GetService().GetRepo(gitExt.ActiveRepositories.First()) + : serviceProvider.GetSolution().GetRepoFromSolution(); } public string GetActiveRepoPath() { var gitExt = serviceProvider.GetService(); - if (gitExt.ActiveRepositories.Count > 0) + if (gitExt.ActiveRepositories.Any()) return gitExt.ActiveRepositories.First().RepositoryPath; var repo = serviceProvider.GetSolution().GetRepoFromSolution(); return repo?.Info?.Path ?? string.Empty; From 56b36e5370552118278dd1b1f5b981476ffc10b3 Mon Sep 17 00:00:00 2001 From: Haacked Date: Fri, 18 Sep 2015 14:06:15 -0700 Subject: [PATCH 3/4] Use expression bodies wherever possible Also we should prefer to use `var` unless the type's really not clear at all from the right hand side. Also, I prefer all the usings on top and not within namespaces. --- .../SimpleRepositoryModelExtensions.cs | 12 ++---- src/GitHub.Exports/Extensions/VSExtensions.cs | 14 +++---- .../Models/SimpleRepositoryModel.cs | 40 ++++++++----------- src/GitHub.Exports/Services/Services.cs | 33 +++++---------- 4 files changed, 36 insertions(+), 63 deletions(-) diff --git a/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs b/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs index 68ed82339b..939818e4fd 100644 --- a/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs +++ b/src/GitHub.Exports/Extensions/SimpleRepositoryModelExtensions.cs @@ -1,13 +1,11 @@ -using GitHub.Models; -using System; +using System; using System.Linq; using System.IO; +using GitHub.Models; using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility; namespace GitHub.Extensions { - using Services; - using VisualStudio; public static class SimpleRepositoryModelExtensions { /// @@ -15,14 +13,12 @@ public static class SimpleRepositoryModelExtensions /// public static ISimpleRepositoryModel ToModel(this IGitRepositoryInfo repo) { - if (repo == null) - return null; - return new SimpleRepositoryModel(repo.RepositoryPath); + return repo == null ? null : new SimpleRepositoryModel(repo.RepositoryPath); } public static bool HasCommits(this ISimpleRepositoryModel repository) { - var repo = Services.IGitService.GetRepo(repository.LocalPath); + var repo = VisualStudio.Services.IGitService.GetRepo(repository.LocalPath); return repo?.Commits.Any() ?? false; } diff --git a/src/GitHub.Exports/Extensions/VSExtensions.cs b/src/GitHub.Exports/Extensions/VSExtensions.cs index e9bc4dbc1f..1fd3e7350a 100644 --- a/src/GitHub.Exports/Extensions/VSExtensions.cs +++ b/src/GitHub.Exports/Extensions/VSExtensions.cs @@ -1,12 +1,10 @@ -using GitHub.Services; -using Microsoft.TeamFoundation.Controls; -using System; +using System; using System.Diagnostics; +using GitHub.Services; +using Microsoft.TeamFoundation.Controls; namespace GitHub.Extensions { - using VisualStudio; - public static class VSExtensions { public static T TryGetService(this IServiceProvider serviceProvider) where T : class @@ -41,9 +39,9 @@ public static T GetService(this IServiceProvider serviceProvider) public static T GetExportedValue(this IServiceProvider serviceProvider) { var ui = serviceProvider as IUIProvider; - if (ui != null) - return ui.GetService(); - return Services.ComponentModel.DefaultExportProvider.GetExportedValue(); + return ui != null + ? ui.GetService() + : VisualStudio.Services.ComponentModel.DefaultExportProvider.GetExportedValue(); } public static ITeamExplorerSection GetSection(this IServiceProvider serviceProvider, Guid section) diff --git a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs index c7785fcca6..419411f96a 100644 --- a/src/GitHub.Exports/Models/SimpleRepositoryModel.cs +++ b/src/GitHub.Exports/Models/SimpleRepositoryModel.cs @@ -1,18 +1,13 @@ -using GitHub.Extensions; -using GitHub.Primitives; -using GitHub.Services; -using GitHub.UI; -using GitHub.VisualStudio; -using GitHub.VisualStudio.Helpers; -using System; +using System; using System.Diagnostics; using System.Globalization; using System.IO; +using GitHub.Primitives; +using GitHub.UI; +using GitHub.VisualStudio.Helpers; namespace GitHub.Models { - using VisualStudio; - [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SimpleRepositoryModel : NotificationAwareObject, ISimpleRepositoryModel, INotifyPropertySource, IEquatable { @@ -31,10 +26,8 @@ public SimpleRepositoryModel(string path) var dir = new DirectoryInfo(path); if (!dir.Exists) throw new ArgumentException("Path does not exist", nameof(path)); - var uri = Services.IGitService.GetUri(path); - var name = uri?.NameWithOwner; - if (name == null) - name = dir.Name; + var uri = VisualStudio.Services.IGitService.GetUri(path); + var name = uri?.NameWithOwner ?? dir.Name; Name = name; LocalPath = path; CloneUrl = uri; @@ -54,15 +47,15 @@ public void Refresh() { if (LocalPath == null) return; - var uri = Services.IGitService.GetUri(LocalPath); + var uri = VisualStudio.Services.IGitService.GetUri(LocalPath); if (CloneUrl != uri) CloneUrl = uri; } - public string Name { get; private set; } + public string Name { get; } UriString cloneUrl; public UriString CloneUrl { get { return cloneUrl; } set { cloneUrl = value; this.RaisePropertyChange(); } } - public string LocalPath { get; private set; } + public string LocalPath { get; } Octicon icon; public Octicon Icon { get { return icon; } set { icon = value; this.RaisePropertyChange(); } } @@ -91,13 +84,12 @@ bool IEquatable.Equals(SimpleRepositoryModel other) return other != null && String.Equals(Name, other.Name) && String.Equals(CloneUrl, other.CloneUrl) && String.Equals(LocalPath?.TrimEnd('\\'), other.LocalPath?.TrimEnd('\\'), StringComparison.CurrentCultureIgnoreCase); } - internal string DebuggerDisplay - { - get - { - return String.Format(CultureInfo.InvariantCulture, - "{3}\tName: {0} CloneUrl: {1} LocalPath: {2}", Name, CloneUrl, LocalPath, GetHashCode()); - } - } + internal string DebuggerDisplay => String.Format( + CultureInfo.InvariantCulture, + "{3}\tName: {0} CloneUrl: {1} LocalPath: {2}", + Name, + CloneUrl, + LocalPath, + GetHashCode()); } } diff --git a/src/GitHub.Exports/Services/Services.cs b/src/GitHub.Exports/Services/Services.cs index 5c2eff06f4..ceeaf640a4 100644 --- a/src/GitHub.Exports/Services/Services.cs +++ b/src/GitHub.Exports/Services/Services.cs @@ -1,18 +1,17 @@ using System; using EnvDTE; using EnvDTE80; +using GitHub.Extensions; +using GitHub.Info; +using GitHub.Primitives; using GitHub.Services; using LibGit2Sharp; using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Shell.Interop; -using GitHub.Info; -using GitHub.Primitives; -using GitHub.Extensions; namespace GitHub.VisualStudio { - public static class Services { public static IServiceProvider PackageServiceProvider { get; set; } @@ -37,20 +36,14 @@ static Ret GetGlobalService(IServiceProvider provider = null) where T : return PackageServiceProvider.GetService(typeof(T)) as Ret; } - public static IComponentModel ComponentModel - { - get { return GetGlobalService(); } - } + public static IComponentModel ComponentModel => GetGlobalService(); public static IVsWebBrowsingService GetWebBrowsingService(this IServiceProvider provider) { return GetGlobalService(provider); } - public static IVsOutputWindow OutputWindow - { - get { return GetGlobalService(); } - } + public static IVsOutputWindow OutputWindow => GetGlobalService(); static IVsOutputWindowPane outputWindowPane = null; public static IVsOutputWindowPane OutputWindowPane @@ -62,14 +55,14 @@ public static IVsOutputWindowPane OutputWindowPane // First make sure the output window is visible var uiShell = GetGlobalService(); // Get the frame of the output window - Guid outputWindowGuid = new Guid("{34e76e81-ee4a-11d0-ae2e-00a0c90fffc3}"); + var outputWindowGuid = new Guid("{34e76e81-ee4a-11d0-ae2e-00a0c90fffc3}"); IVsWindowFrame outputWindowFrame = null; ErrorHandler.ThrowOnFailure(uiShell.FindToolWindow((uint)__VSCREATETOOLWIN.CTW_fForceCreate, ref outputWindowGuid, out outputWindowFrame)); // Show the output window if (outputWindowFrame != null) ErrorHandler.ThrowOnFailure(outputWindowFrame.Show()); - Guid paneGuid = new Guid("E37A42B1-C1AE-475C-9982-7F49FE61918D"); + var paneGuid = new Guid("E37A42B1-C1AE-475C-9982-7F49FE61918D"); ErrorHandler.ThrowOnFailure(OutputWindow.CreatePane(ref paneGuid, ApplicationInfo.ApplicationSafeName, 1 /*visible=true*/, 0 /*clearWithSolution=false*/)); ErrorHandler.ThrowOnFailure(OutputWindow.GetPane(ref paneGuid, out outputWindowPane)); } @@ -78,15 +71,9 @@ public static IVsOutputWindowPane OutputWindowPane } } - public static DTE Dte - { - get { return GetGlobalService(); } - } + public static DTE Dte => GetGlobalService(); - public static DTE2 Dte2 - { - get { return Dte as DTE2; } - } + public static DTE2 Dte2 => Dte as DTE2; public static IVsActivityLog GetActivityLog(this IServiceProvider provider) { @@ -130,6 +117,6 @@ static UriString GetUri(IRepository repo) { return UriString.ToUriString(GitService.GetUriFromRepository(repo)?.ToRepositoryUrl()); } - public static IGitService IGitService { get { return PackageServiceProvider.GetService(); } } + public static IGitService IGitService => PackageServiceProvider.GetService(); } } From ef0a40ec716110c602b32e97e7cb194454383186 Mon Sep 17 00:00:00 2001 From: Haacked Date: Fri, 18 Sep 2015 14:06:35 -0700 Subject: [PATCH 4/4] Move IGitService to its own file and document it I documented all the methods. --- src/GitHub.Exports/GitHub.Exports.csproj | 1 + src/GitHub.Exports/Services/GitService.cs | 82 +++++++++++++++------- src/GitHub.Exports/Services/IGitService.cs | 68 ++++++++++++++++++ 3 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 src/GitHub.Exports/Services/IGitService.cs diff --git a/src/GitHub.Exports/GitHub.Exports.csproj b/src/GitHub.Exports/GitHub.Exports.csproj index 9f37bb637f..2256606870 100644 --- a/src/GitHub.Exports/GitHub.Exports.csproj +++ b/src/GitHub.Exports/GitHub.Exports.csproj @@ -112,6 +112,7 @@ + diff --git a/src/GitHub.Exports/Services/GitService.cs b/src/GitHub.Exports/Services/GitService.cs index 31043087b2..ecaa487003 100644 --- a/src/GitHub.Exports/Services/GitService.cs +++ b/src/GitHub.Exports/Services/GitService.cs @@ -1,57 +1,89 @@ -using GitHub.Primitives; -using LibGit2Sharp; -using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility; -using System; -using System.Collections.Generic; +using System; using System.ComponentModel.Composition; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using GitHub.Primitives; +using LibGit2Sharp; +using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility; namespace GitHub.Services { - public interface IGitService - { - UriString GetUri(string path); - UriString GetUri(IRepository repo); - UriString GetUri(IGitRepositoryInfo repoInfo); - IRepository GetRepo(IGitRepositoryInfo repoInfo); - IRepository GetRepo(string path); - } - [Export(typeof(IVSServices))] [PartCreationPolicy(CreationPolicy.Shared)] public class GitService : IGitService { - public UriString GetUri(IRepository repo) + /// + /// Returns the URL of the remote named "origin" for the specified . If the repository + /// is null or no remote named origin exists, this method returns null + /// + /// The repository to look at for the remote. + /// A representing the origin or null if none found. + public UriString GetUri(IRepository repository) { - return UriString.ToUriString(GetUriFromRepository(repo)?.ToRepositoryUrl()); + return UriString.ToUriString(GetUriFromRepository(repository)?.ToRepositoryUrl()); } + /// + /// Probes for a git repository and if one is found, returns a for the repository's + /// remote named "origin" if one is found + /// + /// + /// The lookup checks to see if the specified is a repository. If it's not, it then + /// walks up the parent directories until it either finds a repository, or reaches the root disk. + /// + /// The path to start probing + /// A representing the origin or null if none found. public UriString GetUri(string path) { return GetUri(GetRepo(path)); } + /// + /// Probes for a git repository and if one is found, returns a for the repository's + /// remote named "origin" if one is found + /// + /// + /// The lookup checks to see if the path specified by the RepositoryPath property of the specified + /// is a repository. If it's not, it then walks up the parent directories until it + /// either finds a repository, or reaches the root disk. + /// + /// The repository information containing the path to start probing + /// A representing the origin or null if none found. public UriString GetUri(IGitRepositoryInfo repoInfo) { return GetUri(GetRepo(repoInfo)); } + /// + /// Probes for a git repository and if one is found, returns a instance for the + /// repository. + /// + /// + /// The lookup checks to see if the path specified by the RepositoryPath property of the specified + /// is a repository. If it's not, it then walks up the parent directories until it + /// either finds a repository, or reaches the root disk. + /// + /// The repository information containing the path to start probing + /// An instance of or null + public IRepository GetRepo(IGitRepositoryInfo repoInfo) { - var repoPath = Repository.Discover(repoInfo?.RepositoryPath); - if (repoPath == null) - return null; - return new Repository(repoPath); + return GetRepo(repoInfo?.RepositoryPath); } + /// + /// Probes for a git repository and if one is found, returns a instance for the + /// repository. + /// + /// + /// The lookup checks to see if the specified is a repository. If it's not, it then + /// walks up the parent directories until it either finds a repository, or reaches the root disk. + /// + /// The path to start probing + /// An instance of or null public IRepository GetRepo(string path) { var repoPath = Repository.Discover(path); - if (repoPath == null) - return null; - return new Repository(repoPath); + return repoPath == null ? null : new Repository(repoPath); } internal static UriString GetUriFromRepository(IRepository repo) diff --git a/src/GitHub.Exports/Services/IGitService.cs b/src/GitHub.Exports/Services/IGitService.cs new file mode 100644 index 0000000000..dea51dbd41 --- /dev/null +++ b/src/GitHub.Exports/Services/IGitService.cs @@ -0,0 +1,68 @@ +using GitHub.Primitives; +using LibGit2Sharp; +using Microsoft.VisualStudio.TeamFoundation.Git.Extensibility; + +namespace GitHub.Services +{ + public interface IGitService + { + /// + /// Returns the URL of the remote named "origin" for the specified . If the repository + /// is null or no remote named origin exists, this method returns null + /// + /// The repository to look at for the remote. + /// A representing the origin or null if none found. + UriString GetUri(IRepository repository); + + /// + /// Probes for a git repository and if one is found, returns a for the repository's + /// remote named "origin" if one is found + /// + /// + /// The lookup checks to see if the specified is a repository. If it's not, it then + /// walks up the parent directories until it either finds a repository, or reaches the root disk. + /// + /// The path to start probing + /// A representing the origin or null if none found. + UriString GetUri(string path); + + /// + /// Probes for a git repository and if one is found, returns a for the repository's + /// remote named "origin" if one is found + /// + /// + /// The lookup checks to see if the path specified by the RepositoryPath property of the specified + /// is a repository. If it's not, it then walks up the parent directories until it + /// either finds a repository, or reaches the root disk. + /// + /// The repository information containing the path to start probing + /// A representing the origin or null if none found. + UriString GetUri(IGitRepositoryInfo repoInfo); + + /// + /// Probes for a git repository and if one is found, returns a instance for the + /// repository. + /// + /// + /// The lookup checks to see if the path specified by the RepositoryPath property of the specified + /// is a repository. If it's not, it then walks up the parent directories until it + /// either finds a repository, or reaches the root disk. + /// + /// The repository information containing the path to start probing + /// An instance of or null + + IRepository GetRepo(IGitRepositoryInfo repoInfo); + + /// + /// Probes for a git repository and if one is found, returns a instance for the + /// repository. + /// + /// + /// The lookup checks to see if the specified is a repository. If it's not, it then + /// walks up the parent directories until it either finds a repository, or reaches the root disk. + /// + /// The path to start probing + /// An instance of or null + IRepository GetRepo(string path); + } +} \ No newline at end of file