libpropeller
Making PropellerGCC Easier
 All Classes Files Functions Variables Enumerations Enumerator Macros Pages
sd.test.h
Go to the documentation of this file.
1 // Copyright 2013 SRLM and Red9
2 
3 #include <propeller.h>
4 #include "unity.h"
5 #include "sd.h"
6 #include <stdlib.h>
7 
8 const int kDoPin = 10;
9 const int kClkPin = 11;
10 const int kDiPin = 12;
11 const int kCsPin = 13;
12 
13 const int kDoPinNoSd = 18;
14 const int kClkPinNoSd = 20;
15 const int kDiPinNoSd = 19;
16 const int kCsPinNoSd = 21;
17 
34 
35 class UnityTests {
36 public:
37 
38  static void help_TestFilename(const char * filename) {
39  char letter = CNT & 0x7F; //Semi random noise...
40 
41  sut.Open(filename, 'w');
42  sut.Put(letter);
43  sut.Open(filename, 'r');
44  TEST_ASSERT_EQUAL_INT(letter, sut.Get());
45  TEST_ASSERT_EQUAL_INT(-1, sut.Get());
46  }
47 
48  static int help_DeleteAllFiles(void) {
49  const int MAXIMUM_FILE_COUNT = 1000;
50  int count;
51  for (count = 0; count < MAXIMUM_FILE_COUNT; count++) {
52 
53  char filename [13];
54  sut.OpenRootDirectory();
55  if (sut.NextFile(filename) != true) {
56  break;
57  }
58  sut.Open(filename, 'd');
59  }
60  return count;
61  }
62 
63  static void cog_DoNothing(void * arg) {
64  waitcnt(CLKFREQ * 50 + CNT);
65  }
66 
67  static int help_CountNumberOfFreeCogs(void) {
68  const int stacksize = sizeof (_thread_state_t) + sizeof (int) * 10;
69  int * cog_stack = (int*) malloc(stacksize);
70  int cog_id = cogstart(cog_DoNothing, NULL, cog_stack, stacksize);
71 
72  int free_cogs = 0;
73 
74  if (cog_id != -1) {
75  free_cogs = help_CountNumberOfFreeCogs() + 1;
76  cogstop(cog_id);
77  }
78 
79  free(cog_stack);
80 
81  return free_cogs;
82  }
83 
84  static void setUp(void) {
85  sut.ClearError();
87  }
88 
89  static void tearDown(void) {
90  help_DeleteAllFiles();
91  sut.Unmount();
92  }
93 
94  // -----------------------------------------------------------------------------
95  // Mount Operations
96  // -----------------------------------------------------------------------------
97 
98  static void test_Mount(void) {
99  //Assume: mount in setUp();
100  TEST_ASSERT_FALSE(sut.HasError());
101  }
102 
103  static void test_MountMultiple(void) {
104  sut.Mount(kDoPin, kClkPin, kDiPin, kCsPin);
105  TEST_ASSERT_FALSE(sut.HasError());
106  sut.Mount(kDoPin, kClkPin, kDiPin, kCsPin);
107  TEST_ASSERT_FALSE(sut.HasError());
108 
109  }
110 
111  static void test_MultipleUnmounts(void) {
112  sut.Unmount();
113  TEST_ASSERT_EQUAL_INT(0, sut.GetError());
114  sut.Unmount();
115  TEST_ASSERT_EQUAL_INT(0, sut.GetError());
116  }
117 
118  static void test_MountNoSd(void) {
120  TEST_ASSERT_EQUAL_INT(SD::kErrorCardNotReset, sut.GetError());
121  }
122 
123  static void test_UnmountFreesCog(void) {
124  int cogsFreeBefore = help_CountNumberOfFreeCogs();
125  sut.Unmount();
126  TEST_ASSERT_EQUAL_INT(cogsFreeBefore + 1, help_CountNumberOfFreeCogs());
127  }
128 
129  static void test_DestructorFreesCog(void) {
130  sut.Unmount();
131  int cogsFreeBefore = help_CountNumberOfFreeCogs();
132  {
133  SD temp;
134  temp.Mount(kDoPin, kClkPin, kDiPin, kCsPin);
135  TEST_ASSERT_EQUAL_INT(cogsFreeBefore - 1, help_CountNumberOfFreeCogs());
136  }
137  TEST_ASSERT_EQUAL_INT(cogsFreeBefore, help_CountNumberOfFreeCogs());
138  }
139 
140  // -----------------------------------------------------------------------------
141  // File operations (open, close, etc.)
142  // -----------------------------------------------------------------------------
143 
144  static void test_OpenExistingFileForWrite(void) {
145  sut.Open("RANDOM.RND", 'w');
146  sut.Put('a');
147  sut.Open("RANDOM.RND", 'w');
148  sut.Put('b');
149  sut.Open("RANDOM.RND", 'r');
150  TEST_ASSERT_EQUAL_INT('b', sut.Get());
151 
152  }
153 
154  static void test_OpenNonexistentFileForRead(void) {
155  sut.Open("RANDOM.RND", 'r');
156  TEST_ASSERT_TRUE(sut.HasError());
157  }
158 
159  static void test_OpenNonexistentFileForWrite(void) {
160  sut.Open("RANDOM.RND", 'w');
161  TEST_ASSERT_FALSE(sut.HasError());
162  }
163 
164  static void test_OpenForDeleteNonexistentFile(void) {
165  sut.Open("RANDOM.RND", 'd');
166  TEST_ASSERT_FALSE(sut.HasError());
167  }
168 
169  static void test_OpenForAppendNonexistentFile(void) {
170  sut.Open("RANDOM.RND", 'a');
171  TEST_ASSERT_FALSE(sut.HasError());
172  }
173 
174  static void test_OpenTooLongFilename(void) {
175  sut.Open("REALLONGNAME.RND", 'w');
176  TEST_ASSERT_FALSE(sut.HasError());
177  }
178 
179  static void test_CloseFileTwice(void) {
180  sut.Close();
181  TEST_ASSERT_FALSE(sut.HasError());
182  sut.Close();
183  TEST_ASSERT_FALSE(sut.HasError());
184  }
185 
186  static void test_OpenSpecialCharacterFilenames(void) {
187  help_TestFilename("$%-_@~`!.(){");
188  help_TestFilename("}^#& ");
189  }
190 
191  static void test_OpenFilenameWithExtensionOnly(void) {
192  help_TestFilename(".WAT");
193  }
194 
195  static void test_OpenFilenameWithoutExtension(void) {
196  help_TestFilename("Hello");
197  }
198 
199  static void test_OpenShorterFilenameThan8dot3(void) {
200  help_TestFilename("a.a");
201  }
202 
203  static void test_OpenEmptyFilename(void) {
204  help_TestFilename("");
205  }
206 
207 
208 
209  //TO DO(SRLM): I don't think I can test to tell if the pins are Tristated. This
210  //is because each cog has it's own DIRA register, and the results, although OR'd
211  //together with the other DIRA registers, is not accessible.
212  //One solution might be to
213  // 1. Tristate pins
214  // 2. Set DIRA for SD pin to output
215  // 3. Set OUTA for SD pin to low
216  // 4. Set a neighboring pin (with resistor to SD pin) to input
217  // 5. Read neighboring pin, check that it's low.
218  // This wouldn't work in the case that the SPI driver is holding the pin low,
219  // but in the other cases it would work.
220  // 6. Set SD pin high
221  // 7. Read neighboring pin, check that it's high.
222  //static void test_CloseReleasePinsToTristate(void)
223  //{
224  // const unsigned int kDoPinMask = 1 << kDoPin;
225  // const unsigned int kClkPinMask = 1 << kClkPin;
226  // const unsigned int kDiPinMask = 1 << kDiPin;
227  // const unsigned int kCsPinMask = 1 << kCsPin;
228  //
229  // const unsigned int kSdPinMask = kDoPinMask | kClkPinMask | kDiPinMask | kCsPinMask;
230  //
231  // TEST_ASSERT_EQUAL_INT(0, sut.Open("RANDOM.RND", 'w'));
232  // TEST_ASSERT_BITS_HIGH(kSdPinMask, DIRA);
233  // sut.Close();
234  // TEST_ASSERT_BITS_LOW(kSdPinMask, 0xFFFFFFFF);
235  //}
236 
237 
238 
239  // -----------------------------------------------------------------------------
240  // Writing to and from files
241  // -----------------------------------------------------------------------------
242 
243  static void test_PutChar(void) {
244  sut.Open("RANDOM.RND", 'w');
245  TEST_ASSERT_EQUAL_INT(0, sut.Put('a'));
246  }
247 
248  static void test_GetCharFromExistingFile(void) {
249  sut.Open("RANDOM.RND", 'd');
250  sut.Open("RANDOM.RND", 'w');
251  sut.Put('x');
252  sut.Open("RANDOM.RND", 'r');
253  TEST_ASSERT_EQUAL_INT('x', sut.Get());
254  }
255 
256  static void test_GetCharAfterEndOfFile(void) {
257  sut.Open("RANDOM.RND", 'd');
258  sut.Open("RANDOM.RND", 'w');
259  sut.Put('x');
260  sut.Open("RANDOM.RND", 'r');
261  sut.Get();
262  TEST_ASSERT_EQUAL_INT(-1, sut.Get());
263  }
264 
265  static void test_PutCharAppend(void) {
266  sut.Open("APPEND.TXT", 'a');
267  TEST_ASSERT_EQUAL_INT(0, sut.Put('-'));
268  sut.Open("APPEND.TXT", 'r');
269  TEST_ASSERT_EQUAL_INT('-', sut.Get());
270  TEST_ASSERT_EQUAL_INT(-1, sut.Get());
271  }
272 
273  static void test_Put(void) {
274  sut.Open("RANDOM.RND", 'w');
275  TEST_ASSERT_EQUAL_INT(5, sut.Put("Hello"));
276  sut.Open("RANDOM.RND", 'r');
277  TEST_ASSERT_EQUAL_INT('H', sut.Get());
278  TEST_ASSERT_EQUAL_INT('e', sut.Get());
279  TEST_ASSERT_EQUAL_INT('l', sut.Get());
280  TEST_ASSERT_EQUAL_INT('l', sut.Get());
281  TEST_ASSERT_EQUAL_INT('o', sut.Get());
282  TEST_ASSERT_EQUAL_INT(-1, sut.Get());
283  }
284 
285  static void test_PutSEmptyString(void) {
286  sut.Open("RANDOM.RND", 'w');
287  TEST_ASSERT_EQUAL_INT(0, sut.Put(""));
288  }
289 
290  static void test_Get(void) {
291  sut.Open("RANDOM.RND", 'w');
292  sut.Put("World\0ABC", 6);
293 
294  char buffer[6];
295  sut.Open("RANDOM.RND", 'r');
296  TEST_ASSERT_EQUAL_INT(6, sut.Get(buffer, 6));
297  TEST_ASSERT_EQUAL_STRING("World", buffer);
298  }
299 
300  static void test_GetBufferPastEndOfFile(void) {
301  sut.Open("RANDOM.RND", 'w');
302  sut.Put("World\0", 6);
303 
304  char buffer[10];
305  sut.Open("RANDOM.RND", 'r');
306  TEST_ASSERT_EQUAL_INT(6, sut.Get(buffer, 10));
307  TEST_ASSERT_EQUAL_STRING("World", buffer);
308  }
309 
310  static void test_WriteLargeFile(void) {
311  sut.Open("RANDOM.RND", 'w');
312 
313  const int kAlphabetCount = 2048;
314 
315  for (int i = 0; i < kAlphabetCount; i++) {
316  for (int letter = 'a'; letter <= 'z'; letter++) {
317  TEST_ASSERT_EQUAL_INT(0, sut.Put(letter));
318  }
319  }
320 
321  sut.Open("RANDOM.RND", 'r');
322  for (int i = 0; i < kAlphabetCount; i++) {
323  for (int letter = 'a'; letter <= 'z'; letter++) {
324  TEST_ASSERT_EQUAL_INT(letter, sut.Get());
325  }
326  }
327 
328  TEST_ASSERT_EQUAL_INT(-1, sut.Get());
329  }
330 
331 
332  // -----------------------------------------------------------------------------
333  // Test file system functionality
334  // -----------------------------------------------------------------------------
335 
336  static void test_SetDate(void) {
337  // Fat16 date and time information here:
338  // http://www.maverick-os.dk/FileSystemFormats/FAT16_FileSystem.html#LastWriteTime
339 
340  //Hour = 3, minute = 30, seconds = 58
341  // 0bHHHHHMMMMMMSSSSS
342  int time = 0b0001101111011101;
343 
344  // Year = 2000, month = 1, day = 2
345  // 0bYYYYYYYMMMMDDDDD
346  int date = 0b0010100000100010;
347 
348  int datetime = (date << 16) + time;
349 
350  TEST_ASSERT_BITS(0xFFFFFFFF, datetime, sut.SetDate(2000, 1, 2, 3, 30, 58));
351  }
352 
353  static void test_SeekSmallFile(void) {
354  sut.Open("RANDOM.RND", 'w');
355  sut.Put("Hello World!");
356  sut.Open("RANDOM.RND", 'r');
357 
358  for (int i = 0; i < 5; i++)
359  sut.Get();
360 
361  TEST_ASSERT_EQUAL_INT(0, sut.Seek(2));
362  TEST_ASSERT_EQUAL_INT('l', sut.Get());
363  }
364 
365  static void test_SeekOnWriteAfterOpening(void) {
366  sut.Open("RANDOM.RND", 'w');
367  TEST_ASSERT_EQUAL_INT(-1, sut.Seek(0));
368  }
369 
370  static void test_SeekOnWriteAfterWriting(void) {
371  sut.Open("RANDOM.RND", 'w');
372  sut.Put("Hello World!");
373  TEST_ASSERT_EQUAL_INT(-1, sut.Seek(0));
374  }
375 
376  static void test_SeekOnWriteAndCanStillWriteAfter(void) {
377  sut.Open("RANDOM.RND", 'w');
378  sut.Put("Hello");
379  sut.Seek(0);
380  sut.Put("World");
381  sut.Open("RANDOM.RND", 'r');
382  for (int i = 0; i < 5; i++)
383  sut.Get();
384  TEST_ASSERT_EQUAL_INT('W', sut.Get());
385  }
386 
387  static void test_SeekOnRead(void) {
388  sut.Open("RANDOM.RND", 'w');
389  sut.Put("Hello World");
390  sut.Open("RANDOM.RND", 'r');
391  sut.Seek(6);
392  TEST_ASSERT_EQUAL_INT('W', sut.Get());
393  }
394 
395  static void test_SeekOnLargeFile(void) {
396  //Should be more than 32KB
397  //32KB clusters * 1024 B/Clust / 16 byte test sequence * 1.5 cluster span = 3072
398  //Repeat a 16 byte test sequence across 1.5 32KB clusters.
399 
400  sut.Open("RANDOM.RND", 'w');
401 
402  for (int i = 0; i < 3072; i++)
403  for (char testchar = 'a'; testchar <= 'p'; testchar++)
404  sut.Put(testchar);
405 
406  sut.Open("RANDOM.RND", 'r');
407 
408  //Back across cluster boundry
409  TEST_ASSERT_EQUAL_INT(0, sut.Seek(0));
410  TEST_ASSERT_EQUAL_INT('a', sut.Get());
411 
412  //Within 32KB cluster
413  TEST_ASSERT_EQUAL_INT(0, sut.Seek(16 * 1024 + 3));
414  TEST_ASSERT_EQUAL_INT('d', sut.Get());
415 
416  //Across cluster boundry
417  TEST_ASSERT_EQUAL_INT(0, sut.Seek(40 * 1024 + 8));
418  TEST_ASSERT_EQUAL_INT('i', sut.Get());
419  }
420 
421  static void test_GetClusterSize(void) {
422  TEST_ASSERT_EQUAL_INT_MESSAGE(32768, sut.GetClusterSize(), "SD card should be formatted in 32K clusters.");
423  }
424 
425  static void test_getNextFileFindsAllFiles(void) {
426  //TODO(SRLM): Does this assume a that there are files on the disk at this point?
427  const int MAXIMUM_FILE_COUNT = 100;
428  int count;
429  for (count = 0; count < MAXIMUM_FILE_COUNT; count++) {
430 
431  char filename [13];
432  sut.OpenRootDirectory();
433  if (sut.NextFile(filename) != true) {
434  break;
435  }
436  sut.Open(filename, 'd');
437  }
438 
439  TEST_ASSERT_TRUE(count != MAXIMUM_FILE_COUNT);
440 
441  }
442 
443  static void test_getNextFileFindsCorrectFiles(void) {
444 
445 
446  const int FILECOUNT = 3;
447 
448  //Note: filenames must be uppercase!
449  const char * filenames[FILECOUNT];
450  filenames[0] = "NEXTA.TXT";
451  filenames[1] = "NEXTB.TXT";
452  filenames[2] = "NEXTC.TXT";
453 
454  bool filenameFound[FILECOUNT];
455  for (int filenameFoundI = 0; filenameFoundI < FILECOUNT; filenameFoundI++) {
456  filenameFound[filenameFoundI] = false;
457  }
458 
459 
460  sut.Open(filenames[0], 'w');
461  sut.Put('A');
462 
463  sut.Open(filenames[1], 'w');
464  sut.Put('B');
465 
466  sut.Open(filenames[2], 'w');
467  sut.Put('C');
468 
469  sut.OpenRootDirectory();
470 
471  char nextFilename[13];
472  while (sut.NextFile(nextFilename) == true) {
473  int i;
474  for (i = 0; i < FILECOUNT; i++) {
475  if (strcmp(filenames[i], nextFilename) == 0) {
476  TEST_ASSERT_FALSE_MESSAGE(filenameFound[i], "Should not already be found.");
477  filenameFound[i] = true;
478  break;
479  }
480  }
481  TEST_ASSERT_TRUE_MESSAGE(i != FILECOUNT, "Unmatched filename!");
482  }
483 
484  for (int i = 0; i < FILECOUNT; i++) {
485  TEST_ASSERT_TRUE(filenameFound[i]);
486  }
487  }
488 
489  static void test_OpenRootDirMultipleTimesInARowReturnsAllFilesEveryTime(void) {
490  const int FILECOUNT = 3;
491 
492  //Note: filenames must be uppercase!
493  const char * filenames[FILECOUNT];
494  filenames[0] = "NEXTA.TXT";
495  filenames[1] = "NEXTB.TXT";
496  filenames[2] = "NEXTC.TXT";
497 
498  bool filenameFound[FILECOUNT];
499 
500  sut.Open(filenames[0], 'w');
501  sut.Put('A');
502 
503  sut.Open(filenames[1], 'w');
504  sut.Put('B');
505 
506  sut.Open(filenames[2], 'w');
507  sut.Put('C');
508 
509 
510  for (int iterationsThroughRoot = 0; iterationsThroughRoot < 5; iterationsThroughRoot++) {
511  //printf("\r\nIteration: %i", iterationsThroughRoot);
512 
513  for (int filenameFoundI = 0; filenameFoundI < FILECOUNT; filenameFoundI++) {
514  filenameFound[filenameFoundI] = false;
515  }
516 
517  sut.OpenRootDirectory();
518 
519  char nextFilename[13];
520  while (sut.NextFile(nextFilename) == true) {
521  int i;
522  for (i = 0; i < FILECOUNT; i++) {
523  if (strcmp(filenames[i], nextFilename) == 0) {
524  TEST_ASSERT_FALSE_MESSAGE(filenameFound[i], "Should not already be found.");
525  filenameFound[i] = true;
526  break;
527  }
528  }
529  TEST_ASSERT_TRUE_MESSAGE(i != FILECOUNT, "Unmatched filename!");
530  }
531 
532  for (int i = 0; i < FILECOUNT; i++) {
533  TEST_ASSERT_TRUE(filenameFound[i]);
534  }
535  }
536  }
537 
538  static void test_GetFilesizeSmall(void) {
539  const char filename[] = "FILESIZE.TXT";
540  const char content[] = "Hello";
541  sut.Open(filename, 'w');
542  sut.Put(content);
543  sut.Close();
544  sut.Open(filename, 'r');
545  TEST_ASSERT_EQUAL_INT(strlen(content), sut.GetFilesize());
546  sut.Close();
547  }
548 
549  static void test_GetFilesizeNothing(void) {
550  const char filename[] = "EMPTY.TXT";
551  sut.Open(filename, 'w');
552  sut.Close();
553  sut.Open(filename, 'r');
554  TEST_ASSERT_EQUAL_INT(0, sut.GetFilesize());
555  sut.Close();
556  }
557 
558  static void test_GetFilesizeLotsOfContent(void) {
559  const char filename[] = "LARGE.TXT";
560  sut.Open(filename, 'w');
561 
562  const int kByteCount = 1024 * 128;
563 
564  for (int i = 0; i < kByteCount; i++) {
565  sut.Put('A');
566  }
567 
568  sut.Close();
569 
570  sut.Open(filename, 'r');
571 
572  TEST_ASSERT_EQUAL_INT(kByteCount, sut.GetFilesize());
573 
574  sut.Close();
575  }
576 
577  static void test_GetFilesizeAfterReadingSome(void) {
578  const char filename[] = "AFTER.TXT";
579  const char content[] = "Some text to take up space";
580  sut.Open(filename, 'w');
581  sut.Put(content);
582  sut.Close();
583  sut.Open(filename, 'r');
584  for (int i = 0; i < 5; i++) {
585  sut.Get();
586  }
587  TEST_ASSERT_EQUAL_INT(strlen(content), sut.GetFilesize());
588  }
589 
590  static void test_GetFilesizeAfterReadingPastEndOfFile(void) {
591  const char filename[] = "AFTER.TXT";
592  const char content[] = "Some text to take up space";
593  sut.Open(filename, 'w');
594  sut.Put(content);
595  sut.Close();
596  sut.Open(filename, 'r');
597  while (sut.Get() != -1) {
598  }
599 
600  TEST_ASSERT_EQUAL_INT(strlen(content), sut.GetFilesize());
601  }
602 
603 };