2012-09-18 15:50:52 +04:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using LibGit2Sharp.Tests.TestHelpers;
|
|
|
|
|
using Xunit;
|
|
|
|
|
|
|
|
|
|
namespace LibGit2Sharp.Tests
|
|
|
|
|
{
|
|
|
|
|
public class OdbBackendFixture : BaseFixture
|
|
|
|
|
{
|
|
|
|
|
[Fact]
|
|
|
|
|
public void SimpleOdbBackendFixtureTest()
|
|
|
|
|
{
|
2013-01-23 16:55:16 +04:00
|
|
|
|
var scd = new SelfCleaningDirectory(this);
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
2013-01-23 16:55:16 +04:00
|
|
|
|
using (Repository repository = Repository.Init(scd.RootedDirectoryPath))
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
repository.ObjectDatabase.AddBackend(new MockOdbBackend(), priority: 5);
|
|
|
|
|
|
2013-06-20 15:10:44 +04:00
|
|
|
|
const string filename = "file.txt";
|
|
|
|
|
const string fileContents = "Hello!";
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
|
|
|
|
// Exercises read, write, writestream, exists
|
2013-06-20 15:10:44 +04:00
|
|
|
|
Touch(scd.DirectoryPath, filename, fileContents);
|
|
|
|
|
repository.Index.Stage(filename);
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
2013-01-23 16:55:16 +04:00
|
|
|
|
var signature = new Signature("SimpleOdbBackendFixtureTest", "user@example.com", DateTimeOffset.Now);
|
2012-09-18 15:50:52 +04:00
|
|
|
|
repository.Commit(String.Empty, signature, signature);
|
|
|
|
|
|
|
|
|
|
// Exercises read
|
2013-01-23 16:55:16 +04:00
|
|
|
|
var blob = repository.Lookup<Blob>(new ObjectId("69342c5c39e5ae5f0077aecc32c0f81811fb8193"));
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
|
|
|
|
Assert.NotNull(blob);
|
|
|
|
|
Assert.True(fileContents.Length == blob.Size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region MockOdbBackend
|
|
|
|
|
|
|
|
|
|
private class MockOdbBackend : OdbBackend
|
|
|
|
|
{
|
2013-01-23 16:55:16 +04:00
|
|
|
|
protected override OdbBackendOperations SupportedOperations
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return OdbBackendOperations.Read |
|
|
|
|
|
OdbBackendOperations.ReadPrefix |
|
|
|
|
|
OdbBackendOperations.Write |
|
|
|
|
|
OdbBackendOperations.WriteStream |
|
|
|
|
|
OdbBackendOperations.Exists;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public override int Read(byte[] oid, out Stream data, out ObjectType objectType)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
data = null;
|
2013-05-01 00:02:18 +04:00
|
|
|
|
objectType = default(ObjectType);
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
|
|
|
|
MockGitObject gitObject;
|
|
|
|
|
|
|
|
|
|
if (m_objectIdToContent.TryGetValue(oid, out gitObject))
|
|
|
|
|
{
|
|
|
|
|
data = Allocate(gitObject.Data.LongLength);
|
|
|
|
|
data.Write(gitObject.Data, 0, gitObject.Data.Length);
|
|
|
|
|
|
|
|
|
|
objectType = gitObject.ObjectType;
|
|
|
|
|
|
|
|
|
|
return GIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GIT_ENOTFOUND;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public override int ReadPrefix(byte[] shortOid, out byte[] oid, out Stream data, out ObjectType objectType)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
oid = null;
|
|
|
|
|
data = null;
|
2013-05-01 00:02:18 +04:00
|
|
|
|
objectType = default(ObjectType);
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
|
|
|
|
MockGitObject gitObjectAlreadyFound = null;
|
|
|
|
|
|
|
|
|
|
foreach (MockGitObject gitObject in m_objectIdToContent.Values)
|
|
|
|
|
{
|
|
|
|
|
bool match = true;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < shortOid.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (gitObject.ObjectId[i] != shortOid[i])
|
|
|
|
|
{
|
|
|
|
|
match = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!match)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (null != gitObjectAlreadyFound)
|
|
|
|
|
{
|
|
|
|
|
return GIT_EAMBIGUOUS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gitObjectAlreadyFound = gitObject;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (null != gitObjectAlreadyFound)
|
|
|
|
|
{
|
|
|
|
|
oid = gitObjectAlreadyFound.ObjectId;
|
|
|
|
|
objectType = gitObjectAlreadyFound.ObjectType;
|
|
|
|
|
|
|
|
|
|
data = Allocate(gitObjectAlreadyFound.Data.LongLength);
|
|
|
|
|
data.Write(gitObjectAlreadyFound.Data, 0, gitObjectAlreadyFound.Data.Length);
|
|
|
|
|
|
|
|
|
|
return GIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GIT_ENOTFOUND;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public override int Write(byte[] oid, Stream dataStream, long length, ObjectType objectType, out byte[] finalOid)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
2013-01-23 16:55:16 +04:00
|
|
|
|
using (var sha1 = new SHA1CryptoServiceProvider())
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
finalOid = sha1.ComputeHash(dataStream);
|
|
|
|
|
|
|
|
|
|
dataStream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_objectIdToContent.ContainsKey(finalOid))
|
|
|
|
|
{
|
|
|
|
|
return GIT_EEXISTS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (length > (long)int.MaxValue)
|
|
|
|
|
{
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] buffer = new byte[length];
|
|
|
|
|
int bytesRead = dataStream.Read(buffer, 0, (int)length);
|
|
|
|
|
|
|
|
|
|
if (bytesRead != (int)length)
|
|
|
|
|
{
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_objectIdToContent.Add(finalOid, new MockGitObject(finalOid, objectType, buffer));
|
|
|
|
|
|
|
|
|
|
return GIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public override int WriteStream(long length, ObjectType objectType, out OdbBackendStream stream)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
stream = new MockOdbBackendStream(this, objectType, length);
|
|
|
|
|
|
|
|
|
|
return GIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool Exists(byte[] oid)
|
|
|
|
|
{
|
|
|
|
|
return m_objectIdToContent.ContainsKey(oid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Dictionary<byte[], MockGitObject> m_objectIdToContent = new Dictionary<byte[], MockGitObject>(MockGitObjectComparer.Instance);
|
|
|
|
|
|
|
|
|
|
private const int GIT_OK = 0;
|
|
|
|
|
private const int GIT_ERROR = -1;
|
|
|
|
|
private const int GIT_ENOTFOUND = -3;
|
|
|
|
|
private const int GIT_EEXISTS = -4;
|
|
|
|
|
private const int GIT_EAMBIGUOUS = -5;
|
|
|
|
|
|
|
|
|
|
#region Unimplemented
|
|
|
|
|
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public override int ReadHeader(byte[] oid, out int length, out ObjectType objectType)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int ReadStream(byte[] oid, out OdbBackendStream stream)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-14 06:55:32 +04:00
|
|
|
|
public override int ForEach(ForEachCallback callback)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region MockOdbBackendStream
|
|
|
|
|
|
|
|
|
|
private class MockOdbBackendStream : OdbBackendStream
|
|
|
|
|
{
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public MockOdbBackendStream(MockOdbBackend backend, ObjectType objectType, long length)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
: base(backend)
|
|
|
|
|
{
|
|
|
|
|
m_type = objectType;
|
|
|
|
|
m_length = length;
|
|
|
|
|
m_hash = new SHA1CryptoServiceProvider();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void Dispose()
|
|
|
|
|
{
|
|
|
|
|
((IDisposable)m_hash).Dispose();
|
|
|
|
|
|
|
|
|
|
base.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool CanRead
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool CanWrite
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int Write(Stream dataStream, long length)
|
|
|
|
|
{
|
|
|
|
|
if (null == m_buffer)
|
|
|
|
|
{
|
|
|
|
|
m_buffer = new byte[length];
|
|
|
|
|
|
|
|
|
|
if (length > (long)int.MaxValue)
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
|
|
|
|
|
int bytesRead = dataStream.Read(m_buffer, 0, (int)length);
|
|
|
|
|
|
|
|
|
|
if (bytesRead != (int)length)
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
|
|
|
|
|
m_hash.TransformBlock(m_buffer, 0, (int)length, null, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
long newLength = m_buffer.LongLength + length;
|
|
|
|
|
|
|
|
|
|
if (newLength > (long)int.MaxValue)
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
|
|
|
|
|
byte[] newBuffer = new byte[newLength];
|
|
|
|
|
Array.Copy(m_buffer, newBuffer, m_buffer.Length);
|
|
|
|
|
|
|
|
|
|
int bytesRead = dataStream.Read(newBuffer, m_buffer.Length, (int)length);
|
|
|
|
|
|
|
|
|
|
if (bytesRead != (int)length)
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
|
|
|
|
|
m_hash.TransformBlock(newBuffer, m_buffer.Length, (int)length, null, 0);
|
|
|
|
|
|
|
|
|
|
m_buffer = newBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int FinalizeWrite(out byte[] oid)
|
|
|
|
|
{
|
|
|
|
|
m_hash.TransformFinalBlock(m_buffer, 0, 0);
|
|
|
|
|
oid = m_hash.Hash;
|
|
|
|
|
|
|
|
|
|
if (m_buffer.Length != (int)m_length)
|
|
|
|
|
{
|
|
|
|
|
return GIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-23 16:55:16 +04:00
|
|
|
|
var backend = (MockOdbBackend)Backend;
|
2012-09-18 15:50:52 +04:00
|
|
|
|
|
|
|
|
|
if (!backend.m_objectIdToContent.ContainsKey(oid))
|
|
|
|
|
{
|
|
|
|
|
backend.m_objectIdToContent.Add(oid, new MockGitObject(oid, m_type, m_buffer));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte[] m_buffer;
|
|
|
|
|
|
2013-05-01 00:02:18 +04:00
|
|
|
|
private readonly ObjectType m_type;
|
2012-09-18 15:50:52 +04:00
|
|
|
|
private readonly long m_length;
|
|
|
|
|
private readonly HashAlgorithm m_hash;
|
|
|
|
|
|
|
|
|
|
#region Unimplemented
|
|
|
|
|
|
|
|
|
|
public override int Read(Stream dataStream, long length)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region MockGitObject
|
|
|
|
|
|
|
|
|
|
private class MockGitObject
|
|
|
|
|
{
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public MockGitObject(byte[] objectId, ObjectType objectType, byte[] data)
|
2012-09-18 15:50:52 +04:00
|
|
|
|
{
|
|
|
|
|
if (objectId.Length != 20)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.ObjectId = objectId;
|
|
|
|
|
this.ObjectType = objectType;
|
|
|
|
|
this.Data = data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ObjectId;
|
2013-05-01 00:02:18 +04:00
|
|
|
|
public ObjectType ObjectType;
|
2012-09-18 15:50:52 +04:00
|
|
|
|
public byte[] Data;
|
|
|
|
|
|
|
|
|
|
public int Length
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.Data.Length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region MockGitObjectComparer
|
|
|
|
|
|
|
|
|
|
private class MockGitObjectComparer : IEqualityComparer<byte[]>
|
|
|
|
|
{
|
|
|
|
|
public bool Equals(byte[] x, byte[] y)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 20; i++)
|
|
|
|
|
{
|
|
|
|
|
if (x[i] != y[i])
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int GetHashCode(byte[] obj)
|
|
|
|
|
{
|
|
|
|
|
int toReturn = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < obj.Length / 4; i++)
|
|
|
|
|
{
|
|
|
|
|
toReturn ^= (int)obj[4 * i] << 24 +
|
|
|
|
|
(int)obj[4 * i + 1] << 16 +
|
|
|
|
|
(int)obj[4 * i + 2] << 8 +
|
|
|
|
|
(int)obj[4 * i + 3];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return toReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static MockGitObjectComparer Instance
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (null == s_instance)
|
|
|
|
|
{
|
|
|
|
|
s_instance = new MockGitObjectComparer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s_instance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static MockGitObjectComparer s_instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|