Skip to content

Fetch #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed

Fetch #221

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions LibGit2Sharp.Tests/BranchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,37 @@ public void CanListAllBranchesIncludingRemoteRefs()
}
}

[Fact]
public void CanResolveRemote()
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please split this test into three smaller ones?
This should allow you to have more explicit test method names and possibly get rid of the comments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure - will split this test.

using (var repo = new Repository(StandardTestRepoPath))
{
Branch master = repo.Branches["master"];
Assert.Equal(repo.Remotes["origin"], master.Remote);
}
}

[Fact]
public void RemoteForNonTrackingBranchIsNull()
{
using (var repo = new Repository(StandardTestRepoPath))
{
Branch test = repo.Branches["i-do-numbers"];
Assert.Null(test.Remote);
}
}

[Fact]
public void QueryRemoteForLocalTrackingBranch()
{
// There is not a Remote to resolve for a local tracking branch.
using (var repo = new Repository(StandardTestRepoPath))
{
Branch trackLocal = repo.Branches["track-local"];
Assert.Null(trackLocal.Remote);
}
}

[Fact]
public void CanLookupABranchByItsCanonicalName()
{
Expand Down
2 changes: 2 additions & 0 deletions LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
<Compile Include="RepositoryFixture.cs" />
<Compile Include="TagFixture.cs" />
<Compile Include="TestHelpers\DirectoryHelper.cs" />
<Compile Include="TestHelpers\ExpectedFetchState.cs" />
<Compile Include="TestHelpers\TestRemoteInfo.cs" />
<Compile Include="TestHelpers\IPostTestDirectoryRemover.cs" />
<Compile Include="TestHelpers\SelfCleaningDirectory.cs" />
<Compile Include="TestHelpers\SignatureExtensions.cs" />
Expand Down
84 changes: 83 additions & 1 deletion LibGit2Sharp.Tests/RemoteFixture.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using LibGit2Sharp.Tests.TestHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;
using Xunit.Extensions;

namespace LibGit2Sharp.Tests
{
Expand Down Expand Up @@ -66,6 +70,84 @@ public void CanCheckEqualityOfRemote()
}
}

[Theory]
[InlineData("http://github.com/libgit2/TestGitRepository")]
[InlineData("https://github.com/libgit2/TestGitRepository")]
[InlineData("git://github.com/libgit2/TestGitRepository.git")]
public void CanFetchIntoAnEmptyRepository(string url)
{
string remoteName = "testRemote";

var scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.RootedDirectoryPath))
{
Remote remote = repo.Remotes.Add(remoteName, url);

// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
TestRemoteInfo expectedResults = TestRemoteInfo.TestRemoteInstance;
ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName);

// Add expected branch objects
foreach (KeyValuePair<string, ObjectId> kvp in expectedResults.BranchTips)
{
expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value);
}

// Add the expected tags
string[] expectedTagNames = { "blob", "commit_tree", "annotated_tag" };
foreach (string tagName in expectedTagNames)
{
TestRemoteInfo.ExpectedTagInfo expectedTagInfo = expectedResults.Tags[tagName];
expectedFetchState.AddExpectedTag(tagName, ObjectId.Zero, expectedTagInfo);
}

// Perform the actual fetch
remote.Fetch(new FetchProgress(), onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
}
}

[Theory]
[InlineData("http://github.com/libgit2/TestGitRepository")]
[InlineData("https://github.com/libgit2/TestGitRepository")]
[InlineData("git://github.com/libgit2/TestGitRepository.git")]
public void CanFetchAllTagsIntoAnEmptyRepository(string url)
{
string remoteName = "testRemote";

var scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.RootedDirectoryPath))
{
Remote remote = repo.Remotes.Add(remoteName, url);

// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
TestRemoteInfo remoteInfo = TestRemoteInfo.TestRemoteInstance;
ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName);

// Add expected branch objects
foreach (KeyValuePair<string, ObjectId> kvp in remoteInfo.BranchTips)
{
expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value);
}

// Add expected tags
foreach (KeyValuePair<string, TestRemoteInfo.ExpectedTagInfo> kvp in remoteInfo.Tags)
{
expectedFetchState.AddExpectedTag(kvp.Key, ObjectId.Zero, kvp.Value);
}

// Perform the actual fetch
remote.Fetch(new FetchProgress(), tagOption: TagOption.All, onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler);

// Verify the expected
expectedFetchState.CheckUpdatedReferences(repo);
}
}

