* Introduced IFileStorage interface: Better abstraction than current IOConnection (suitable for cloud support). Currently only implemented by the built-in IOConnection (local/http/ftp)

* Implemented Merge functionality for SaveDB. UI is not yet implemented!
* Added tests for merge functionality
This commit is contained in:
Philipp Crocoll
2013-07-09 09:59:17 +02:00
parent 64e62cae70
commit 84aeb31fd0
42 changed files with 912 additions and 161 deletions

View File

@@ -20,7 +20,8 @@ namespace Kp2aUnitTests
// Run all tests from this assembly
runner.AddTests(Assembly.GetExecutingAssembly());
//runner.AddTests(new List<Type> { typeof(TestSaveDb)});
//runner.AddTests(typeof(TestLoadDb).GetMethod("TestLoadWithPasswordOnly"));}}
//runner.AddTests(typeof(TestSaveDb).GetMethod("TestLoadEditSaveWithSyncConflict"));
//runner.AddTests(typeof(TestSaveDb).GetMethod("TestLoadEditSave"));
return runner;
}
}

View File

@@ -12,9 +12,9 @@ namespace Kp2aUnitTests
}
public void SetMessage(string getResourceString)
public void SetMessage(string resourceString)
{
Kp2aLog.Log("Progress message: " + resourceString);
}
public void Dismiss()

View File

@@ -74,7 +74,7 @@ namespace Kp2aUnitTests
})
);
ProgressTask pt = new ProgressTask(app, Application.Context, task, UiStringKey.loading_database);
ProgressTask pt = new ProgressTask(app, Application.Context, task);
pt.Run();
pt.JoinWorkerThread();
Assert.IsTrue(loadSuccesful);
@@ -83,16 +83,27 @@ namespace Kp2aUnitTests
protected void SaveDatabase(IKp2aApp app)
{
bool saveSuccesful = false;
SaveDb save = new SaveDb(Application.Context, app.GetDb(), new ActionOnFinish((success, message) =>
{
saveSuccesful = success;
}), false);
save.Run();
bool saveSuccesful = TrySaveDatabase(app);
Assert.IsTrue(saveSuccesful);
}
public static bool TrySaveDatabase(IKp2aApp app)
{
bool saveSuccesful = false;
SaveDb save = new SaveDb(Application.Context, app, new ActionOnFinish((success, message) =>
{
saveSuccesful = success;
if (!success)
{
Kp2aLog.Log("Error during TestBase.SaveDatabase: " + message);
}
}), false);
save.Run();
save.JoinWorkerThread();
return saveSuccesful;
}
protected IKp2aApp SetupAppWithDefaultDatabase()
{
IKp2aApp app = new TestKp2aApp();

View File

@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using Android.Content;
using Android.OS;
using KeePassLib.Serialization;
using keepass2android;
using keepass2android.Io;
namespace Kp2aUnitTests
{
@@ -11,7 +13,14 @@ namespace Kp2aUnitTests
/// </summary>
internal class TestKp2aApp : IKp2aApp
{
internal enum YesNoCancelResult
{
Yes, No, Cancel
}
private Database _db;
private YesNoCancelResult _yesNoCancelResult = YesNoCancelResult.Yes;
private Dictionary<PreferenceKey, bool> _preferences = new Dictionary<PreferenceKey, bool>();
public void SetShutdown()
{
@@ -43,13 +52,32 @@ namespace Kp2aUnitTests
public bool GetBooleanPreference(PreferenceKey key)
{
if (_preferences.ContainsKey(key))
return _preferences[key];
return true;
}
public UiStringKey? LastYesNoCancelQuestionTitle { get; set; }
public void AskYesNoCancel(UiStringKey titleKey, UiStringKey messageKey, EventHandler<DialogClickEventArgs> yesHandler, EventHandler<DialogClickEventArgs> noHandler,
EventHandler<DialogClickEventArgs> cancelHandler, Context ctx)
{
yesHandler(null, null);
LastYesNoCancelQuestionTitle = titleKey;
switch (_yesNoCancelResult)
{
case YesNoCancelResult.Yes:
yesHandler(null, null);
break;
case YesNoCancelResult.No:
noHandler(null, null);
break;
case YesNoCancelResult.Cancel:
cancelHandler(null, null);
break;
default:
throw new Exception("unexpected case!");
}
}
public Handler UiThreadHandler {
@@ -59,5 +87,20 @@ namespace Kp2aUnitTests
{
return new ProgressDialogStub();
}
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
{
return new BuiltInFileStorage();
}
public void SetYesNoCancelResult(YesNoCancelResult yesNoCancelResult)
{
_yesNoCancelResult = yesNoCancelResult;
}
public void SetPreference(PreferenceKey key, bool value)
{
_preferences[key] = value;
}
}
}

View File

@@ -26,7 +26,7 @@ namespace Kp2aUnitTests
loadSuccesful = success;
})
);
ProgressTask pt = new ProgressTask(app, Application.Context, task, UiStringKey.loading_database);
ProgressTask pt = new ProgressTask(app, Application.Context, task);
Android.Util.Log.Debug("KP2ATest", "Running ProgressTask");
pt.Run();
pt.JoinWorkerThread();

View File

