 tests/Makefile.am         |    6 +-
 tests/testarraylist.vala  |    6 +-
 tests/testcollection.vala |  520 +++++++++++++++++++++++++++++++++++++++++++--
 tests/testvalues.vala     |   70 ++++++
 4 files changed, 579 insertions(+), 23 deletions(-)

diff --git a/tests/Makefile.am b/tests/Makefile.am
index e7a82e0..6c7666c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -12,8 +12,10 @@ noinst_PROGRAMS = $(TEST_PROGS)
 progs_ldadd = $(GLIB_LIBS) ../gee/libgee.la
 
 TEST_PROGS += testarraylist
-testarraylist_VALASOURCES = testarraylist.vala testcollection.vala testfixture.vala
-testarraylist_SOURCES = testarraylist.c testcollection.c testfixture.c
+testarraylist_VALASOURCES = testarraylist.vala testcollection.vala \
+	testvalues.vala testfixture.vala
+testarraylist_SOURCES = testarraylist.c testcollection.c \
+	testvalues.c testfixture.c
 $(testarraylist_SOURCES): $(testarraylist_VALASOURCES)
 	$(VALAC) -C --basedir $(top_srcdir) --vapidir $(top_srcdir)/gee --pkg gee-1.0 $^
 	touch $@
diff --git a/tests/testarraylist.vala b/tests/testarraylist.vala
index 0c79dba..80b1386 100644
--- a/tests/testarraylist.vala
+++ b/tests/testarraylist.vala
@@ -23,7 +23,11 @@
 using GLib;
 using Gee;
 
