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