@@ -1,11 +1,13 @@
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;
@@ -23,6 +25,7 @@ namespace Kp2aUnitTests
{
//create the default database:
IKp2aApp app = SetupAppWithDefaultDatabase();
IOConnection.DeleteFile(new IOConnectionInfo { Path = DefaultFilename });
//save it and reload it so we have a base version
SaveDatabase(app);
app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
@@ -56,9 +59,14 @@ namespace Kp2aUnitTests
//save the database from app 1:
SaveDatabase(app);
((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.Yes);
//save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync:
SaveDatabase(app2);
//make sure the right question was asked
Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle);
//add group 2 to app 1:
app.GetDb().KpDatabase.RootGroup.AddGroup(group2, true);
@@ -68,7 +76,162 @@ namespace Kp2aUnitTests
//ensure the sync was successful:
AssertDatabasesAreEqual(app.GetDb().KpDatabase, resultApp.GetDb().KpDatabase);
Assert.IsTrue(false, "todo: test for sync question, test overwrite or cancel!");
}
[TestMethod]
public void TestLoadEditSaveWithSyncOverwrite()
{
//create the default database:
IKp2aApp app = SetupAppWithDefaultDatabase();
//save it and reload it so we have a base version
SaveDatabase(app);
app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//load it once again:
IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//modify the database by adding a group in both databases:
app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true);
var group2 = new PwGroup(true, true, "TestGroup2", PwIcon.Energy);
app2.GetDb().KpDatabase.RootGroup.AddGroup(group2, true);
//save the database from app 1:
SaveDatabase(app);
//the user clicks the "no" button when asked if the sync should be performed -> overwrite expected!
((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.No);
//save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync:
SaveDatabase(app2);
//make sure the right question was asked
Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle);
//load database to a new app instance:
IKp2aApp resultApp = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//ensure the sync was NOT performed (overwrite expected!):
AssertDatabasesAreEqual(app2.GetDb().KpDatabase, resultApp.GetDb().KpDatabase);
}
[TestMethod]
public void TestLoadEditSaveWithSyncOverwriteBecauseOfNoCheck()
{
//create the default database:
IKp2aApp app = SetupAppWithDefaultDatabase();
//save it and reload it so we have a base version
SaveDatabase(app);
app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//load it once again:
IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//modify the database by adding a group in both databases:
app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true);
var group2 = new PwGroup(true, true, "TestGroup2", PwIcon.Energy);
app2.GetDb().KpDatabase.RootGroup.AddGroup(group2, true);
//save the database from app 1:
SaveDatabase(app);
//the user doesn't want to perform check for file change:
((TestKp2aApp) app2).SetPreference(PreferenceKey.CheckForFileChangesOnSave, false);
//save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync:
SaveDatabase(app2);
//make sure no question was asked
Assert.AreEqual(null, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle);
//load database to a new app instance:
IKp2aApp resultApp = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//ensure the sync was NOT performed (overwrite expected!):
AssertDatabasesAreEqual(app2.GetDb().KpDatabase, resultApp.GetDb().KpDatabase);
}
[TestMethod]
public void TestLoadEditSaveWithSyncCancel()
{
//create the default database:
IKp2aApp app = SetupAppWithDefaultDatabase();
//save it and reload it so we have a base version
SaveDatabase(app);
app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//load it once again:
IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//modify the database by adding a group in both databases:
app.GetDb().KpDatabase.RootGroup.AddGroup(new PwGroup(true, true, "TestGroup", PwIcon.Apple), true);
var group2 = new PwGroup(true, true, "TestGroup2", PwIcon.Energy);
app2.GetDb().KpDatabase.RootGroup.AddGroup(group2, true);
//save the database from app 1:
SaveDatabase(app);
//the user clicks the "cancel" button when asked if the sync should be performed
((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.Cancel);
//save the database from app 2: This save operation must detect the changes made from app 1 and ask if it should sync:
Assert.AreEqual(false, TrySaveDatabase(app2));
//make sure the right question was asked
Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle);
//load database to a new app instance:
IKp2aApp resultApp = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//ensure the sync was NOT performed (cancel expected!):
AssertDatabasesAreEqual(app.GetDb().KpDatabase, resultApp.GetDb().KpDatabase);
}
[TestMethod]
public void TestLoadEditSaveWithSyncConflict()
{
//create the default database:
IKp2aApp app = SetupAppWithDefaultDatabase();
//save it and reload it so we have a base version
SaveDatabase(app);
app = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//load it once again:
IKp2aApp app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
//modify the database by renaming the same group in both databases:
app.GetDb().KpDatabase.RootGroup.Groups.Single(g => g.Name == "Internet").Name += "abc";
app2.GetDb().KpDatabase.RootGroup.Groups.Single(g => g.Name == "Internet").Name += "abcde";
//app1 also changes the master password:
var compositeKey = app.GetDb().KpDatabase.MasterKey;
compositeKey.RemoveUserKey(compositeKey.GetUserKey(typeof (KcpPassword)));
compositeKey.AddUserKey(new KcpPassword("abc"));
//save the database from app 1:
SaveDatabase(app);
((TestKp2aApp)app2).SetYesNoCancelResult(TestKp2aApp.YesNoCancelResult.Yes);
//save the database from app 2: This save operation must fail because the target file cannot be loaded:
Assert.IsFalse(TrySaveDatabase(app2));
//make sure the right question was asked
Assert.AreEqual(UiStringKey.TitleSyncQuestion, ((TestKp2aApp)app2).LastYesNoCancelQuestionTitle);
}
[TestMethod]
public void TestSaveAsWhenReadOnly()
{
Assert.Fail("TODO: Test ");
}
[TestMethod]
public void TestSaveAsWhenSyncError()
{
Assert.Fail("TODO: Test ");
}
[TestMethod]
@@ -119,4 +282,6 @@ namespace Kp2aUnitTests
return sOutput.Text;
}
}
}