1 #include <errno.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8
9 #include "test.h"
10
11 const char *progname;
12 static const char *LIBSPECTRUM_MIN_VERSION = "0.4.0";
13
14 typedef test_return_t (*test_fn)( void );
15
16 #ifndef O_BINARY
17 #define O_BINARY 0
18 #endif
19
20 int
read_file(libspectrum_byte ** buffer,size_t * length,const char * filename)21 read_file( libspectrum_byte **buffer, size_t *length, const char *filename )
22 {
23 int fd;
24 struct stat info;
25 ssize_t bytes;
26
27 fd = open( filename, O_RDONLY | O_BINARY );
28 if( fd == -1 ) {
29 fprintf( stderr, "%s: couldn't open `%s': %s\n", progname, filename,
30 strerror( errno ) );
31 return errno;
32 }
33
34 if( fstat( fd, &info ) ) {
35 fprintf( stderr, "%s: couldn't stat `%s': %s\n", progname, filename,
36 strerror( errno ) );
37 return errno;
38 }
39
40 *length = info.st_size;
41 *buffer = libspectrum_malloc( *length );
42
43 bytes = read( fd, *buffer, *length );
44 if( bytes == -1 ) {
45 fprintf( stderr, "%s: error reading from `%s': %s\n", progname, filename,
46 strerror( errno ) );
47 return errno;
48 } else if( bytes < *length ) {
49 fprintf( stderr, "%s: read only %lu of %lu bytes from `%s'\n", progname,
50 (unsigned long)bytes, (unsigned long)*length, filename );
51 return 1;
52 }
53
54 if( close( fd ) ) {
55 fprintf( stderr, "%s: error closing `%s': %s\n", progname, filename,
56 strerror( errno ) );
57 return errno;
58 }
59
60 return 0;
61 }
62
63 static test_return_t
load_tape(libspectrum_tape ** tape,const char * filename,libspectrum_error expected_result)64 load_tape( libspectrum_tape **tape, const char *filename,
65 libspectrum_error expected_result )
66 {
67 libspectrum_byte *buffer = NULL;
68 size_t filesize = 0;
69
70 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
71
72 *tape = libspectrum_tape_alloc();
73
74 if( libspectrum_tape_read( *tape, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
75 filename ) != expected_result ) {
76 fprintf( stderr, "%s: reading `%s' did not give expected result\n",
77 progname, filename );
78 libspectrum_tape_free( *tape );
79 libspectrum_free( buffer );
80 return TEST_INCOMPLETE;
81 }
82
83 libspectrum_free( buffer );
84
85 return TEST_PASS;
86 }
87
88 static test_return_t
read_tape(const char * filename,libspectrum_error expected_result)89 read_tape( const char *filename, libspectrum_error expected_result )
90 {
91 libspectrum_tape *tape;
92 test_return_t r;
93
94 r = load_tape( &tape, filename, expected_result );
95 if( r != TEST_PASS ) return r;
96
97 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
98
99 return TEST_PASS;
100 }
101
102 static test_return_t
read_snap(const char * filename,const char * filename_to_pass,libspectrum_error expected_result)103 read_snap( const char *filename, const char *filename_to_pass,
104 libspectrum_error expected_result )
105 {
106 libspectrum_byte *buffer = NULL;
107 size_t filesize = 0;
108 libspectrum_snap *snap;
109
110 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
111
112 snap = libspectrum_snap_alloc();
113
114 if( libspectrum_snap_read( snap, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
115 filename_to_pass ) != expected_result ) {
116 fprintf( stderr, "%s: reading `%s' did not give expected result\n",
117 progname, filename );
118 libspectrum_snap_free( snap );
119 libspectrum_free( buffer );
120 return TEST_INCOMPLETE;
121 }
122
123 libspectrum_free( buffer );
124
125 if( libspectrum_snap_free( snap ) ) return TEST_INCOMPLETE;
126
127 return TEST_PASS;
128 }
129
130 static test_return_t
play_tape(const char * filename)131 play_tape( const char *filename )
132 {
133 libspectrum_byte *buffer = NULL;
134 size_t filesize = 0;
135 libspectrum_tape *tape;
136 libspectrum_dword tstates;
137 int flags;
138
139 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
140
141 tape = libspectrum_tape_alloc();
142
143 if( libspectrum_tape_read( tape, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
144 filename ) ) {
145 libspectrum_tape_free( tape );
146 libspectrum_free( buffer );
147 return TEST_INCOMPLETE;
148 }
149
150 libspectrum_free( buffer );
151
152 do {
153
154 if( libspectrum_tape_get_next_edge( &tstates, &flags, tape ) ) {
155 libspectrum_tape_free( tape );
156 return TEST_INCOMPLETE;
157 }
158
159 } while( !( flags & LIBSPECTRUM_TAPE_FLAGS_STOP ) );
160
161 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
162
163 return TEST_PASS;
164 }
165
166 /* Specific tests begin here */
167
168 /* Test for bugs #1479451 and #1706994: tape object incorrectly freed
169 after reading invalid tape */
170 static test_return_t
test_1(void)171 test_1( void )
172 {
173 return read_tape( STATIC_TEST_PATH( "invalid.tzx" ), LIBSPECTRUM_ERROR_UNKNOWN );
174 }
175
176 /* Test for bugs #1720238: TZX turbo blocks with zero pilot pulses and
177 #1720270: freeing a turbo block with no data produces segfault */
178 static test_return_t
test_2(void)179 test_2( void )
180 {
181 libspectrum_byte *buffer = NULL;
182 size_t filesize = 0;
183 libspectrum_tape *tape;
184 const char *filename = STATIC_TEST_PATH( "turbo-zeropilot.tzx" );
185 libspectrum_dword tstates;
186 int flags;
187
188 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
189
190 tape = libspectrum_tape_alloc();
191
192 if( libspectrum_tape_read( tape, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
193 filename ) ) {
194 libspectrum_tape_free( tape );
195 libspectrum_free( buffer );
196 return TEST_INCOMPLETE;
197 }
198
199 libspectrum_free( buffer );
200
201 if( libspectrum_tape_get_next_edge( &tstates, &flags, tape ) ) {
202 libspectrum_tape_free( tape );
203 return TEST_INCOMPLETE;
204 }
205
206 if( flags ) {
207 fprintf( stderr, "%s: reading first edge of `%s' gave unexpected flags 0x%04x; expected 0x0000\n",
208 progname, filename, flags );
209 libspectrum_tape_free( tape );
210 return TEST_FAIL;
211 }
212
213 if( tstates != 667 ) {
214 fprintf( stderr, "%s: first edge of `%s' was %d tstates; expected 667\n",
215 progname, filename, tstates );
216 libspectrum_tape_free( tape );
217 return TEST_FAIL;
218 }
219
220 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
221
222 return TEST_PASS;
223 }
224
225 /* Test for bug #1725864: writing empty .tap file causes crash */
226 static test_return_t
test_3(void)227 test_3( void )
228 {
229 libspectrum_tape *tape;
230 libspectrum_byte *buffer = (libspectrum_byte*)1;
231 size_t length = 0;
232
233 tape = libspectrum_tape_alloc();
234
235 if( libspectrum_tape_write( &buffer, &length, tape, LIBSPECTRUM_ID_TAPE_TAP ) ) {
236 libspectrum_tape_free( tape );
237 return TEST_INCOMPLETE;
238 }
239
240 /* `buffer' should now have been set to NULL */
241 if( buffer ) {
242 fprintf( stderr, "%s: `buffer' was not NULL after libspectrum_tape_write()\n", progname );
243 libspectrum_tape_free( tape );
244 return TEST_FAIL;
245 }
246
247 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
248
249 return TEST_PASS;
250 }
251
252 /* Test for bug #1753279: invalid compressed file causes crash */
253 static test_return_t
test_4(void)254 test_4( void )
255 {
256 const char *filename = STATIC_TEST_PATH( "invalid.gz" );
257 return read_snap( filename, filename, LIBSPECTRUM_ERROR_UNKNOWN );
258 }
259
260 /* Further test for bug #1753279: invalid compressed file causes crash */
261 static test_return_t
test_5(void)262 test_5( void )
263 {
264 return read_snap( STATIC_TEST_PATH( "invalid.gz" ), NULL, LIBSPECTRUM_ERROR_UNKNOWN );
265 }
266
267 /* Test for bug #1753938: pointer wraparound causes segfault */
268 static test_return_t
test_6(void)269 test_6( void )
270 {
271 const char *filename = STATIC_TEST_PATH( "invalid.szx" );
272 return read_snap( filename, filename, LIBSPECTRUM_ERROR_CORRUPT );
273 }
274
275 /* Test for bug #1755124: lack of sanity check in GDB code */
276 static test_return_t
test_7(void)277 test_7( void )
278 {
279 return read_tape( STATIC_TEST_PATH( "invalid-gdb.tzx" ), LIBSPECTRUM_ERROR_CORRUPT );
280 }
281
282 /* Test for bug #1755372: empty DRB causes segfault */
283 static test_return_t
test_8(void)284 test_8( void )
285 {
286 return read_tape( STATIC_TEST_PATH( "empty-drb.tzx" ), LIBSPECTRUM_ERROR_NONE );
287 }
288
289 /* Test for bug #1755539: problems with invalid archive info block */
290 static test_return_t
test_9(void)291 test_9( void )
292 {
293 return read_tape( STATIC_TEST_PATH( "invalid-archiveinfo.tzx" ), LIBSPECTRUM_ERROR_CORRUPT );
294 }
295
296 /* Test for bug #1755545: invalid hardware info blocks can leak memory */
297 static test_return_t
test_10(void)298 test_10( void )
299 {
300 return read_tape( STATIC_TEST_PATH( "invalid-hardwareinfo.tzx" ), LIBSPECTRUM_ERROR_CORRUPT );
301 }
302
303 /* Test for bug #1756375: invalid Warajevo tape block offset causes segfault */
304 static test_return_t
test_11(void)305 test_11( void )
306 {
307 return read_tape( STATIC_TEST_PATH( "invalid-warajevo-blockoffset.tap" ), LIBSPECTRUM_ERROR_CORRUPT );
308 }
309
310 /* Test for bug #1757587: invalid custom info block causes memory leak */
311 static test_return_t
test_12(void)312 test_12( void )
313 {
314 return read_tape( STATIC_TEST_PATH( "invalid-custominfo.tzx" ), LIBSPECTRUM_ERROR_CORRUPT );
315 }
316
317 /* Test for bug #1758860: loop end without a loop start block accesses
318 uninitialised memory */
319 static test_return_t
test_13(void)320 test_13( void )
321 {
322 libspectrum_byte *buffer = NULL;
323 size_t filesize = 0;
324 libspectrum_tape *tape;
325 const char *filename = STATIC_TEST_PATH( "loopend.tzx" );
326 libspectrum_dword tstates;
327 int flags;
328
329 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
330
331 tape = libspectrum_tape_alloc();
332
333 if( libspectrum_tape_read( tape, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
334 filename ) ) {
335 libspectrum_tape_free( tape );
336 libspectrum_free( buffer );
337 return TEST_INCOMPLETE;
338 }
339
340 libspectrum_free( buffer );
341
342 if( libspectrum_tape_get_next_edge( &tstates, &flags, tape ) ) {
343 libspectrum_tape_free( tape );
344 return TEST_INCOMPLETE;
345 }
346
347 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
348
349 return TEST_PASS;
350 }
351
352 /* Test for bug #1758860: TZX loop blocks broken */
353 static test_return_t
test_14(void)354 test_14( void )
355 {
356 return play_tape( STATIC_TEST_PATH( "loop.tzx" ) );
357 }
358
359 /* Test for bug #1802607: TZX loop blocks still broken */
360 static test_return_t
test_16(void)361 test_16( void )
362 {
363 return play_tape( STATIC_TEST_PATH( "loop2.tzx" ) );
364 }
365
366 /* Test for bug #1802618: TZX jump blocks broken */
367 static test_return_t
test_17(void)368 test_17( void )
369 {
370 return play_tape( STATIC_TEST_PATH( "jump.tzx" ) );
371 }
372
373 /* Test for bug #1821425: crashes writing and reading empty CSW files */
374 static test_return_t
test_18(void)375 test_18( void )
376 {
377 return play_tape( STATIC_TEST_PATH( "empty.csw" ) );
378 }
379
380 /* Test for bug #1828945: .tap writing code does not handle all block types */
381 static test_return_t
test_19(void)382 test_19( void )
383 {
384 libspectrum_byte *buffer = NULL;
385 size_t length = 0;
386 libspectrum_tape *tape;
387 const char *filename = DYNAMIC_TEST_PATH( "complete-tzx.tzx" );
388 test_return_t r;
389
390 r = load_tape( &tape, filename, LIBSPECTRUM_ERROR_NONE );
391 if( r ) return r;
392
393 if( libspectrum_tape_write( &buffer, &length, tape,
394 LIBSPECTRUM_ID_TAPE_TAP ) ) {
395 fprintf( stderr, "%s: writing `%s' to a .tap file was not successful\n",
396 progname, filename );
397 libspectrum_tape_free( tape );
398 return TEST_INCOMPLETE;
399 }
400
401 libspectrum_free( buffer );
402
403 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
404
405 return TEST_PASS;
406 }
407
408 /* Tests for bug #1841085: SP not sanity checked when reading .sna files;
409 also tests bug #1841111: compressed snapshots cause segfault */
410 static test_return_t
test_20(void)411 test_20( void )
412 {
413 const char *filename = STATIC_TEST_PATH( "sp-2000.sna.gz" );
414 return read_snap( filename, filename, LIBSPECTRUM_ERROR_CORRUPT );
415 }
416
417 static test_return_t
test_21(void)418 test_21( void )
419 {
420 const char *filename = STATIC_TEST_PATH( "sp-ffff.sna.gz" );
421 return read_snap( filename, filename, LIBSPECTRUM_ERROR_CORRUPT );
422 }
423
424 /* Tests for bug #2002682: .mdr code does not correctly handle write protect
425 flag */
426 static test_return_t
test_22(void)427 test_22( void )
428 {
429 libspectrum_byte *buffer = NULL;
430 size_t filesize = 0;
431 libspectrum_microdrive *mdr;
432 const char *filename = STATIC_TEST_PATH( "writeprotected.mdr" );
433 test_return_t r;
434
435 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
436
437 /* writeprotected.mdr deliberately includes an extra 0 on the end;
438 we want this in the buffer so we know what happens if we read off the
439 end of the file; however, we don't want it in the length */
440 filesize--;
441
442 mdr = libspectrum_microdrive_alloc();
443
444 if( libspectrum_microdrive_mdr_read( mdr, buffer, filesize ) ) {
445 libspectrum_microdrive_free( mdr );
446 libspectrum_free( buffer );
447 return TEST_INCOMPLETE;
448 }
449
450 libspectrum_free( buffer );
451
452 r = libspectrum_microdrive_write_protect( mdr ) ? TEST_PASS : TEST_FAIL;
453
454 libspectrum_microdrive_free( mdr );
455
456 return r;
457 }
458
459 static test_return_t
test_23(void)460 test_23( void )
461 {
462 libspectrum_byte *buffer = NULL;
463 size_t filesize = 0, length;
464 libspectrum_microdrive *mdr;
465 const char *filename = STATIC_TEST_PATH( "writeprotected.mdr" );
466 test_return_t r;
467
468 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
469
470 /* writeprotected.mdr deliberately includes an extra 0 on the end;
471 we want this in the buffer so we know what happens if we read off the
472 end of the file; however, we don't want it in the length */
473 filesize--;
474
475 mdr = libspectrum_microdrive_alloc();
476
477 if( libspectrum_microdrive_mdr_read( mdr, buffer, filesize ) ) {
478 libspectrum_microdrive_free( mdr );
479 libspectrum_free( buffer );
480 return TEST_INCOMPLETE;
481 }
482
483 libspectrum_free( buffer ); buffer = NULL;
484
485 libspectrum_microdrive_mdr_write( mdr, &buffer, &length );
486
487 libspectrum_microdrive_free( mdr );
488
489 r = ( length == filesize && buffer[ length - 1 ] == 1 ) ? TEST_PASS : TEST_FAIL;
490
491 libspectrum_free( buffer );
492
493 return r;
494 }
495
496 static test_return_t
test_24(void)497 test_24( void )
498 {
499 const char *filename = DYNAMIC_TEST_PATH( "complete-tzx.tzx" );
500 libspectrum_byte *buffer;
501 size_t filesize;
502 libspectrum_tape *tape;
503 libspectrum_tape_iterator it;
504 libspectrum_tape_block *block;
505 libspectrum_dword expected_sizes[20] = {
506 15216886, /* ROM */
507 3493371, /* Turbo */
508 356310, /* Pure tone */
509 1761, /* Pulses */
510 1993724, /* Pure data */
511 2163000, /* Pause */
512 0, /* Group start */
513 0, /* Group end */
514 0, /* Jump */
515 205434, /* Pure tone */
516 0, /* Loop start */
517 154845, /* Pure tone */
518 0, /* Loop end */
519 0, /* Stop tape if in 48K mode */
520 0, /* Comment */
521 0, /* Message */
522 0, /* Archive info */
523 0, /* Hardware */
524 0, /* Custom info */
525 771620, /* Pure tone */
526 };
527 libspectrum_dword *next_size = &expected_sizes[ 0 ];
528 test_return_t r = TEST_PASS;
529
530 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
531
532 tape = libspectrum_tape_alloc();
533
534 if( libspectrum_tape_read( tape, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
535 filename ) ) {
536 libspectrum_tape_free( tape );
537 libspectrum_free( buffer );
538 return TEST_INCOMPLETE;
539 }
540
541 libspectrum_free( buffer );
542
543 block = libspectrum_tape_iterator_init( &it, tape );
544
545 while( block )
546 {
547 libspectrum_dword actual_size = libspectrum_tape_block_length( block );
548
549 if( actual_size != *next_size )
550 {
551 fprintf( stderr, "%s: block had length %lu, but expected %lu\n", progname, (unsigned long)actual_size, (unsigned long)*next_size );
552 r = TEST_FAIL;
553 break;
554 }
555
556 block = libspectrum_tape_iterator_next( &it );
557 next_size++;
558 }
559
560 if( libspectrum_tape_free( tape ) ) return TEST_INCOMPLETE;
561
562 return r;
563 }
564
565 static test_return_t
test_25(void)566 test_25( void )
567 {
568 const char *filename = STATIC_TEST_PATH( "empty.z80" );
569 libspectrum_byte *buffer = NULL;
570 size_t filesize = 0, length = 0;
571 libspectrum_snap *snap;
572 int flags;
573 test_return_t r = TEST_INCOMPLETE;
574
575 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
576
577 snap = libspectrum_snap_alloc();
578
579 if( libspectrum_snap_read( snap, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
580 filename ) != LIBSPECTRUM_ERROR_NONE ) {
581 fprintf( stderr, "%s: reading `%s' failed\n", progname, filename );
582 libspectrum_snap_free( snap );
583 libspectrum_free( buffer );
584 return TEST_INCOMPLETE;
585 }
586
587 libspectrum_free( buffer );
588 buffer = NULL;
589
590 if( libspectrum_snap_write( &buffer, &length, &flags, snap,
591 LIBSPECTRUM_ID_SNAPSHOT_SNA, NULL, 0 ) !=
592 LIBSPECTRUM_ERROR_NONE ) {
593 fprintf( stderr, "%s: serialising to SNA failed\n", progname );
594 libspectrum_snap_free( snap );
595 return TEST_INCOMPLETE;
596 }
597
598 libspectrum_snap_free( snap );
599 snap = libspectrum_snap_alloc();
600
601 if( libspectrum_snap_read( snap, buffer, length, LIBSPECTRUM_ID_SNAPSHOT_SNA,
602 NULL ) != LIBSPECTRUM_ERROR_NONE ) {
603 fprintf( stderr, "%s: restoring from SNA failed\n", progname );
604 libspectrum_snap_free( snap );
605 libspectrum_free( buffer );
606 return TEST_INCOMPLETE;
607 }
608
609 libspectrum_free( buffer );
610
611 if( libspectrum_snap_pc( snap ) != 0x1234 ) {
612 fprintf( stderr, "%s: PC is 0x%04x, not the expected 0x1234\n", progname,
613 libspectrum_snap_pc( snap ) );
614 r = TEST_FAIL;
615 } else if( libspectrum_snap_sp( snap ) != 0x8000 ) {
616 fprintf( stderr, "%s: SP is 0x%04x, not the expected 0x8000\n", progname,
617 libspectrum_snap_sp( snap ) );
618 r = TEST_FAIL;
619 } else {
620 r = TEST_PASS;
621 }
622
623 libspectrum_snap_free( snap );
624
625 return r;
626 }
627
628 /* Tests for bug #3078262: last out to 0x1ffd is not serialised into .z80
629 files */
630 static test_return_t
test_26(void)631 test_26( void )
632 {
633 const char *filename = STATIC_TEST_PATH( "plus3.z80" );
634 libspectrum_byte *buffer = NULL;
635 size_t filesize = 0, length = 0;
636 libspectrum_snap *snap;
637 int flags;
638 test_return_t r = TEST_INCOMPLETE;
639
640 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
641
642 snap = libspectrum_snap_alloc();
643
644 if( libspectrum_snap_read( snap, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
645 filename ) != LIBSPECTRUM_ERROR_NONE ) {
646 fprintf( stderr, "%s: reading `%s' failed\n", progname, filename );
647 libspectrum_snap_free( snap );
648 libspectrum_free( buffer );
649 return TEST_INCOMPLETE;
650 }
651
652 libspectrum_free( buffer );
653 buffer = NULL;
654
655 if( libspectrum_snap_write( &buffer, &length, &flags, snap,
656 LIBSPECTRUM_ID_SNAPSHOT_Z80, NULL, 0 ) !=
657 LIBSPECTRUM_ERROR_NONE ) {
658 fprintf( stderr, "%s: serialising to Z80 failed\n", progname );
659 libspectrum_snap_free( snap );
660 return TEST_INCOMPLETE;
661 }
662
663 libspectrum_snap_free( snap );
664 snap = libspectrum_snap_alloc();
665
666 if( libspectrum_snap_read( snap, buffer, length, LIBSPECTRUM_ID_SNAPSHOT_Z80,
667 NULL ) != LIBSPECTRUM_ERROR_NONE ) {
668 fprintf( stderr, "%s: restoring from Z80 failed\n", progname );
669 libspectrum_snap_free( snap );
670 libspectrum_free( buffer );
671 return TEST_INCOMPLETE;
672 }
673
674 if( libspectrum_snap_out_plus3_memoryport( snap ) == 0xaa ) {
675 r = TEST_PASS;
676 } else {
677 fprintf( stderr,
678 "%s: Last out to 0x1ffd is 0x%02x, not the expected 0xaa\n",
679 progname, libspectrum_snap_out_plus3_memoryport( snap ) );
680 r = TEST_FAIL;
681 }
682
683 libspectrum_snap_free( snap );
684
685 return r;
686 }
687
688 /* Tests for bug #2857419: SZX files were written with A and F reversed */
689 static test_return_t
test_27(void)690 test_27( void )
691 {
692 const char *filename = STATIC_TEST_PATH( "empty.szx" );
693 libspectrum_byte *buffer = NULL;
694 size_t filesize = 0;
695 libspectrum_snap *snap;
696 test_return_t r = TEST_INCOMPLETE;
697
698 if( read_file( &buffer, &filesize, filename ) ) return TEST_INCOMPLETE;
699
700 snap = libspectrum_snap_alloc();
701
702 if( libspectrum_snap_read( snap, buffer, filesize, LIBSPECTRUM_ID_UNKNOWN,
703 filename ) != LIBSPECTRUM_ERROR_NONE ) {
704 fprintf( stderr, "%s: reading `%s' failed\n", progname, filename );
705 libspectrum_snap_free( snap );
706 libspectrum_free( buffer );
707 return TEST_INCOMPLETE;
708 }
709
710 libspectrum_free( buffer );
711
712 if( libspectrum_snap_a( snap ) != 0x12 ) {
713 fprintf( stderr, "%s: A is 0x%02x, not the expected 0x12\n", progname,
714 libspectrum_snap_a( snap ) );
715 r = TEST_FAIL;
716 } else if( libspectrum_snap_f( snap ) != 0x34 ) {
717 fprintf( stderr, "%s: F is 0x%02x, not the expected 0x34\n", progname,
718 libspectrum_snap_f( snap ) );
719 r = TEST_FAIL;
720 } else if( libspectrum_snap_a_( snap ) != 0x56 ) {
721 fprintf( stderr, "%s: A' is 0x%02x, not the expected 0x56\n", progname,
722 libspectrum_snap_a_( snap ) );
723 r = TEST_FAIL;
724 } else if( libspectrum_snap_f_( snap ) != 0x78 ) {
725 fprintf( stderr, "%s: F' is 0x%02x, not the expected 0x78\n", progname,
726 libspectrum_snap_f_( snap ) );
727 r = TEST_FAIL;
728 } else {
729 r = TEST_PASS;
730 }
731
732 return r;
733 }
734
735 struct test_description {
736
737 test_fn test;
738 const char *description;
739 int active;
740
741 };
742
743 static struct test_description tests[] = {
744 { test_1, "Tape with unknown block", 0 },
745 { test_2, "TZX turbo data with zero pilot pulses and zero data", 0 },
746 { test_3, "Writing empty .tap file", 0 },
747 { test_4, "Invalid compressed file 1", 0 },
748 { test_5, "Invalid compressed file 2", 0 },
749 { test_6, "Pointer wraparound in SZX file", 0 },
750 { test_7, "Invalid TZX GDB", 0 },
751 { test_8, "Empty TZX DRB", 0 },
752 { test_9, "Invalid TZX archive info block", 0 },
753 { test_10, "Invalid hardware info block causes memory leak", 0 },
754 { test_11, "Invalid Warajevo tape file", 0 },
755 { test_12, "Invalid TZX custom info block causes memory leak", 0 },
756 { test_13, "TZX loop end block with loop start block", 0 },
757 { test_14, "TZX loop blocks", 0 },
758 { test_15, "Complete TZX file", 0 },
759 { test_16, "TZX loop blocks 2", 0 },
760 { test_17, "TZX jump blocks", 0 },
761 { test_18, "CSW empty file", 0 },
762 { test_19, "Complete TZX to TAP conversion", 0 },
763 { test_20, "SNA file with SP < 0x4000", 0 },
764 { test_21, "SNA file with SP = 0xffff", 0 },
765 { test_22, "MDR write protection 1", 0 },
766 { test_23, "MDR write protection 2", 0 },
767 { test_24, "Complete TZX timings", 0 },
768 { test_25, "Writing SNA file", 0 },
769 { test_26, "Writing +3 .Z80 file", 0 },
770 { test_27, "Reading old SZX file", 0 },
771 };
772
773 static size_t test_count = sizeof( tests ) / sizeof( tests[0] );
774
775 static void
parse_test_specs(char ** specs,int count)776 parse_test_specs( char **specs, int count )
777 {
778 int i, j;
779
780 for( i = 0; i < count; i++ ) {
781
782 const char *spec = specs[i];
783 const char *dash = strchr( spec, '-' );
784
785 if( dash ) {
786 int begin = atoi( spec ), end = atoi( dash + 1 );
787 if( begin < 1 ) begin = 1;
788 if( end == 0 || end > test_count ) end = test_count;
789 for( j = begin; j <= end; j++ ) tests[j-1].active = 1;
790 } else {
791 int test = atoi( spec );
792 if( test < 1 || test > test_count ) continue;
793 tests[ test - 1 ].active = 1;
794 }
795
796 }
797 }
798
799 int
main(int argc,char * argv[])800 main( int argc, char *argv[] )
801 {
802 struct test_description *test;
803 size_t i;
804 int tests_done = 0, tests_skipped = 0;
805 int pass = 0, fail = 0, incomplete = 0;
806
807 progname = argv[0];
808
809 if( libspectrum_check_version( LIBSPECTRUM_MIN_VERSION ) ) {
810 if( libspectrum_init() ) return 2;
811 } else {
812 fprintf( stderr, "%s: libspectrum version %s found, but %s required",
813 progname, libspectrum_version(), LIBSPECTRUM_MIN_VERSION );
814 return 2;
815 }
816
817 if( argc < 2 ) {
818 for( i = 0; i < test_count; i++ ) tests[i].active = 1;
819 } else {
820 parse_test_specs( &argv[1], argc - 1 );
821 }
822
823 for( i = 0, test = tests;
824 i < test_count;
825 i++, test++ ) {
826 printf( "Test %d: %s... ", (int)i + 1, test->description );
827 if( test->active ) {
828 tests_done++;
829 switch( test->test() ) {
830 case TEST_PASS:
831 printf( "passed\n" );
832 pass++;
833 break;
834 case TEST_FAIL:
835 printf( "FAILED\n" );
836 fail++;
837 break;
838 case TEST_INCOMPLETE:
839 printf( "NOT COMPLETE\n" );
840 incomplete++;
841 break;
842 }
843 } else {
844 tests_skipped++;
845 printf( "skipped\n" );
846 }
847
848 }
849
850 /* Stop silly divisions occuring */
851 if( !tests_done ) tests_done = 1;
852
853 printf( "\n%3d tests run\n\n", (int)test_count );
854 printf( "%3d passed (%6.2f%%)\n", pass, 100 * (float)pass/tests_done );
855 printf( "%3d failed (%6.2f%%)\n", fail, 100 * (float)fail/tests_done );
856 printf( "%3d incomplete (%6.2f%%)\n", incomplete, 100 * (float)incomplete/tests_done );
857 printf( "%3d skipped\n", tests_skipped );
858
859 if( fail == 0 && incomplete == 0 ) {
860 return 0;
861 } else {
862 return 1;
863 }
864 }
865