2009-12-26 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / web / mono-contribution-howto
blobea3a4eaae53af0e0c3c77c7daf87bcf461f17a17
2                         <Mono newbie coders start file>
3         <h1> A little help for mono newbie coders </h1>
6         For those who are new to Mono and are impatient to contribute
7         with code (uhh... you are brave!!) here is the document you 
8         should read.
10         
11         You will see all Mono hackers say the same (great minds have
12         similar way of thinking): First, DO WRITE TESTS!!!. In order
13         to do that:
14         
15         <ul>
16                 * Start with the NUnit Tests Guidelines.  In the cvs
17                   they are located at: mcs/class/doc/NUnitGuideli...
19                 * But wait, this is a document for impatient
20                   people. So EVERYTHING should be here. Well, it is. 
21         </ul>
24         <h2> The NUnit Tests Guidelines document </h2>
26         Mono NUnit Test Guidelines and Best Practices
28         Authors: Nick Drochak  <ndrochak@gol.com>
29         Martin Baulig  <martin@gnome.org>
30         Last Update: 2002-03-02
31         Rev: 0.3
33  <b> Purpose </b>
35         This document captures all the good ideas people have had 
36         about writing NUnit tests for the mono project. This document 
37         will be useful for anyone who writes or maintains unit tests.
39  <b> Other resources </b>
41         - mcs/class/README has an explanation of the build process and
42           how it relates to the tests.
43         - http://nunit.sourceforge.net is the place to find out about 
44           NUnit
46  <b> Getting Started </b>
48         If you are new to writing NUnit tests, there is a template 
49         you may use to help get started. The file is:
51         mcs/class/doc/TemplateTest.cs
54         (2.- This is the point two!. This file is just after the end 
55         of the guidelines. Copy/paste it in another buffer. And keep 
56         reading.)
58         Save a copy of this file in the appropriate test subdirecty 
59         (see below), and replace all the [text] markers with 
60         appropriate code. Comments in the template are there to guide 
61         you. You should also look at existing tests to see how other 
62         people have written them. 
64         mcs/class/corlib/Test/System.Collections/CollectionBaseTest.cs 
66         is a small one that might help.
68         (3.- You reached the third point. And as expected, it's just 
69         here to tell you that the content of CollectionBaseTest.cs is 
70         after the TemplateTest.cs code at the end of these 
71         guidelines.)
73         The directory that will contain your new file depends on the
74         assembly/namespace of the class for which you are creating the
75         tests. Under mcs/class there is a directory for each assembly. 
76         In each assembly there is a Test directory, e.g. 
77         mcs/class/corlib/Test. In the Test directory there are 
78         sub-directories for each namespace in the assembly, e.g. 
79         mcs/class/corlib/Test/Sytem. Put your new test file in the 
80         appropriate sub-directory under Test for the class you are
81         testing.
83         Once your test class is complete, you need to add it to the
84         AllTests.cs file in the same directory as your new test. Add a 
85         call to "suite.AddTest()" passing the name of your new test 
86         class's suite property as the parameter.  You will see examples 
87         in the AllTests.cs file, so just copy and paste inside there.
89         Once all of that is done, you can do a 'make test' from the top 
90         mcs directory.  Your test class will be automagically included 
91         in the build and the tests will be run along with all the 
92         others.
94  <b> Tips </b>
96  <b> Provide an unique error message for Assert() </b>
98         Include an unique message for each Assert() so that when the 
99         assert fails, it is trivial to locate the failing one. 
100         Otherwise, it may be difficult to determine which part of the 
101         test is failing. A good way to ensure unique messages is to use 
102         something like #A01, #A02 etc.
104         Bad:
105 <pre>
106         AssertEquals("array match", compare[0], i1[0]);
107         AssertEquals("array match", compare[1], i1[1]);
108         AssertEquals("array match", compare[2], i1[2]);
109         AssertEquals("array match", compare[3], i1[3]);
110 </pre>
111         Good:
112 <pre>
113         AssertEquals("#A01", compare[0], i1[0]);
114         AssertEquals("#A02", compare[1], i1[1]);
115         AssertEquals("#A03", compare[2], i1[2]);
116         AssertEquals("#A04", compare[3], i1[3]);
117 </pre>
118         Once you used such a number in an Assert(), don't change it 
119         later on - people might use it it identify the test in bug 
120         reports or in mailing lists.
122  <b> Use AssertEquals() to compare things, not Assert(). </b>
124         Never compare two values with Assert() - if the test fails, 
125         people have no idea what went wrong while AssertEquals() 
126         reports the failed value.
128         Bad:
129 <pre>
130         Assert ("A01", myTicks[0] == t1.Ticks);
131 </pre>
132         Good:
133 <pre>
134         AssertEquals ("A01", myTicks[0], t1.Ticks);
135 </pre>
137  <b> Constructors </b>
139         When writing your testcase, please make sure to provide a 
140         constructor which takes no arguments:
142 <pre>
143         public class DateTimeTest : TestCase
144         {
146                 public DateTimeTest() : base ("[MonoTests.System.DateTimeTest]") {}
147                 public DateTimeTest (string name): base(name) {}
149                 public static ITest Suite
150                 {
151                         get {
152                                 TestSuite suite = new TestSuite ();
153                                 return suite;
154                         }
155                 }
156         }
157 </pre>
159  <b> Namespace </b>
161         Please keep the namespace within each test directory
162         consistent - all tests which are referenced in the same
163         AllTests.cs must be in the same namespace. Of course you can
164         use subnamespaces as you like - especially for subdirectories
165         of your testsuite.
166         
167         For instance, if your AllTests.cs is in namespace "MonoTests"
168         and you have a subdirectory called "System", you can put all
169         the tests in that dir into namespace "MonoTests.System".
171  <b> Test your test with the microsoft runtime </b>
173         If possible, try to run your testsuite with the Microsoft
174         runtime on Windows and make sure all tests in it pass. This is
175         especially important if you're writing a totally new testcase
176         - without this check you can never be sure that your testcase
177         contains no bugs ....
178         
179         Don't worry if you're writing your test on Linux, other people
180         can test it for you on Windows.
181         
182         Sometimes you may discover that a test doesn't show the
183         expected result when run with the Microsoft runtime - either
184         because there is a bug in their runtime or something is
185         misleading or wrong in their documentation. In this case,
186         please put a detailed description of the problem to
187         mcs/class/doc/API-notes and do also report it to the list -
188         we'll forward this to the Microsoft people from time to time
189         to help them fix their documentation and runtime.
191 <pre>
192 -------------------- TemplateTest.cs begins ----------
194         // this is a template for making NUnit tests.  Text enclosed 
195         // in square brackets (and the brackets themselves) should be 
196         // replaced by appropiate code.
198         // [File Name].cs - NUnit Test Cases for [explain here]
199         //
200         // [Author Name] ([Author email Address])
201         //
202         // (C) [Copyright holder]
203         // 
205         // these are the standard namespaces you will need.  You may 
206         // need to add more depending on your tests.
207         using NUnit.Framework;
208         using System;
210         // all test namespaces start with "MonoTests."  Append the 
211         // Namespace that contains the class you are testing, e.g. 
212         // MonoTests.System.Collections
213         namespace MonoTests.[Namespace]
214         {
216         // the class name should end with "Test" and start with the name 
217         // of the class you are testing, e.g. CollectionBaseTest
218         public class [Class to be tested]Test : TestCase {
219         
220         // there should be two constructors for your class.  The first 
221         // one (without parameters) should set the name to something 
222         // unique.
223         // Of course the name of the method is the same as the name of 
224         // the class
225         public [Constructor]() : base ("[Namespace.Class]") {}
226         public [Constructor](string name) : base(name) {}
228         // this method is run before each Test* method is called. You 
229         // can put variable initialization, etc. here that is common to 
230         // each test.
231         // Just leave the method empty if you don't need to use it.
232         protected override void SetUp() {}
234         // this method is run after each Test* method is called. You 
235         // can put clean-up code, etc. here.  Whatever needs to be done 
236         // after each test. Just leave the method empty if you don't need 
237         // to use it.
238         protected override void TearDown() {}
240         // this property is required.  You need change the parameter for
241         // typeof() below to be your class.
242         public static ITest Suite {
243                 get { 
244                         return new TestSuite(typeof([Classname here])); 
245                 }
246         }
248         // this is just one of probably many test methods in your test 
249         // class. each test method must start with "Test".  All methods 
250         // in your class which start with "Test" will be automagically 
251         // called by the NUnit framework.
252         public void Test[Something] {
253                 // inside here you will exercise your class and then 
254                 // call Assert()
255         }
258 ---------------------- TemplateTest.cs ends --------------
260 ---------------------- CollectionBaseTest.cs begins ------
261         //
262         // System.Collections.CollectionBase
263         // Test suite for System.Collections.CollectionBase
264         //
265         // Author:
266         //    Nick D. Drochak II
267         //
268         // (C) 2001 Nick D. Drochak II
269         //
272         using System;
273         using System.Collections;
274         using NUnit.Framework;
276         namespace MonoTests.System.Collections
277         {
279         public class CollectionBaseTest : TestCase      
280         {
281                 public CollectionBaseTest () : base 
282                         ("System.Collection.CollectionBase testsuite") 
283                         {}
284                 public CollectionBaseTest (String name) : base (name) 
285                         {}
287                 // We need a concrete class to test the abstract base 
288                 // class
289                 public class ConcreteCollection : CollectionBase 
290                 {
291                         // These fields are used as markers to test 
292                         // the On* hooks.
293                         public bool onClearFired;
294                         public bool onClearCompleteFired;
296                         public bool onInsertFired;
297                         public int onInsertIndex;
298                         public bool onInsertCompleteFired;
299                         public int onInsertCompleteIndex;
301                         public bool onRemoveFired;
302                         public int onRemoveIndex;
303                         public bool onRemoveCompleteFired;
304                         public int onRemoveCompleteIndex;
306                         public bool onSetFired;
307                         public int onSetOldValue;
308                         public int onSetNewValue;
309                         public bool onSetCompleteFired;
310                         public int onSetCompleteOldValue;
311                         public int onSetCompleteNewValue;
313                         // This constructor is used to test OnValid()
314                         public ConcreteCollection()     
315                         {
316                                 IList listObj;
317                                 listObj = this;
318                                 listObj.Add(null);
319                         }
321                         // This constructor puts consecutive integers into the list
322                         public ConcreteCollection(int i) {
323                                 IList listObj;
324                                 listObj = this;
326                                 int j;
327                                 for (j = 0; j< i; j++) {
328                                         listObj.Add(j);
329                                 }
330                         }
332                         // A helper method to look at a value in the 
333                         // list at a specific index
334                         public int PeekAt(int index)
335                         {
336                                 IList listObj;
337                                 listObj = this;
338                                 return (int) listObj[index];
339                         }
341                         // Mark the flag if this hook is fired
342                         protected override void OnClear() {
343                                 this.onClearFired = true;
344                         }
346                         // Mark the flag if this hook is fired
347                         protected override void OnClearComplete() 
348                         {
349                                 this.onClearCompleteFired = true;
350                         }
352                         // Mark the flag, and save the paramter if 
353                         // this hook is fired
354                         protected override void OnInsert(int index, 
355                                                         object value) 
356                         {
357                                 this.onInsertFired = true;
358                                 this.onInsertIndex = index;
359                         }
361                         // Mark the flag, and save the paramter if 
362                         // this hook is fired
363                         protected override void OnInsertComplete(int index, 
364                                                         object value) 
365                         {
366                                 this.onInsertCompleteFired = true;
367                                 this.onInsertCompleteIndex = index;
368                         }
369                 
370                         // Mark the flag, and save the paramter if this hook 
371                         // is fired
372                         protected override void OnRemove(int index, 
373                                                         object value) 
374                         {
375                                 this.onRemoveFired = true;
376                                 this.onRemoveIndex = index;
377                         }
378                 
379                         // Mark the flag, and save the paramter if this hook 
380                         // is fired
381                         protected override void OnRemoveComplete(int index, 
382                                                                 object value) 
383                         {
384                                 this.onRemoveCompleteFired = true;
385                                 this.onRemoveCompleteIndex = index;
386                         }
387                 
388                         // Mark the flag, and save the paramters if this hook 
389                         // is fired
390                         protected override void OnSet(int index, object oldValue, 
391                                                                 object newValue) 
392                         {
393                                 this.onSetFired = true;
394                                 this.onSetOldValue = (int) oldValue;
395                                 this.onSetNewValue = (int) newValue;
396                         }
397                 
398                         // Mark the flag, and save the paramters if this hook 
399                         // is fired
400                         protected override void OnSetComplete(int index, 
401                                                         object oldValue, 
402                                                         object newValue) 
403                         {
404                                 this.onSetCompleteFired = true;
405                                 this.onSetCompleteOldValue = (int) oldValue;
406                                 this.onSetCompleteNewValue = (int) newValue;
407                         }
408                 }  // public class ConcreteCollection
410                 public static ITest Suite {
411                         get {
412                                 return new TestSuite 
413                                         (typeof(CollectionBaseTest));
414                         }
415                 }
417                 // Check the count property
418                 public void TestCount() {
419                         ConcreteCollection myCollection;
420                         myCollection = new ConcreteCollection(4);
421                         Assert(4 == myCollection.Count);
422                 }
424         // Make sure GetEnumerator returns an object
425         public void TestGetEnumerator() {
426                 ConcreteCollection myCollection;
427                 myCollection = new ConcreteCollection(4);
428                 Assert(null != myCollection.GetEnumerator());
429         }
431         // OnValid disallows nulls
432         public void TestOnValid() {
433                 ConcreteCollection myCollection;
434                 try {
435                         myCollection = new ConcreteCollection();
436                 }
437                 catch (ArgumentNullException) {
438                 }
439         }
441         // Test various Insert paths
442         public void TestInsert() {
443                 ConcreteCollection myCollection;
444                 int numberOfItems;
445                 numberOfItems = 3;
446                 // The constructor inserts
447                 myCollection = new ConcreteCollection(numberOfItems);
448                 Assert(myCollection.onInsertFired);
449                 Assert(myCollection.onInsertCompleteFired);
451                 // Using the IList interface, check inserts in the middle
452                 IList listObj = myCollection;
453                 listObj.Insert(1, 9);
454                 Assert(myCollection.onInsertIndex == 1);
455                 Assert(myCollection.onInsertCompleteIndex == 1);
456                 Assert(myCollection.PeekAt(1) == 9);
457         }
459         // Test Clear and it's hooks
460         public void TestClear() 
461         {
462                 ConcreteCollection myCollection;
463                 int numberOfItems;
464                 numberOfItems = 1;
465                 myCollection = new ConcreteCollection(numberOfItems);
466                 myCollection.Clear();
467                 Assert(myCollection.Count == 0);
468                 Assert(myCollection.onClearFired);
469                 Assert(myCollection.onClearCompleteFired);
470         }
472         // Test RemoveAt, other removes and the hooks
473         public void TestRemove() 
474         {
475                 ConcreteCollection myCollection;
476                 int numberOfItems;
477                 numberOfItems = 3;
478                 // Set up a test collection
479                 myCollection = new ConcreteCollection(numberOfItems);
481                 // The list is 0-based.  So if we remove the second one
482                 myCollection.RemoveAt(1);
484                 // We should see the original third one in it's place
485                 Assert(myCollection.PeekAt(1) == 2);
486                 Assert(myCollection.onRemoveFired);
487                 Assert(myCollection.onRemoveIndex == 1);
488                 Assert(myCollection.onRemoveCompleteFired);
489                 Assert(myCollection.onRemoveCompleteIndex == 1);
490                 IList listObj = myCollection;
491                 listObj.Remove(0);
492                 // Confirm parameters are being passed to the hooks
493                 Assert(myCollection.onRemoveIndex == 0);
494                 Assert(myCollection.onRemoveCompleteIndex == 0);
495         }
497         // Test the random access feature
498         public void TestSet() 
499         {
500                 ConcreteCollection myCollection;
501                 int numberOfItems;
502                 numberOfItems = 3;
503                 myCollection = new ConcreteCollection(numberOfItems);
504                 IList listObj = myCollection;
505                 listObj[0] = 99;
506                 Assert((int) listObj[0] == 99);
507                 Assert(myCollection.onSetFired);
508                 Assert(myCollection.onSetCompleteFired);
509                 Assert(myCollection.onSetOldValue == 0);
510                 Assert(myCollection.onSetCompleteOldValue == 0);
511                 Assert(myCollection.onSetNewValue == 99);
512                 Assert(myCollection.onSetCompleteNewValue == 99);
513         }
517 ----------------------- CollectionBaseTest.cs ends --------
519 </pre>
520         <ul>
521                 * If you use Emacs, you might want to use the .emacs 
522                   file and the package developed by Brad Merrill 
523                   mailto:zbrad@cybercom.net. It will allow you to 
524                   highlight and indent in C# style in your Emacs
525                   editor. (XEmacs will still work but it'll also 
526                   complain).
528                 * CSharpDevelop is a GPLed IDE developed by IC#Code. 
529                   Search for it at sourceforge if you are interested 
530                   in it.
532                 * For those who Java: "A comparison of Microsoft's 
533                   C# programming language to Sun Microsystem's Java 
534                   Programming language" by Dare Obasanjo is a really good 
535                   (very complete) text to read. 
537                 * Suggest this point and more, now I can't think of 
538                   anything more.
539         </ul>
541         Enjoy!!.
543         (c) 2002, <a href="mailto:jaime@geneura.ugr.es">Jaime Anguiano Olarra</a>.
545         The parts included in this document are property of their 
546         respective authors.
548         Note: The identation of the source code has been changed a bit
549         so it could fit better in the website. Anyway, as nothing more
550         changed, the files should work as expected.