[Mono-list] Re: mono-contribution-howto

Jaime Anguiano Olarra jaime@geneura.ugr.es
19 Apr 2002 17:03:06 +0200


--=-Rr/sutRLTVBfeM3khlae
Content-Type: multipart/mixed; boundary="=-xMV3Ulgjqa6hbrME5P96"


--=-xMV3Ulgjqa6hbrME5P96
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: quoted-printable

Oh!

I forgot to attach!, sorry. Here it is.

Greets,

Jaime.


El mi=E9, 17-04-2002 a las 17:53, Jaime Anguiano Olarra escribi=F3:
> Hi!
>=20
> Miguel told me to post this to the lists, well, here it is.
>=20
> Enjoy!
>=20
> Jaime.
>=20
> Btw. I'd like to add it to the cvs, where in the path?. /mono/doc is
> ok?. Tell me and I'll add and commit.
> --=20
> // http://geneura.ugr.es -- The Geneura Team
> // http://www.go-mono.com -- The Mono Project
--=20
// http://geneura.ugr.es -- The Geneura Team
// http://www.go-mono.com -- The Mono Project

--=-xMV3Ulgjqa6hbrME5P96
Content-Disposition: attachment; filename=mono-contribution-howto
Content-Type: text/plain; name=mono-contribution-howto; charset=ISO-8859-15
Content-Transfer-Encoding: quoted-printable


			<Mono newbie coders start file>


	For those who are new to Mono and are impatient to contribute with=20
code (uhh... you are brave!!) here is the document you should read.=20

=09
	You will see all Mono hackers say the same (great minds have similar=20
way of thinking): First, DO WRITE TESTS!!!. In order to do that:
=09
	1.- Start with the NUnit Tests Guidelines.
	    In the cvs they are located at: mcs/class/doc/NUnitGuideli...

	    But wait, this is a document for impatient people. So EVERYTHING=20
	    should be here. Well, it is.=20


	    Point "2" is waiting for you inside the guidelines. Hey! this is a
	    Must Read Line by Line Text.

-------------------- cut here --------------------

Mono NUnit Test Guidelines and Best Practices

Authors: Nick Drochak  <ndrochak@gol.com>
	 Martin Baulig  <martin@gnome.org>
Last Update: 2002-03-02
Rev: 0.3

* Purpose

This document captures all the good ideas people have had about
writing NUnit tests for the mono project. This document will be useful
for anyone who writes or maintains unit tests.

* Other resources
	- mcs/class/README has an explanation of the build process and
	  how it relates to the tests.
	- http://nunit.sourceforge.net is the place to find out about NUnit

* Getting Started

If you are new to writing NUnit tests, there is a template you may use
to help get started. The file is:

	mcs/class/doc/TemplateTest.cs


	(2.- This is the point two!. This file is just after the end of the=20
	guidelines. Copy/paste it in another buffer. And keep reading.)


Save a copy of this file in the appropriate test subdirecty (see
below), and replace all the [text] markers with appropriate
code. Comments in the template are there to guide you. You should also
look at existing tests to see how other people have written them.
mcs/class/corlib/Test/System.Collections/CollectionBaseTest.cs is a
small one that might help.

      (3.- You reached the third point. And as expected, it's just here to=20
      tell you that the content of CollectionBaseTest.cs is after the=20
      TemplateTest.cs code at the end of these guidelines.)

The directory that will contain your new file depends on the
assembly/namespace of the class for which you are creating the
tests. Under mcs/class there is a directory for each assembly. In each
assembly there is a Test directory, e.g. mcs/class/corlib/Test. In the
Test directory there are sub-directories for each namespace in the
assembly, e.g. mcs/class/corlib/Test/Sytem. Put your new test file in
the appropriate sub-directory under Test for the class you are
testing.

Once your test class is complete, you need to add it to the
AllTests.cs file in the same directory as your new test. Add a call to
"suite.AddTest()" passing the name of your new test class's suite
property as the parameter.  You will see examples in the AllTests.cs
file, so just copy and paste inside there.

Once all of that is done, you can do a 'make test' from the top mcs
directory.  Your test class will be automagically included in the
build and the tests will be run along with all the others.

* Tips

-- Provide an unique error message for Assert()

