1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-09 14:34:05 +02:00

Add a TwoKeyDictionary collection with unit tests

This commit is contained in:
chylex 2017-03-07 17:45:13 +01:00
parent 7cadb1c403
commit 1e4f673f9e
4 changed files with 303 additions and 0 deletions

View File

@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.Linq;
namespace TweetDck.Core.Utils{
class TwoKeyDictionary<K1, K2, V>{
private readonly Dictionary<K1, Dictionary<K2, V>> dict;
private readonly int innerCapacity;
public TwoKeyDictionary() : this(16, 16){}
public TwoKeyDictionary(int outerCapacity, int innerCapacity){
this.dict = new Dictionary<K1, Dictionary<K2, V>>(outerCapacity);
this.innerCapacity = innerCapacity;
}
// Properties
public V this[K1 outerKey, K2 innerKey]{
get{ // throws on missing key
return dict[outerKey][innerKey];
}
set{
Dictionary<K2, V> innerDict;
if (!dict.TryGetValue(outerKey, out innerDict)){
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
}
innerDict[innerKey] = value;
}
}
// Members
public void Add(K1 outerKey, K2 innerKey, V value){ // throws on duplicate
Dictionary<K2, V> innerDict;
if (!dict.TryGetValue(outerKey, out innerDict)){
dict.Add(outerKey, innerDict = new Dictionary<K2, V>(innerCapacity));
}
innerDict.Add(innerKey, value);
}
public void Clear(){
this.dict.Clear();
}
public void Clear(K1 outerKey){ // throws on missing key, but keeps the key unlike Remove(K1)
dict[outerKey].Clear();
}
public bool Contains(K1 outerKey){
return dict.ContainsKey(outerKey);
}
public bool Contains(K1 outerKey, K2 innerKey){
Dictionary<K2, V> innerDict;
return dict.TryGetValue(outerKey, out innerDict) && innerDict.ContainsKey(innerKey);
}
public int Count(){
return dict.Values.Sum(d => d.Count);
}
public int Count(K1 outerKey){ // throws on missing key
return dict[outerKey].Count;
}
public bool Remove(K1 outerKey){
return dict.Remove(outerKey);
}
public bool Remove(K1 outerKey, K2 innerKey){
Dictionary<K2, V> innerDict;
if (dict.TryGetValue(outerKey, out innerDict) && innerDict.Remove(innerKey)){
if (innerDict.Count == 0){
dict.Remove(outerKey);
}
return true;
}
else return false;
}
public bool TryGetValue(K1 outerKey, K2 innerKey, out V value){
Dictionary<K2, V> innerDict;
if (dict.TryGetValue(outerKey, out innerDict)){
return innerDict.TryGetValue(innerKey, out value);
}
else{
value = default(V);
return false;
}
}
}
}

View File

@ -192,6 +192,7 @@
</Compile>
<Compile Include="Core\Notification\NotificationFlags.cs" />
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
<Compile Include="Core\Utils\TwoKeyDictionary.cs" />
<Compile Include="Core\Utils\WindowState.cs" />
<Compile Include="Core\Utils\WindowsUtils.cs" />
<Compile Include="Core\Bridge\TweetDeckBridge.cs" />

View File