[Fact]
public void CreatingANewRemoteAddsADefaultRefSpec()
{
Expand Down
52 changes: 52 additions & 0 deletions LibGit2Sharp.Tests/RepositoryFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LibGit2Sharp.Tests.TestHelpers;
Expand Down Expand Up @@ -76,6 +77,57 @@ private static void AssertIsHidden(string repoPath)
Assert.Equal(FileAttributes.Hidden, (attribs & FileAttributes.Hidden));
}

[Fact]
public void CanFetchFromRemoteByName()
{
string remoteName = "testRemote";
string url = "http://github.com/libgit2/TestGitRepository";

var scd = BuildSelfCleaningDirectory();
using (var repo = Repository.Init(scd.RootedDirectoryPath))
{
Remote remote = repo.Remotes.Add(remoteName, url);

// We will first fetch without specifying any Tag options.
// After we verify this fetch, we will perform a second fetch
// where we will download all tags, and verify that the
// nearly-dangling tag is now present.

// Set up structures for the expected results
// and verifying the RemoteUpdateTips callback.
TestRemoteInfo remoteInfo = TestRemoteInfo.TestRemoteInstance;
ExpectedFetchState expectedFetchState = new ExpectedFetchState(remoteName);

// Add expected branch objects
foreach (KeyValuePair<string, ObjectId> kvp in remoteInfo.BranchTips)
{
expectedFetchState.AddExpectedBranch(kvp.Key, ObjectId.Zero, kvp.Value);
}

// Add the expected tags
string[] expectedTagNames = { "blob", "commit_tree", "annotated_tag" };
foreach (string tagName in expectedTagNames)
{
TestRemoteInfo.ExpectedTagInfo expectedTagInfo = remoteInfo.Tags[tagName];
expectedFetchState.AddExpectedTag(tagName, ObjectId.Zero, expectedTagInfo);
}

// Perform the actual fetch
repo.Fetch(remote.Name, onUpdateTips: expectedFetchState.RemoteUpdateTipsHandler);

// Verify the expected state
expectedFetchState.CheckUpdatedReferences(repo);

// Now fetch the rest of the tags
repo.Fetch(remote.Name,tagOption:TagOption.All);

// Verify that the "nearly-dangling" tag is now in the repo.
Tag nearlyDanglingTag = repo.Tags["nearly-dangling"];
Assert.NotNull(nearlyDanglingTag);
Assert.Equal(remoteInfo.Tags["nearly-dangling"].TargetId, nearlyDanglingTag.Target.Id);
}
}

