kick off
[mono.git] / doc / 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
6         with code (uhh... you are brave!!) here is the document you
7         should read.
8
9         
10         You will see all Mono hackers say the same (great minds have
11         similar way of thinking): First, DO WRITE TESTS!!!. In order
12         to do that:
13         
14         <ul>
15                 * Start with the NUnit Tests Guidelines.  In the cvs
16                   they are located at: mcs/class/doc/NUnitGuideli...
17
18                   But wait, this is a document for impatient
19                   people. So EVERYTHING should be here. Well, it is. 
20         </ul>
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 <pre>
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 </pre>
100     Good:
101 <pre>
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 </pre>
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 <pre>
119         Assert ("A01", myTicks[0] == t1.Ticks);
120 </pre>
121     Good:
122 <pre>
123         AssertEquals ("A01", myTicks[0], t1.Ticks);
124 </pre>
125
126 ** Constructors
127
128 When writing your testcase, please make sure to provide a constructor
129 which takes no arguments:
130
131 <pre>
132         public class DateTimeTest : TestCase
133         {
134
135                 public DateTimeTest() : base ("[MonoTests.System.DateTimeTest]") {}
136                 public DateTimeTest (string name): base(name) {}
137
138                 public static ITest Suite
139                 {
140                         get {
141                                 TestSuite suite = new TestSuite ();
142                                 return suite;
143                         }
144                 }
145         }
146 </pre>
147
148 * Namespace
149
150         Please keep the namespace within each test directory
151         consistent - all tests which are referenced in the same
152         AllTests.cs must be in the same namespace. Of course you can
153         use subnamespaces as you like - especially for subdirectories
154         of your testsuite.
155         
156         For instance, if your AllTests.cs is in namespace "MonoTests"
157         and you have a subdirectory called "System", you can put all
158         the tests in that dir into namespace "MonoTests.System".
159
160 * Test your test with the microsoft runtime
161
162         If possible, try to run your testsuite with the Microsoft
163         runtime on Windows and make sure all tests in it pass. This is
164         especially important if you're writing a totally new testcase
165         - without this check you can never be sure that your testcase
166         contains no bugs ....
167         
168         Don't worry if you're writing your test on Linux, other people
169         can test it for you on Windows.
170         
171         Sometimes you may discover that a test doesn't show the
172         expected result when run with the Microsoft runtime - either
173         because there is a bug in their runtime or something is
174         misleading or wrong in their documentation. In this case,
175         please put a detailed description of the problem to
176         mcs/class/doc/API-notes and do also report it to the list -
177         we'll forward this to the Microsoft people from time to time
178         to help them fix their documentation and runtime.
179
180 -------------------- cut here ------------------------
181
182 <pre>
183 -------------------- TemplateTest.cs begins ----------
184
185 // this is a template for making NUnit tests.  Text enclosed in square
186 // brackets (and the brackets themselves) should be replaced by appropiate
187 // code.
188
189 // [File Name].cs - NUnit Test Cases for [explain here]
190 //
191 // [Author Name] ([Author email Address])
192 //
193 // (C) [Copyright holder]
194 // 
195
196 // these are the standard namespaces you will need.  You may need to add more
197 // depending on your tests.
198 using NUnit.Framework;
199 using System;
200
201 // all test namespaces start with "MonoTests."  Append the Namespace that
202 // contains the class you are testing, e.g. MonoTests.System.Collections
203 namespace MonoTests.[Namespace]
204 {
205
206 // the class name should end with "Test" and start with the name of the class
207 // you are testing, e.g. CollectionBaseTest
208 public class [Class to be tested]Test : TestCase {
209         
210         // there should be two constructors for your class.  The first one
211         // (without parameters) should set the name to something unique.
212         // Of course the name of the method is the same as the name of the
213         // class
214         public [Constructor]() : base ("[Namespace.Class]") {}
215         public [Constructor](string name) : base(name) {}
216
217         // this method is run before each Test* method is called. You can put
218         // variable initialization, etc. here that is common to each test.
219         // Just leave the method empty if you don't need to use it.
220         protected override void SetUp() {}
221
222         // this method is run after each Test* method is called. You can put
223         // clean-up code, etc. here.  Whatever needs to be done after each test.
224         // Just leave the method empty if you don't need to use it.
225         protected override void TearDown() {}
226
227         // this property is required.  You need change the parameter for
228         // typeof() below to be your class.
229         public static ITest Suite {
230                 get { 
231                         return new TestSuite(typeof([Classname here])); 
232                 }
233         }
234
235         // this is just one of probably many test methods in your test class.
236         // each test method must start with "Test".  All methods in your class
237         // which start with "Test" will be automagically called by the NUnit
238         // framework.
239         public void Test[Something] {
240                 // inside here you will exercise your class and then call Assert()
241         }
242 }
243
244 ---------------------- TemplateTest.cs ends --------------
245 ---------------------- CollectionBaseTest.cs begins ------
246 //
247 // System.Collections.CollectionBase
248 // Test suite for System.Collections.CollectionBase
249 //
250 // Author:
251 //    Nick D. Drochak II
252 //
253 // (C) 2001 Nick D. Drochak II
254 //
255
256
257 using System;
258 using System.Collections;
259 using NUnit.Framework;
260
261 namespace MonoTests.System.Collections
262 {
263
264 public class CollectionBaseTest : TestCase      
265 {
266         public CollectionBaseTest () : base ("System.Collection.CollectionBase testsuite") {}
267         public CollectionBaseTest (String name) : base (name) {}
268
269         // We need a concrete class to test the abstract base class
270         public class ConcreteCollection : CollectionBase 
271         {
272                 // These fields are used as markers to test the On* hooks.
273                 public bool onClearFired;
274                 public bool onClearCompleteFired;
275
276                 public bool onInsertFired;
277                 public int onInsertIndex;
278                 public bool onInsertCompleteFired;
279                 public int onInsertCompleteIndex;
280
281                 public bool onRemoveFired;
282                 public int onRemoveIndex;
283                 public bool onRemoveCompleteFired;
284                 public int onRemoveCompleteIndex;
285
286                 public bool onSetFired;
287                 public int onSetOldValue;
288                 public int onSetNewValue;
289                 public bool onSetCompleteFired;
290                 public int onSetCompleteOldValue;
291                 public int onSetCompleteNewValue;
292
293                 // This constructor is used to test OnValid()
294                 public ConcreteCollection()     
295                 {
296                         IList listObj;
297                         listObj = this;
298                         listObj.Add(null);
299                 }
300
301                 // This constructor puts consecutive integers into the list
302                 public ConcreteCollection(int i) {
303                         IList listObj;
304                         listObj = this;
305
306                         int j;
307                         for (j = 0; j< i; j++) {
308                                 listObj.Add(j);
309                         }
310                 }
311
312                 // A helper method to look at a value in the list at a specific index
313                 public int PeekAt(int index)
314                 {
315                         IList listObj;
316                         listObj = this;
317                         return (int) listObj[index];
318                 }
319
320                 // Mark the flag if this hook is fired
321                 protected override void OnClear() {
322                         this.onClearFired = true;
323                 }
324
325                 // Mark the flag if this hook is fired
326                 protected override void OnClearComplete() 
327                 {
328                         this.onClearCompleteFired = true;
329                 }
330
331                 // Mark the flag, and save the paramter if this hook is fired
332                 protected override void OnInsert(int index, object value) 
333                 {
334                         this.onInsertFired = true;
335                         this.onInsertIndex = index;
336                 }
337
338                 // Mark the flag, and save the paramter if this hook is fired
339                 protected override void OnInsertComplete(int index, object value) 
340                 {
341                         this.onInsertCompleteFired = true;
342                         this.onInsertCompleteIndex = index;
343                 }
344                 
345                 // Mark the flag, and save the paramter if this hook is fired
346                 protected override void OnRemove(int index, object value) 
347                 {
348                         this.onRemoveFired = true;
349                         this.onRemoveIndex = index;
350                 }
351                 
352                 // Mark the flag, and save the paramter if this hook is fired
353                 protected override void OnRemoveComplete(int index, object value) 
354                 {
355                         this.onRemoveCompleteFired = true;
356                         this.onRemoveCompleteIndex = index;
357                 }
358                 
359                 // Mark the flag, and save the paramters if this hook is fired
360                 protected override void OnSet(int index, object oldValue, object newValue) 
361                 {
362                         this.onSetFired = true;
363                         this.onSetOldValue = (int) oldValue;
364                         this.onSetNewValue = (int) newValue;
365                 }
366                 
367                 // Mark the flag, and save the paramters if this hook is fired
368                 protected override void OnSetComplete(int index, object oldValue, object newValue) 
369                 {
370                         this.onSetCompleteFired = true;
371                         this.onSetCompleteOldValue = (int) oldValue;
372                         this.onSetCompleteNewValue = (int) newValue;
373                 }
374         }  // public class ConcreteCollection
375
376         public static ITest Suite {
377                 get {
378                         return new TestSuite (typeof(CollectionBaseTest));
379                 }
380         }
381
382         // Check the count property
383         public void TestCount() {
384                 ConcreteCollection myCollection;
385                 myCollection = new ConcreteCollection(4);
386                 Assert(4 == myCollection.Count);
387         }
388
389         // Make sure GetEnumerator returns an object
390         public void TestGetEnumerator() {
391                 ConcreteCollection myCollection;
392                 myCollection = new ConcreteCollection(4);
393                 Assert(null != myCollection.GetEnumerator());
394         }
395
396         // OnValid disallows nulls
397         public void TestOnValid() {
398                 ConcreteCollection myCollection;
399                 try {
400                         myCollection = new ConcreteCollection();
401                 }
402                 catch (ArgumentNullException) {
403                 }
404         }
405
406         // Test various Insert paths
407         public void TestInsert() {
408                 ConcreteCollection myCollection;
409                 int numberOfItems;
410                 numberOfItems = 3;
411                 // The constructor inserts
412                 myCollection = new ConcreteCollection(numberOfItems);
413                 Assert(myCollection.onInsertFired);
414                 Assert(myCollection.onInsertCompleteFired);
415
416                 // Using the IList interface, check inserts in the middle
417                 IList listObj = myCollection;
418                 listObj.Insert(1, 9);
419                 Assert(myCollection.onInsertIndex == 1);
420                 Assert(myCollection.onInsertCompleteIndex == 1);
421                 Assert(myCollection.PeekAt(1) == 9);
422         }
423
424         // Test Clear and it's hooks
425         public void TestClear() 
426         {
427                 ConcreteCollection myCollection;
428                 int numberOfItems;
429                 numberOfItems = 1;
430                 myCollection = new ConcreteCollection(numberOfItems);
431                 myCollection.Clear();
432                 Assert(myCollection.Count == 0);
433                 Assert(myCollection.onClearFired);
434                 Assert(myCollection.onClearCompleteFired);
435         }
436
437         // Test RemoveAt, other removes and the hooks
438         public void TestRemove() 
439         {
440                 ConcreteCollection myCollection;
441                 int numberOfItems;
442                 numberOfItems = 3;
443                 // Set up a test collection
444                 myCollection = new ConcreteCollection(numberOfItems);
445
446                 // The list is 0-based.  So if we remove the second one
447                 myCollection.RemoveAt(1);
448
449                 // We should see the original third one in it's place
450                 Assert(myCollection.PeekAt(1) == 2);
451                 Assert(myCollection.onRemoveFired);
452                 Assert(myCollection.onRemoveIndex == 1);
453                 Assert(myCollection.onRemoveCompleteFired);
454                 Assert(myCollection.onRemoveCompleteIndex == 1);
455                 IList listObj = myCollection;
456                 listObj.Remove(0);
457                 // Confirm parameters are being passed to the hooks
458                 Assert(myCollection.onRemoveIndex == 0);
459                 Assert(myCollection.onRemoveCompleteIndex == 0);
460         }
461
462         // Test the random access feature
463         public void TestSet() 
464         {
465                 ConcreteCollection myCollection;
466                 int numberOfItems;
467                 numberOfItems = 3;
468                 myCollection = new ConcreteCollection(numberOfItems);
469                 IList listObj = myCollection;
470                 listObj[0] = 99;
471                 Assert((int) listObj[0] == 99);
472                 Assert(myCollection.onSetFired);
473                 Assert(myCollection.onSetCompleteFired);
474                 Assert(myCollection.onSetOldValue == 0);
475                 Assert(myCollection.onSetCompleteOldValue == 0);
476                 Assert(myCollection.onSetNewValue == 99);
477                 Assert(myCollection.onSetCompleteNewValue == 99);
478         }
479 }
480
481 }
482 ----------------------- CollectionBaseTest.cs ends --------
483 </pre>
484         <ul>
485                 * If you use Emacs, you might want to use the .emacs file and the 
486                   package developed by Brad Merrill mailto:zbrad@cybercom.net. It 
487                   will allow you to highlight and indent in C# style in your Emacs
488                   editor. (XEmacs will still work but it'll also complain).
489
490                 *  CSharpDevelop is a GPLed IDE developed by IC#Code. Search for it
491                    at sourceforge if you are interested in it.
492
493                 * For those who Java: "A comparison of Microsoft's C# programming
494                   language to Sun Microsystem's Java Programming language" by Dare
495                   Obasanjo is a really good (very complete) text to read. 
496
497                 * Suggest this point and more, now I can't think of anything more.
498         </ul>
499
500         Enjoy!!.
501
502         (c) Jaime Anguiano Olarra.
503
504         The parts included in this document are property of their respective authors.
505