@ -0,0 +1,201 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TweetDck.Core.Utils;
using System.Collections.Generic;
namespace UnitTests.Core.Utils{
[TestClass]
public class TestTwoKeyDictionary{
private static TwoKeyDictionary<string, int, string> CreateDict(){
TwoKeyDictionary<string, int, string> dict = new TwoKeyDictionary<string, int, string>();
dict.Add("aaa", 0, "x");
dict.Add("aaa", 1, "y");
dict.Add("aaa", 2, "z");
dict.Add("bbb", 0, "test 1");
dict.Add("bbb", 10, "test 2");
dict.Add("bbb", 20, "test 3");
dict.Add("bbb", 30, "test 4");
dict.Add("ccc", -5, "");
dict.Add("", 0, "");
return dict;
}
[TestMethod]
public void TestAdd(){
CreateDict();
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void TestAddDuplicate(){
var dict = new TwoKeyDictionary<string, int, string>();
dict.Add("aaa", 0, "test");
dict.Add("aaa", 0, "oops");
}
[TestMethod]
public void TestAccessor(){
var dict = CreateDict();
// get
Assert.AreEqual("x", dict["aaa", 0]);
Assert.AreEqual("y", dict["aaa", 1]);
Assert.AreEqual("z", dict["aaa", 2]);
Assert.AreEqual("test 3", dict["bbb", 20]);
Assert.AreEqual("", dict["ccc", -5]);
Assert.AreEqual("", dict["", 0]);
// set
dict["aaa", 0] = "replaced entry";
Assert.AreEqual("replaced entry", dict["aaa", 0]);
dict["aaa", 3] = "new entry";
Assert.AreEqual("new entry", dict["aaa", 3]);
dict["xxxxx", 150] = "new key and entry";
Assert.AreEqual("new key and entry", dict["xxxxx", 150]);
}
[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void TestAccessorMissingKey1(){
var _ = CreateDict()["missing", 0];
}
[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void TestAccessorMissingKey2(){
var _ = CreateDict()["aaa", 3];
}
[TestMethod]
public void TestClear(){
var dict = CreateDict();
Assert.IsTrue(dict.Contains("bbb"));
dict.Clear("bbb");
Assert.IsTrue(dict.Contains("bbb"));
Assert.IsTrue(dict.Contains(""));
dict.Clear("");
Assert.IsTrue(dict.Contains(""));
Assert.IsTrue(dict.Contains("aaa"));
Assert.IsTrue(dict.Contains("ccc"));
dict.Clear();
Assert.IsFalse(dict.Contains("aaa"));
Assert.IsFalse(dict.Contains("ccc"));
}
[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void TestClearMissingKey(){
CreateDict().Clear("missing");
}
[TestMethod]
public void TestContains(){
var dict = CreateDict();
// positive
Assert.IsTrue(dict.Contains("aaa"));
Assert.IsTrue(dict.Contains("aaa", 0));
Assert.IsTrue(dict.Contains("aaa", 1));
Assert.IsTrue(dict.Contains("aaa", 2));
Assert.IsTrue(dict.Contains("ccc"));
Assert.IsTrue(dict.Contains("ccc", -5));
Assert.IsTrue(dict.Contains(""));
Assert.IsTrue(dict.Contains("", 0));
// negative
Assert.IsFalse(dict.Contains("missing"));
Assert.IsFalse(dict.Contains("missing", 999));
Assert.IsFalse(dict.Contains("aaa", 3));
Assert.IsFalse(dict.Contains("", -1));
}
[TestMethod]
public void TestCount(){
var dict = CreateDict();
Assert.AreEqual(9, dict.Count());
Assert.AreEqual(3, dict.Count("aaa"));
Assert.AreEqual(4, dict.Count("bbb"));
Assert.AreEqual(1, dict.Count("ccc"));
Assert.AreEqual(1, dict.Count(""));
}
[TestMethod]
[ExpectedException(typeof(KeyNotFoundException))]
public void TestCountMissingKey(){
CreateDict().Count("missing");
}
[TestMethod]
public void TestRemove(){
var dict = CreateDict();
// negative
Assert.IsFalse(dict.Remove("missing"));
Assert.IsFalse(dict.Remove("aaa", 3));
// positive
Assert.IsTrue(dict.Contains("aaa"));
Assert.IsTrue(dict.Remove("aaa"));
Assert.IsFalse(dict.Contains("aaa"));
Assert.IsTrue(dict.Contains("bbb", 10));
Assert.IsTrue(dict.Remove("bbb", 10));
Assert.IsFalse(dict.Contains("bbb", 10));
Assert.IsTrue(dict.Contains("bbb"));
Assert.IsTrue(dict.Contains("bbb", 20));
Assert.IsTrue(dict.Remove("bbb", 0));
Assert.IsTrue(dict.Remove("bbb", 20));
Assert.IsTrue(dict.Remove("bbb", 30));
Assert.IsFalse(dict.Contains("bbb"));
Assert.IsTrue(dict.Contains(""));
Assert.IsTrue(dict.Remove("", 0));
Assert.IsFalse(dict.Contains(""));
}
[TestMethod]
public void TestTryGetValue(){
var dict = CreateDict();
string val;
// positive
Assert.IsTrue(dict.TryGetValue("bbb", 10, out val));
Assert.AreEqual("test 2", val);
Assert.IsTrue(dict.TryGetValue("ccc", -5, out val));
Assert.AreEqual("", val);
Assert.IsTrue(dict.TryGetValue("", 0, out val));
Assert.AreEqual("", val);
// negative
Assert.IsFalse(dict.TryGetValue("ccc", -50, out val));
Assert.IsFalse(dict.TryGetValue("", 1, out val));
Assert.IsFalse(dict.TryGetValue("missing", 0, out val));
}
}
}

View File

@ -51,6 +51,7 @@
<Compile Include="Core\Utils\TestBrowserUtils.cs" />
<Compile Include="Core\Utils\TestCommandLineArgs.cs" />
<Compile Include="Core\Utils\TestCommandLineArgsParser.cs" />
<Compile Include="Core\Utils\TestTwoKeyDictionary.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestUtils.cs" />
</ItemGroup>