-public class ArrayListTests : CollectionTests {
+public class ArrayListTests : TestFixture {
+
+	private ArrayList<int> int_collection;
+	private ArrayList<string> string_collection;
+	private ArrayList<Object> object_collection;
 
 	public ArrayListTests () {
 		base ("ArrayList");
diff --git a/tests/testcollection.vala b/tests/testcollection.vala
index bbaed2a..7578ed6 100644
--- a/tests/testcollection.vala
+++ b/tests/testcollection.vala
@@ -1,7 +1,8 @@
 /* testcollection.vala
  *
- * Copyright (C) 2008  Jürg Billeter
- * Copyright (C) 2009  Didier Villevalois
+ * Copyright (C) 2008 Jürg Billeter
+ *               2009 Didier 'Ptitjes' Villevalois
+ *               2009 Julien Peeters
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -10,39 +11,518 @@
 
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  *
- * Author:
- * 	Didier 'Ptitjes' Villevalois <ptitjes@free.fr>
+ * Authors:
+ *   Didier 'Ptitjes' Villevalois <ptitjes@free.fr>
+ *   Julien Peeters <contact@julienpeeters.fr>
  */
 
 using GLib;
 using Gee;
 
-public abstract class CollectionTests : TestFixture {
+public abstract class CollectionTests<G> : TestFixture {
+
+	protected Collection<G> test_collection_1;
+	protected Collection<G> test_collection_2;
+	protected weak G[] test_values;
 
 	public CollectionTests (string name) {
-		base(name);
-		//add_test ("Collection.to_array ", test_to_array);
+		base (name);
+		add_test ("Collection.add", test_add);
+		add_test ("Collection.clear", test_clear);
+		add_test ("Collection.contains", test_contains);
+		add_test ("Collection.remove", test_remove);
+		add_test ("Collection.size", test_size);
+		add_test ("Collection.empty", test_empty);
+		add_test ("Collection.add_all", test_add_all);
+		add_test ("Collection.contains_all", test_contains_all);
+		add_test ("Collection.remove_all", test_remove_all);
+		add_test ("Collection.retain_all", test_remove_all);
+		//add_test ("Collection.to_array", test_to_array);
+	}
+
+	protected abstract Collection<G> create_collection_to_test ();
+
+	public override void setup () {
+		test_collection_1 = create_collection_to_test ();
+		test_collection_2 = create_collection_to_test ();
+		test_values = TestValues.get_unique_values<G> ();
+
+		assert (test_collection_1 != null);
+		assert (test_collection_2 != null);
+		assert (test_values != null && test_values.length > 0);
+	}
+
+	public override void teardown () {
+		test_collection_1 = null;
+		test_collection_2 = null;
+		test_values = null;
+	}
+
+	public virtual void test_add () {
+		// Check that adding a value add it effectively to the container
+		// and increase the container size without changing other elements
+		var size = 0;
+		for (int index = 0; index < test_values.length; index++) {
+			test_collection_1.add (test_values[index]);
+			size++;
+			assert (test_collection_1.size == size);
+
+			for (int index2 = 0; index2 <= index; index2++) {
+				assert (test_collection_1.contains (test_values[index2]));
+			}
+		}
+		assert (size == test_values.length);
+
+		// Check that adding a already added element does not
+		// change the container content
+		foreach (G val in test_values) {
+			if (test_collection_1.add (val)) {
+				size++;
+			}
+			assert (test_collection_1.size == size);
+		}
+
+		foreach (G val in test_values) {
+			assert (test_collection_1.contains (val));
+		}
+	}
+
+	public virtual void test_clear () {
+		// Check the collection is empty
+		assert (test_collection_1.is_empty);
+
+		// Check clear on empty collection
+		test_collection_1.clear ();
+		assert (test_collection_1.is_empty);
+
+		//Check clear multiple items
+		var size = 0;
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			size++;
+			assert (test_collection_1.size == size);
+		}
+		assert (size == test_values.length);
+
+		test_collection_1.clear ();
+		assert (test_collection_1.is_empty);
+	}
+
+	public virtual void test_contains () {
+		// Check empty test_collection_1
+		foreach (G val in test_values) {
+			assert (! test_collection_1.contains (val));
+		}
+
+		// Check content elements are strictly which are previously
+		// added.
+		for (int index = 0; index < test_values.length; index++) {
+			test_collection_1.add (test_values[index]);
+			assert (test_collection_1.contains (test_values[index]));
+
+			for (int index2 = index + 1;
+				 index2 < test_values.length;
+				 index2++) {
+				assert (! test_collection_1.contains (test_values[index2]));
+			}
+		}
+
+		test_collection_1.clear ();
+
+		foreach (G val in test_values) {
+			assert (! test_collection_1.contains (val));
+		}
+	}
+
+	public virtual void test_remove () {
+		// Add all elements
+		var size = 0;
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			size++;
+		}
+		assert (test_collection_1.size == test_values.length);
+
+		// Check that removing an element does not touch to other elements
+		// in the container
+		for (int index = 0; index < test_values.length; index++) {
+			test_collection_1.remove (test_values[index]);
+			size--;
+			assert (test_collection_1.size == size);
+
+			for (int index2 = index + 1;
+				 index2 < test_values.length;
+				 index2++) {
+				assert (test_collection_1.contains (test_values[index2]));
+			}
+		}
+
+		assert (test_collection_1.is_empty);
+	}
+
+	public virtual void test_size () {
+		// Check adding elements increase size
+		var size = 0;
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			size++;
+			assert (test_collection_1.size == size);
+		}
+		assert (size == test_values.length);
+
+		// Check size is null after clearing collection
+		test_collection_1.clear ();
+		assert (test_collection_1.size == 0);
+	}
+
+	public virtual void test_empty () {
+		// Check empty list
+		assert (test_collection_1.is_empty);
+
+		// Check when adding items
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			assert (! test_collection_1.is_empty);
+		}
+
+		// Check when items cleared
+		test_collection_1.clear ();
+		assert (test_collection_1.is_empty);
+	}
+
+	public virtual void test_add_all () {
+		// Check empty lists
+		test_collection_1.add_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.is_empty);
+
+		// Check collection 1 not empty, collection 2 is empty
+		var size = 0;
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			size++;
+			assert (test_collection_1.size == size);
+			assert (test_collection_1.contains (val));
+		}
+		test_collection_1.add_all (test_collection_2);
+		assert (test_collection_1.size == size);
+		assert (test_collection_2.is_empty);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Check collection 1 is empty, collection 2 not empty
+		size = 0;
+		foreach (G val in test_values) {
+			test_collection_2.add (val);
+			size++;
+			assert (test_collection_2.size == size);
+			assert (test_collection_2.contains (val));
+		}
+
+		test_collection_1.add_all (test_collection_2);
+		assert (test_collection_1.size == size);
+
+		foreach (G val in test_values) {
+			assert (test_collection_1.contains (val));
+		}
+	}
+
+	public virtual void test_contains_all () {
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.is_empty);
+
+		// Check empty
+		assert (test_collection_1.contains_all (test_collection_2));
+
+		// Collection 1 has elements, collection 2 is empty
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+		}
+		assert (test_collection_1.contains_all (test_collection_2));
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 is empty, collection 2 has elements
+		foreach (G val in test_values) {
+			test_collection_2.add (val);
+		}
+		assert (! test_collection_1.contains_all (test_collection_2));
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 and collection 2 are the same
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			test_collection_2.add (val);
+		}
+		assert (test_collection_1.contains_all (test_collection_2));
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 does not include collection 2 values
+		for (int index = 0; index < test_values.length; index++) {
+			if(index < test_values.length / 2) {
+				test_collection_1.add (test_values [index]);
+			} else {
+				test_collection_2.add (test_values [index]);
+			}
+		}
+		assert (! test_collection_1.contains_all (test_collection_2));
+
+		// Collection 1 includes collection 2 values
+		for (int index = 0; index < test_values.length; index++) {
+			if(index < test_values.length / 2) {
+				test_collection_1.add (test_values [index]);
+			} else {
+				test_collection_1.add (test_values [index]);
+				test_collection_2.add (test_values [index]);
+			}
+		}
+		assert (test_collection_1.contains_all (test_collection_2));
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 includes a subset of collection 2 except one element.
+		var limit = test_values.length - 1;
+		for (int index = 0; index < limit; index++) {
+			if(index < test_values.length / 2) {
+				test_collection_1.add (test_values [index]);
+			} else {
+				test_collection_1.add (test_values [index]);
+				test_collection_2.add (test_values [index]);
+			}
+		}
+		test_collection_2.add (test_values [limit]);
+		assert (test_collection_1.contains_all (test_collection_2));
+	}
+
+	public virtual void test_remove_all () {
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.is_empty);
+
+		// Check empty
+		test_collection_1.remove_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.is_empty);
+
+		// Collection 2 has no elements -> no elements of collection 1 are removed
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+		}
+		assert (test_collection_1.size == test_values.length);
+
+		test_collection_1.remove_all (test_collection_2);
+		assert (test_collection_1.size == test_values.length);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 and 2 have the same elements -> all should be removed.
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			test_collection_2.add (val);
+		}
+		assert (test_collection_1.size == test_values.length);
+		assert (test_collection_2.size == test_values.length);
+
+		test_collection_1.remove_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.size == test_values.length);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Removing of the same elements
+		// NOTE: precondition: test_values has one element at least (see setup).
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+
+		test_collection_2.add (test_values[0]);
+		test_collection_2.add (test_values[0]);
+
+		assert (test_collection_1.size == 5);
+		assert (test_collection_2.size == 2);
+
+		test_collection_1.remove_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.size == 2);
+	}
+
+	public virtual void test_retain_all () {
+		// Check empty
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.is_empty);
+
+		test_collection_1.retain_all (test_collection_2);
+
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.is_empty);
+
+		// Collection 1 has elements, collection 2 not -> empty collection 1
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+		}
+		assert (test_collection_1.size == test_values.length);
+		assert (test_collection_2.is_empty);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 2 has elements, collection 1 not -> nothing changes
+		foreach (G val in test_values) {
+			test_collection_2.add (val);
+		}
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.size == test_values.length);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.size == test_values.length);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 and 2 have no common elements -> empty collection 1
+		var size1 = 0, size2 = 0;
+		for (int index = 0; index < test_values.length; index++) {
+			if(index < test_values.length / 2) {
+				test_collection_1.add (test_values [index]);
+				size1++;
+			} else {
+				test_collection_2.add (test_values [index]);
+				size2++;
+			}
+		}
+		assert (test_collection_1.size == size1);
+		assert (test_collection_2.size == size2);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.size == size2);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Collection 1 and 2 have all the same elements -> nothing changes.
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+			test_collection_2.add (val);
+		}
+		assert (test_collection_1.size == test_values.length);
+		assert (test_collection_2.size == test_values.length);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.size == test_values.length);
+		assert (test_collection_2.size == test_values.length);
+
+		// Collection 1 and 2 are not equal but have an intersection -> collection
+		// equals the intersection.
+		var inter = new G[0];
+		for (int index = 0; index < test_values.length; index++) {
+			if(index > 0) {
+				if(index < test_values.length-1) {
+					inter += test_values [index];
+					test_collection_1.add (test_values [index]);
+				}
+				test_collection_2.add (test_values [index]);
+			} else { // index == 0
+				test_collection_1.add (test_values [index]);
+			}
+		}
+		assert (test_collection_1.size == test_values.length-1);
+		assert (test_collection_2.size == test_values.length-1);
+		// Intersection does not contain first and last element of testing
+		// values.
+		assert (inter.length == test_values.length-2);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.size == inter.length);
+
+		foreach (G val in inter) {
+			assert (test_collection_1.contains (val));
+		}
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Removing of same elements wehn collection 2 has the same
+		// element -> nothing changes.
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+
+		test_collection_2.add (test_values[0]);
+		test_collection_2.add (test_values[0]);
+
+		assert (test_collection_1.size == 5);
+		assert (test_collection_2.size == 2);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.size == 5);
+		assert (test_collection_2.size == 2);
+
+		test_collection_1.clear ();
+		test_collection_2.clear ();
+
+		// Removing of same elements when collection 2 has NOT the
+		// same element -> empty collection 1
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+		test_collection_1.add (test_values[0]);
+
+		for (int index = 1; index < test_values.length; index++) {
+			test_collection_2.add (test_values [index]);
+		}
+		assert (test_collection_1.size == 5);
+		assert (test_collection_2.size == test_values.length-1);
+
+		test_collection_1.retain_all (test_collection_2);
+		assert (test_collection_1.is_empty);
+		assert (test_collection_2.size == test_values.length-1);
 	}
 
