From b2c3b61f9b99036ed0da2a2cc43844e7955e2fa1 Mon Sep 17 00:00:00 2001 From: filzrev <103790468+filzrev@users.noreply.github.com> Date: Tue, 5 Mar 2024 21:39:28 +0900 Subject: [PATCH] chore: Add snapshot tests support that are executed on forked repository --- src/Docfx.Common/Git/GitUtility.cs | 63 +++++++++++++++- .../GitUtilityWithSourceRepoUrlTest.cs | 71 +++++++++++++++++++ test/docfx.Snapshot.Tests/SamplesTest.cs | 17 ++++- 3 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 test/Docfx.Common.Tests/GitUtilityWithSourceRepoUrlTest.cs diff --git a/src/Docfx.Common/Git/GitUtility.cs b/src/Docfx.Common/Git/GitUtility.cs index ca3ed56ddb3..ec42755b988 100644 --- a/src/Docfx.Common/Git/GitUtility.cs +++ b/src/Docfx.Common/Git/GitUtility.cs @@ -38,9 +38,11 @@ record Repo(string path, string url, string branch); if (repo is null) return null; + var repoUrl = ResolveDocfxSourceRepoUrl(repo.url); + return new() { - Repo = repo.url, + Repo = repoUrl, Branch = repo.branch, Path = Path.GetRelativePath(repo.path, filePath).Replace('\\', '/'), }; @@ -49,10 +51,12 @@ record Repo(string path, string url, string branch); public static string? RawContentUrlToContentUrl(string rawUrl) { // GitHub - return Regex.Replace( + var url = Regex.Replace( rawUrl, @"^https://raw\.githubusercontent\.com/([^/]+)/([^/]+)/([^/]+)/(.+)$", string.IsNullOrEmpty(s_branch) ? "https://github.com/$1/$2/blob/$3/$4" : $"https://github.com/$1/$2/blob/{s_branch}/$4"); + + return ResolveDocfxSourceRepoUrl(url); } public static string? GetSourceUrl(GitSource source) @@ -65,7 +69,7 @@ record Repo(string path, string url, string branch); var path = source.Path.Replace('\\', '/'); - return url.Host switch + var sourceUrl = url.Host switch { "github.com" => $"https://github.com{url.AbsolutePath}/blob/{source.Branch}/{path}{(source.Line > 0 ? $"#L{source.Line}" : null)}", "bitbucket.org" => $"https://bitbucket.org{url.AbsolutePath}/src/{source.Branch}/{path}{(source.Line > 0 ? $"#lines-{source.Line}" : null)}", @@ -74,6 +78,11 @@ _ when url.Host.EndsWith(".visualstudio.com") || url.Host == "dev.azure.com" => _ => null, }; + if (sourceUrl == null) + return null; + + return ResolveDocfxSourceRepoUrl(sourceUrl); + static bool IsCommit(string branch) { return branch.Length == 40 && branch.All(char.IsLetterOrDigit); @@ -173,4 +182,52 @@ static string GitUrlToHttps(string url) } } } + + /// + /// Rewrite path if `DOCFX_SOURCE_REPOSITORY` environment variable is specified. + /// + private static string ResolveDocfxSourceRepoUrl(string originalUrl) + { + var docfxSourceRepoUrl = Environment.GetEnvironmentVariable("DOCFX_SOURCE_REPOSITORY"); + if (docfxSourceRepoUrl == null) + return originalUrl; + + // Parse value that defined with `{orgName}/{repoName}` format. + var parts = docfxSourceRepoUrl.Split('/', StringSplitOptions.TrimEntries); + if (parts.Length != 2) + return originalUrl; + + string orgName = parts[0]; + string repoName = parts[1]; + + if (!Uri.TryCreate(originalUrl, UriKind.Absolute, out var parsedUrl)) + { + return originalUrl; + } + + switch (parsedUrl.Host) + { + case "github.com": + case "bitbucket.org": + case "dev.azure.com": + { + // Replace `/{orgName}/{repoName}` and remove `.git` suffix. + var builder = new UriBuilder(parsedUrl); + builder.Path = Regex.Replace(builder.Path, @"^/[^/]+/[^/]+", $"/{orgName}/{repoName}") + .TrimEnd(".git"); + return builder.Uri.ToString(); + } + + case string hostName when hostName.EndsWith(".visualstudio.com"): + { + // Replace `https://{orgName}.visualstudio.com/{repoName}`. + var builder = new UriBuilder(parsedUrl); + builder.Host = $"{orgName}.visualstudio.com"; + builder.Path = Regex.Replace(builder.Path, @"^/[^/]+", $"/{repoName}"); + return builder.Uri.ToString(); + } + default: + return originalUrl; + } + } } diff --git a/test/Docfx.Common.Tests/GitUtilityWithSourceRepoUrlTest.cs b/test/Docfx.Common.Tests/GitUtilityWithSourceRepoUrlTest.cs new file mode 100644 index 00000000000..e805c48a8ff --- /dev/null +++ b/test/Docfx.Common.Tests/GitUtilityWithSourceRepoUrlTest.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Docfx.Common.Git; +using Xunit; + +namespace Docfx.Common.Tests; + +[Collection("docfx STA")] +public class GitUtilityWithSourceRepoUrlTest : IDisposable +{ + private readonly string _originalBranchName; + private readonly string _originalSourceRepoUrl; + + private const string ORG_NAME = "dotnet"; + private const string REPO_NAME = "docfx"; + + private const string BRANCH_NAME = "special-branch"; + private const string DOCFX_SOURCE_BRANCH_NAME = nameof(DOCFX_SOURCE_BRANCH_NAME); + private const string DOCFX_SOURCE_REPOSITORY = nameof(DOCFX_SOURCE_REPOSITORY); + + public GitUtilityWithSourceRepoUrlTest() + { + _originalBranchName = Environment.GetEnvironmentVariable(DOCFX_SOURCE_BRANCH_NAME); + _originalSourceRepoUrl = Environment.GetEnvironmentVariable(DOCFX_SOURCE_REPOSITORY); + + Environment.SetEnvironmentVariable(DOCFX_SOURCE_BRANCH_NAME, BRANCH_NAME); + Environment.SetEnvironmentVariable(DOCFX_SOURCE_REPOSITORY, $"{ORG_NAME}/{REPO_NAME}"); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable(DOCFX_SOURCE_BRANCH_NAME, _originalBranchName); + Environment.SetEnvironmentVariable(DOCFX_SOURCE_REPOSITORY, _originalSourceRepoUrl); + } + + [Fact] + public void TryGetFileDetailTest() + { + var info = GitUtility.TryGetFileDetail(Directory.GetCurrentDirectory()); + Assert.Equal(BRANCH_NAME, info.Branch); + Assert.Equal("https://github.com/dotnet/docfx", info.Repo); + } + + [Fact] + public void RawContentUrlToContentUrlTest() + { + string rawUrl = "https://raw.githubusercontent.com/dotnet/docfx/main/README.md"; + string expected = "https://github.com/dotnet/docfx/blob/special-branch/README.md"; + + var result = GitUtility.RawContentUrlToContentUrl(rawUrl); + + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("https://github.com/user/repo.git", "main", "path/to/file.cs", 0, $"https://github.com/{ORG_NAME}/{REPO_NAME}/blob/main/path/to/file.cs")] + [InlineData("https://github.com/user/repo.git", "main", "path/to/file.cs", 10, $"https://github.com/{ORG_NAME}/{REPO_NAME}/blob/main/path/to/file.cs#L10")] + [InlineData("https://bitbucket.org/user/repo.git", "main", "path/to/file.cs", 0, $"https://bitbucket.org/{ORG_NAME}/{REPO_NAME}/src/main/path/to/file.cs")] + [InlineData("https://bitbucket.org/user/repo.git", "main", "path/to/file.cs", 10, $"https://bitbucket.org/{ORG_NAME}/{REPO_NAME}/src/main/path/to/file.cs#lines-10")] + [InlineData("https://dev.azure.com/user/repo/_git/repo", "main", "path/to/file.cs", 0, $"https://dev.azure.com/{ORG_NAME}/{REPO_NAME}/_git/repo?path=path/to/file.cs&version=GBmain")] + [InlineData("https://dev.azure.com/user/repo/_git/repo", "0123456789abcdef0123456789abcdef01234567", "path/to/file.cs", 10, $"https://dev.azure.com/{ORG_NAME}/{REPO_NAME}/_git/repo?path=path/to/file.cs&version=GC0123456789abcdef0123456789abcdef01234567&line=10")] + [InlineData("https://user.visualstudio.com/repo/_git/repo", "main", "path/to/file.cs", 0, $"https://{ORG_NAME}.visualstudio.com/{REPO_NAME}/_git/repo?path=path/to/file.cs&version=GBmain")] + [InlineData("https://user.visualstudio.com/repo/_git/repo", "0123456789abcdef0123456789abcdef01234567", "path/to/file.cs", 10, $"https://{ORG_NAME}.visualstudio.com/{REPO_NAME}/_git/repo?path=path/to/file.cs&version=GC0123456789abcdef0123456789abcdef01234567&line=10")] + [InlineData("git@github.com:user/repo.git", "main", "path/to/file.cs", 0, $"https://github.com/{ORG_NAME}/{REPO_NAME}/blob/main/path/to/file.cs")] + [InlineData("ssh://mseng@vs-ssh.visualstudio.com:22/FakeProject/_ssh/Docfx", "main", "path/to/file.cs", 0, $"https://{ORG_NAME}.visualstudio.com/{REPO_NAME}/_ssh/Docfx?path=path/to/file.cs&version=GBmain")] + public void GetSourceUrlTest(string repo, string branch, string path, int line, string result) + { + Assert.Equal(result, GitUtility.GetSourceUrl(new(repo, branch, path, line))); + } +} diff --git a/test/docfx.Snapshot.Tests/SamplesTest.cs b/test/docfx.Snapshot.Tests/SamplesTest.cs index f0e0f925195..962d87e8755 100644 --- a/test/docfx.Snapshot.Tests/SamplesTest.cs +++ b/test/docfx.Snapshot.Tests/SamplesTest.cs @@ -8,6 +8,7 @@ using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Docfx.Dotnet; +using Docfx.Plugins; using UglyToad.PdfPig; using UglyToad.PdfPig.Actions; using UglyToad.PdfPig.Annotations; @@ -15,10 +16,24 @@ namespace Docfx.Tests; -public class SamplesTest +[Collection("docfx STA")] +[Trait("Stage", "Snapshot")] +public class SamplesTest : IDisposable { private static readonly string s_samplesDir = Path.GetFullPath("../../../../../samples"); + private const string DOCFX_SOURCE_REPOSITORY = nameof(DOCFX_SOURCE_REPOSITORY); + + public SamplesTest() + { + Environment.SetEnvironmentVariable(DOCFX_SOURCE_REPOSITORY, "dotnet/docfx"); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable(DOCFX_SOURCE_REPOSITORY, null); + } + private class SamplesFactAttribute : FactAttribute { public SamplesFactAttribute()