Skip to content
Merged
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
129 changes: 76 additions & 53 deletions LiteDB.Tests/Engine/Rebuild_Crash_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,89 +3,112 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Xunit;
using Xunit.Abstractions;

#if DEBUG || TESTING
namespace LiteDB.Tests.Engine
{
public class Rebuild_Crash_Tests
{
private readonly ITestOutputHelper _output;

[Fact]
public void Rebuild_Crash_IO_Write_Error()
public Rebuild_Crash_Tests(ITestOutputHelper output)
{
var N = 1_000;
_output = output;
}

[Fact(Timeout = 30000)]
public async Task Rebuild_Crash_IO_Write_Error()
{
var testName = nameof(Rebuild_Crash_IO_Write_Error);

_output.WriteLine($"starting {testName}");

using (var file = new TempFile())
try
{
var settings = new EngineSettings
{
AutoRebuild = true,
Filename = file.Filename,
Password = "46jLz5QWd5fI3m4LiL2r"
};
var N = 1000;

var data = Enumerable.Range(1, N).Select(i => new BsonDocument
{
["_id"] = i,
["name"] = Faker.Fullname(),
["age"] = Faker.Age(),
["created"] = Faker.Birthday(),
["lorem"] = Faker.Lorem(5, 25)
}).ToArray();

try
using (var file = new TempFile())
{
using (var db = new LiteEngine(settings))
var settings = new EngineSettings
{
db.SimulateDiskWriteFail = (page) =>
{
var p = new BasePage(page);
AutoRebuild = true,
Filename = file.Filename,
Password = "46jLz5QWd5fI3m4LiL2r"
};

var initial = new DateTime(2024, 1, 1);

if (p.PageID == 28)
var data = Enumerable.Range(1, N).Select(i => new BsonDocument
{
["_id"] = i,
["name"] = $"user-{i:D4}",
["age"] = 18 + (i % 60),
["created"] = initial.AddDays(i),
["lorem"] = new string((char)('a' + (i % 26)), 800)
}).ToArray();

try
{
using (var db = new LiteEngine(settings))
{
db.SimulateDiskWriteFail = (page) =>
{
p.ColID.Should().Be(1);
p.PageType.Should().Be(PageType.Data);
var p = new BasePage(page);

page.Write((uint)123123123, 8192 - 4);
}
};
if (p.PageID == 28)
{
p.ColID.Should().Be(1);
p.PageType.Should().Be(PageType.Data);

db.Pragma("USER_VERSION", 123);
page.Write((uint)123123123, 8192 - 4);
}
};

db.EnsureIndex("col1", "idx_age", "$.age", false);
db.Pragma("USER_VERSION", 123);

db.Insert("col1", data, BsonAutoId.Int32);
db.Insert("col2", data, BsonAutoId.Int32);
db.EnsureIndex("col1", "idx_age", "$.age", false);

db.Checkpoint();
db.Insert("col1", data, BsonAutoId.Int32);
db.Insert("col2", data, BsonAutoId.Int32);

// will fail
var col1 = db.Query("col1", Query.All()).ToList().Count;
db.Checkpoint();

// will fail
var col1 = db.Query("col1", Query.All()).ToList().Count;

// never run here
Assert.Fail("should get error in query");
// never run here
Assert.Fail("should get error in query");
}
}
catch (Exception ex)
{
Assert.True(ex is LiteException lex && lex.ErrorCode == 999);
}
}
catch (Exception ex)
{
Assert.True(ex is LiteException lex && lex.ErrorCode == 999);
}

//Console.WriteLine("Recovering database...");
//Console.WriteLine("Recovering database...");

using (var db = new LiteEngine(settings))
{
var col1 = db.Query("col1", Query.All()).ToList().Count;
var col2 = db.Query("col2", Query.All()).ToList().Count;
var errors = db.Query("_rebuild_errors", Query.All()).ToList().Count;
using (var db = new LiteEngine(settings))
{
var col1 = db.Query("col1", Query.All()).ToList().Count;
var col2 = db.Query("col2", Query.All()).ToList().Count;
var errors = db.Query("_rebuild_errors", Query.All()).ToList().Count;

col1.Should().Be(N - 1);
col2.Should().Be(N);
errors.Should().Be(1);
col1.Should().Be(N - 1);
col2.Should().Be(N);
errors.Should().Be(1);

}
}

await Task.CompletedTask;
}
finally
{
_output.WriteLine($"{testName} completed");
}
}
}
Expand Down
Loading
Loading