+ SynchronizeCachedDatabase.cs: Synchronizes the local cache with the remote file. Applies merging if necessary.
+ Tests (not yet complete)
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
<AndroidApplication>true</AndroidApplication>
|
||||
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -58,16 +59,19 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="ProgressDialogStub.cs" />
|
||||
<Compile Include="TestBase.cs" />
|
||||
<Compile Include="TestCacheSupervisor.cs" />
|
||||
<Compile Include="TestDrawableFactory.cs" />
|
||||
<Compile Include="TestCreateDb.cs" />
|
||||
<Compile Include="MainActivity.cs" />
|
||||
<Compile Include="Resources\Resource.Designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TestFileStorage.cs" />
|
||||
<Compile Include="TestKp2aApp.cs" />
|
||||
<Compile Include="TestLoadDb.cs" />
|
||||
<Compile Include="TestLoadDbCredentials.cs" />
|
||||
<Compile Include="TestCachingFileStorage.cs" />
|
||||
<Compile Include="TestSaveDb.cs" />
|
||||
<Compile Include="TestSynchronizeCachedDatabase.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
@@ -96,6 +100,9 @@
|
||||
<Name>MonoDroidUnitTesting</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Properties\AndroidManifest.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace Kp2aUnitTests
|
||||
{
|
||||
TestRunner runner = new TestRunner();
|
||||
// Run all tests from this assembly
|
||||
//runner.AddTests(Assembly.GetExecutingAssembly());
|
||||
runner.AddTests(new List<Type> { typeof(TestCachingFileStorage) });
|
||||
runner.AddTests(Assembly.GetExecutingAssembly());
|
||||
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase) });
|
||||
//runner.AddTests(typeof(TestCachingFileStorage).GetMethod("TestSaveToRemote"));
|
||||
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadKdbpWithPasswordOnly"));
|
||||
//runner.AddTests(typeof(TestSaveDb).GetMethod("TestLoadKdbxAndSaveKdbp_TestIdenticalFiles"));
|
||||
|
||||
5
src/Kp2aUnitTests/Properties/AndroidManifest.xml
Normal file
5
src/Kp2aUnitTests/Properties/AndroidManifest.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" />
|
||||
<application></application>
|
||||
</manifest>
|
||||
@@ -61,9 +61,9 @@ namespace Kp2aUnitTests
|
||||
get { return DefaultDirectory + "savedWithDesktop/"; }
|
||||
}
|
||||
|
||||
protected IKp2aApp LoadDatabase(string filename, string password, string keyfile)
|
||||
protected TestKp2aApp LoadDatabase(string filename, string password, string keyfile)
|
||||
{
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
var app = CreateTestKp2aApp();
|
||||
app.CreateNewDatabase();
|
||||
bool loadSuccesful = false;
|
||||
LoadDb task = new LoadDb(app, new IOConnectionInfo() { Path = filename }, password, keyfile, new ActionOnFinish((success, message) =>
|
||||
@@ -81,6 +81,12 @@ namespace Kp2aUnitTests
|
||||
return app;
|
||||
}
|
||||
|
||||
protected virtual TestKp2aApp CreateTestKp2aApp()
|
||||
{
|
||||
TestKp2aApp app = new TestKp2aApp();
|
||||
return app;
|
||||
}
|
||||
|
||||
protected void SaveDatabase(IKp2aApp app)
|
||||
{
|
||||
bool saveSuccesful = TrySaveDatabase(app);
|
||||
@@ -104,16 +110,16 @@ namespace Kp2aUnitTests
|
||||
return saveSuccesful;
|
||||
}
|
||||
|
||||
protected IKp2aApp SetupAppWithDefaultDatabase()
|
||||
protected TestKp2aApp SetupAppWithDefaultDatabase()
|
||||
{
|
||||
string filename = DefaultFilename;
|
||||
|
||||
return SetupAppWithDatabase(filename);
|
||||
}
|
||||
|
||||
protected IKp2aApp SetupAppWithDatabase(string filename)
|
||||
protected TestKp2aApp SetupAppWithDatabase(string filename)
|
||||
{
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
TestKp2aApp app = CreateTestKp2aApp();
|
||||
|
||||
IOConnectionInfo ioc = new IOConnectionInfo {Path = filename};
|
||||
Database db = app.CreateNewDatabase();
|
||||
|
||||
29
src/Kp2aUnitTests/TestCacheSupervisor.cs
Normal file
29
src/Kp2aUnitTests/TestCacheSupervisor.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using KeePassLib.Serialization;
|
||||
using keepass2android.Io;
|
||||
|
||||
namespace Kp2aUnitTests
|
||||
{
|
||||
class TestCacheSupervisor: ICacheSupervisor
|
||||
{
|
||||
public bool CouldntOpenFromRemoteCalled { get; set; }
|
||||
public bool CouldntSaveToRemoteCalled { get; set; }
|
||||
public bool NotifyOpenFromLocalDueToConflictCalled { get; set; }
|
||||
|
||||
|
||||
public void CouldntSaveToRemote(IOConnectionInfo ioc, Exception e)
|
||||
{
|
||||
CouldntSaveToRemoteCalled = true;
|
||||
}
|
||||
|
||||
public void CouldntOpenFromRemote(IOConnectionInfo ioc, Exception ex)
|
||||
{
|
||||
CouldntOpenFromRemoteCalled = true;
|
||||
}
|
||||
|
||||
public void NotifyOpenFromLocalDueToConflict(IOConnectionInfo ioc)
|
||||
{
|
||||
NotifyOpenFromLocalDueToConflictCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -20,118 +19,6 @@ namespace Kp2aUnitTests
|
||||
private string _defaultCacheFileContents = "default contents";
|
||||
private TestCacheSupervisor _testCacheSupervisor;
|
||||
|
||||
class TestCacheSupervisor: ICacheSupervisor
|
||||
{
|
||||
public bool CouldntOpenFromRemoteCalled { get; set; }
|
||||
public bool CouldntSaveToRemoteCalled { get; set; }
|
||||
public bool NotifyOpenFromLocalDueToConflictCalled { get; set; }
|
||||
|
||||
|
||||
public void CouldntSaveToRemote(IOConnectionInfo ioc, Exception e)
|
||||
{
|
||||
CouldntSaveToRemoteCalled = true;
|
||||
}
|
||||
|
||||
public void CouldntOpenFromRemote(IOConnectionInfo ioc, Exception ex)
|
||||
{
|
||||
CouldntOpenFromRemoteCalled = true;
|
||||
}
|
||||
|
||||
public void NotifyOpenFromLocalDueToConflict(IOConnectionInfo ioc)
|
||||
{
|
||||
NotifyOpenFromLocalDueToConflictCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestFileStorage: IFileStorage
|
||||
{
|
||||
private BuiltInFileStorage _builtIn = new BuiltInFileStorage();
|
||||
|
||||
public bool Offline { get; set; }
|
||||
|
||||
|
||||
public void DeleteFile(IOConnectionInfo ioc)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
_builtIn.DeleteFile(ioc);
|
||||
}
|
||||
|
||||
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
|
||||
{
|
||||
if (Offline)
|
||||
return false;
|
||||
return _builtIn.CheckForFileChangeFast(ioc, previousFileVersion);
|
||||
}
|
||||
|
||||
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
return _builtIn.GetCurrentFileVersionFast(ioc);
|
||||
}
|
||||
|
||||
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
return _builtIn.OpenFileForRead(ioc);
|
||||
}
|
||||
|
||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
return new TestFileTransaction(ioc, useFileTransaction, Offline);
|
||||
}
|
||||
|
||||
public class TestFileTransaction : IWriteTransaction
|
||||
{
|
||||
private readonly bool _offline;
|
||||
private readonly FileTransactionEx _transaction;
|
||||
|
||||
public TestFileTransaction(IOConnectionInfo ioc, bool useFileTransaction, bool offline)
|
||||
{
|
||||
_offline = offline;
|
||||
_transaction = new FileTransactionEx(ioc, useFileTransaction);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Stream OpenFile()
|
||||
{
|
||||
if (_offline)
|
||||
throw new IOException("offline");
|
||||
return _transaction.OpenWrite();
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
if (_offline)
|
||||
throw new IOException("offline");
|
||||
_transaction.CommitWrite();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompleteIoId()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool? FileExists()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||
{
|
||||
return _builtIn.GetFilenameWithoutPathAndExt(ioc);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests correct behavior in case that either remote or cache are not available
|
||||
/// </summary>
|
||||
|
||||
96
src/Kp2aUnitTests/TestFileStorage.cs
Normal file
96
src/Kp2aUnitTests/TestFileStorage.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using KeePassLib.Serialization;
|
||||
using keepass2android.Io;
|
||||
|
||||
namespace Kp2aUnitTests
|
||||
{
|
||||
internal class TestFileStorage: IFileStorage
|
||||
{
|
||||
private BuiltInFileStorage _builtIn = new BuiltInFileStorage();
|
||||
|
||||
public bool Offline { get; set; }
|
||||
|
||||
|
||||
public void DeleteFile(IOConnectionInfo ioc)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
_builtIn.DeleteFile(ioc);
|
||||
}
|
||||
|
||||
public bool CheckForFileChangeFast(IOConnectionInfo ioc, string previousFileVersion)
|
||||
{
|
||||
if (Offline)
|
||||
return false;
|
||||
return _builtIn.CheckForFileChangeFast(ioc, previousFileVersion);
|
||||
}
|
||||
|
||||
public string GetCurrentFileVersionFast(IOConnectionInfo ioc)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
return _builtIn.GetCurrentFileVersionFast(ioc);
|
||||
}
|
||||
|
||||
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
return _builtIn.OpenFileForRead(ioc);
|
||||
}
|
||||
|
||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
{
|
||||
if (Offline)
|
||||
throw new IOException("offline");
|
||||
return new TestFileTransaction(ioc, useFileTransaction, Offline);
|
||||
}
|
||||
|
||||
public class TestFileTransaction : IWriteTransaction
|
||||
{
|
||||
private readonly bool _offline;
|
||||
private readonly FileTransactionEx _transaction;
|
||||
|
||||
public TestFileTransaction(IOConnectionInfo ioc, bool useFileTransaction, bool offline)
|
||||
{
|
||||
_offline = offline;
|
||||
_transaction = new FileTransactionEx(ioc, useFileTransaction);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Stream OpenFile()
|
||||
{
|
||||
if (_offline)
|
||||
throw new IOException("offline");
|
||||
return _transaction.OpenWrite();
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
if (_offline)
|
||||
throw new IOException("offline");
|
||||
_transaction.CommitWrite();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompleteIoId()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool? FileExists()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||
{
|
||||
return _builtIn.GetFilenameWithoutPathAndExt(ioc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using KeePassLib.Serialization;
|
||||
@@ -22,6 +23,7 @@ namespace Kp2aUnitTests
|
||||
private YesNoCancelResult _yesNoCancelResult = YesNoCancelResult.Yes;
|
||||
private Dictionary<PreferenceKey, bool> _preferences = new Dictionary<PreferenceKey, bool>();
|
||||
|
||||
|
||||
public void SetShutdown()
|
||||
{
|
||||
|
||||
@@ -40,7 +42,7 @@ namespace Kp2aUnitTests
|
||||
public Database CreateNewDatabase()
|
||||
{
|
||||
TestDrawableFactory testDrawableFactory = new TestDrawableFactory();
|
||||
_db = new Database(testDrawableFactory, new TestKp2aApp());
|
||||
_db = new Database(testDrawableFactory, this);
|
||||
return _db;
|
||||
|
||||
}
|
||||
@@ -98,6 +100,9 @@ namespace Kp2aUnitTests
|
||||
public Handler UiThreadHandler {
|
||||
get { return null; } //ensure everything runs in the same thread. Otherwise the OnFinish-callback would run after the test has already finished (with failure)
|
||||
}
|
||||
|
||||
public IFileStorage FileStorage { get; set; }
|
||||
|
||||
public IProgressDialog CreateProgressDialog(Context ctx)
|
||||
{
|
||||
return new ProgressDialogStub();
|
||||
@@ -105,7 +110,20 @@ namespace Kp2aUnitTests
|
||||
|
||||
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
|
||||
{
|
||||
return new BuiltInFileStorage();
|
||||
return FileStorage;
|
||||
}
|
||||
|
||||
|
||||
public bool TriggerReloadCalled;
|
||||
|
||||
public TestKp2aApp()
|
||||
{
|
||||
FileStorage = new BuiltInFileStorage();
|
||||
}
|
||||
|
||||
public void TriggerReload(Context ctx)
|
||||
{
|
||||
TriggerReloadCalled = true;
|
||||
}
|
||||
|
||||
public void SetYesNoCancelResult(YesNoCancelResult yesNoCancelResult)
|
||||
|
||||
117
src/Kp2aUnitTests/TestSynchronizeCachedDatabase.cs
Normal file
117
src/Kp2aUnitTests/TestSynchronizeCachedDatabase.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using keepass2android;
|
||||
using keepass2android.Io;
|
||||
|
||||
namespace Kp2aUnitTests
|
||||
{
|
||||
[TestClass]
|
||||
internal class TestSynchronizeCachedDatabase : TestBase
|
||||
{
|
||||
private TestCacheSupervisor _testCacheSupervisor = new TestCacheSupervisor();
|
||||
private TestFileStorage _testFileStorage = new TestFileStorage();
|
||||
|
||||
[TestMethod]
|
||||
public void TestTodos()
|
||||
{
|
||||
Assert.IsFalse(true, "Wird immer ManagedTransform benutzt??");
|
||||
Assert.IsFalse(true, "TODOs in SyncDb");
|
||||
Assert.IsFalse(true, "FileNotFound");
|
||||
Assert.IsFalse(true, "Test merge files");
|
||||
}
|
||||
|
||||
protected override TestKp2aApp CreateTestKp2aApp()
|
||||
{
|
||||
TestKp2aApp app = base.CreateTestKp2aApp();
|
||||
app.FileStorage = new CachingFileStorage(_testFileStorage, "/mnt/sdcard/kp2atest/cache/", _testCacheSupervisor);
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that synchronizing works if
|
||||
/// - no changes in remote and local db
|
||||
/// - remote is offline -> error
|
||||
/// - only local file was changed
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestSimpleSyncCases()
|
||||
{
|
||||
|
||||
//create the default database:
|
||||
TestKp2aApp app = SetupAppWithDefaultDatabase();
|
||||
|
||||
|
||||
IOConnection.DeleteFile(new IOConnectionInfo {Path = DefaultFilename});
|
||||
//save it and reload it so we have a base version ("remote" and in the cache)
|
||||
SaveDatabase(app);
|
||||
app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
|
||||
|
||||
string resultMessage;
|
||||
bool wasSuccessful;
|
||||
|
||||
//sync without changes on any side:
|
||||
Synchronize(app, out wasSuccessful, out resultMessage);
|
||||
Assert.IsTrue(wasSuccessful);
|
||||
Assert.AreEqual(resultMessage, app.GetResourceString(UiStringKey.FilesInSync));
|
||||
|
||||
//go offline:
|
||||
_testFileStorage.Offline = true;
|
||||
|
||||
//sync when offline (->error)
|
||||
Synchronize(app, out wasSuccessful, out resultMessage);
|
||||
Assert.IsFalse(wasSuccessful);
|
||||
Assert.AreEqual(resultMessage, "offline");
|
||||
|
||||
//modify the database by adding a group:
|
||||
app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true);
|
||||
//save the database again (will be saved locally only)
|
||||
SaveDatabase(app);
|
||||
Assert.IsTrue(_testCacheSupervisor.CouldntSaveToRemoteCalled);
|
||||
_testCacheSupervisor.CouldntSaveToRemoteCalled = false;
|
||||
|
||||
//go online again:
|
||||
_testFileStorage.Offline = false;
|
||||
|
||||
//sync with local changes only (-> upload):
|
||||
Synchronize(app, out wasSuccessful, out resultMessage);
|
||||
Assert.IsTrue(wasSuccessful);
|
||||
Assert.AreEqual(resultMessage, app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||
|
||||
//ensure both files are identical and up to date now:
|
||||
_testFileStorage.Offline = true;
|
||||
var appOfflineLoaded = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
|
||||
_testCacheSupervisor.CouldntOpenFromRemoteCalled = false;
|
||||
_testFileStorage.Offline = false;
|
||||
var appRemoteLoaded = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
|
||||
Assert.IsFalse(_testCacheSupervisor.CouldntOpenFromRemoteCalled);
|
||||
|
||||
AssertDatabasesAreEqual(app.GetDb().KpDatabase, appOfflineLoaded.GetDb().KpDatabase);
|
||||
AssertDatabasesAreEqual(app.GetDb().KpDatabase, appRemoteLoaded.GetDb().KpDatabase);
|
||||
}
|
||||
|
||||
private void Synchronize(TestKp2aApp app, out bool wasSuccessful, out string resultMessage)
|
||||
{
|
||||
bool success = false;
|
||||
string result = null;
|
||||
var sync = new SynchronizeCachedDatabase(Application.Context, app, new ActionOnFinish((_success, _result) =>
|
||||
{
|
||||
success = _success;
|
||||
result = _result;
|
||||
}));
|
||||
sync.Run();
|
||||
wasSuccessful = success;
|
||||
resultMessage = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user