-	protected Collection<int> int_collection;
-	protected Collection<string> string_collection;
-	protected Collection<Object> object_collection;
+	public virtual void test_to_array() {
+		foreach (G val in test_values) {
+			test_collection_1.add (val);
+		}
+
+		G[] array = (G[]) test_collection_1.to_array ();
 
-	public void test_to_array() {
-		string_collection.add ("42");
-		string_collection.add ("43");
-		string_collection.add ("44");
+		assert (test_values.length == array.length);
 
-		string[] array = (string[]) string_collection.to_array ();
-		assert (array[0] == "42");
-		assert (array[1] == "43");
-		assert (array[2] == "44");	
+		// NOTE: this test is absolutely not optimized but because
+		// collections does not suppose order of elements, we cannot do
+		// it better for now.
+		for (int index = 0; index < test_values.length; index++) {
+			var found = false;
+			for (int index2 = 0; !found && index2 < array.length; index2++) {
+				if (array [index2] == test_values [index]) {
+					found = true;
+				}
+			}
+			assert (found);
+		}
 	}
 }
diff --git a/tests/testvalues.vala b/tests/testvalues.vala
new file mode 100644
index 0000000..9e257a1
--- /dev/null
+++ b/tests/testvalues.vala
@@ -0,0 +1,70 @@
+/* testvalues.vala
+ *
+ * Copyright (C) 2009 Didier 'Ptitjes' Villevalois
+ *               2009 Julien Peeters
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Authors:
+ *      Didier 'Ptitjes' Villevalois <ptitjes@free.fr>
+ * 	    Julien Peeters <contact@julienpeeters.fr>
+ */
+
+using GLib;
+
+public class TestValues : Object {
+
+	private static TestValues instance;
+
+	private int[] int_values;
+	private string[] string_values;
+	private Object[] object_values;
+
+	construct {
+		int_values = new int[3];
+		int_values[0] = 42;
+		int_values[1] = 43;
+		int_values[2] = 45;
+
+		string_values = new string[3];
+		string_values[0] = "42";
+		string_values[1] = "43";
+		string_values[2] = "45";
+
+		object_values = new Object[3];
+		object_values[0] = new Object();
+		object_values[1] = new Object();
+		object_values[2] = new Object();
+	}
+
+	private TestValues () { }
+
+	public static weak G[] get_unique_values<G> () {
+		if (instance == null) {
+			instance = new TestValues ();
+		}
+
+		if (typeof(G) == typeof (int)) {
+			return (G[]) instance.int_values;
+		} else if (typeof(G) == typeof (string)) {
+			return (G[]) instance.string_values;
+		} else if (typeof(G) == typeof (Object)) {
+			return (G[]) instance.object_values;
+		} else {
+			assert_not_reached ();
+		}
+
+	}
+}