Include an unique message for each Assert() so that when the assert
fails, it is trivial to locate the failing one. Otherwise, it may be
difficult to determine which part of the test is failing. A good way
to ensure unique messages is to use something like #A01, #A02 etc.

    Bad:

	AssertEquals("array match", compare[0], i1[0]);
	AssertEquals("array match", compare[1], i1[1]);
	AssertEquals("array match", compare[2], i1[2]);
	AssertEquals("array match", compare[3], i1[3]);

    Good:

	AssertEquals("#A01", compare[0], i1[0]);
	AssertEquals("#A02", compare[1], i1[1]);
	AssertEquals("#A03", compare[2], i1[2]);
	AssertEquals("#A04", compare[3], i1[3]);

Once you used such a number in an Assert(), don't change it later on -
people might use it it identify the test in bug reports or in mailing
lists.

-- Use AssertEquals() to compare things, not Assert().

Never compare two values with Assert() - if the test fails, people
have no idea what went wrong while AssertEquals() reports the failed
value.

    Bad:

        Assert ("A01", myTicks[0] =3D=3D t1.Ticks);

    Good:

        AssertEquals ("A01", myTicks[0], t1.Ticks);

-- Constructors

When writing your testcase, please make sure to provide a constructor
which takes no arguments:

        public class DateTimeTest : TestCase
        {

                public DateTimeTest() : base ("[MonoTests.System.DateTimeTe=
st]") {}
                public DateTimeTest (string name): base(name) {}

        	public static ITest Suite
                {
                        get {
                                TestSuite suite =3D new TestSuite ();
        			return suite;
        		}
        	}
        }

-- Namespace

Please keep the namespace within each test directory consistent - all
tests which are referenced in the same AllTests.cs must be in the same
namespace. Of course you can use subnamespaces as you like -
especially for subdirectories of your testsuite.

For instance, if your AllTests.cs is in namespace "MonoTests" and you
have a subdirectory called "System", you can put all the tests in that
dir into namespace "MonoTests.System".

-- Test your test with the microsoft runtime

If possible, try to run your testsuite with the Microsoft runtime on
Windows and make sure all tests in it pass. This is especially
important if you're writing a totally new testcase - without this
check you can never be sure that your testcase contains no bugs ....

Don't worry if you're writing your test on Linux, other people can
test it for you on Windows.

Sometimes you may discover that a test doesn't show the expected
result when run with the Microsoft runtime - either because there is a
bug in their runtime or something is misleading or wrong in their
documentation. In this case, please put a detailed description of the
problem to mcs/class/doc/API-notes and do also report it to the list -
we'll forward this to the Microsoft people from time to time to help
them fix their documentation and runtime.

-------------------- cut here ------------------------

-------------------- TemplateTest.cs begins ----------

// this is a template for making NUnit tests.  Text enclosed in square
// brackets (and the brackets themselves) should be replaced by appropiate
// code.

// [File Name].cs - NUnit Test Cases for [explain here]
//
// [Author Name] ([Author email Address])
//
// (C) [Copyright holder]
//=20

// these are the standard namespaces you will need.  You may need to add mo=
re
// depending on your tests.
using NUnit.Framework;
using System;

// all test namespaces start with "MonoTests."  Append the Namespace that
// contains the class you are testing, e.g. MonoTests.System.Collections
namespace MonoTests.[Namespace]
{

// the class name should end with "Test" and start with the name of the cla=
ss
// you are testing, e.g. CollectionBaseTest
public class [Class to be tested]Test : TestCase {
=09
	// there should be two constructors for your class.  The first one
	// (without parameters) should set the name to something unique.
	// Of course the name of the method is the same as the name of the
	// class
	public [Constructor]() : base ("[Namespace.Class]") {}
	public [Constructor](string name) : base(name) {}

	// this method is run before each Test* method is called. You can put
	// variable initialization, etc. here that is common to each test.
	// Just leave the method empty if you don't need to use it.
	protected override void SetUp() {}

	// this method is run after each Test* method is called. You can put
	// clean-up code, etc. here.  Whatever needs to be done after each test.
	// Just leave the method empty if you don't need to use it.
	protected override void TearDown() {}

	// this property is required.  You need change the parameter for
	// typeof() below to be your class.
	public static ITest Suite {
		get {=20
			return new TestSuite(typeof([Classname here]));=20
		}
	}