[Fact]
public void CanReinitARepository()
{
Expand Down
186 changes: 186 additions & 0 deletions LibGit2Sharp.Tests/TestHelpers/ExpectedFetchState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;

namespace LibGit2Sharp.Tests.TestHelpers
{
/// <summary>
/// Class to verify the expected state after fetching github.com/nulltoken/TestGitRepository into an empty repository.
/// Includes the expected reference callbacks and the expected branches / tags after fetch is completed.
/// </summary>
internal class ExpectedFetchState
{
/// <summary>
/// Name of the Remote being fetched from.
/// </summary>
internal string RemoteName { get; private set; }

/// <summary>
/// Expected branch tips after fetching into an empty repository.
/// </summary>
private Dictionary<string, ObjectId> ExpectedBranchTips = new Dictionary<string, ObjectId>();

/// <summary>
/// Expected tags after fetching into an empty repository
/// </summary>
private Dictionary<string, TestRemoteInfo.ExpectedTagInfo> ExpectedTags = new Dictionary<string, TestRemoteInfo.ExpectedTagInfo>();

/// <summary>
/// References that we expect to be updated in the UpdateReferenceTips callback.
/// </summary>
private Dictionary<string, ReferenceUpdate> ExpectedReferenceUpdates = new Dictionary<string, ReferenceUpdate>();

/// <summary>
/// References that were actually updated in the UpdateReferenceTips callback.
/// </summary>
private Dictionary<string, ReferenceUpdate> ObservedReferenceUpdates = new Dictionary<string, ReferenceUpdate>();

/// <summary>
/// Constructor.
/// </summary>
/// <param name="remoteName">Name of the remote being updated.</param>
/// <param name="url">Url of the remote.</param>
public ExpectedFetchState(string remoteName)
{
RemoteName = remoteName;
}

/// <summary>
/// Add information on a branch that is expected to be updated during a fetch.
/// </summary>
/// <param name="branchName">Name of the branch.</param>
/// <param name="oldId">Old ID of the branch reference.</param>
/// <param name="newId">Expected updated ID of the branch reference.</param>
public void AddExpectedBranch(string branchName, ObjectId oldId, ObjectId newId)
{
string referenceUpdateBase = "refs/remotes/" + RemoteName + "/";
ExpectedBranchTips.Add(referenceUpdateBase + branchName, newId);
ExpectedReferenceUpdates.Add(referenceUpdateBase + branchName, new ReferenceUpdate(oldId, newId));
}

/// <summary>
/// Add information on a tag that is expected to be updated during a fetch.
/// </summary>
/// <param name="tagName">Name of the tag.</param>
/// <param name="oldId">Old ID of the tag.</param>
/// <param name="tag">Datastructure containing expected updated tag information.</param>
public void AddExpectedTag(string tagName, ObjectId oldId, TestRemoteInfo.ExpectedTagInfo tag)
{
string tagReferenceBase = "refs/tags/";
ExpectedTags.Add(tagReferenceBase + tagName, tag);

ObjectId referenceId = tag.IsAnnotated ? tag.AnnotationId : tag.TargetId;
ExpectedReferenceUpdates.Add(tagReferenceBase + tagName, new ReferenceUpdate(oldId, referenceId));
}

/// <summary>
/// Handler to hook up to UpdateTips callback.
/// </summary>
/// <param name="referenceName">Name of reference being updated.</param>
/// <param name="oldId">Old ID of reference.</param>
/// <param name="newId">New ID of reference.</param>
/// <returns></returns>
public int RemoteUpdateTipsHandler(string referenceName, ObjectId oldId, ObjectId newId)
{
// assert that we have not seen this reference before
Assert.DoesNotContain(referenceName, ObservedReferenceUpdates.Keys);
ObservedReferenceUpdates.Add(referenceName, new ReferenceUpdate(oldId, newId));

// verify that this reference is in the list of expected references
ReferenceUpdate referenceUpdate;
bool isReferenceFound = ExpectedReferenceUpdates.TryGetValue(referenceName, out referenceUpdate);
Assert.True(isReferenceFound, string.Format("Could not find the reference {0} in the list of expected reference updates.", referenceName));

// verify that the old / new Object IDs
if (isReferenceFound)
{
Assert.Equal(referenceUpdate.OldId, oldId);
Assert.Equal(referenceUpdate.NewId, newId);
}

return 0;
}

/// <summary>
/// Check that all expected references have been updated.
/// </summary>
/// <param name="repo">Repository object whose state will be checked against expected state.</param>
public void CheckUpdatedReferences(Repository repo)
{
// Verify the expected branches.
// First, verify the expected branches have been created and
List<string> sortedObservedBranches = repo.Branches.Select(branch => branch.CanonicalName).ToList();
sortedObservedBranches.Sort();
List<string> sortedExpectedBranches = ExpectedBranchTips.Keys.ToList();
sortedExpectedBranches.Sort();
Assert.Equal(sortedExpectedBranches, sortedObservedBranches);

// Verify branches reference expected commits.
foreach (KeyValuePair<string, ObjectId> kvp in ExpectedBranchTips)
{
Branch branch = repo.Branches[kvp.Key];
Assert.NotNull(branch);
Assert.Equal(kvp.Value, branch.Tip.Id);
}

// Verify the expected tags
// First, verify the expected tags have been created
List<string> sortedObservedTags = repo.Tags.Select(tag => tag.CanonicalName).ToList();
sortedObservedTags.Sort();
List<string> sortedExpectedTags = ExpectedTags.Keys.ToList();
sortedExpectedTags.Sort();
Assert.Equal(sortedExpectedTags, sortedObservedTags);

// Verify tags reference the expected IDs.
foreach (KeyValuePair<string, TestRemoteInfo.ExpectedTagInfo> kvp in ExpectedTags)
{
Tag tag = repo.Tags[kvp.Key];
TestRemoteInfo.ExpectedTagInfo expectedTagInfo = kvp.Value;

Assert.NotNull(tag);
Assert.NotNull(tag.Target);

Assert.Equal(expectedTagInfo.TargetId, tag.Target.Id);

if (expectedTagInfo.IsAnnotated)
{
Assert.NotNull(tag.Annotation);
Assert.Equal(expectedTagInfo.AnnotationId, tag.Annotation.Id);
}
}

// We have already verified that all observed reference updates are expected,
// verify that we have seen all expected reference updates.
Assert.Equal(ExpectedReferenceUpdates.Count, ObservedReferenceUpdates.Count);
}


#region ExpectedFetchState

/// <summary>
/// Structure to track a reference that has been updated.
/// </summary>
private struct ReferenceUpdate
{
/// <summary>
/// Old ID of the reference.
/// </summary>
public ObjectId OldId;

/// <summary>
/// New ID of the reference.
/// </summary>
public ObjectId NewId;

public ReferenceUpdate(ObjectId oldId, ObjectId newId)
{
OldId = oldId;
NewId = newId;
}
}

#endregion
}
}
Loading