Skip to content

Commit

Permalink
More progress for SqliteFileSystem
Browse files Browse the repository at this point in the history
- FIxed bug in database backed memory stream so it no longer writes to
the backing store when no data has been changed
- Added support for copying files
- Implemented (a noop) for SetAttributes
- Implemented setting timestamps
  • Loading branch information
atruskie committed Nov 29, 2017
1 parent 11968d0 commit f4a043e
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 46 deletions.
32 changes: 23 additions & 9 deletions Acoustics/Acoustics.Test/SqliteFileSystem/AdapterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static void InsertBlobManually(SqliteConnection connection, (UPath Path,
}
}

private static void AssertBlobMetadata(
public static void AssertBlobMetadata(
SqliteConnection connection,
int expectedLength,
long expectedAccessed,
Expand All @@ -92,7 +92,7 @@ private static void AssertBlobMetadata(
string path = "/test.blob")
{
using (var command = new SqliteCommand(
$"SELECT length(blob), accessed, created, modified FROM files WHERE path = '{path}'",
$"SELECT length(blob), accessed, created, written FROM files WHERE path = '{path}'",
connection))
{
var reader = command.ExecuteReader();
Expand All @@ -114,16 +114,30 @@ public void GetBlobTest()
{
connection.Open();

// before test, everything should be set up according to mock data
AssertBlobMetadata(connection, 1024, 0, 0, 0);
var results = new List<TimeSpan>(10);
var lastNow = 0L;
for (var i = 0; i < 10; i++)
{

var now = Now;
var blob = Adapter.GetBlob(connection, UPath.Root / "test.blob");
// before test, everything should be set up according to mock data
AssertBlobMetadata(connection, 1024, lastNow, 0, 0);

CollectionAssert.AreEqual(prepared.blobData, blob);
var now = Now;
var timer = Stopwatch.StartNew();
var blob = Adapter.GetBlob(connection, UPath.Root / "test.blob");

// now the accessed date should have been updated
AssertBlobMetadata(connection, 1024, now, 0, 0);
timer.Stop();
results.Add(timer.Elapsed);

CollectionAssert.AreEqual(prepared.blobData, blob);

// now the accessed date should have been updated
AssertBlobMetadata(connection, 1024, now, 0, 0);
lastNow = now;
}

var average = results.Average(x => x.TotalSeconds);
Debug.WriteLine($"10 getblobs took an average of {average} seconds.\nRaw: { string.Join(", ", results) }");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ public void TestDatabaseBackedMemoryStream()
using (var connection = new SqliteConnection(prepared.ConnectionString))
{
connection.Open();
AdapterTests.AssertBlobMetadata(connection, 1024, 0, 0, 0);
var now = Date.Now;
using (var stream = new DatabaseBackedMemoryStream(connection, UPath.Root / "test.blob", true, true))
{
AdapterTests.AssertBlobMetadata(connection, 1024, now, 0, 0);

Assert.IsTrue(stream.CanRead);
Assert.IsTrue(stream.CanSeek);
Expand All @@ -61,6 +64,9 @@ public void TestDatabaseBackedMemoryStream()
Assert.AreEqual(1024, read);
CollectionAssert.AreEqual(prepared.blobData, actualBlob);
}

// the meta data should not have been updated because no changes were made
AdapterTests.AssertBlobMetadata(connection, 1024, now, 0, 0);
}
}

Expand Down
146 changes: 138 additions & 8 deletions Acoustics/Acoustics.Test/SqliteFileSystem/SqliteFileSystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void Setup()

[TestCleanup]
public void Cleanup()
{
{
this.localFileSystem.Dispose();
this.fs.Dispose();

Expand Down Expand Up @@ -112,7 +112,7 @@ public void GetAttributesThrowsForNotExists()
public void GetAttributesReturnsReadOnlyWhenConnectionReadOnly()
{
this.fs.WriteAllBytes(this.testData.Path, this.testData.Data);


var readonlyFs = new SqliteFileSystem(this.testDatabase.FullName, SqliteOpenMode.ReadOnly);

Expand All @@ -122,6 +122,57 @@ public void GetAttributesReturnsReadOnlyWhenConnectionReadOnly()
readonlyFs.GetAttributes(this.testData.Path));
}

[TestMethod]
public void GetTimesReturnsDefaultsWhenFilesDoNotExist()
{
var path = UPath.Root / GetRandomName();

Assert.AreEqual(FileSystem.DefaultFileTime, this.fs.GetCreationTime(path));
Assert.AreEqual(FileSystem.DefaultFileTime, this.fs.GetLastWriteTime(path));
Assert.AreEqual(FileSystem.DefaultFileTime, this.fs.GetLastAccessTime(path));
}

[DataTestMethod]
[DataRow(FileMode.Open, true)]
[DataRow(FileMode.Append, false)]
[DataRow(FileMode.Create, false)]
[DataRow(FileMode.CreateNew, false)]
[DataRow(FileMode.OpenOrCreate, false)]
[DataRow(FileMode.Truncate, false)]
public void TestFileOpenReadOnly(FileMode openMode, bool shouldSucceed)
{
this.fs.WriteAllBytes(this.testData.Path, this.testData.Data);
var readonlyFs = new SqliteFileSystem(this.testDatabase.FullName, SqliteOpenMode.ReadOnly);

if (shouldSucceed)
{
using (readonlyFs.OpenFile(this.testData.Path, openMode, FileAccess.Read))
{
Assert.IsTrue(true);
}
}
else
{
Assert.ThrowsException<IOException>(
() => readonlyFs.OpenFile(this.testData.Path, openMode, FileAccess.ReadWrite));
}
}

[TestMethod]
public void TestFileDeleteCopyMoveReadOnly()
{
this.fs.WriteAllBytes(this.testData.Path, this.testData.Data);
var readonlyFs = new SqliteFileSystem(this.testDatabase.FullName, SqliteOpenMode.ReadOnly);

Assert.ThrowsException<IOException>(
() => readonlyFs.DeleteFile(this.testData.Path));
Assert.ThrowsException<IOException>(
() => readonlyFs.CopyFile(this.testData.Path, "/elsewhere.blob", true));
Assert.ThrowsException<IOException>(
() => readonlyFs.MoveFile(this.testData.Path, "/elsewhere.blob"));

}

[TestMethod]
public void TestFile()
{
Expand All @@ -131,26 +182,105 @@ public void TestFile()

var before = DateTime.Now;
this.fs.WriteAllBytes(path, this.testData.Data);

var after = DateTime.Now;

Assert.IsTrue(this.fs.FileExists(path));
Assert.AreEqual(1024, this.fs.GetFileLength(path));

var attributes = this.fs.GetAttributes(path);
Assert.AreEqual(FileAttributes.Normal, attributes);

var creationTime = this.fs.GetCreationTime(path);
var lastAccessTime = this.fs.GetLastAccessTime(path);
var getLastWriteTime = this.fs.GetLastWriteTime(path);
var creationTime = this.fs.GetCreationTime(path);
var lastWriteTime = this.fs.GetLastWriteTime(path);

Assert.IsTrue(
lastAccessTime.Kind == DateTimeKind.Local &&
creationTime.Kind == DateTimeKind.Local &&
lastWriteTime.Kind == DateTimeKind.Local);

// when file is opened it is created first, and thus created stamp is closer to our makrer
Assert.That.AreClose(creationTime, before, after - before);
Assert.IsTrue(creationTime < lastAccessTime);

Assert.That.AreClose(creationTime.Ticks , before.Ticks, 15_000_0);
Assert.That.AreClose(lastAccessTime.Ticks, before.Ticks, 15_000_0);
Assert.That.AreClose(getLastWriteTime.Ticks, before.Ticks, 15_000_0);
// other two timestamps are set after writing has finished
Assert.That.AreClose(lastAccessTime, before, after - before);
Assert.That.AreClose(lastWriteTime, before, after - before);
Assert.AreEqual(lastWriteTime, lastAccessTime);

// read the data
var actualBytes = this.fs.ReadAllBytes(path);
CollectionAssert.AreEqual(this.testData.Data, actualBytes);

// copy the file
var copyPath = UPath.Root / GetRandomName();
this.fs.CopyFile(path, copyPath, true);

Assert.IsTrue(this.fs.FileExists(path));
Assert.IsTrue(this.fs.FileExists(copyPath));
CollectionAssert.AreEqual(actualBytes, this.fs.ReadAllBytes(copyPath));

// copy throws when overwrite is false
Assert.ThrowsException<IOException>(() => this.fs.CopyFile(path, copyPath, false));

// copied files attributes test
Assert.AreEqual(1024, this.fs.GetFileLength(copyPath));
Assert.AreEqual(attributes, this.fs.GetAttributes(copyPath));
var copyAccessTime = this.fs.GetLastAccessTime(copyPath);
var copyCreationTime = this.fs.GetCreationTime(copyPath);
var copyWriteTime = this.fs.GetLastWriteTime(copyPath);
Assert.IsTrue(lastAccessTime < copyAccessTime);
Assert.IsTrue(creationTime < copyCreationTime);
Assert.AreEqual(lastWriteTime, copyWriteTime);

// Test move file
var movePath = UPath.Root / GetRandomName();
this.fs.MoveFile(copyPath, movePath);
Assert.IsTrue(this.fs.FileExists(movePath));
Assert.IsFalse(this.fs.FileExists(copyPath));
Assert.IsTrue(this.fs.FileExists(path));
CollectionAssert.AreEqual(actualBytes, this.fs.ReadAllBytes(movePath));

// modify timestamps
var now = DateTime.Now;
this.fs.SetLastAccessTime(path, now);
this.fs.SetCreationTime(path, now + 1.0.Seconds());
this.fs.SetLastWriteTime(path, now + 2.0.Seconds());

lastAccessTime = this.fs.GetLastAccessTime(path);
creationTime = this.fs.GetCreationTime(path);
lastWriteTime = this.fs.GetLastWriteTime(path);

Assert.That.AreClose(lastAccessTime, now, 0.Seconds());
Assert.That.AreClose(creationTime, now + 1.0.Seconds(), 0.Seconds());
Assert.That.AreClose(lastWriteTime, now + 2.0.Seconds(), 0.Seconds());

// test enumerate files
var allFiles = this.fs.EnumeratePaths(UPath.Root);
CollectionAssert.AreEquivalent(new [] {path, movePath}, allFiles.ToList());

var allDirs = this.fs.EnumerateDirectories(UPath.Root).ToList();
Assert.AreEqual(0, allDirs.Count);

// check replace file
var replaceFile = GenerateTestData(this.random);
this.fs.WriteAllBytes(replaceFile.Path, replaceFile.Data);
this.fs.ReplaceFile(replaceFile.Path, movePath, movePath + ".bak", true);
Assert.IsTrue(this.fs.FileExists(movePath));
Assert.IsTrue(this.fs.FileExists(movePath + ".bak"));
Assert.IsFalse(this.fs.FileExists(replaceFile.Path));
CollectionAssert.AreEqual(replaceFile.Data, this.fs.ReadAllBytes(movePath));
CollectionAssert.AreEqual(actualBytes, this.fs.ReadAllBytes(movePath + ".bak"));

// check set attributes has no effect (this file system does not encode attributes)
this.fs.SetAttributes(path, FileAttributes.ReadOnly);
Assert.AreEqual(FileAttributes.Normal, this.fs.GetAttributes(path));

// check deleting a file
Assert.IsTrue(this.fs.FileExists(path));
this.fs.DeleteFile(path);
Assert.IsFalse(this.fs.FileExists(path));
Assert.IsTrue(this.fs.FileExists(movePath));
}

[DataTestMethod]
Expand Down
11 changes: 11 additions & 0 deletions Acoustics/Acoustics.Test/TestHelpers/Assertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ public static void AreClose(this Assert assert, long expected, long actual, long
}
}

public static void AreClose(this Assert assert, DateTime expected, DateTime actual, TimeSpan delta, string message = null)
{
var actualDelta = TimeSpan.FromTicks(Math.Abs((expected - actual).Ticks));
if (actualDelta > delta)
{
message = message == null ? string.Empty : message + "\n";
Assert.Fail(
$"{message}Actual delta ({actualDelta}) between expected value ({expected:O}) and actual value ({actual:O}) was not less than {delta}");
}
}

public static void DirectoryExists(this Assert assert, DirectoryInfo directory)
{
DirectoryExists(assert, directory.FullName);
Expand Down
Loading

0 comments on commit f4a043e

Please sign in to comment.