	// this is just one of probably many test methods in your test class.
	// each test method must start with "Test".  All methods in your class
	// which start with "Test" will be automagically called by the NUnit
	// framework.
	public void Test[Something] {
		// inside here you will exercise your class and then call Assert()
	}
}

---------------------- TemplateTest.cs ends --------------
---------------------- CollectionBaseTest.cs begins ------
//
// System.Collections.CollectionBase
// Test suite for System.Collections.CollectionBase
//
// Author:
//    Nick D. Drochak II
//
// (C) 2001 Nick D. Drochak II
//


using System;
using System.Collections;
using NUnit.Framework;

namespace MonoTests.System.Collections
{

public class CollectionBaseTest : TestCase =09
{
	public CollectionBaseTest () : base ("System.Collection.CollectionBase tes=
tsuite") {}
	public CollectionBaseTest (String name) : base (name) {}

	// We need a concrete class to test the abstract base class
	public class ConcreteCollection : CollectionBase=20
	{
		// These fields are used as markers to test the On* hooks.
		public bool onClearFired;
		public bool onClearCompleteFired;

		public bool onInsertFired;
		public int onInsertIndex;
		public bool onInsertCompleteFired;
		public int onInsertCompleteIndex;

		public bool onRemoveFired;
		public int onRemoveIndex;
		public bool onRemoveCompleteFired;
		public int onRemoveCompleteIndex;

		public bool onSetFired;
		public int onSetOldValue;
		public int onSetNewValue;
		public bool onSetCompleteFired;
		public int onSetCompleteOldValue;
		public int onSetCompleteNewValue;

		// This constructor is used to test OnValid()
		public ConcreteCollection()=09
		{
			IList listObj;
			listObj =3D this;
			listObj.Add(null);
		}

		// This constructor puts consecutive integers into the list
		public ConcreteCollection(int i) {
			IList listObj;
			listObj =3D this;

			int j;
			for (j =3D 0; j< i; j++) {
				listObj.Add(j);
			}
		}

		// A helper method to look at a value in the list at a specific index
		public int PeekAt(int index)
		{
			IList listObj;
			listObj =3D this;
			return (int) listObj[index];
		}

		// Mark the flag if this hook is fired
		protected override void OnClear() {
			this.onClearFired =3D true;
		}

		// Mark the flag if this hook is fired
		protected override void OnClearComplete()=20
		{
			this.onClearCompleteFired =3D true;
		}

		// Mark the flag, and save the paramter if this hook is fired
		protected override void OnInsert(int index, object value)=20
		{
			this.onInsertFired =3D true;
			this.onInsertIndex =3D index;
		}

		// Mark the flag, and save the paramter if this hook is fired
		protected override void OnInsertComplete(int index, object value)=20
		{
			this.onInsertCompleteFired =3D true;
			this.onInsertCompleteIndex =3D index;
		}
	=09
		// Mark the flag, and save the paramter if this hook is fired
		protected override void OnRemove(int index, object value)=20
		{
			this.onRemoveFired =3D true;
			this.onRemoveIndex =3D index;
		}
	=09
		// Mark the flag, and save the paramter if this hook is fired
		protected override void OnRemoveComplete(int index, object value)=20
		{
			this.onRemoveCompleteFired =3D true;
			this.onRemoveCompleteIndex =3D index;
		}
	=09
		// Mark the flag, and save the paramters if this hook is fired
		protected override void OnSet(int index, object oldValue, object newValue=
)=20
		{
			this.onSetFired =3D true;
			this.onSetOldValue =3D (int) oldValue;
			this.onSetNewValue =3D (int) newValue;
		}
	=09
		// Mark the flag, and save the paramters if this hook is fired
		protected override void OnSetComplete(int index, object oldValue, object =
newValue)=20
		{
			this.onSetCompleteFired =3D true;
			this.onSetCompleteOldValue =3D (int) oldValue;
			this.onSetCompleteNewValue =3D (int) newValue;
		}
	}  // public class ConcreteCollection

	public static ITest Suite {
		get {
			return new TestSuite (typeof(CollectionBaseTest));
		}
	}

	// Check the count property
	public void TestCount() {
		ConcreteCollection myCollection;
		myCollection =3D new ConcreteCollection(4);
		Assert(4 =3D=3D myCollection.Count);
	}

	// Make sure GetEnumerator returns an object
	public void TestGetEnumerator() {
		ConcreteCollection myCollection;
		myCollection =3D new ConcreteCollection(4);
		Assert(null !=3D myCollection.GetEnumerator());
	}

	// OnValid disallows nulls
	public void TestOnValid() {
		ConcreteCollection myCollection;
		try {
			myCollection =3D new ConcreteCollection();
		}
		catch (ArgumentNullException) {
		}
	}

	// Test various Insert paths
	public void TestInsert() {
		ConcreteCollection myCollection;
		int numberOfItems;
		numberOfItems =3D 3;
		// The constructor inserts
		myCollection =3D new ConcreteCollection(numberOfItems);
		Assert(myCollection.onInsertFired);
		Assert(myCollection.onInsertCompleteFired);

		// Using the IList interface, check inserts in the middle
		IList listObj =3D myCollection;
		listObj.Insert(1, 9);
		Assert(myCollection.onInsertIndex =3D=3D 1);
		Assert(myCollection.onInsertCompleteIndex =3D=3D 1);
		Assert(myCollection.PeekAt(1) =3D=3D 9);
	}

	// Test Clear and it's hooks
	public void TestClear()=20
	{
		ConcreteCollection myCollection;
		int numberOfItems;
		numberOfItems =3D 1;
		myCollection =3D new ConcreteCollection(numberOfItems);
		myCollection.Clear();
		Assert(myCollection.Count =3D=3D 0);
		Assert(myCollection.onClearFired);
		Assert(myCollection.onClearCompleteFired);
	}

	// Test RemoveAt, other removes and the hooks
	public void TestRemove()=20
	{
		ConcreteCollection myCollection;
		int numberOfItems;
		numberOfItems =3D 3;
		// Set up a test collection
		myCollection =3D new ConcreteCollection(numberOfItems);

		// The list is 0-based.  So if we remove the second one
		myCollection.RemoveAt(1);

		// We should see the original third one in it's place
		Assert(myCollection.PeekAt(1) =3D=3D 2);
		Assert(myCollection.onRemoveFired);
		Assert(myCollection.onRemoveIndex =3D=3D 1);
		Assert(myCollection.onRemoveCompleteFired);
		Assert(myCollection.onRemoveCompleteIndex =3D=3D 1);
		IList listObj =3D myCollection;
		listObj.Remove(0);
		// Confirm parameters are being passed to the hooks
		Assert(myCollection.onRemoveIndex =3D=3D 0);
		Assert(myCollection.onRemoveCompleteIndex =3D=3D 0);
	}

	// Test the random access feature
	public void TestSet()=20
	{
		ConcreteCollection myCollection;
		int numberOfItems;
		numberOfItems =3D 3;
		myCollection =3D new ConcreteCollection(numberOfItems);
		IList listObj =3D myCollection;
		listObj[0] =3D 99;
		Assert((int) listObj[0] =3D=3D 99);
		Assert(myCollection.onSetFired);
		Assert(myCollection.onSetCompleteFired);
		Assert(myCollection.onSetOldValue =3D=3D 0);
		Assert(myCollection.onSetCompleteOldValue =3D=3D 0);
		Assert(myCollection.onSetNewValue =3D=3D 99);
		Assert(myCollection.onSetCompleteNewValue =3D=3D 99);
	}
}

}
----------------------- CollectionBaseTest.cs ends --------

	4.- If you use Emacs, you might want to use the .emacs file and the=20
	    package developed by Brad Merrill mailto:zbrad@cybercom.net. It=20
	    will allow you to highlight and indent in C# style in your Emacs
	    editor. (XEmacs will still work but it'll also complain).

	5.- CSharpDevelop is a GPLed IDE developed by IC#Code. Search for it
	    at sourceforge if you are interested in it.

	6.- For those who Java: "A comparison of Microsoft's C# programming
	    language to Sun Microsystem's Java Programming language" by Dare
	    Obasanjo is a really good (very complete) text to read.=20

	7.- Suggest this point and more, now I can't think of anything more.

	Enjoy!!.

	(c) Jaime Anguiano Olarra.

	The parts included in this document are property of their respective autho=
rs.


--=-xMV3Ulgjqa6hbrME5P96--

--=-Rr/sutRLTVBfeM3khlae
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: Esta parte del mensaje esta firmada digitalmente

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQA8wDGqrpvk7eLqszcRAm5LAJ4u2jQ29UdkPPFF99zGkx4e4KuXVwCfVHaq
wnqpw8iOgLiUE+E0BLiS4rU=
=ODeT
-----END PGP SIGNATURE-----

--=-Rr/sutRLTVBfeM3khlae--