diff --git a/Cmdline/CKAN-cmdline.csproj b/Cmdline/CKAN-cmdline.csproj
index ce1a82730b..3cc270858b 100644
--- a/Cmdline/CKAN-cmdline.csproj
+++ b/Cmdline/CKAN-cmdline.csproj
@@ -9,12 +9,15 @@
CmdLine
CKAN.CmdLine.MainClass
..\GUI\assets\ckan.ico
+ v4.5
+
false
bin\Debug
4
true
+ false
@@ -63,6 +66,7 @@
+
-
+
\ No newline at end of file
diff --git a/Cmdline/Main.cs b/Cmdline/Main.cs
index 4c727b15ab..331afe002a 100644
--- a/Cmdline/Main.cs
+++ b/Cmdline/Main.cs
@@ -20,12 +20,12 @@ internal class MainClass
private static readonly ILog log = LogManager.GetLogger(typeof (MainClass));
/*
- * When the STAThread is applied, it changes the apartment state of the current thread to be single threaded.
+ * When the STAThread is applied, it changes the apartment state of the current thread to be single threaded.
* Without getting into a huge discussion about COM and threading,
* this attribute ensures the communication mechanism between the current thread an
* other threads that may want to talk to it via COM. When you're using Windows Forms,
* depending on the feature you're using, it may be using COM interop in order to communicate with
- * operating system components. Good examples of this are the Clipboard and the File Dialogs.
+ * operating system components. Good examples of this are the Clipboard and the File Dialogs.
*/
[STAThread]
public static int Main(string[] args)
@@ -66,7 +66,7 @@ public static int Main(string[] args)
{
if (!options.AsRoot)
{
- user.RaiseError(@"You are trying to run CKAN as root.
+ user.RaiseError(@"You are trying to run CKAN as root.
This is a bad idea and there is absolutely no good reason to do it. Please run CKAN from a user account (or use --asroot if you are feeling brave).");
return Exit.ERROR;
}
@@ -154,7 +154,7 @@ public static int Main(string[] args)
default:
break;
- }
+ }
#endregion
@@ -228,16 +228,16 @@ private static void CheckMonoVersion(IUser user, int rec_major, int rec_minor, i
MethodInfo display_name = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (display_name != null)
- {
+ {
var version_string = (string) display_name.Invoke(null, null);
var match = Regex.Match(version_string, @"^\D*(?[\d]+)\.(?\d+)\.(?\d+).*$");
-
+
if (match.Success)
- {
+ {
int major = Int32.Parse(match.Groups["major"].Value);
int minor = Int32.Parse(match.Groups["minor"].Value);
int patch = Int32.Parse(match.Groups["revision"].Value);
-
+
if (major < rec_major || (major == rec_major && minor < rec_minor))
{
user.RaiseMessage(
diff --git a/Cmdline/app.config b/Cmdline/app.config
new file mode 100644
index 0000000000..51278a4563
--- /dev/null
+++ b/Cmdline/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Core/Relationships/RelationshipResolver.cs b/Core/Relationships/RelationshipResolver.cs
index 331ee597f0..2274cd5d60 100644
--- a/Core/Relationships/RelationshipResolver.cs
+++ b/Core/Relationships/RelationshipResolver.cs
@@ -63,8 +63,8 @@ public object Clone()
// If we resolved in things breadth-first order, we're less likely to encounter surprises
// where a nth-deep recommend blocks a top-level recommend.
- // TODO: Add mechanism so that clients can add mods with relationshup other than UserAdded.
- // Currently only made to support the with_{} options.
+ // TODO: Add mechanism so that clients can add mods with relationshup other than UserAdded.
+ // Currently only made to support the with_{} options.
public class RelationshipResolver
{
// A list of all the mods we're going to install.
@@ -111,7 +111,7 @@ public RelationshipResolver(ICollection modules, RelationshipResolve
{
log.DebugFormat("Preparing to resolve relationships for {0} {1}", module.identifier, module.version);
- var module1 = module; //Silence a warning re. closures over foreach var.
+ var module1 = module; //Silence a warning re. closures over foreach var.
foreach (CkanModule listed_mod in modlist.Values.Where(listed_mod => listed_mod.ConflictsWith(module1)))
{
if (options.procede_with_inconsistencies)
@@ -155,7 +155,7 @@ public RelationshipResolver(ICollection modules, RelationshipResolve
///
/// Returns the default options for relationship resolution.
///
-
+
// TODO: This should just be able to return a new RelationshipResolverOptions
// and the defaults in the class definition should do the right thing.
public static RelationshipResolverOptions DefaultOpts()
@@ -202,10 +202,10 @@ private void Resolve(CkanModule module, RelationshipResolverOptions options)
/// Resolve a relationship stanza (a list of relationships).
/// This will add modules to be installed, if required.
/// May recurse back to Resolve for those new modules.
- ///
+ ///
/// If `soft_resolve` is true, we warn rather than throw exceptions on mods we cannot find.
/// If `soft_resolve` is false (default), we throw a ModuleNotFoundKraken if we can't find a dependency.
- ///
+ ///
/// Throws a TooManyModsProvideKraken if we have too many choices and
/// options.without_toomanyprovides_kraken is not set.
///
@@ -232,7 +232,8 @@ private void ResolveStanza(IEnumerable stanza, Relations
continue;
}
- List candidates = registry.LatestAvailableWithProvides(dep_name, kspversion);
+ List candidates = registry.LatestAvailableWithProvides(dep_name, kspversion)
+ .Where(mod=>MightBeInstallable(mod)).ToList();
if (candidates.Count == 0)
{
@@ -334,7 +335,37 @@ private void Add(CkanModule module, Relationship reason)
}
///
- /// Returns a list of all modules to install to satisify the changes required.
+ /// Tests that a module might be able to be installed via checking if dependencies
+ /// exist for current version.
+ ///
+ /// The module to consider
+ /// For internal use
+ /// If it has dependencies compatible for the current version
+ private bool MightBeInstallable(CkanModule module, List compatible = null)
+ {
+ if (module.depends == null) return true;
+ if (compatible == null)
+ {
+ compatible = new List();
+ }
+ else if (compatible.Contains(module.identifier))
+ {
+ return true;
+ }
+ //When checking the dependencies we assume that this module is installable
+ // in case a dependent depends on it
+ compatible.Add(module.identifier);
+
+ var needed = module.depends.Select(depend => registry.LatestAvailableWithProvides(depend.name, kspversion));
+ //We need every dependency to have at least one possible module
+ var installable = needed.All(need => need.Any(mod => MightBeInstallable(mod, compatible)));
+ compatible.Remove(module.identifier);
+ return installable;
+ }
+
+
+ ///
+ /// Returns a list of all modules to install to satisfy the changes required.
///
public List ModList()
{
@@ -344,7 +375,7 @@ public List ModList()
///
/// Returns a IList consisting of keyValuePairs containing conflicting mods.
- /// Note: (a,b) in the list should imply that (b,a) is in the list.
+ /// Note: (a,b) in the list should imply that (b,a) is in the list.
///
public Dictionary ConflictList
{
@@ -398,14 +429,14 @@ public string ReasonStringFor(Module mod)
}
///
- /// Used to keep track of the relationships between modules in the resolver.
- /// Intended to be used for displaying messages to the user.
+ /// Used to keep track of the relationships between modules in the resolver.
+ /// Intended to be used for displaying messages to the user.
///
internal abstract class Relationship
{
//Currently assumed to exist for any relationship other than useradded
public virtual CkanModule Parent { get; protected set; }
- //Should contain a newline at the end of the string.
+ //Should contain a newline at the end of the string.
public abstract String Reason { get; }
diff --git a/GUI/App.config b/GUI/App.config
index 009f0e29b6..dcc4cda715 100644
--- a/GUI/App.config
+++ b/GUI/App.config
@@ -3,9 +3,9 @@
-
+
-
+
-
\ No newline at end of file
+
diff --git a/GUI/CKAN-GUI.csproj b/GUI/CKAN-GUI.csproj
index 9dba90277a..c5f6454641 100644
--- a/GUI/CKAN-GUI.csproj
+++ b/GUI/CKAN-GUI.csproj
@@ -27,6 +27,7 @@
false
false
true
+ v4.5
false
diff --git a/GUI/ErrorDialog.Designer.cs b/GUI/ErrorDialog.Designer.cs
index 440b0165a4..b14e10f6ab 100644
--- a/GUI/ErrorDialog.Designer.cs
+++ b/GUI/ErrorDialog.Designer.cs
@@ -29,7 +29,7 @@ protected override void Dispose(bool disposing)
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
- this.ErrorMessage = new System.Windows.Forms.Label();
+ this.ErrorMessage = new System.Windows.Forms.RichTextBox();
this.DismissButton = new System.Windows.Forms.Button();
this.panel1.SuspendLayout();
this.SuspendLayout();
@@ -50,7 +50,7 @@ private void InitializeComponent()
this.ErrorMessage.Size = new System.Drawing.Size(267, 117);
this.ErrorMessage.TabIndex = 0;
this.ErrorMessage.Text = "Error!";
- this.ErrorMessage.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.ErrorMessage.ReadOnly = true;
//
// DismissButton
//
@@ -82,7 +82,7 @@ private void InitializeComponent()
#endregion
private System.Windows.Forms.Panel panel1;
- private System.Windows.Forms.Label ErrorMessage;
+ private System.Windows.Forms.RichTextBox ErrorMessage;
private System.Windows.Forms.Button DismissButton;
}
}
\ No newline at end of file
diff --git a/GUI/Main.Designer.cs b/GUI/Main.Designer.cs
index 71f8b35eef..d714f99465 100644
--- a/GUI/Main.Designer.cs
+++ b/GUI/Main.Designer.cs
@@ -1162,7 +1162,9 @@ private void InitializeComponent()
this.ChooseProvidedModsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader6,
this.columnHeader8});
+ this.ChooseProvidedModsListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.ChooseProvidedModsListView.Location = new System.Drawing.Point(6, 28);
+ this.ChooseProvidedModsListView.MultiSelect = false;
this.ChooseProvidedModsListView.Name = "ChooseProvidedModsListView";
this.ChooseProvidedModsListView.Size = new System.Drawing.Size(1007, 582);
this.ChooseProvidedModsListView.TabIndex = 8;
diff --git a/GUI/Main.cs b/GUI/Main.cs
index 6ca7dbcced..17e14acc73 100644
--- a/GUI/Main.cs
+++ b/GUI/Main.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using System.Windows.Forms;
using CKAN.Properties;
using log4net;
@@ -78,8 +79,9 @@ private IEnumerable> ChangeSet
get { return change_set; }
set
{
+ var orig = change_set;
change_set = value;
- ChangeSetUpdated();
+ if(!ReferenceEquals(orig, value)) ChangeSetUpdated();
}
}
@@ -88,8 +90,9 @@ private Dictionary Conflicts
get { return conflicts; }
set
{
+ var orig = conflicts;
conflicts = value;
- ConflictsUpdated();
+ if(orig != value) ConflictsUpdated();
}
}
@@ -155,7 +158,7 @@ public Main(string[] cmdlineArgs, GUIUser User, bool showConsole)
controlFactory = new ControlFactory();
Instance = this;
- mainModList = new MainModList(source => UpdateFilters(this));
+ mainModList = new MainModList(source => UpdateFilters(this), TooManyModsProvide, User);
InitializeComponent();
// We need to initialize error dialog first to display errors
@@ -405,6 +408,7 @@ private void MarkAllUpdatesToolButton_Click(object sender, EventArgs e)
var mod = ((GUIMod) row.Tag);
if (mod.HasUpdate && row.Cells[1] is DataGridViewCheckBoxCell)
{
+ MarkModForUpdate(mod.Identifier);
mod.SetUpgradeChecked(row, true);
ApplyToolButton.Enabled = true;
}
@@ -543,10 +547,10 @@ private void ModList_KeyDown(object sender, KeyEventArgs e)
///
/// Called on key press when the mod is focused. Scrolls to the first mod
- /// with name begining with the key pressed. If more than one unique keys are pressed
+ /// with name beginning with the key pressed. If more than one unique keys are pressed
/// in under a second, it searches for the combination of the keys pressed.
/// If the same key is being pressed repeatedly, it cycles through mods names
- /// beginnng with that key. If space is pressed, the checkbox at the current row is toggled.
+ /// beginning with that key. If space is pressed, the checkbox at the current row is toggled.
///
private void ModList_KeyPress(object sender, KeyPressEventArgs e)
{
@@ -557,13 +561,10 @@ private void ModList_KeyPress(object sender, KeyPressEventArgs e)
{
if (current_row != null && current_row.Selected)
{
- // Get the checkbox.
- var selected_row_check_box = current_row.Cells["Installed"] as DataGridViewCheckBoxCell;
- // Invert the value.
- if (selected_row_check_box != null)
+ var gui_mod = ((GUIMod)current_row.Tag);
+ if (gui_mod.IsInstallable())
{
- bool selected_value = (bool)selected_row_check_box.Value;
- selected_row_check_box.Value = !selected_value;
+ MarkModForInstall(gui_mod.Identifier,uninstall:gui_mod.IsInstallChecked);
}
}
e.Handled = true;
@@ -644,7 +645,7 @@ private void ModList_CellContentClick(object sender, DataGridViewCellEventArgs e
ModList.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
- private void ModList_CellValueChanged(object sender, DataGridViewCellEventArgs e)
+ private async void ModList_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (mainModList.ModFilter == GUIModFilter.Incompatible)
{
@@ -671,11 +672,13 @@ private void ModList_CellValueChanged(object sender, DataGridViewCellEventArgs e
}
else if (column_index < 2)
{
- var gui_mod = ((GUIMod) row.Tag);
+ var gui_mod = ((GUIMod)row.Tag);
switch (column_index)
{
case 0:
gui_mod.SetInstallChecked(row);
+ if(gui_mod.IsInstallChecked)
+ last_mod_to_have_install_toggled.Push(gui_mod);
break;
case 1:
gui_mod.SetUpgradeChecked(row);
@@ -683,29 +686,44 @@ private void ModList_CellValueChanged(object sender, DataGridViewCellEventArgs e
}
var registry = registry_manager.registry;
- UpdateChangeSetAndConflicts(registry);
+ await UpdateChangeSetAndConflicts(registry);
}
}
- private void UpdateChangeSetAndConflicts(Registry registry)
+ private async Task UpdateChangeSetAndConflicts(Registry registry)
{
- IEnumerable> full_change_set;
- Dictionary conflicts;
+ IEnumerable> full_change_set = null;
+ Dictionary conflicts = null;
+ bool too_many_provides_thrown = false;
var user_change_set = mainModList.ComputeUserChangeSet();
try
{
var module_installer = ModuleInstaller.GetInstance(CurrentInstance, GUI.user);
- full_change_set = MainModList.ComputeChangeSetFromModList(registry, user_change_set, module_installer,
- CurrentInstance.Version());
- conflicts = null;
+ full_change_set =
+ await mainModList.ComputeChangeSetFromModList(registry, user_change_set, module_installer,
+ CurrentInstance.Version());
}
catch (InconsistentKraken)
{
+ //Need to be recomputed due to ComputeChangeSetFromModList possibly changing it with too many provides handling.
+ user_change_set = mainModList.ComputeUserChangeSet();
conflicts = MainModList.ComputeConflictsFromModList(registry, user_change_set, CurrentInstance.Version());
full_change_set = null;
}
-
+ catch (TooManyModsProvideKraken)
+ {
+ //Can be thrown by ComputeChangeSetFromModList if the user cancels out of it.
+ //We can just rerun it as the ModInfoTabControl has been removed.
+ too_many_provides_thrown = true;
+ }
+ if (too_many_provides_thrown)
+ {
+ await UpdateChangeSetAndConflicts(registry);
+ conflicts = Conflicts;
+ full_change_set = ChangeSet;
+ }
+ last_mod_to_have_install_toggled.Clear();
Conflicts = conflicts;
ChangeSet = full_change_set;
}
diff --git a/GUI/MainDialogs.cs b/GUI/MainDialogs.cs
index f943da362e..ed181b556e 100644
--- a/GUI/MainDialogs.cs
+++ b/GUI/MainDialogs.cs
@@ -1,11 +1,13 @@
using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading.Tasks;
using System.Windows.Forms;
namespace CKAN
{
public partial class Main
{
-
private ErrorDialog m_ErrorDialog;
private SettingsDialog m_SettingsDialog;
private PluginsDialog m_PluginsDialog;
@@ -33,5 +35,44 @@ public bool YesNoDialog(string text)
{
return m_YesNoDialog.ShowYesNoDialog(text) == DialogResult.Yes;
}
+
+ //Ugly Hack. Possible fix is to alter the relationship provider so we can use a loop
+ //over reason for to find a user requested mod. Or, you know, pass in a handler to it.
+ private readonly ConcurrentStack last_mod_to_have_install_toggled = new ConcurrentStack();
+ public async Task TooManyModsProvide(TooManyModsProvideKraken kraken)
+ {
+ //We want LMtHIT to be the last user selection. If we alter this handling a too many provides
+ // it needs to be reset so a potential second too many provides doesn't use the wrong mod.
+ GUIMod mod;
+
+ TaskCompletionSource task = new TaskCompletionSource();
+ Util.Invoke(this, () =>
+ {
+ UpdateProvidedModsDialog(kraken, task);
+ m_TabController.ShowTab("ChooseProvidedModsTabPage", 3);
+ m_TabController.SetTabLock(true);
+ });
+ var module = await task.Task;
+
+ if (module == null)
+ {
+ last_mod_to_have_install_toggled.TryPeek(out mod);
+ MarkModForInstall(mod.Identifier,uninstall:true);
+ }
+ Util.Invoke(this, () =>
+ {
+ m_TabController.SetTabLock(false);
+
+ m_TabController.HideTab("ChooseProvidedModsTabPage");
+
+ m_TabController.ShowTab("ManageModsTabPage");
+ });
+
+ if(module!=null)
+ MarkModForInstall(module.identifier);
+
+ last_mod_to_have_install_toggled.TryPop(out mod);
+ return module;
+ }
}
}
\ No newline at end of file
diff --git a/GUI/MainInstall.cs b/GUI/MainInstall.cs
index 5bb4976896..d1adab205b 100644
--- a/GUI/MainInstall.cs
+++ b/GUI/MainInstall.cs
@@ -3,6 +3,7 @@
using System.ComponentModel;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using System.Windows.Forms;
namespace CKAN
@@ -16,7 +17,7 @@ public partial class Main
// this may happen on the recommended/suggested mods dialogs
private volatile bool installCanceled;
- // this will be the final list of mods we want to install
+ // this will be the final list of mods we want to install
private HashSet toInstall = new HashSet();
private void InstallMods(object sender, DoWorkEventArgs e) // this probably needs to be refactored
@@ -29,7 +30,7 @@ private void InstallMods(object sender, DoWorkEventArgs e) // this probably need
(KeyValuePair>, RelationshipResolverOptions>) e.Argument;
ModuleInstaller installer = ModuleInstaller.GetInstance(CurrentInstance, GUI.user);
- // setup progress callback
+ // setup progress callback
toInstall = new HashSet();
var toUninstall = new HashSet();
@@ -103,7 +104,8 @@ private void InstallMods(object sender, DoWorkEventArgs e) // this probably need
try
{
if (
- RegistryManager.Instance(manager.CurrentInstance).registry.LatestAvailable(mod.name, manager.CurrentInstance.Version()) != null &&
+ RegistryManager.Instance(manager.CurrentInstance)
+ .registry.LatestAvailable(mod.name, manager.CurrentInstance.Version()) != null &&
!RegistryManager.Instance(manager.CurrentInstance).registry.IsInstalled(mod.name) &&
!toInstall.Contains(mod.name))
{
@@ -192,7 +194,7 @@ private void InstallMods(object sender, DoWorkEventArgs e) // this probably need
m_TabController.ShowTab("WaitTabPage");
m_TabController.SetTabLock(true);
-
+
var downloader = new NetAsyncDownloader(GUI.user);
cancelCallback = () =>
{
@@ -223,45 +225,15 @@ private void InstallMods(object sender, DoWorkEventArgs e) // this probably need
opts.Key);
return;
}
- try
- {
- var ret = InstallList(toInstall, opts.Value, downloader);
- if (!ret)
- {
- // install failed for some reason, error message is already displayed to the user
- e.Result = new KeyValuePair>>(false,
- opts.Key);
- return;
- }
- resolvedAllProvidedMods = true;
- }
- catch (TooManyModsProvideKraken tooManyProvides)
+ var ret = InstallList(toInstall, opts.Value, downloader);
+ if (!ret)
{
- Util.Invoke(this, () => UpdateProvidedModsDialog(tooManyProvides));
-
- m_TabController.ShowTab("ChooseProvidedModsTabPage", 3);
- m_TabController.SetTabLock(true);
-
- lock (this)
- {
- Monitor.Wait(this);
- }
-
- m_TabController.SetTabLock(false);
-
- m_TabController.HideTab("ChooseProvidedModsTabPage");
-
- if (installCanceled)
- {
- m_TabController.HideTab("WaitTabPage");
- m_TabController.ShowTab("ManageModsTabPage");
- e.Result = new KeyValuePair>>(false,
- opts.Key);
- return;
- }
-
- m_TabController.ShowTab("WaitTabPage");
+ // install failed for some reason, error message is already displayed to the user
+ e.Result = new KeyValuePair>>(false,
+ opts.Key);
+ return;
}
+ resolvedAllProvidedMods = true;
}
e.Result = new KeyValuePair>>(true, opts.Key);
@@ -282,7 +254,9 @@ private bool InstallList(HashSet toInstall, RelationshipResolverOptions
}
catch (ModuleNotFoundKraken ex)
{
- GUI.user.RaiseMessage("Module {0} required, but not listed in index, or not available for your version of KSP", ex.module);
+ GUI.user.RaiseMessage(
+ "Module {0} required, but not listed in index, or not available for your version of KSP",
+ ex.module);
return false;
}
catch (BadMetadataKraken ex)
@@ -418,8 +392,10 @@ private void PostInstallMods(object sender, RunWorkerCompletedEventArgs e)
Util.Invoke(menuStrip1, () => menuStrip1.Enabled = true);
}
- private void UpdateProvidedModsDialog(TooManyModsProvideKraken tooManyProvides)
+ private TaskCompletionSource toomany_source;
+ private void UpdateProvidedModsDialog(TooManyModsProvideKraken tooManyProvides, TaskCompletionSource task)
{
+ toomany_source = task;
ChooseProvidedModsLabel.Text =
String.Format(
"Module {0} is provided by more than one available module, please choose one of the following mods:",
@@ -431,41 +407,40 @@ private void UpdateProvidedModsDialog(TooManyModsProvideKraken tooManyProvides)
foreach (CkanModule module in tooManyProvides.modules)
{
- ListViewItem item = new ListViewItem {Tag = module, Checked = true, Text = module.name};
+ ListViewItem item = new ListViewItem {Tag = module, Checked = false, Text = module.name};
- ListViewItem.ListViewSubItem description =
+ ListViewItem.ListViewSubItem description =
new ListViewItem.ListViewSubItem {Text = module.@abstract};
item.SubItems.Add(description);
ChooseProvidedModsListView.Items.Add(item);
}
+ ChooseProvidedModsListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
+ ChooseProvidedModsContinueButton.Enabled = false;
}
+
private void ChooseProvidedModsListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
+ var any_item_selected = ChooseProvidedModsListView.Items.Cast().Any(item => item.Checked);
+ ChooseProvidedModsContinueButton.Enabled = any_item_selected;
if (!e.Item.Checked)
{
return;
}
- foreach (ListViewItem item in ChooseProvidedModsListView.Items)
+ foreach (ListViewItem item in ChooseProvidedModsListView.Items.Cast()
+ .Where(item => item != e.Item && item.Checked))
{
- if (item != e.Item && item.Checked)
- {
- item.Checked = false;
- }
+ item.Checked = false;
}
+
}
private void ChooseProvidedModsCancelButton_Click(object sender, EventArgs e)
{
- installCanceled = true;
-
- lock (this)
- {
- Monitor.Pulse(this);
- }
+ toomany_source.SetResult(null);
}
private void ChooseProvidedModsContinueButton_Click(object sender, EventArgs e)
@@ -474,16 +449,9 @@ private void ChooseProvidedModsContinueButton_Click(object sender, EventArgs e)
{
if (item.Checked)
{
- var identifier = ((CkanModule) item.Tag).identifier;
- toInstall.Add(identifier);
- break;
+ toomany_source.SetResult((CkanModule)item.Tag);
}
}
-
- lock (this)
- {
- Monitor.Pulse(this);
- }
}
private void UpdateRecommendedDialog(Dictionary> mods, bool suggested = false)
@@ -518,7 +486,7 @@ private void UpdateRecommendedDialog(Dictionary> mods, bool
without_enforce_consistency = false,
without_toomanyprovides_kraken = true
};
-
+
var resolver = new RelationshipResolver(new List() {pair.Key}, opts,
RegistryManager.Instance(manager.CurrentInstance).registry, CurrentInstance.Version());
if (!resolver.ModList().Any())
@@ -526,7 +494,8 @@ private void UpdateRecommendedDialog(Dictionary> mods, bool
continue;
}
- module = RegistryManager.Instance(manager.CurrentInstance).registry.LatestAvailable(pair.Key, CurrentInstance.Version());
+ module = RegistryManager.Instance(manager.CurrentInstance)
+ .registry.LatestAvailable(pair.Key, CurrentInstance.Version());
}
catch
{
diff --git a/GUI/MainModList.cs b/GUI/MainModList.cs
index 10ebd26430..180290afbd 100644
--- a/GUI/MainModList.cs
+++ b/GUI/MainModList.cs
@@ -3,6 +3,7 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
+using System.Threading.Tasks;
using System.Windows.Forms;
namespace CKAN
@@ -52,15 +53,17 @@ private void _UpdateFilters()
// Each time a row in DataGridViewRow is changed, DataGridViewRow updates the view. Which is slow.
// To make the filtering process faster, Copy the list of rows. Filter out the hidden and replace t
// rows in DataGridView.
- var rows = new DataGridViewRow[mainModList.FullListOfModRows.Count];
- mainModList.FullListOfModRows.CopyTo(rows, 0);
+
+ var rows = new DataGridViewRow[mainModList.full_list_of_mod_rows.Count];
+ mainModList.full_list_of_mod_rows.CopyTo(rows, 0);
// Try to remember the current scroll position and selected mod
var scroll_col = Math.Max(0, ModList.FirstDisplayedScrollingColumnIndex);
CkanModule selected_mod = null;
if (ModList.CurrentRow != null)
{
- selected_mod = ((GUIMod)ModList.CurrentRow.Tag).ToCkanModule();
+ selected_mod = ((GUIMod) ModList.CurrentRow.Tag).ToCkanModule();
}
+
ModList.Rows.Clear();
foreach (var row in rows)
{
@@ -141,19 +144,20 @@ private void _UpdateModsList(bool repo_updated)
public void MarkModForInstall(string identifier, bool uninstall = false)
{
- Util.Invoke(this, () => _MarkModForInstall(identifier));
+ Util.Invoke(this, () => _MarkModForInstall(identifier, uninstall));
}
- private void _MarkModForInstall(string identifier, bool uninstall = false)
+ private void _MarkModForInstall(string identifier, bool uninstall)
{
- foreach (DataGridViewRow row in ModList.Rows)
+ foreach (DataGridViewRow row in mainModList.full_list_of_mod_rows)
{
var mod = (GUIMod) row.Tag;
if (mod.Identifier == identifier)
{
- mod.IsInstallChecked = true;
+ mod.IsInstallChecked = !uninstall;
//TODO Fix up MarkMod stuff when I commit the GUIConflict
(row.Cells[0] as DataGridViewCheckBoxCell).Value = !uninstall;
+ if (!uninstall) last_mod_to_have_install_toggled.Push(mod);
break;
}
}
@@ -180,16 +184,21 @@ public void _MarkModForUpdate(string identifier)
public class MainModList
{
- internal List FullListOfModRows;
+ internal List full_list_of_mod_rows;
- public MainModList(ModFiltersUpdatedEvent onModFiltersUpdated)
+ public MainModList(ModFiltersUpdatedEvent onModFiltersUpdated, HandleTooManyProvides too_many_provides,
+ IUser user = null)
{
+ this.too_many_provides = too_many_provides;
+ this.user = user ?? new NullUser();
Modules = new ReadOnlyCollection(new List());
- ModFiltersUpdated += onModFiltersUpdated != null ? onModFiltersUpdated : (source) => { };
+ ModFiltersUpdated += onModFiltersUpdated ?? (source => { });
ModFiltersUpdated(this);
}
public delegate void ModFiltersUpdatedEvent(MainModList source);
+ //TODO Move to relationship resolver and have it use this.
+ public delegate Task HandleTooManyProvides(TooManyModsProvideKraken kraken);
public event ModFiltersUpdatedEvent ModFiltersUpdated;
public ReadOnlyCollection Modules { get; set; }
@@ -230,6 +239,9 @@ public string ModAuthorFilter
private GUIModFilter _modFilter = GUIModFilter.Compatible;
private string _modNameFilter = String.Empty;
private string _modAuthorFilter = String.Empty;
+ private IUser user;
+
+ private readonly HandleTooManyProvides too_many_provides;
///
/// This function returns a changeset based on the selections of the user.
@@ -237,15 +249,16 @@ public string ModAuthorFilter
///
///
///
- public static IEnumerable> ComputeChangeSetFromModList(
+
+ public async Task>> ComputeChangeSetFromModList(
Registry registry, HashSet> changeSet, ModuleInstaller installer,
KSPVersion version)
{
var modules_to_install = new HashSet();
var modules_to_remove = new HashSet();
- var options = new RelationshipResolverOptions()
+ var options = new RelationshipResolverOptions
{
- without_toomanyprovides_kraken = true,
+ without_toomanyprovides_kraken = false,
with_recommends = false
};
@@ -268,7 +281,42 @@ public static IEnumerable> ComputeCha
}
}
- //May throw InconsistentKraken
+
+ bool handled_all_to_many_provides = false;
+ while (!handled_all_to_many_provides)
+ {
+ //Can't await in catch clause - doesn't seem to work in mono. Hence this flag
+ TooManyModsProvideKraken kraken = null;
+ try
+ {
+ new RelationshipResolver(modules_to_install.ToList(), options, registry, version);
+ handled_all_to_many_provides = true;
+ continue;
+ }
+ catch (TooManyModsProvideKraken k)
+ {
+ kraken = k;
+ }
+ catch (ModuleNotFoundKraken k)
+ {
+ //We shouldn't need this. However the relationship provider will throw TMPs with incompatible mods.
+ user.RaiseError("Module {0} has not been found. This may be because it is not compatible " +
+ "with the currently installed version of KSP", k.module);
+ return null;
+ }
+ //Shouldn't get here unless there is a kraken.
+ var mod = await too_many_provides(kraken);
+ if (mod != null)
+ {
+ modules_to_install.Add(mod.identifier);
+ }
+ else
+ {
+ //TODO Is could be a new type of Kraken.
+ throw kraken;
+ }
+ }
+
var resolver = new RelationshipResolver(modules_to_install.ToList(), options, registry, version);
changeSet.UnionWith(
resolver.ModList()
@@ -320,7 +368,7 @@ public int CountModsByFilter(GUIModFilter filter)
public IEnumerable ConstructModList(IEnumerable modules)
{
- FullListOfModRows = new List();
+ full_list_of_mod_rows = new List();
foreach (var mod in modules)
{
var item = new DataGridViewRow {Tag = mod};
@@ -330,7 +378,7 @@ public IEnumerable ConstructModList(IEnumerable modules
: new DataGridViewTextBoxCell();
installed_cell.Value = mod.IsInstallable()
- ? (object)mod.IsInstalled
+ ? (object) mod.IsInstalled
: (mod.IsAutodetected ? "AD" : "-");
var update_cell = mod.HasUpdate && !mod.IsAutodetected
@@ -356,9 +404,9 @@ public IEnumerable ConstructModList(IEnumerable modules
installed_cell.ReadOnly = !mod.IsInstallable();
update_cell.ReadOnly = !mod.IsInstallable() || !mod.HasUpdate;
- FullListOfModRows.Add(item);
+ full_list_of_mod_rows.Add(item);
}
- return FullListOfModRows;
+ return full_list_of_mod_rows;
}
private bool IsNameInNameFilter(GUIMod mod)
diff --git a/Tests/Core/Relationships/RelationshipResolver.cs b/Tests/Core/Relationships/RelationshipResolver.cs
index b2a84ab4c8..82553ceb7c 100644
--- a/Tests/Core/Relationships/RelationshipResolver.cs
+++ b/Tests/Core/Relationships/RelationshipResolver.cs
@@ -8,8 +8,7 @@
namespace Tests.Core.Relationships
{
- [TestFixture]
- public class RelationshipResolverTests
+ [TestFixture] public class RelationshipResolverTests
{
private CKAN.Registry registry;
private RelationshipResolverOptions options;
@@ -43,13 +42,13 @@ public void Constructor_WithConflictingModules()
var mod_a = generator.GeneratorRandomModule();
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier}
+ new RelationshipDescriptor {name = mod_a.identifier}
});
list.Add(mod_a.identifier);
list.Add(mod_b.identifier);
AddToRegistry(mod_a, mod_b);
-
+
Assert.Throws(() => new RelationshipResolver(
list,
options,
@@ -65,16 +64,14 @@ public void Constructor_WithConflictingModules()
Assert.That(resolver.ConflictList, Has.Count.EqualTo(2));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
public void Constructor_WithConflictingModulesVersion_Throws()
{
var list = new List();
var mod_a = generator.GeneratorRandomModule();
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, version=mod_a.version.ToString()}
+ new RelationshipDescriptor {name = mod_a.identifier, version = mod_a.version.ToString()}
});
list.Add(mod_a.identifier);
@@ -88,18 +85,16 @@ public void Constructor_WithConflictingModulesVersion_Throws()
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "0.5")]
- [TestCase("1.0", "1.0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "0.5"),
+ TestCase("1.0", "1.0")]
public void Constructor_WithConflictingModulesVersionMin_Throws(string ver, string conf_min)
{
var list = new List();
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, min_version=conf_min}
+ new RelationshipDescriptor {name = mod_a.identifier, min_version = conf_min}
});
list.Add(mod_a.identifier);
@@ -113,18 +108,16 @@ public void Constructor_WithConflictingModulesVersionMin_Throws(string ver, stri
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "2.0")]
- [TestCase("1.0", "1.0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "2.0"),
+ TestCase("1.0", "1.0")]
public void Constructor_WithConflictingModulesVersionMax_Throws(string ver, string conf_max)
{
var list = new List();
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, max_version=conf_max}
+ new RelationshipDescriptor {name = mod_a.identifier, max_version = conf_max}
});
list.Add(mod_a.identifier);
@@ -138,19 +131,17 @@ public void Constructor_WithConflictingModulesVersionMax_Throws(string ver, stri
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "0.5", "2.0")]
- [TestCase("1.0", "1.0", "2.0")]
- [TestCase("1.0", "0.5", "1.0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "0.5", "2.0"),
+ TestCase("1.0", "1.0", "2.0"),
+ TestCase("1.0", "0.5", "1.0")]
public void Constructor_WithConflictingModulesVersionMinMax_Throws(string ver, string conf_min, string conf_max)
{
var list = new List();
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, min_version=conf_min, max_version=conf_max}
+ new RelationshipDescriptor {name = mod_a.identifier, min_version = conf_min, max_version = conf_max}
});
list.Add(mod_a.identifier);
@@ -164,18 +155,16 @@ public void Constructor_WithConflictingModulesVersionMinMax_Throws(string ver, s
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "0.5")]
- [TestCase("1.0", "2.0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "0.5"),
+ TestCase("1.0", "2.0")]
public void Constructor_WithNonConflictingModulesVersion_DoesNotThrows(string ver, string conf)
{
var list = new List();
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, version=conf}
+ new RelationshipDescriptor {name = mod_a.identifier, version = conf}
});
list.Add(mod_a.identifier);
@@ -189,9 +178,7 @@ public void Constructor_WithNonConflictingModulesVersion_DoesNotThrows(string ve
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
[TestCase("1.0", "2.0")]
public void Constructor_WithConflictingModulesVersionMin_DoesNotThrows(string ver, string conf_min)
{
@@ -199,7 +186,7 @@ public void Constructor_WithConflictingModulesVersionMin_DoesNotThrows(string ve
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, min_version="2.0"}
+ new RelationshipDescriptor {name = mod_a.identifier, min_version = "2.0"}
});
list.Add(mod_a.identifier);
@@ -213,9 +200,7 @@ public void Constructor_WithConflictingModulesVersionMin_DoesNotThrows(string ve
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
[TestCase("1.0", "2.0")]
public void Constructor_WithConflictingModulesVersionMax_DoesNotThrows(string ver, string conf_max)
{
@@ -223,7 +208,7 @@ public void Constructor_WithConflictingModulesVersionMax_DoesNotThrows(string ve
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, max_version=conf_max}
+ new RelationshipDescriptor {name = mod_a.identifier, max_version = conf_max}
});
list.Add(mod_a.identifier);
@@ -237,18 +222,17 @@ public void Constructor_WithConflictingModulesVersionMax_DoesNotThrows(string ve
null));
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "2.0", "3.0")]
- [TestCase("4.0", "2.0", "3.0")]
- public void Constructor_WithConflictingModulesVersionMinMax_DoesNotThrows(string ver, string conf_min, string conf_max)
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "2.0", "3.0"),
+ TestCase("4.0", "2.0", "3.0")]
+ public void Constructor_WithConflictingModulesVersionMinMax_DoesNotThrows(string ver, string conf_min,
+ string conf_max)
{
var list = new List();
var mod_a = generator.GeneratorRandomModule(version: new Version(ver));
var mod_b = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=mod_a.identifier, min_version=conf_min, max_version=conf_max}
+ new RelationshipDescriptor {name = mod_a.identifier, min_version = conf_min, max_version = conf_max}
});
list.Add(mod_a.identifier);
@@ -279,7 +263,7 @@ public void Constructor_WithMultipleModulesProviding_Throws()
});
var mod_d = generator.GeneratorRandomModule(depends: new List
{
- new RelationshipDescriptor {name=mod_a.identifier}
+ new RelationshipDescriptor {name = mod_a.identifier}
});
list.Add(mod_d.identifier);
@@ -289,7 +273,32 @@ public void Constructor_WithMultipleModulesProviding_Throws()
options,
registry,
null));
+ }
+ [Test]
+ public void WithMultipleModulesProviding_AllButOneHasUnmatchDependancy_DoesNotThrow()
+ {
+ options.without_toomanyprovides_kraken = false;
+
+ var list = new List();
+ var mod_a = generator.GeneratorRandomModule();
+ var mod_b = generator.GeneratorRandomModule(provides: new List
+ {
+ mod_a.identifier
+ });
+ var mod_c = generator.GeneratorRandomModule(provides: new List
+ {
+ mod_a.identifier
+ }, depends: new List {new RelationshipDescriptor {name = "invaild"}});
+ var mod_d = generator.GeneratorRandomModule(depends: new List
+ {
+ new RelationshipDescriptor {name = mod_a.identifier}
+ });
+
+ list.Add(mod_d.identifier);
+ AddToRegistry(mod_b, mod_c, mod_d);
+ var mod_list = new RelationshipResolver(list,options,registry,null).ModList();
+ Assert.That(mod_list,Contains.Item(mod_b));
}
[Test]
@@ -304,7 +313,6 @@ public void Constructor_WithMissingModules_Throws()
options,
registry,
null));
-
}
// Right now our RR always returns the modules it was provided. However
@@ -312,7 +320,7 @@ public void Constructor_WithMissingModules_Throws()
// return a list *without* them. This isn't a hard error at the moment,
// since ModuleInstaller.InstallList will ignore already installed mods, but
// it would be nice to have. Discussed a little in GH #521.
- [Test][Category("TODO")][Explicit]
+ [Test, Category("TODO"), Explicit]
public void ModList_WithInstalledModules_DoesNotContainThem()
{
var list = new List();
@@ -378,7 +386,7 @@ public void Constructor_WithConflictingModulesInDependancies_ThrowUnderDefaultSe
});
var conflicts_with_dependant = generator.GeneratorRandomModule(conflicts: new List
{
- new RelationshipDescriptor {name=dependant.identifier}
+ new RelationshipDescriptor {name = dependant.identifier}
});
@@ -460,7 +468,6 @@ public void Constructor_ProvidesSatisfyDependencies()
mod_b,
depender
});
-
}
@@ -481,16 +488,13 @@ public void Constructor_WithMissingDependants_Throws()
options,
registry,
null));
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "2.0")]
- [TestCase("1.0", "0.2")]
- [TestCase("0", "0.2")]
- [TestCase("1.0", "0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "2.0"),
+ TestCase("1.0", "0.2"),
+ TestCase("0", "0.2"),
+ TestCase("1.0", "0")]
public void Constructor_WithMissingDependantsVersion_Throws(string ver, string dep)
{
var list = new List();
@@ -508,12 +512,9 @@ public void Constructor_WithMissingDependantsVersion_Throws(string ver, string d
options,
registry,
null));
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
[TestCase("1.0", "2.0")]
public void Constructor_WithMissingDependantsVersionMin_Throws(string ver, string dep_min)
{
@@ -532,12 +533,9 @@ public void Constructor_WithMissingDependantsVersionMin_Throws(string ver, strin
options,
registry,
null));
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
[TestCase("1.0", "0.5")]
public void Constructor_WithMissingDependantsVersionMax_Throws(string ver, string dep_max)
{
@@ -556,14 +554,11 @@ public void Constructor_WithMissingDependantsVersionMax_Throws(string ver, strin
options,
registry,
null));
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "2.0", "3.0")]
- [TestCase("4.0", "2.0", "3.0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "2.0", "3.0"),
+ TestCase("4.0", "2.0", "3.0")]
public void Constructor_WithMissingDependantsVersionMinMax_Throws(string ver, string dep_min, string dep_max)
{
var list = new List();
@@ -581,19 +576,18 @@ public void Constructor_WithMissingDependantsVersionMinMax_Throws(string ver, st
options,
registry,
null));
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("1.0", "1.0", "2.0")]
- [TestCase("1.0", "1.0", "0.5")]//what to do if a mod is present twice with the same version ?
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("1.0", "1.0", "2.0"),
+ TestCase("1.0", "1.0", "0.5")]
+ //what to do if a mod is present twice with the same version ?
public void Constructor_WithDependantVersion_ChooseCorrectly(string ver, string dep, string other)
{
var list = new List();
var dependant = generator.GeneratorRandomModule(version: new Version(ver));
- var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier, version: new Version(other));
+ var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier,
+ version: new Version(other));
var depender = generator.GeneratorRandomModule(depends: new List
{
@@ -611,20 +605,18 @@ public void Constructor_WithDependantVersion_ChooseCorrectly(string ver, string
dependant,
depender
});
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("2.0", "1.0", "0.5")]
- [TestCase("2.0", "1.0", "1.5")]
- [TestCase("2.0", "2.0", "0.5")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("2.0", "1.0", "0.5"),
+ TestCase("2.0", "1.0", "1.5"),
+ TestCase("2.0", "2.0", "0.5")]
public void Constructor_WithDependantVersionMin_ChooseCorrectly(string ver, string dep_min, string other)
{
var list = new List();
var dependant = generator.GeneratorRandomModule(version: new Version(ver));
- var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier, version: new Version(other));
+ var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier,
+ version: new Version(other));
var depender = generator.GeneratorRandomModule(depends: new List
{
@@ -641,20 +633,18 @@ public void Constructor_WithDependantVersionMin_ChooseCorrectly(string ver, stri
dependant,
depender
});
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("2.0", "2.0", "0.5")]
- [TestCase("2.0", "3.0", "0.5")]
- [TestCase("2.0", "3.0", "4.0")]
+ [Test, Category("Version"), Explicit("Versions relationships not implemented")]
+ [TestCase("2.0", "2.0", "0.5"),
+ TestCase("2.0", "3.0", "0.5"),
+ TestCase("2.0", "3.0", "4.0")]
public void Constructor_WithDependantVersionMax_ChooseCorrectly(string ver, string dep_max, string other)
{
var list = new List();
var dependant = generator.GeneratorRandomModule(version: new Version(ver));
- var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier, version: new Version(other));
+ var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier,
+ version: new Version(other));
var depender = generator.GeneratorRandomModule(depends: new List
{
@@ -671,20 +661,19 @@ public void Constructor_WithDependantVersionMax_ChooseCorrectly(string ver, stri
dependant,
depender
});
-
}
- [Test]
- [Category("Version")]
- [Explicit("Versions relationships not implemented")]
- [TestCase("2.0", "1.0", "3.0", "0.5")]
- [TestCase("2.0", "1.0", "3.0", "1.5")]
- [TestCase("2.0", "1.0", "3.0", "3.5")]
- public void Constructor_WithDependantVersionMinMax_ChooseCorrectly(string ver, string dep_min, string dep_max, string other)
+ [Test, Category("Version"), Explicit("Versions relationships not implemented"),
+ TestCase("2.0", "1.0", "3.0", "0.5"),
+ TestCase("2.0", "1.0", "3.0", "1.5"),
+ TestCase("2.0", "1.0", "3.0", "3.5")]
+ public void Constructor_WithDependantVersionMinMax_ChooseCorrectly(string ver, string dep_min, string dep_max,
+ string other)
{
var list = new List();
var dependant = generator.GeneratorRandomModule(version: new Version(ver));
- var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier, version: new Version(other));
+ var other_dependant = generator.GeneratorRandomModule(identifier: dependant.identifier,
+ version: new Version(other));
var depender = generator.GeneratorRandomModule(depends: new List
{
@@ -701,7 +690,6 @@ public void Constructor_WithDependantVersionMinMax_ChooseCorrectly(string ver, s
dependant,
depender
});
-
}
[Test]
@@ -733,23 +721,22 @@ public void ReasonFor_WithModsNotInList_ThrowsArgumentException()
var relationship_resolver = new RelationshipResolver(list, options, registry, null);
var mod_not_in_resolver_list = generator.GeneratorRandomModule();
- CollectionAssert.DoesNotContain(relationship_resolver.ModList(),mod_not_in_resolver_list);
+ CollectionAssert.DoesNotContain(relationship_resolver.ModList(), mod_not_in_resolver_list);
Assert.Throws(() => relationship_resolver.ReasonFor(mod_not_in_resolver_list));
-
}
[Test]
public void ReasonFor_WithUserAddedMods_GivesReasonUserAdded()
{
var list = new List();
- var mod = generator.GeneratorRandomModule();
+ var mod = generator.GeneratorRandomModule();
list.Add(mod.identifier);
registry.AddAvailable(mod);
AddToRegistry(mod);
var relationship_resolver = new RelationshipResolver(list, options, registry, null);
var reason = relationship_resolver.ReasonFor(mod);
- Assert.That(reason,Is.AssignableTo());
+ Assert.That(reason, Is.AssignableTo());
}
[Test]
@@ -757,31 +744,37 @@ public void ReasonFor_WithSugestedMods_GivesCorrectParent()
{
var list = new List();
var sugested = generator.GeneratorRandomModule();
- var mod = generator.GeneratorRandomModule(sugests: new List {new RelationshipDescriptor { name = sugested.identifier } } );
- list.Add(mod.identifier);
- AddToRegistry(mod,sugested);
+ var mod =
+ generator.GeneratorRandomModule(sugests:
+ new List {new RelationshipDescriptor {name = sugested.identifier}});
+ list.Add(mod.identifier);
+ AddToRegistry(mod, sugested);
options.with_all_suggests = true;
var relationship_resolver = new RelationshipResolver(list, options, registry, null);
var reason = relationship_resolver.ReasonFor(sugested);
Assert.That(reason, Is.AssignableTo());
- Assert.That(reason.Parent,Is.EqualTo(mod));
+ Assert.That(reason.Parent, Is.EqualTo(mod));
}
[Test]
public void ReasonFor_WithTreeOfMods_GivesCorrectParents()
{
- var list = new List();
+ var list = new List();
var sugested = generator.GeneratorRandomModule();
var recommendedA = generator.GeneratorRandomModule();
var recommendedB = generator.GeneratorRandomModule();
- var mod = generator.GeneratorRandomModule(sugests: new List { new RelationshipDescriptor { name = sugested.identifier}});
+ var mod =
+ generator.GeneratorRandomModule(sugests:
+ new List {new RelationshipDescriptor {name = sugested.identifier}});
list.Add(mod.identifier);
sugested.recommends = new List
- { new RelationshipDescriptor {name=recommendedA.identifier},
- new RelationshipDescriptor { name = recommendedB.identifier}};
+ {
+ new RelationshipDescriptor {name = recommendedA.identifier},
+ new RelationshipDescriptor {name = recommendedB.identifier}
+ };
- AddToRegistry(mod, sugested,recommendedA,recommendedB);
+ AddToRegistry(mod, sugested, recommendedA, recommendedB);
options.with_all_suggests = true;
@@ -797,11 +790,6 @@ public void ReasonFor_WithTreeOfMods_GivesCorrectParents()
}
-
-
-
-
-
private void AddToRegistry(params CkanModule[] modules)
{
foreach (var module in modules)
@@ -810,4 +798,4 @@ private void AddToRegistry(params CkanModule[] modules)
}
}
}
-}
+}
\ No newline at end of file
diff --git a/Tests/GUI/MainModList.cs b/Tests/GUI/MainModList.cs
index 0521d46b85..3b816e4abb 100644
--- a/Tests/GUI/MainModList.cs
+++ b/Tests/GUI/MainModList.cs
@@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
using CKAN;
using NUnit.Framework;
using Tests.Core;
using Tests.Data;
+using ModuleInstaller = CKAN.ModuleInstaller;
namespace Tests.GUI
{
@@ -14,7 +18,7 @@ public class MainModListTests
[Test]
public void OnCreation_HasDefaultFilters()
{
- var item = new MainModList(delegate { });
+ var item = new MainModList(delegate { }, delegate { return null; });
Assert.AreEqual(GUIModFilter.Compatible, item.ModFilter, "ModFilter");
Assert.AreEqual(String.Empty, item.ModNameFilter, "ModNameFilter");
}
@@ -23,7 +27,7 @@ public void OnCreation_HasDefaultFilters()
public void OnModTextFilterChanges_CallsEventHandler()
{
var called_n = 0;
- var item = new MainModList(delegate { called_n++; });
+ var item = new MainModList(delegate { called_n++; }, delegate { return null; });
Assert.That(called_n == 1);
item.ModNameFilter = "randomString";
Assert.That(called_n == 2);
@@ -34,7 +38,7 @@ public void OnModTextFilterChanges_CallsEventHandler()
public void OnModTypeFilterChanges_CallsEventHandler()
{
var called_n = 0;
- var item = new MainModList(delegate { called_n++; });
+ var item = new MainModList(delegate { called_n++; }, delegate { return null; });
Assert.That(called_n == 1);
item.ModFilter = GUIModFilter.Installed;
Assert.That(called_n == 2);
@@ -45,16 +49,17 @@ public void OnModTypeFilterChanges_CallsEventHandler()
[Test]
public void ComputeChangeSetFromModList_WithEmptyList_HasEmptyChangeSet()
{
- using (new DisposableKSP())
- {
- var item = new MainModList(delegate { });
+ using (var tidy = new DisposableKSP())
+ {
+ KSPManager manager = new KSPManager(new NullUser(), new FakeWin32Registry(tidy.KSP)) { CurrentInstance = tidy.KSP };
+ var item = new MainModList(delegate { }, delegate { return null; });
Assert.That(item.ComputeUserChangeSet(), Is.Empty);
}
}
[Test]
[Category("Display")]
- public void ComputeChangeSetFromModList_WithConflictingMods_ThrowsInconsistentKraken()
+ public async Task ComputeChangeSetFromModList_WithConflictingMods_ThrowsInconsistentKraken()
{
using (var tidy = new DisposableKSP())
{
@@ -62,17 +67,19 @@ public void ComputeChangeSetFromModList_WithConflictingMods_ThrowsInconsistentKr
var registry = Registry.Empty();
var module = TestData.FireSpitterModule();
- module.conflicts = new List { new RelationshipDescriptor { name = "kOS" }};
+ module.conflicts = new List() { new RelationshipDescriptor { name = "kOS" } };
registry.AddAvailable(TestData.FireSpitterModule());
registry.AddAvailable(TestData.kOS_014_module());
- registry.RegisterModule(module,Enumerable.Empty(), tidy.KSP );
+ registry.RegisterModule(module, Enumerable.Empty(), tidy.KSP);
- var main_mod_list = new MainModList(null);
+ var main_mod_list = new MainModList(null, null);
var mod = new GUIMod(TestData.FireSpitterModule(), registry, manager.CurrentInstance.Version());
var mod2 = new GUIMod(TestData.kOS_014_module(), registry, manager.CurrentInstance.Version());
mod.IsInstallChecked = true;
mod2.IsInstallChecked = true;
- Assert.Throws(()=>MainModList.ComputeChangeSetFromModList(registry,main_mod_list.ComputeUserChangeSet(),null, tidy.KSP.Version()));
+
+ var compute_change_set_from_mod_list = main_mod_list.ComputeChangeSetFromModList(registry, main_mod_list.ComputeUserChangeSet(), null, tidy.KSP.Version());
+ await UtilStatic.Throws(async ()=> { await compute_change_set_from_mod_list; });
}
}
@@ -86,7 +93,7 @@ public void IsVisible_WithAllAndNoNameFilter_ReturnsTrueForCompatible()
var ckan_mod = TestData.FireSpitterModule();
var registry = Registry.Empty();
registry.AddAvailable(ckan_mod);
- var item = new MainModList(delegate { });
+ var item = new MainModList(delegate { }, null);
Assert.That(item.IsVisible(new GUIMod(ckan_mod, registry, manager.CurrentInstance.Version())));
}
}
@@ -94,14 +101,14 @@ public void IsVisible_WithAllAndNoNameFilter_ReturnsTrueForCompatible()
[Test]
public void CountModsByFilter_EmptyModList_ReturnsZeroForAllFilters()
{
- var item = new MainModList(delegate { });
+ var item = new MainModList(delegate { }, null);
foreach (GUIModFilter filter in Enum.GetValues(typeof(GUIModFilter)))
{
Assert.That(item.CountModsByFilter(filter), Is.EqualTo(0));
}
}
-
+
[Test]
[Category("Display")]
public void ConstructModList_NumberOfRows_IsEqualToNumberOfMods()
@@ -112,7 +119,7 @@ public void ConstructModList_NumberOfRows_IsEqualToNumberOfMods()
var registry = Registry.Empty();
registry.AddAvailable(TestData.FireSpitterModule());
registry.AddAvailable(TestData.kOS_014_module());
- var main_mod_list = new MainModList(null);
+ var main_mod_list = new MainModList(null, null);
var mod_list = main_mod_list.ConstructModList(new List
{
new GUIMod(TestData.FireSpitterModule(), registry, manager.CurrentInstance.Version()),
@@ -120,6 +127,42 @@ public void ConstructModList_NumberOfRows_IsEqualToNumberOfMods()
});
Assert.That(mod_list, Has.Count.EqualTo(2));
}
- }
+ }
+
+ [Test]
+ [Category("Display")]
+ public async Task TooManyProvidesCallsHandlers()
+ {
+ using (var tidy = new DisposableKSP())
+ {
+ var registry = Registry.Empty();
+ var generator = new RandomModuleGenerator(new Random(0451));
+ var provide_ident = "provide";
+ var mod = generator.GeneratorRandomModule(depends: new List()
+ {
+ new RelationshipDescriptor() {name = provide_ident}
+ });
+ var moda = generator.GeneratorRandomModule(provides: new List { provide_ident });
+ var modb = generator.GeneratorRandomModule(provides: new List { provide_ident });
+ var choice_of_provide = modb;
+ registry.AddAvailable(mod);
+ registry.AddAvailable(moda);
+ registry.AddAvailable(modb);
+ var installer = ModuleInstaller.GetInstance(tidy.KSP, null);
+ var main_mod_list = new MainModList(null, async kraken => choice_of_provide);
+ var a = new HashSet>()
+ {
+ new KeyValuePair(mod,GUIModChangeType.Install)
+ };
+
+ var mod_list = await main_mod_list.ComputeChangeSetFromModList(registry, a, installer, null);
+ CollectionAssert.AreEquivalent(
+ new[] {
+ new KeyValuePair(mod,GUIModChangeType.Install),
+ new KeyValuePair(modb,GUIModChangeType.Install)}, mod_list);
+
+ }
+ }
+
}
}
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 11ad62b3c4..22312b79b1 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -7,8 +7,9 @@
Library
false
CKAN.Tests
- v4.0
+ v4.5
512
+
true
@@ -18,6 +19,7 @@
DEBUG;TRACE
prompt
4
+ false
pdbonly
@@ -26,6 +28,7 @@
TRACE
prompt
4
+ false
Tests
@@ -134,6 +137,7 @@
+
@@ -165,4 +169,4 @@
-
+
\ No newline at end of file
diff --git a/Tests/Util.cs b/Tests/Util.cs
new file mode 100644
index 0000000000..8b1870ee0b
--- /dev/null
+++ b/Tests/Util.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using NUnit.Framework;
+
+namespace Tests
+{
+ [TestFixture] public class Util
+ {
+ [Test]
+ public void AssembliesHaveNoAsyncVoids()
+ {
+ UtilStatic.AssertNoAsyncVoidMethods(GetType().Assembly);
+ }
+ }
+
+ public static class UtilStatic
+ {
+ private static bool HasAttribute(this MethodInfo method) where TAttribute : Attribute
+ {
+ return method.GetCustomAttributes(typeof (TAttribute), false).Any();
+ }
+
+ public static void AssertNoAsyncVoidMethods(Assembly assembly)
+ {
+ var messages = assembly
+ .GetAsyncVoidMethods()
+ .Select(method =>
+ string.Format("'{0}.{1}' is an async void method.",
+ method.DeclaringType.Name,
+ method.Name))
+ .ToList();
+ Assert.False(messages.Any(),
+ "Async void methods found!" + Environment.NewLine + String.Join(Environment.NewLine, messages));
+ }
+
+ private static IEnumerable GetAsyncVoidMethods(this Assembly assembly)
+ {
+ return assembly.GetLoadableTypes()
+ .SelectMany(type => type.GetMethods(
+ BindingFlags.NonPublic
+ | BindingFlags.Public
+ | BindingFlags.Instance
+ | BindingFlags.Static
+ | BindingFlags.DeclaredOnly))
+ .Where(method => method.HasAttribute())
+ .Where(method => method.ReturnType == typeof (void));
+ }
+
+ private static IEnumerable GetLoadableTypes(this Assembly assembly)
+ {
+ if (assembly == null) throw new ArgumentNullException("assembly");
+ try
+ {
+ return assembly.GetTypes();
+ }
+ catch (ReflectionTypeLoadException e)
+ {
+ return e.Types.Where(t => t != null);
+ }
+ }
+
+ public static async Task Throws(Func async) where T : Exception
+ {
+ try
+ {
+ await async();
+ Assert.Fail("Expected exception of type: {0}", typeof (T));
+ }
+ catch (T)
+ {
+ return;
+ }
+ }
+ }
+}
\ No newline at end of file