FreeCalypso > hg > fc-magnetite
view src/cs/drivers/drv_app/ffs/board/tcases.c @ 638:cab2f315827e
FFS dev.c: added Spansion PL032J to the "generic" table
With the discovery of first GTM900 and then Tango, it now appears that
Openmoko was not the only manuf after all who kept TI's TCS211 firmware
largely intact (as opposed to changing it beyond all recognition like
Compal, Chi-Mei and BenQ did), thus we are now getting new "alien" targets
on which we reuse the original manuf's FFS with IMEI and RF calibration
tables as if it were native. On these targets we use the original
device table for FFS, even though we previously thought that it would
never apply to any target other than dsample, leonardo and gtamodem.
We have previously added Samsung K5L33xxCAM (a new kind of multi-ID device)
to the generic table to support its use in Huawei GTM900-B modules; now
we got news that some slightly older GTM900-B specimen used S71PL032J
instead, so we are now adding PL032J as well.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Thu, 30 Jan 2020 17:45:48 +0000 |
parents | 945cf7f506b2 |
children |
line wrap: on
line source
/****************************************************************************** * Flash File System (ffs) * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com * * ffs test cases * * $Id: tcases.c 1.13.1.1.1.66 Thu, 08 Jan 2004 15:05:23 +0100 tsj $ * ******************************************************************************/ #ifndef TARGET #include "ffs.cfg" #endif #include "ffs/ffs_api.h" // Temp #include "ffs/ffs.h" #include "ffs/board/tffs.h" #include "ffs/board/core.h" // only for block/object recovery test flags #include "ffs/board/tdata.h" #include "ffs/board/ffstrace.h" #include "ffs/board/drv.h" #include "ffs/pcm.h" #if((TARGET == 1) || (RIV_ENV==1)) #include "rvf/rvf_api.h" // this include rv_general.h and rvf_target.h #include "rvm/rvm_use_id_list.h" #endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <assert.h> #define LUDIR "/europe/sweden" #define MFDIR "/pcm" // TODO: reimplement test case bfull (not valid any more). // TODO: Cleanup in beginning of each test case for as many cases as // possible, so they can be re-run. // TODO: Make as many cases as possible fully independent of number of // blocks and size of blocks in ffs. // TODO: Every testcase should test if it is applicable to the current // environment, e.g. some tests are impossible if e have a 2 or 3 block // file system. // TODO: test case for testing age functionality // TODO: Implement case_ren // TODO: Implement case_rm // NOTEME: DO something with test case flags (PC, IT, RND)? // Should we make a test case where we use case_lsr to make map of objects ( // ensuring mapsize is prime). In a loop, we select a random object (object // index[rnd%size]) and perform a random operation on that object? // Should test data (tdata[]) be of sizes 2^n+n or Xn+n where n = 0..15 // Should all test files have suffix ".<tdata-index>"? This would make very // easy data checking! // Add compiler define: WITH_PCM //unsigned char ffs_image[4*4*1024]; //int ffs_ram_image_address = (int) &ffs_image; /****************************************************************************** * Prototypes and Globals ******************************************************************************/ // Helper functions int case_cleanup(int min_space); int case_mk_rand_file(char *dirname, int max_size, int min_size); int ignore_file(char *pathname); int case_trace_mask(int p0, int p1); int case_reinit(int p0, int p1); int case_debug_help(int p0, int p1); struct object_s { char *name; struct xstat_s stat; }; const struct testcase_s testcase[]; // Benchmark (not in use yet) struct results_s { int w16B; int w256B; int w4096B; int rew4096B; int r16B; int r256B; int r4096B; int lfile; int ldir; int lonef; }; /****************************************************************************** * Collective Test Cases ******************************************************************************/ // test cases: all, alot, most, much + specific ones int case_all(int p0, int p1) { #if (TARGET == 1) UINT32 time_begin, elapsed; time_begin = tffs_timer_begin(); #endif // We have to run test case 'init' after test case 'format'. This is // because 'init' test case calls test_ffs_params_get() which // initializes variables used in many of the testcases. error = test_run("ninit;format;i;world;eu;pcm;apiexc;" "bigf;open;rw;seek;append;ren;mopen;" "jnl;irec;drec;trunc;brec;stat;" "lu;fc;root;dirs;frd;bfull;dsync;" "ssym;find;ri;x;fwflags;query;octrl"); #if (TARGET == 1) elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrAll, "Time elapsed %ds" NL, elapsed / 1000)); #endif test_statistics_print(); return error; // Not implemented test cases... return test_run("rm;pcm;"); } int case_alot(int p0, int p1) { return test_run("ninit;format;ri;i;ri;world;ri;eu;ri;fwflags;ri;" "bigf;ri;open;ri;rw;ri;seek;ri;append;ri;ren;ri;" "jnl;ri;irec;ri;brec;ri;stat;ri;trunc;ri;mopen;ri;" "lu;ri;fc;ri;root;ri;dirs;ri;frd;ri;bfull;ri;" "ssym;ri;find;ri;x;ri;dsync;ri;pcm;ri;query;ri;octrl"); } int case_tall(int p0, int p1) { return test_run("ninit;format;i;world;eu;fwflags;" "bigf;jnl;irec;drec;ren;apiexc;" "stat;open;rw;seek;trunc;append;octrl;" "lu;fc;root;dirs;frd;bfull;pcm;query;" "ssym;find;x;ex;bf;nb;mopen;dsync;brec"); } // Ad hoc test case int case_test(int p0, int p1) { return test_run("format;i;world;eu;ri;mopen1;ri;fwflags;ri;" "bigf;ri;jnl;ri;irec;ri;drec;ri;brec;ri;pcm;ri;" "trunc1;ri;stat;open1;ri;rw1;ri;seek1;ri;ren;ri;" "lu;ri;fc;ri;root;ri;dirs;ri;frd;ri;bfull;ri;" "ssym;ri;find;ri;x;ri;append;ri;dsync;ri;"); } extern struct dev_s dev; // Agressive all. Run case 'all' for dev.numblocks in the range dev.numblocks..4 int case_aall(int p0, int p1) { char myname[20]; int i, failed = 0; if (dev.numblocks * dev.blocksize > 1024*1024) strcpy(myname, "/ffs/b999"); else strcpy(myname, "/ffs/b99"); /** There's no need to test for i=127,126,125,124..3,2,1. So for i * >= 20 we progress a little faster. */ for (i = dev.numblocks; i >= 3; i -= (i >= 20 ? i/4 : 1)) { tw(tr(TR_FUNC, TrTest, "TEST aall. %d\n", i)); ttw(ttr(TTrTest, "TEST aall. %d" NL, i)); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); sprintf(myname, "/ffs/b%d", i); tffs_format(myname, 0x2BAD); expect(error, EFFS_OK); failed += test_run("i;world;eu;bigf;bfull;" "jnl;irec;drec;brec;stat;dsync;" "open;rw;seek;trunc;ren;mopen;" "lu;fc;root;dirs;frd;append;" "ssym;find;ri;x"); } return failed; } // This is a collection of all failing testcases to be investigated. int case_fail(int p0, int p1) { int result; const char imeifile[] = "/europe/norway/IMEI"; switch (p0) { case 1: tw(tr(TR_FUNC, TrTestHigh, "remember to run case_fcontrol before this one\n")); // Make symlink to imeifile and try to update it error = tffs_symlink("/europe/imie", imeifile); expect(error, EFFS_ACCESS); error = tffs_fwrite("/europe/imie", TDATA(1)); expect(error, EFFS_ACCESS); break; case 2: if (1) { const char bigfile[] = "/iceberg"; int bytes_max, file_size; char myname[] = "/ffs/b7"; error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format(myname, 0x2BAD); expect(error, EFFS_OK); ffs_query(Q_BYTES_FREE, (uint32 *) &bytes_max); // File the system with this huge file file_size = bytes_max; ttw(ttr(TTrTest, "Bigfile of size %d" NL, file_size)); tw(tr(TR_FUNC, TrTestHigh, "Bigfile of size %d\n", file_size)); error = tffs_fcreate(bigfile, (char *) tdata[TDATA_HUGE], file_size); expect(error, EFFS_OK); } break; default: result = 1; } return result; } // zzz to easy search ;-) // NOTE: Move the below tests extern char *tdata_huge; // Test that a FFS API blocking call that not is able to send a mail to FFS fails as expected and don't lock the system (it must unlock and delete the mutex). int case_okay(int p0, int p1) { error = tffs_fwrite("/test", tdata[TDATA_HUGE], 20); expect_ok(error); error = tffs_fread("/test", bigbuf, 20); expect_ok(error); bigbuf[20] = 0; tw(tr(TR_FUNC, TrTestHigh, "String '%s'\n", bigbuf)); return EFFS_OK; } // Run testcases in a random order. Only the test cases in the test case // table that are marked as re-runnable, are run. int case_rand(int p0, int p1) { int i, n, seed, max = 0, error = 0; const struct testcase_s *p; // This is a way to activate trace at a defined test number. If rand // test number 134 fail it is possible to activate trace at test number // 133 etc. Note we have to change activate_trace_nr manual before compile. int activate_trace_nr = 0; int trace_mask = 0xFFDFF; // NOTE: use p1 as active_trace_nr? or make it p0 = (p0 == 0 ? 117 : p0); // Number of test cases to run p1 = (p1 == 0 ? 567 : p1); // Initial seed seed = p1; if (seed == 1) { ; // TODO: Set seed as a variable of current time } // TODO: Initialize seed from p1. // First count total number of test cases for (p = testcase; p->name; p++) max++; tw(tr(TR_FUNC, TrTestHigh, "Number of available random test cases = %d\n", max)); for (i = 0; i < p0; i++) { do { n = rand() % max; } while ((testcase[n].flags & RND) == 0); if ((i + 1) == activate_trace_nr) { #if (TARGET == 0) tr_init(trace_mask, 2, 0 ); #else ttr_init(trace_mask); #endif tw(tr_bstat()); } tw(tr(TR_FUNC, TrTest, "Nr: %d", i + 1)); ttw(ttr(TTrTest, "Nr: %d" NL, i + 1)); if ((error = test_run(testcase[n].name))) break; } if (p1 == 1) tw(tr(TR_FUNC, TrTestHigh, "Initial seed = %d\n", seed)); test_statistics_print(); return error; } /****************************************************************************** * Population Tests ******************************************************************************/ int case_world(int p0, int p1) { int i; const char *dirs[] = { "/antarctica", "/africa", "/asia", "/europe", "/north-america", "/south-america", "/australia" }; // Cleanup for (i = 0; i < sizeof(dirs)/sizeof(char *); i++) { tffs_remove(dirs[i]); } for (i = 0; i < sizeof(dirs)/sizeof(char *); i++) { error = tffs_mkdir(dirs[i]); expect(error, EFFS_OK); } return 0; } int case_europe(int p0, int p1) { int i; const char *dirs[] = { "/europe/denmark", "/europe/sweden", "/europe/norway", "/europe/finland" }; // Cleanup for (i = 0; i < sizeof(dirs)/sizeof(char *); i++) { error = tffs_remove(dirs[i]); } for (i = 0; i < sizeof(dirs)/sizeof(char *); i++) { error = tffs_mkdir(dirs[i]); expect(error, EFFS_OK); } return 0; } int case_denmark(int p0, int p1) { // Cleanup tffs_remove("/europe/denmark/jutland"); tffs_remove("/europe/denmark/sealand"); error = tffs_mkdir("/europe/denmark/jutland"); expect(error, EFFS_OK); error = tffs_mkdir("/europe/denmark/sealand"); expect(error, EFFS_OK); return 0; } /****************************************************************************** * Atomic Tests ******************************************************************************/ int case_init(int p0, int p1) { error = tffs_initialize(); // ignore error error = test_ffs_params_get(); expect(error, EFFS_OK); return 0; } int case_exit(int p0, int p1) { error = tffs_exit(); expect(error, EFFS_OK); return 0; } int case_only_preformat(int p0, int p1) { tffs_preformat(0xDEAD); expect(error, EFFS_OK); return 0; } int case_only_format(int p0, int p1) { tffs_format("/ffs", 0x2BAD); expect(error, EFFS_OK); return 0; } int case_reset(int p0, int p1) { error = tffs_initialize(); // ignore result error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs", 0x2BAD); expect(error, EFFS_OK); error = tffs_exit(); expect(error, EFFS_OK); return 0; } int case_status(int p0, int p1) { error = test_run("lsr"); return error; } /****************************************************************************** * Special Tests ******************************************************************************/ // Test FFS with only 2 blocks in device int case_twob(int p0, int p1) { int space_left; int size, i; char myname[] = "/two_block99"; // Format ffs to use only 2 block no journal file and no reserved space. // Make some directoryes. // Fill ffs allmost to the edge and try to write over the edge. // Read back all the files. error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs/b2j0m0", 0x2BAD); expect(error, EFFS_OK); error = test_run("i;world;eu;x"); if(error) return 1; // Calculate the amount of space that is left to user data because some space // is used to directoryes space_left = dev.blocksize - (13 * 20); tw(tr(TR_FUNC, TrTestHigh, "Space left: %d\n", space_left)); size = space_left; for(i = 0; i < 6; i++) { size = size/2; sprintf(myname, "/two_block%d", i); error = tffs_fwrite(myname, (char*)tdata[TDATA_HUGE], size >= 20 ? size - 20 : 0); expect(error, EFFS_OK); } error = tffs_fwrite(myname, (char*)tdata[TDATA_HUGE], space_left/5); expect(error, EFFS_NOSPACE); // 20 is the average space used to the file name + word align. size = space_left; for(i = 0; i < 6; i++) { size = size/2; sprintf(myname, "/two_block%d", i); error = tffs_fread(myname, bigbuf, bigbuf_size); expect(error, size >= 20 ? size - 20 : 0); } //error = ffs_object_control(0, OC_DEV_DEVICE, 0x080D); //if (error < 0) return error; return 0; } // Test FFS with only 3 blocks in device int case_threeb(int p0, int p1) { char mytest[20]; // Format ffs to use only 3 blocks, make directoryes and run test case // rand <p0> number of times. error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs/b3r24", 0x2BAD); expect(error, EFFS_OK); error = test_run("i;world;eu;x"); if (error) return 1; if(p0 == 0) p0 = 233; sprintf(mytest, "rand%d", p0); return test_run(mytest); } // Simulate a typical usage of ffs. We expect one part of the data will be // what we call 'static' data (calibration files etc) the other part is the // 'dynamic' data (wap, sms etc) we do also expect that they don't fill FFS // completely thus we have a 'free' part. int case_customer(int nblocks, int nloops) { int free_part = 5, static_part = 47, dynamic_part = 47; int free_size, static_size, dynamic_size; int bytes_free, bytes_max, bytes_used; int file_size_max, file_size_min, file_size_avg; int i, j, max_obj_block, num_static_files; char myname[] = "/ffs/bxxx/"; char file_name[FFS_FILENAME_MAX + 1]; char path[FFS_FILENAME_MAX * 2]; char static_dir[] ="/READONLY"; char dynamic_dir[] = "/rand"; struct dir_s dir; if (nblocks == 0) nblocks = 7; if (nloops == 0) nloops = 300; error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); sprintf(myname, "/ffs/b%d", nblocks); error = tffs_format(myname, 0x2BAD); expect(error, EFFS_OK); tffs_mkdir(dynamic_dir); expect(error, EFFS_OK); tffs_mkdir(static_dir); expect(error, EFFS_OK); ffs_query(Q_BYTES_FREE, &bytes_max); free_size = bytes_max * free_part / 100; static_size = bytes_max * static_part / 100; dynamic_size = bytes_max * dynamic_part / 100; tw(tr(TR_FUNC, TrTest, "Free:%d,%dkB. Static:%d,%dkB, Dynamic:%d,%dkB\n", free_part, free_size/1024, static_part, static_size/1024, dynamic_part, dynamic_size/1024)); // Find max average objects per block max_obj_block = fs.block_files_max; if (max_obj_block > fs.objects_max / (dev.numblocks -1 - fs.blocks_free_min)) max_obj_block = fs.objects_max / (dev.numblocks -1 - fs.blocks_free_min); tw(tr(TR_FUNC, TrTest, "Max average objects per block %d\n", max_obj_block)); ttw(ttr(TTrTest, "Max avg obj per block %d" NL, max_obj_block)); // Average (minimum) file size (avoid to reach block_files_max and // objects_max) file_size_avg = dev.blocksize / max_obj_block; ffs_query(Q_BYTES_USED, &bytes_used); tw(tr(TR_FUNC, TrTest, "Write static files (avg size %d)\n", file_size_avg)); ttw(ttr(TTrTest, "avg size %d" NL, file_size_avg)); do { i++; file_size_min = 5; // For each 5 file we make one huge. file_size_max = i % 5 ? file_size_avg * 2 : file_size_avg * 15; // For each 6 file we make one small. file_size_max = i % 6 ? file_size_max : 5; // Saturate, avoid to make one file that parses the total static_size if (file_size_max > static_size - bytes_used) file_size_max = file_size_min = static_size - bytes_used; // For each 7 file we create one dynamic (If the block is full with only // valid objects do we risk that this block never will be reclaimed!). if (i % 7) error = case_mk_rand_file(dynamic_dir, file_size_max, file_size_min); else error = case_mk_rand_file(static_dir, file_size_max, file_size_min); expect_ok(error); ffs_query(Q_BYTES_USED, &bytes_used); } while (bytes_used < static_size); num_static_files = tffs_opendir(static_dir, &dir); expect_ok(num_static_files); tw(tr(TR_FUNC, TrTest, "Written %d files\n", num_static_files)); ttw(ttr(TTrTest, "Written %d files" NL, num_static_files)); // In a loop we continue to write data until only the wanted free_part is // left, after that we remove half of the just created files and then write // again and again... j = 0; tw(tr(TR_FUNC, TrTest, "Write and remove dynamic files\n")); ttw(ttr(TTrTest, "Write and remove dynamic files" NL)); for (i = 0; i < nloops; i++) { if (i % 10 == 0) { tw(tr(TR_FUNC, TrTest, "loop %d\n", i)); ttw(ttr(TTrTest, "loop %d" NL, i)); } ffs_query(Q_BYTES_FREE, &bytes_free); do { j++; file_size_min = 5; file_size_max = j % 5 ? file_size_avg * 2 : file_size_avg * 15; file_size_max = j % 6 ? file_size_max : 5; // Saturate, avoid using the free space. if (file_size_max > bytes_free - free_size) file_size_max = file_size_min = bytes_free - free_size; if (file_size_max < 0) break; error = case_mk_rand_file(dynamic_dir, file_size_max, file_size_min); // NOTE it is very difficult to avoid EFFS_FSFULL because cleanup will // sometimes only remove the big files thus there will exist a lot of small // that use all the inodes. if (error == EFFS_FSFULL) { tw(tr(TR_FUNC, TrTest, "Warning no free inodes\n")); ttw(ttr(TTrTest, "Warning no free inodes" NL)); error = case_cleanup(free_size + dynamic_size); } else expect_ok(error); ffs_query(Q_BYTES_FREE, &bytes_free); } while (bytes_free > free_size); error = case_cleanup(free_size + dynamic_size / 2); expect_ok(error); } error = case_reinit(0, 0); expect(EFFS_OK, error); tw(tr(TR_FUNC, TrTest, "Verify all files\n")); ttw(ttr(TTrTest, "Verify all files" NL)); for (i = 0; i < 2; i++) { if (i == 0) { error = tffs_opendir(static_dir, &dir); expect_eq(error, num_static_files); } else { error = tffs_opendir(dynamic_dir, &dir); expect_ok(error); } while ((error = tffs_readdir(&dir, file_name, sizeof(file_name))) > 0) { if (i == 0) strcpy(path, static_dir); else strcpy(path, dynamic_dir); strcat(path, "/"); strcat(path, file_name); error = tffs_stat(path, &stat); expect_ok(error); error = test_expect_file(path, tdata[TDATA_HUGE], stat.size); if (error) return 1; } } test_statistics_print(); return 0; } // TODO cleanup this test. Use stat and verify some of the files etc. // Used to test ConQuest issue FFS_FIX-11368 We fill FFS with a lot of small // files. Then by turn remove some files and write new files, always very // close to max avaible bytes. // Note the below test is not able to run in big configuration because it // creates a lot of small files thus we reach EFFS_FSFULL 'out of inodes' int case_ffull(int p0, int p1) { int error, i, bytes_free, file_size, max_write_size = 1000; char myname[] = "/ffs/bxxx/"; if (p0 == 0) p0 = 100; // Default 100 loops if (p1 == 0 || p1 > 999) p1 = 3; // Default run in 3 blocks error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); sprintf(myname, "/ffs/b%d", p1); error = tffs_format(myname, 0x2BAD); expect(error, EFFS_OK); tffs_mkdir("/rand"); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTest, "Fill it with files until it fails\n")); ttw(ttr(TTrTest, "Fill it with files until it fails" NL)); // We have to avoid too many small files or else do we reach objects_max ffs_query(Q_BYTES_FREE, &bytes_free); max_write_size = bytes_free / fs.objects_max * 2.5; do { error = case_mk_rand_file("/rand", max_write_size, 0); } while (error >= 0 ); tw(tr(TR_FUNC, TrTest, "Fill done (%d)\n" ,error)); ttw(ttr(TTrTest, "Fill done (%d)" NL, error)); expect(error, EFFS_NOSPACE); // Now are we sure that it is full tw(tr(TR_FUNC, TrTest, "Cleanup and fill again \n")); for (i = 0; i < (1 + p0); i++) { tw(tr(TR_FUNC, TrTestHigh, "Loop %d \n", i + 1)); ttw(ttr(TTrTest, "Loop %d" NL, i + 1)); tw(tr_bstat()); error = case_cleanup(max_write_size * 3); tw(tr_bstat()); do { ffs_query(Q_BYTES_FREE, &bytes_free); // Saturate size file_size = bytes_free > max_write_size ? max_write_size :bytes_free; // Query BYTES_FREE don't count the below data use. //file_size -= (FFS_FILENAME_MAX + 1 + dev.atomsize); if (file_size < 0) break; error = case_mk_rand_file("/rand", file_size - (FFS_FILENAME_MAX+1), 0); if (error == EFFS_FSFULL) { // Out of inodes error = case_cleanup(max_write_size * 6); // Create bigger files to avoid '-11', auto fit :-) max_write_size *= 1.1; // Add 10% tw(tr(TR_FUNC, TrTestHigh, "Out of inodes, write size: %d \n", max_write_size)); continue; } expect_ok(error); } while (file_size != bytes_free); } tw(tr(TR_FUNC, TrTest, "Test finish (%d) \n", error)); ttw(ttr(TTrTest, "Test finish (%d)" NL, error)); if (error >= 0) return EFFS_OK; return error; } int case_rivtype(int p0, int p1) { // Use the compiler to test if the types are correctly defined. We don't // need to run this test just make a compilation. T_FFS_OPEN_FLAGS flags = FFS_O_RDONLY; T_FFS_SIZE size = 20; T_FFS_RET error = -10; T_FFS_WHENCE whence = FFS_SEEK_SET; T_FFS_STAT stat; T_FFS_DIR dir; T_FFS_FD fd = FFS_FD_OFFSET; T_RV_RETURN callback; T_FFS_FILE_CNF confirm; T_FFS_OBJECT_TYPE objt = OT_FILE; // Use the defined variables to avoid the boring warning: unused // variable 'xx' if (flags != FFS_O_RDONLY) return 1; if (size != 20) return 1; if (error != -10) return 1; if (whence != FFS_SEEK_SET) return 1; if (fd != FFS_FD_OFFSET) return 1; if (objt != OT_FILE) return 1; stat.type = 1; if (stat.type != 1) return 1; dir.this = 5; if (dir.this != 5) return 1; callback.addr_id = 3; if (callback.addr_id != 3) return 1; confirm.error = -1; if (confirm.error != -1) return 1; return 0; } /****************************************************************************** * Pseudo Tests ******************************************************************************/ // Recursively read objects in directory. Does NOT check for buffer // overflow! int case_dir_list(char *dirname, struct object_s **pplist, char **ppnames) { struct object_s *plist, *plist_start, *plist_end; char pathname[6 * 20]; struct dir_s dir; char *pnames, *pn; int i, pathlen; //tw(tr(TR_BEGIN, TrTestHigh, "dir_list('%s', %d, 0x%x)\n", // dirname, (int)*pplist/sizeof(struct object_s), *ppnames)); plist_start = plist = *pplist; pnames = *ppnames; strcpy(pathname, dirname); pathlen = strlen(pathname); // remove trailing slash. It is tiring to handle the root directory // differently from other directories. In a future ffs revision, this // trailing slash should be allowed! if (pathname[pathlen - 1] == '/') { pathname[pathlen - 1] = 0; pathlen--; } if (strlen(pathname) == 0) error = tffs_opendir("/", &dir); else error = tffs_opendir(pathname, &dir); expect_ok(error); pn = pathname + strlen(pathname); *pn++ = '/'; *pn = 0; error = 1; for (i = 0; (error = tffs_readdir(&dir, pn, 21)) > 0; i++) { error = tffs_xlstat(pathname, &plist->stat); expect(error, EFFS_OK); // Copy full object pathname to buffer, working downwards. pnames -= strlen(pathname) + 1; // Check for buffer overflow (if pnames <= plist) expect_gt((int) pnames, (int) plist); strcpy(pnames, pathname); plist->name = pnames; plist++; } *pplist = plist; *ppnames = pnames; // For each directory in the retrieved list, recurse. plist_end = plist; for (plist = plist_start; plist < plist_end; plist++) { if (plist->stat.type == OT_DIR) i += case_dir_list(plist->name, pplist, ppnames); } // tw(tr(TR_END, TrTestHigh, "} %d\n", i)); return i; } int case_find(int p0, int p1) { struct object_s *plist, *plist_start; char *pnames, *pnames_start; int n, names_used, list_used; plist = plist_start = (struct object_s *) bigbuf; pnames = pnames_start = bigbuf + bigbuf_size; n = case_dir_list("/", &plist, &pnames); list_used = n * sizeof(struct object_s); names_used = pnames_start - pnames; tw(tr(TR_FUNC, TrTestHigh, "Buffer space used: %d + %d = %d\n", list_used, names_used, list_used + names_used)); ttw(ttr(TTrTest, "Buffer space used: %d + %d = %d" NL, list_used, names_used, list_used + names_used)); return 0; } // TODO: We should accumulate all stat.space and check it vs. the number of // bytes used! int case_lsr(int p0, int p1) { struct object_s *plist, *plist_start; char *pnames, *pnames_start; char of[3]; int i, n; plist = plist_start = (struct object_s *) bigbuf; pnames = pnames_start = bigbuf + bigbuf_size; n = case_dir_list("/", &plist, &pnames); tw(tr(TR_FUNC, TrTestHigh, "Total %d objects.\n", n)); ttw(ttr(TTrTest, "Total %d objects" NL, n)); plist = plist_start; for (i = 0; i < n; i++, plist++) { strcpy(of, " "); switch (plist->stat.type) { case OT_FILE: of[0] = ' '; break; case OT_DIR: of[0] = 'd'; break; case OT_LINK: of[0] = 'l'; break; } if (plist->stat.flags & OF_READONLY) of[1] = 'r'; #if (TARGET == 0) printf("%3d: %s %3d %2d/%04X (%4d,%4d) %5d %s\n", i, of, plist->stat.inode, plist->stat.block, plist->stat.location, plist->stat.sequence, plist->stat.updates, plist->stat.size, &plist->name[1]); #else ttw(ttr(TTrTest, "%3d: %s %3d %2d/%04X (%4d,%4d) %5d %s" NL, i, of, plist->stat.inode, plist->stat.block, plist->stat.location, plist->stat.sequence, plist->stat.updates, plist->stat.size, &plist->name[1])); #endif } return 0; } /****************************************************************************** * Normal Tests ******************************************************************************/ int case_format(int p0, int p1) { error = tffs_preformat(0xD00D); expect(error, EFFS_INVALID); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs/", 0xDEAD); expect(error, EFFS_INVALID); error = tffs_format("ffs", 0x2BAD); expect(error, EFFS_BADNAME); error = tffs_format("", 0x2BAD); expect(error, EFFS_BADNAME); error = tffs_format("/", 0x2BAD); expect(error, EFFS_OK); error = tffs_format("/ffs", 0x2BAD); expect(error, EFFS_NOPREFORMAT); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format(0, 0x2BAD); //error = tffs_format("/ffs/i256o128", 0x2BAD); expect(error, EFFS_OK); return 0; } // Test that it is illegal to modify root inode as if it was a normal file // or directory int case_root(int p0, int p1) { error = tffs_opendir("/", &dir); expect_ok(error); error = tffs_fcreate("/", "foo", 3); expect(error, EFFS_EXISTS); error = tffs_fupdate("/", "bar", 3); expect(error, EFFS_NOTAFILE); error = tffs_fwrite("/", "foo", 3); expect(error, EFFS_NOTAFILE); error = tffs_remove("/"); expect(error, EFFS_ACCESS); return 0; } // Test object lookup, object names etc. int case_lookup(int p0, int p1) { // Swedish Provinces: Blekinge, Smaaland, Halland, Vaermland, Dalarna, // Dalsland, Gotland, Gaestrikland, Haelsingland, Bohuslaen, // Haerjedalen, Jaemtland, Lappland, Medelpad, Norrbotten, Naerke, // Soedermanland, Uppland, Vaesterbotten // Smaaland Cities: Vetlanda, Bodafors, Rottne, Ljungby, Nybro, // Hultsfred, Oskarshamn, Vimmerby, Hyltebruk, Joenkoeping, Vaexjoe tffs_mkdir("/europe"); tffs_mkdir("/europe/sweden"); // test init error = tffs_mkdir(LUDIR "/Smaaland"); expect(error, EFFS_OK); // test BAD_FILENAME error = tffs_fcreate("", TDATA(0)); expect(error, EFFS_BADNAME); // test EFFS_EXISTS error = tffs_fcreate(LUDIR "/Smaaland/vetlanda", TDATA(0)); expect(error, EFFS_OK); error = tffs_fcreate(LUDIR "/Smaaland/vetlanda", TDATA(0)); expect(error, EFFS_EXISTS); error = tffs_mkdir(LUDIR "/Smaaland"); expect(error, EFFS_EXISTS); error = tffs_fwrite(LUDIR "/Smaaland/vetlanda", TDATA(1)); expect(error, EFFS_OK); // test EFFS_BADNAME error = tffs_fcreate(LUDIR "/Smaaland/A_Zaz.0+9-7!", TDATA(2)); expect(error, EFFS_BADNAME); error = tffs_fcreate(LUDIR "/Smaaland/A_Zaz.0+9-7#%$", TDATA(2)); expect(error, EFFS_OK); // test ending slash error = tffs_mkdir(LUDIR "/Smaaland/Vaexjoe/"); expect(error, EFFS_NOTADIR); error = tffs_fcreate(LUDIR "/Smaaland/Vaexjoe/", TDATA(3)); expect(error, EFFS_NOTADIR); // test EFFS_NAMETOOLONG error = tffs_fcreate(LUDIR "/Smaaland/Hultsfred-is-21-chars", TDATA(4)); expect(error, EFFS_NAMETOOLONG); error = tffs_fcreate(LUDIR "/Smaaland/Bodafors-is-20-chars", TDATA(4)); expect(error, EFFS_OK); error = tffs_mkdir(LUDIR "/Vaermland-is-21-chars"); expect(error, EFFS_NAMETOOLONG); error = tffs_mkdir(LUDIR "/Dalsland-is-20-chars"); expect(error, EFFS_OK); // test EFFS_NOTADIR error = tffs_mkdir(LUDIR "/DontMakeSeveral/DirsAt/TheSameTime"); expect(error, EFFS_NOTADIR); error = tffs_fread(LUDIR "/Lappland/Bodafors-is-20-chars", bigbuf, 1024); tw(tr(TR_END, TrApi, "} %d\n", error)); // Avoid wrong indent expect(error, EFFS_NOTADIR); error = tffs_fread(LUDIR "/Lappland/", bigbuf, 1024); tw(tr(TR_END, TrApi, "} %d\n", error)); // Avoid wrong indent expect(error, EFFS_NOTADIR); error = tffs_fread(LUDIR "/Smaaland/Bodafors", TDATA(4)); tw(tr(TR_END, TrApi, "} %d\n", error)); // Avoid wrong indent expect(error, EFFS_NOTFOUND); // test EFFS_PATHTOODEEP error = tffs_mkdir(LUDIR "/Gotland"); // 3. level expect(error, EFFS_OK); error = tffs_mkdir(LUDIR "/Gotland/Visby"); // 4. level expect(error, EFFS_OK); error = tffs_mkdir(LUDIR "/Gotland/Visby/level5"); // 5. level expect(error, EFFS_OK); error = tffs_mkdir(LUDIR "/Gotland/Visby/level5/level6"); // 6. level expect(error, EFFS_OK); error = tffs_mkdir(LUDIR "/Gotland/Visby/level5/level6/level7"); // 7. level expect(error, EFFS_PATHTOODEEP); error = tffs_fcreate(LUDIR "/Gotland/Visby/level5/level6/level7", TDATA(5)); // 7. level expect(error, EFFS_PATHTOODEEP); // final checks error = test_expect_file(LUDIR "/Smaaland/vetlanda", TDATA(1)); if (error) return 1; error = test_expect_file(LUDIR "/Smaaland/Bodafors-is-20-chars", TDATA(4)); if (error) return 1; error = tffs_opendir(LUDIR "/Dalsland-is-20-chars", &dir); expect_ok(error); error = tffs_opendir(LUDIR "/Gotland/Visby/level5/level6", &dir); expect_ok(error); // cleanup error = tffs_remove(LUDIR "/Smaaland/A_Zaz.0+9-7"); return 0; } // Test fcontrol and read-only semantics. TODO: We still need to perform // same tests on a dir and a symlink and thru a symlink. int case_fcontrol(int p0, int p1) { struct ffs_state_s old, new; const char rofile[] = "/europe/norway/rofile"; const char imeifile[] = "/europe/norway/IMEI"; fd_t fdi; // Cleanup tffs_remove(rofile); tffs_remove("/europe/norway"); tffs_remove("/europe"); // Initialize error = tffs_mkdir("/europe"); error = tffs_mkdir("/europe/norway"); error = tffs_fcreate(rofile, TDATA(2)); expect(error, EFFS_OK); error = tffs_stat(rofile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, 0); test_ffs_state_get(&old); // set read-only flag error = tffs_fcontrol(rofile, OC_FLAGS, OF_READONLY); expect(error, EFFS_OK); error = tffs_stat(rofile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, OF_READONLY); test_ffs_state_get(&new); expect_eq(new.objects_total, old.objects_total); expect_ne(new.inodes_used, old.inodes_used); test_ffs_state_copy(&old, &new); // Set illegal flags. Try to fupdate file. Then try to set read-only // flag again. error = tffs_fcontrol(rofile, OC_FLAGS, 1<<0); expect(error, EFFS_INVALID); error = tffs_fcontrol(rofile, OC_FLAGS, 1<<3); expect(error, EFFS_INVALID); error = tffs_fcontrol(rofile, OC_FLAGS, 1<<5); expect(error, EFFS_INVALID); error = tffs_fcontrol(rofile, OC_FLAGS, 1<<6); expect(error, EFFS_INVALID); error = tffs_fupdate(rofile, TDATA(3)); expect(error, EFFS_OK); error = tffs_fcreate("/europe/norway/tease", TDATA(2)); expect(error, EFFS_OK); tffs_remove("/europe/norway/tease"); error = tffs_fcontrol(rofile, OC_FLAGS, OF_READONLY); expect(error, EFFS_OK); error = tffs_stat(rofile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, OF_READONLY); test_ffs_state_get(&new); expect_eq(new.objects_total, old.objects_total); expect_ne(new.inodes_used, old.inodes_used); test_ffs_state_copy(&old, &new); // clear read-only flag (this works because ffs_is_modifiable() by // default returns true for all objects except for object names ending // in "IMEI". error = tffs_fcontrol(rofile, OC_FLAGS, 0); expect(error, EFFS_OK); error = tffs_stat(rofile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, 0); test_ffs_state_get(&new); expect_eq(new.objects_total, old.objects_total); expect_ne(new.inodes_used, old.inodes_used); test_ffs_state_copy(&old, &new); // Set read-only flag (again) error = tffs_fcontrol(rofile, OC_FLAGS, OF_READONLY); expect(error, EFFS_OK); error = tffs_stat(rofile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, OF_READONLY); test_ffs_state_get(&new); expect_eq(new.objects_total, old.objects_total); expect_ne(new.inodes_used, old.inodes_used); test_ffs_state_copy(&old, &new); // Set read-only flag of IMEI file error = tffs_fcreate(imeifile, TDATA(2)); expect(error, EFFS_OK); error = tffs_stat(imeifile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, 0); error = tffs_fcontrol(imeifile, OC_FLAGS, OF_READONLY); expect(error, EFFS_OK); error = tffs_stat(imeifile, &stat); expect(error, EFFS_OK); expect_eq(stat.flags, OF_READONLY); test_ffs_state_get(&new); expect_eq(new.objects_total, old.objects_total + 1); expect_ne(new.inodes_used, old.inodes_used); test_ffs_state_copy(&old, &new); // Try to remove, fupdate, fwrite and fcontrol IMEI file. error = tffs_remove(imeifile); expect(error, EFFS_ACCESS); error = tffs_fupdate(imeifile, TDATA(0)); expect(error, EFFS_ACCESS); error = tffs_fwrite(imeifile, TDATA(0)); expect(error, EFFS_ACCESS); error = tffs_fcontrol(imeifile, OC_FLAGS, 0); expect(error, EFFS_ACCESS); // Try to open IMEI file in write-only and read-only fdi = tffs_open(imeifile, FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, EFFS_ACCESS); fdi = tffs_open(imeifile, FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); error = tffs_close(fdi); expect(error, EFFS_OK); return 0; } // Test symlink functionality of simple symlink implementation. // Fixme: add remove file through symlink, stat a dir through a symlink. int case_ssym(int p0, int p1) { int size; fd_t fdi; // two links, read files thru links // link to link to file, open file // link to non-valid object // // make three test files, a link to each of these, one link to dir, one // link to non-existing object, one link to link to file tffs_mkdir("/europe"); tffs_mkdir("/europe/denmark"); error = tffs_fwrite("/europe/denmark/aalborg", TDATA(1)); expect(error, EFFS_OK); error = tffs_symlink("/europe/aal", "/europe/denmark/aalborg"); expect(error, EFFS_OK); error = tffs_symlink("/dk", "/europe/denmark"); expect(error, EFFS_OK); error = tffs_fwrite("/europe/denmark/aarhus", TDATA(2)); expect(error, EFFS_OK); error = tffs_symlink("/europe/aar", "/europe/denmark/aarhus"); expect(error, EFFS_OK); error = tffs_symlink("/europe/se", "/europe/non-existing"); expect(error, EFFS_OK); error = tffs_fwrite("/europe/denmark/billund", TDATA(3)); expect(error, EFFS_OK); error = tffs_symlink("/europe/bil", "/europe/denmark/billund"); expect(error, EFFS_OK); error = tffs_symlink("/lego", "/europe/bil"); expect(error, EFFS_OK); error = tffs_fwrite("/europe/denmark/norresundby", TDATA(2)); expect(error, EFFS_OK); error = tffs_symlink("/europe/nor", "/europe/denmark/norresundby"); expect(error, EFFS_OK); // Test link to dir error = tffs_opendir("/dk", &dir); expect(error, EFFS_NOTAFILE); // TODO: strange error! error = tffs_stat("/dk", &stat); expect(error, EFFS_NOTAFILE); // TODO: strange error! error = tffs_linkstat("/dk", &stat); expect(error, EFFS_OK); // Test link to link to file error = tffs_stat("/lego", &stat); expect(error, EFFS_NOTAFILE); // TODO: strange error?! error = tffs_linkstat("/lego", &stat); expect(error, EFFS_OK); error = tffs_fread("/lego", bigbuf, bigbuf_size); tw(tr(TR_END, TrApi, "} %d\n", error)); // Avoid wrong indent expect(error, EFFS_NOTAFILE); // Test link to non-existing object error = tffs_opendir("/europe/se", &dir); expect(error, EFFS_NOTFOUND); error = tffs_stat("/europe/se", &stat); expect(error, EFFS_NOTFOUND); error = tffs_linkstat("/europe/se", &stat); expect(error, EFFS_OK); // Read files through links error = test_expect_file("/europe/aal", TDATA(1)); if (error) return 1; error = test_expect_file("/europe/aar", TDATA(2)); if (error) return 1; error = test_expect_file("/europe/bil", TDATA(3)); if (error) return 1; // Write files through links error = tffs_fwrite("/europe/aal", TDATA(3)); expect(error, EFFS_OK); error = tffs_fupdate("/europe/aar", TDATA(4)); expect(error, EFFS_OK); error = tffs_fupdate("/europe/bil", TDATA(5)); expect(error, EFFS_OK); // Open file and write and read files through links fdi = tffs_open("/europe/nor", FFS_O_WRONLY | FFS_O_TRUNC | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 50); expect(size, 50); error = tffs_close(fdi); expect(error, 0); fdi = tffs_open("/europe/nor", FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); size = tffs_read(fdi, bigbuf, 62); expect(size, 50); error = test_expect_data((char*)tdata[TDATA_HUGE], bigbuf, 50); if (error) return 1; error = tffs_close(fdi); expect(error, 0); // remove "lego" link, recreate it to link to "billund", read file data // through link, re-write file data through link error = tffs_symlink("/lego", "/europe/denmark/billund"); expect(error, EFFS_NOTAFILE); // TODO: strange error?! error = tffs_fupdate("/lego", "/europe/denmark/billund", strlen("/europe/denmark/billund")); expect(error, EFFS_NOTAFILE); error = tffs_remove("/lego"); expect(error, EFFS_OK); error = tffs_symlink("/lego", "/europe/denmark/billund"); expect(error, EFFS_OK); error = test_expect_file("/lego", TDATA(5)); if (error) return 1; error = tffs_fupdate("/lego", TDATA(2)); expect(error, EFFS_OK); // Re-Read files through links error = test_expect_file("/europe/aal", TDATA(3)); if (error) return 1; error = test_expect_file("/europe/aar", TDATA(4)); if (error) return 1; error = test_expect_file("/europe/bil", TDATA(2)); if (error) return 1; // Clean up error = tffs_remove("/europe/aal"); expect(error, EFFS_OK); error = tffs_remove("/dk"); expect(error, EFFS_OK); error = tffs_remove("/europe/aar"); expect(error, EFFS_OK); error = tffs_remove("/europe/se"); expect(error, EFFS_OK); error = tffs_remove("/europe/bil"); expect(error, EFFS_OK); error = tffs_remove("/lego"); expect(error, EFFS_OK); error = tffs_remove("/europe/nor"); expect(error, EFFS_OK); return 0; } // Test symlink functionality of full symlink implementation int case_fsym(int p0, int p1) { fd_t fdi; // make relative link to directory, stat link and directory error = tffs_symlink("/sa", "/south-america"); // relative link expect(error, EFFS_OK); error = tffs_linkstat("/sa", &stat); expect(error, EFFS_OK); expect_eq(stat.type, OT_LINK); error = tffs_stat("/sa", &stat); expect(error, EFFS_OK); expect_eq(stat.type, OT_DIR); // create directory thru a symlink error = tffs_mkdir("/sa/brazil"); expect(error, EFFS_OK); error = tffs_symlink("/br.ba", "/brazil/buenos-aires"); // relative link expect(error, EFFS_OK); // create file via symlink, stat the new file error = tffs_fcreate("/sa/brazil/buenos-aires", TDATA(2)); expect(error, EFFS_OK); error = tffs_stat("/sa/br.ba", &stat); expect(error, EFFS_OK); expect_eq(stat.type, OT_FILE); fdi = tffs_open("/sa/peru", FFS_O_WRONLY | FFS_O_TRUNC | FFS_O_APPEND | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); error = tffs_close(fdi); expect(error, 0); // create file thru a symlink, stat the new file. Will this work? error = tffs_symlink("/br.br", "/brazil/brasilia"); // relative link expect(error, EFFS_OK); error = tffs_fcreate("/sa/br.br", TDATA(3)); // ??? expect(error, EFFS_OK); error = tffs_stat("/sa/br.br", &stat); expect(error, EFFS_OK); expect_eq(stat.type, OT_FILE); // Create symlink that is a link to absolute link path error = tffs_symlink("/south-america/cape-horn", "/sa/ar"); // absolute link expect(error, EFFS_OK); error = tffs_fcreate("/south-america/argentina", TDATA(0)); expect(error, EFFS_OK); error = tffs_symlink("/sa/ar", "/south-america/argentina"); expect(error, EFFS_OK); // TODO: Test very deep path // TODO: Test circular link // TODO: Test if ending slash is allowed on dirs and symlinks tw(tr(TR_FUNC, TrTest, "WARNING: Test case not implemented!\n")); ttw(str(TTrTest, "Test case not implemented!" NL)); return 0; } // Check every combination of name length and data size within the device // atomsize, int case_fread(int p0, int p1) { const char fullname[] = "123456789ABCDEF0123456789ABCDEF"; int i, j, dirlen, mysize; char *mydata; char myname[20+20+32] = "/australia/f"; case_cleanup(0x11 * param.atomsize * param.atomsize); tffs_mkdir("/australia"); mydata = (char *) tdata[TDATA_HUGE]; dirlen = strlen(myname); for (j = 0; j < param.atomsize; j++) { mysize = j * 0x11 /* + 0x100 */; ttw(ttr(TTrTest, "frd: size = %x" NL, mysize)); tw(tr(TR_FUNC, TrTestHigh, "frd: size = %x\n", mysize)); // remove files for (i = 0; i < param.atomsize; i++) { strncpy(&myname[dirlen], fullname, i); myname[dirlen + i] = 0; error = tffs_remove(myname); } // fcreate files with varying name lengths but same alignment. for (i = 0; i < param.atomsize; i++) { strncpy(&myname[dirlen], fullname, i); myname[dirlen + i] = 0; error = tffs_fwrite(myname, mydata, mysize); expect(error, EFFS_OK); } // Now check all the files written for (i = 0; i < param.atomsize; i++) { strncpy(&myname[dirlen], fullname, i); myname[dirlen + i] = 0; //tw(tr(TR_FUNC, TrTestHigh, "frd: txf('f...', %x, %d)\n", // mydata, mysize)); error = test_expect_file(myname, mydata, mysize); if (error) return 1; } mydata += 0x10; } for (i = 0; i < param.atomsize; i++) { strncpy(&myname[dirlen], fullname, i); myname[dirlen + i] = 0; error = tffs_remove(myname); expect(error, EFFS_OK); } return 0; } /****************************************************************************** * Non-finished ******************************************************************************/ // Test Directories int case_dirs(int p0, int p1) { // remove empty dir, non-empty dir // open/readdir empty dir, non-empty dir char name[21]; int i, j; struct dir_s dir[3]; const char *names[3][5] = { { "china", "japan", "korea", "india", 0 }, { "bombay", "newdelhi", 0, 0, 0 }, { "hongkong", "shanghai", "hk", 0, 0 } }; // Cleanup tffs_mkdir("/asia"); tffs_remove("/asia/india/bombay"); tffs_remove("/asia/india/newdelhi"); tffs_remove("/asia/india"); error = tffs_mkdir("/asia/china"); expect(error, EFFS_OK); error = tffs_mkdir("/asia/china/beijing"); expect(error, EFFS_OK); error = tffs_mkdir("/asia/china/hongkong"); expect(error, EFFS_OK); error = tffs_fcreate("/asia/china/hongkong/hkfile1", TDATA(1)); expect(error, EFFS_OK); error = tffs_fcreate("/asia/china/shanghai", TDATA(2)); expect(error, EFFS_OK); error = tffs_symlink("/asia/china/hk", "/asia/china/hongkong"); expect(error, EFFS_OK); error = tffs_mkdir("/asia/japan"); expect(error, EFFS_OK); error = tffs_fcreate("/asia/thailand", TDATA(0)); expect(error, EFFS_OK); error = tffs_fcreate("/asia/korea", TDATA(2)); expect(error, EFFS_OK); error = tffs_fcreate("/asia/japan/tokyo", TDATA(3)); expect(error, EFFS_OK); error = tffs_fupdate("/asia/japan/tokyo", TDATA(4)); expect(error, EFFS_OK); error = tffs_mkdir("/asia/india"); expect(error, EFFS_OK); error = tffs_fcreate("/asia/india/bombay", TDATA(0)); expect(error, EFFS_OK); error = tffs_fcreate("/asia/india/newdelhi", TDATA(1)); expect(error, EFFS_OK); error = tffs_fcreate("/asia/india/calcutta", TDATA(2)); expect(error, EFFS_OK); error = tffs_opendir("/asia", &dir[0]); expect(error, 5); error = tffs_opendir("/asia/india", &dir[1]); expect(error, 3); error = tffs_opendir("/asia/china", &dir[2]); expect(error, 4); // remove first, middle and last entry in a dir error = tffs_remove("/asia/china/beijing"); expect(error, EFFS_OK); error = tffs_remove("/asia/thailand"); expect(error, EFFS_OK); error = tffs_remove("/asia/india/calcutta"); expect(error, EFFS_OK); for (j = 0; j < 5; j++) { for (i = 0; i < 3; i++) { error = tffs_readdir(&dir[i], name, 21); if (names[i][j] == NULL) { expect(error, EFFS_OK); } else { expect_gt(error, EFFS_OK); tw(tr(TR_FUNC, TrTestHigh, "dir[%d]: %10s, expected: %s\n", i, name, names[i][j])); test_expect_data(name, TDATA_STRING(names[i][j])); } } } error = tffs_remove("/asia/china"); expect(error, EFFS_DIRNOTEMPTY); error = tffs_remove("/asia/china/hongkong"); expect(error, EFFS_DIRNOTEMPTY); error = tffs_remove("/asia/china/hongkong/hkfile1"); expect(error, EFFS_OK); error = tffs_remove("/asia/china/shanghai"); expect(error, EFFS_OK); error = tffs_remove("/asia/china/hk"); expect(error, EFFS_OK); error = tffs_remove("/asia/china"); expect(error, EFFS_DIRNOTEMPTY); error = tffs_remove("/asia/china/hongkong"); expect(error, EFFS_OK); error = tffs_opendir("/asia/china", &dir[2]); expect(error, 0); error = tffs_remove("/asia/china"); expect(error, EFFS_OK); error = tffs_remove("/asia/korea"); expect(error, EFFS_OK); error = tffs_remove("/asia/japan/tokyo"); expect(error, EFFS_OK); error = tffs_remove("/asia/japan"); expect(error, EFFS_OK); return 0; } // Check that expected stat data was read int case_expect_stat(const char *name, int n) { error = tffs_stat(name, &stat); expect(error, EFFS_OK); // test type, size, flags, space? // test range of location, inode, block return 0; } // Test stat int case_stat(int p0, int p1) { struct xstat_s xstat; struct stat_s stat; char myname[] = "/Stat_file"; char sym_name[] = "/sf"; char stream_name[] = "/Stat_stream"; fd_t fdi; int size, i, file_size = 0; // test stat on dirs, symlinks and files // check stat.block (by writing two HUGE files) case_cleanup(fs.chunk_size_max / 5 * 20); // NOTEME: this is a very limited test error = tffs_fwrite(myname, (char *)tdata[TDATA_HUGE], 99); expect_ok(error); error = tffs_stat(myname, &stat); expect(error, EFFS_OK); expect(stat.type, OT_FILE); expect(stat.flags, 0); expect_ok(stat.inode); expect(stat.size, 99); // error = tffs_xstat(myname, &xstat); // expect(error, EFFS_OK); // expect(error, EFFS_OK); // expect(xstat.type, OT_FILE); // expect(xstat.flags, 0); // expect_ok(xstat.inode); // expect(xstat.size, 99); // expect(xstat.space, 112); // expect_ok(xstat.location); // expect_ok(xstat.block); // expect_ok(xstat.sequence); // expect_ok(xstat.updates); error = tffs_linkstat(myname, &stat); expect(error, EFFS_OK); expect(error, EFFS_OK); expect(stat.type, OT_FILE); expect(stat.flags, 0); expect_ok(stat.inode); expect(stat.size, 99); error = tffs_xlstat(myname, &xstat); expect(error, EFFS_OK); expect(error, EFFS_OK); expect(xstat.type, OT_FILE); expect(xstat.flags, 0); expect_ok(xstat.inode); expect(xstat.size, 99); expect(xstat.space, 112); expect_ok(xstat.location); expect_ok(xstat.block); expect_ok(xstat.sequence); expect_ok(xstat.updates); // Symlink tffs_symlink(sym_name, myname); error = tffs_xlstat(sym_name, &xstat); expect(error, EFFS_OK); expect(error, EFFS_OK); expect(xstat.type, OT_LINK); expect(xstat.flags, 0); expect_ok(xstat.inode); expect(xstat.size, sizeof(myname)); expect(xstat.space, 16); expect_ok(xstat.location); expect_ok(xstat.block); expect_ok(xstat.sequence); expect_ok(xstat.updates); // Stream stat test // Use xstat to return the size of the file fdi = tffs_open(stream_name, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); for (i = 0; i < 20; i++) { size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], fs.chunk_size_max / 5); expect(size, fs.chunk_size_max / 5); file_size +=size; error = tffs_xlstat(stream_name, &xstat); expect(error, EFFS_OK); expect_eq(xstat.size, file_size); error = tffs_fstat(fdi, &stat); expect(error, EFFS_OK); expect_eq(stat.size, file_size); } error = tffs_close(fdi); expect(error, EFFS_OK); // Test if the file size is right if opened in read-only fdi = tffs_open(stream_name, FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); error = tffs_xlstat(stream_name, &xstat); expect(error, EFFS_OK); expect_eq(xstat.size, file_size); // Test a lot of difference fdi for (i = -3; i < FFS_FD_OFFSET + fs.fd_max + 3; i++) { error = tffs_fstat(i, &stat); if (i == FFS_FD_OFFSET) { expect(error, EFFS_OK); } else expect(error, EFFS_BADFD); } error = tffs_close(fdi); expect(error, EFFS_OK); return 0; } int case_remove(int p0, int p1) { tw(tr(TR_FUNC, TrTest, "WARNING: Test case not implemented!\n")); ttw(str(TTrTest, "Test case not implemented!" NL)); return 0; } int case_rename(int p0, int p1) { fd_t fdi; int size, i; struct dir_s dir; // create file A // rename A to B // check file A is gone // check file B is same as file A // do all the same for directory // do all the same for symlink // test and ensure that dir rename "foo" to "foo/bar" is impossible! // Cleanup before run tffs_remove("/Walt_Disney/RUP"); tffs_remove("/Uncle_Scrooge"); tffs_remove("/Walt_Disney/Minnie"); tffs_remove("/Walt_Disney"); tffs_remove("/Duck"); // Init tffs_mkdir("/Disney"); tffs_mkdir("/Hell"); error = tffs_fwrite("/IMEI", TDATA(2)); if (error < 0 && error != EFFS_ACCESS) return 1; error = tffs_fcontrol("/IMEI", OC_FLAGS, OF_READONLY); if (error < 0 && error != EFFS_ACCESS) return 1; tw(tr(TR_FUNC, TrTestHigh, "Rename file\n")); /* Rename file */ error = tffs_fwrite("/Mickey", TDATA(3)); expect(error, EFFS_OK); error = tffs_fwrite("/Pluto", TDATA(2)); expect(error, EFFS_OK); error = tffs_rename("/Pluto", "/Dog"); expect(error, EFFS_OK); error = tffs_fupdate("/Pluto", TDATA(2)); expect(error, EFFS_NOTFOUND); error = test_expect_file("/Dog", TDATA(2)); if (error) return 1; error = tffs_rename("/Dog", "/Hell/RIP"); expect(error, EFFS_OK); error = tffs_rename("/Hell/RIP", "/RAP"); expect(error, EFFS_OK); error = tffs_rename("/RAP", "/Disney/RUP"); expect(error, EFFS_OK); error = test_expect_file("/Disney/RUP", TDATA(2)); if (error) return 1; // Rename a file to an existing file error = tffs_rename("/Mickey", "/Disney/RUP"); expect(error, EFFS_OK); // Have the data changed? error = test_expect_file("/Disney/RUP", TDATA(3)); if (error) return 1; // Try rename a file to an exisitng read-only file error = tffs_rename("/Disney/RUP", "/IMEI"); expect(error, EFFS_ACCESS); // Try rename a file to an existing dir error = tffs_rename("/Disney/RUP", "/Hell"); expect(error, EFFS_NOTAFILE); error = tffs_rename("/Disney/RUP", "/BADNAME?"); expect(error, EFFS_BADNAME); error = tffs_remove("/Hell"); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTestHigh, "Rename symlink\n")); /* Rename symlink */ tffs_symlink("/Goose", "/Disney/RUP"); // 5 error = tffs_rename("/Goose", "/Duck"); expect(error, EFFS_OK); error = tffs_fupdate("/Goose", TDATA(2)); expect(error, EFFS_NOTFOUND); error = test_expect_file("/Duck", TDATA(3)); if (error) return 1; error = test_expect_file("/Disney/RUP", TDATA(3)); if (error) return 1; tw(tr(TR_FUNC, TrTestHigh, "Rename dir\n")); /* Rename dir */ // * FIXME BUG * FIXME BUG * FIXME BUG * The below test to not fail instead // the directory is removed // error = tffs_rename("/Disney", "/Disney/foo"); // expect(error, EFFS_OK); tffs_mkdir("/Disney/Donald_Duck"); expect(error, EFFS_OK); error = ffs_rename("/Disney/Donald_Duck", "/Uncle_Scrooge"); expect(error, EFFS_OK); error = tffs_opendir("/Uncle_Scrooge", &dir); expect(error, EFFS_OK); error = tffs_opendir("/Disney/Donald_Duck", &dir); expect(error, EFFS_NOTFOUND); error = tffs_rename("/Disney", "/Walt_Disney"); expect(error, EFFS_OK); tffs_mkdir("/Disney"); // Create 'Disney' dir again expect(error, EFFS_OK); // Try rename to existing dir error = tffs_rename("/Disney", "/Walt_Disney"); expect(error, EFFS_EXISTS); // Try rename to existing file error = tffs_rename("/Disney", "/Walt_Disney/RUP"); expect(error, EFFS_EXISTS); tw(tr(TR_FUNC, TrTestHigh, "Rename seghead\n")); /* Rename seghead */ fdi = tffs_open("/Walt_Disney/Mickey", FFS_O_WRONLY | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], fs.chunk_size_max + 1); expect(size, fs.chunk_size_max + 1); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + size, fs.chunk_size_max); expect(size, fs.chunk_size_max); error = tffs_rename("/Walt_Disney/Mickey", "/Walt_Disney/Minnie"); expect(error, EFFS_LOCKED); tffs_close(fdi); error = tffs_rename("/Walt_Disney/Mickey", "/Walt_Disney/Minnie"); expect(error, EFFS_OK); fdi = tffs_open("/Walt_Disney/Minnie", FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); i = 0; do { size = tffs_read(fdi, bigbuf, bigbuf_size); error = test_expect_data((char*)tdata[TDATA_HUGE] + i, bigbuf, size); if (error) return 1; i += size; } while (size); error = tffs_close(fdi); expect(error, 0); error = test_expect_file("/Walt_Disney/Minnie", (char *)tdata[TDATA_HUGE], 2 * fs.chunk_size_max + 1); if (error) return 1; // Rename a big file to an existing big file error = tffs_fwrite("/Mickey", (char *)tdata[TDATA_HUGE] + 5, 2.5 * fs.chunk_size_max); expect(error, EFFS_OK); error = tffs_rename("/Mickey", "/Walt_Disney/Minnie"); expect(error, EFFS_OK); error = test_expect_file("/Walt_Disney/Minnie", (char *)tdata[TDATA_HUGE] + 5, 2.5 * fs.chunk_size_max); if (error) return 1; error = tffs_fread("/Mickey", 0 , 0); expect(error, EFFS_NOTFOUND); return 0; } // One of the problems with the rename function is that we need to copy the // data part from the old obj to the new obj. When we allocate data for the // new obj we risk that the data alloc caused a data reclaim which relocated // the old obj thus the source have been moved! int case_rename_extended(int p0, int p1) { int i, old_drec_most_lost, rename_file_relocated = 0; int fsize, offset; char myname1[] = "/rename1/rename1/foo"; char myname2[] = "/rename2/rename2/bar"; if (p0 == 0) p0 = 100; if (p1 == 0) p1 = 5; error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs/b3r24", 0x2BAD); expect(error, EFFS_OK); fsize = 2.5 * fs.chunk_size_max; // Test with more than one chunk tffs_mkdir("/rename1"); tffs_mkdir("/rename1/rename1"); tffs_mkdir("/rename2"); tffs_mkdir("/rename2/rename2"); error = tffs_file_write(myname1, (char*)tdata[TDATA_HUGE], fsize, FFS_O_CREATE | FFS_O_TRUNC); // Test if rename can handle when the objects are relocaded while the // rename is in progress for (i = 0; i < p0; i++) { tw(tr(TR_FUNC, TrTest, "Rename number %d\n", i)); offset = i % 2 ? 3 : 9; error = tffs_file_write(myname2, (char*)tdata[TDATA_HUGE] + offset, fsize, FFS_O_CREATE | FFS_O_TRUNC); // Get data reclaim candidate most_lost old_drec_most_lost = stats.drec.most_lost; error = tffs_rename(myname2, myname1); expect(error, EFFS_OK); error = test_expect_file(myname1, tdata[TDATA_HUGE] + offset, fsize); expect(error, EFFS_OK); error = tffs_fread(myname2, 0, 0); expect(error, EFFS_NOTFOUND); // Has the rename triggered a data_reclaim()? if (old_drec_most_lost != stats.drec.most_lost) { rename_file_relocated++; tw(tr(TR_FUNC, TrTest, "Rename objects relocated %d\n", rename_file_relocated)); if (rename_file_relocated >= p1) return EFFS_OK; } } return 1; } // Test Big files int case_bigfile(int p0, int p1) { struct ffs_state_s old, new; const char bigfile[] = "/antarctica/iceberg"; int bytes_max, file_size; #if 1 ttw(ttr(TTrTest, "WARNING: We need to re-implement this test. Skip test" NL)); tw(tr(TR_FUNC, TrTest, "WARNING: We need to re-implement this test. Skip test\n")); return 0; #endif if (param.data_blocks <= 1) { ttw(ttr(TTrTest, "WARNING: Too few blocks to run. Skip test" NL)); tw(tr(TR_FUNC, TrTest, "WARNING: Too few blocks to run. Skip test\n")); return 0; } error = case_cleanup(fs.filesize_max); expect(error, EFFS_NOSPACE); // We can not cleanup this amount. ffs_query(Q_BYTES_FREE, (uint32 *) &bytes_max); test_ffs_state_get(&old); // We don't have enough space for this amount because the name dos also // use some space file_size = bytes_max + 1; // Try to make a file of a size that is bigger than the total free space // in the file system. ttw(ttr(TTrTest, "Bigfile of size %d" NL, file_size)); tw(tr(TR_FUNC, TrTestHigh, "Bigfile of size %d\n", file_size)); error = tffs_fcreate(bigfile, (char *) tdata[TDATA_HUGE], file_size); expect(error, EFFS_NOSPACE); test_ffs_state_get(&new); expect_eq(old.bytes_free, new.bytes_free); // File the system with this huge file file_size = bytes_max - FFS_FILENAME_MAX - dev.atomsize; ttw(ttr(TTrTest, "Bigfile of size %d" NL, file_size)); tw(tr(TR_FUNC, TrTestHigh, "Bigfile of size %d\n", file_size)); error = tffs_fcreate(bigfile, (char *) tdata[TDATA_HUGE], file_size); expect(error, EFFS_OK); error = test_expect_file((char *) bigfile, (char *) tdata[TDATA_HUGE], file_size); if (error) return 1; test_ffs_state_get(&new); expect_gt(old.bytes_free, new.bytes_free - file_size); tffs_remove(bigfile); return 0; } /****************************************************************************** * Reclaim Test Cases ******************************************************************************/ // Test that ffs internal state is same after a re-ffs_init() int case_reinit(int p0, int p1) { struct ffs_state_s old, new; test_ffs_state_get(&old); error = tffs_initialize(); expect(error, EFFS_OK); test_ffs_state_get(&new); error = test_expect_state(&old, &new); if (error) return 1; return 0; } // Test inodes reclaim int case_irec(int p0, int p1) { // African states: kenya, egypt, marocco, namibia, nigeria, mozambique, // tanzania, ghana, togo, liberia, mali, congo struct ffs_state_s old, new; int i, j, updates; if (p0 == 0) p0 = 7; tffs_mkdir("/africa"); // Initialize tffs_fwrite("/africa/kenya", TDATA(0)); // 1 tffs_mkdir("/africa/east"); // 2 tffs_fwrite("/africa/tanzania", TDATA(1)); // 3 tffs_fwrite("/africa/east/egypt", TDATA(2)); // 4 tffs_symlink("/africa/et", "africa/east/egypt"); // 5 tffs_mkdir("/africa/west"); // 7 tffs_fupdate("/africa/et", TDATA(3)); // 6 tffs_fwrite("/africa/west/liberia", TDATA(0)); // 8 for (j = 0; j < p0; j++) { test_ffs_state_get(&old); updates = param.inodes_max - old.inodes_used; ttw(ttr(TTrTest, "loop %d: updates = %d" NL, j, updates)); tw(tr(TR_FUNC, TrTestHigh, "loop %d: updates = %d\n", j, updates)); for (i = 0; i < (updates / 10) + 10; i++) { // cleanup... error = tffs_remove("/africa/east/egypt"); expect(error, EFFS_OK); error = tffs_remove("/africa/east"); expect(error, EFFS_OK); error = tffs_remove("/africa/et"); expect(error, EFFS_OK); error = tffs_remove("/africa/west/liberia"); expect(error, EFFS_OK); error = tffs_remove("/africa/west"); expect(error, EFFS_OK); // use 10 inodes... error = tffs_fwrite("/africa/kenya", TDATA(0)); // 1 expect(error, EFFS_OK); error = tffs_mkdir("/africa/east"); // 2 expect(error, EFFS_OK); error = tffs_fwrite("/africa/tanzania", (char *) tdata[0], 0); // 3 expect(error, EFFS_OK); error = tffs_fcreate("/africa/east/egypt", TDATA(2)); // 4 expect(error, EFFS_OK); error = tffs_symlink("/africa/et", "/africa/east/egypt"); // 5 expect(error, EFFS_OK); error = tffs_mkdir("/africa/west"); // 7 expect(error, EFFS_OK); error = tffs_fupdate("/africa/et", TDATA(3)); // 6 expect(error, EFFS_OK); error = tffs_fcreate("/africa/west/liberia", TDATA(0)); // 8 expect(error, EFFS_OK); error = tffs_fupdate("/africa/west/liberia", TDATA(1)); // 9 expect(error, EFFS_OK); error = tffs_fupdate("/africa/tanzania", TDATA(2)); // 10 expect(error, EFFS_OK); } test_ffs_state_get(&new); expect_eq(old.objects_total, new.objects_total); error = test_expect_file("/africa/tanzania", TDATA(2)); if (error) return 1; error = test_expect_file("/africa/west/liberia", TDATA(1)); if (error) return 1; error = test_expect_file("/africa/east/egypt", TDATA(3)); if (error) return 1; error = test_expect_file("/africa/kenya", TDATA(0)); if (error) return 1; } return 0; } #if 1 // FIXME: Is this test case still valid after the new ffs_file_write() which // makes several chunks instead of on big? // Test data reclaim. Use up to maximum half the total space available. // drec params: percentage of avail to use, numupdates. We must not have too // big files due to fragmentation problems #define DREC_DIR "/north-america/usa" #define DREC_CHUNKS 6 int case_drec(int p0, int p1) { static struct test_file_s files[] = { { DREC_DIR "/alaska", 0, 0 }, { DREC_DIR "/arkansas", 0, 0 }, { DREC_DIR "/california", 0, 0 }, { DREC_DIR "/colorado", 0, 0 }, { DREC_DIR "/dakota", 0, 0 }, // north, south? { DREC_DIR "/DistrictOfColumbia", 0, 0 }, // state? { DREC_DIR "/florida", 0, 0 }, { DREC_DIR "/Georgia", 0, 0 }, { DREC_DIR "/hawaii", 0, 0 }, { DREC_DIR "/Idaho", 0, 0 }, { DREC_DIR "/Illinois", 0, 0 }, { DREC_DIR "/Iowa", 0, 0 }, { DREC_DIR "/kentucky", 0, 0 }, { DREC_DIR "/maine", 0, 0 }, { DREC_DIR "/Massachusettes", 0, 0 }, // spelling? { DREC_DIR "/michigan", 0, 0 }, { DREC_DIR "/minnesota", 0, 0 }, { DREC_DIR "/mississippi", 0, 0 }, { DREC_DIR "/missouri", 0, 0 }, { DREC_DIR "/Montana", 0, 0 }, { DREC_DIR "/Nevada", 0, 0 }, { DREC_DIR "/NewHampshire", 0, 0 }, { DREC_DIR "/NewJersey", 0, 0 }, { DREC_DIR "/NewMexico", 0, 0 }, { DREC_DIR "/NewYork", 0, 0 }, // state? { DREC_DIR "/north-carolina", 0, 0 }, { DREC_DIR "/ohio", 0, 0 }, { DREC_DIR "/oklahoma", 0, 0 }, { DREC_DIR "/Oregon", 0, 0 }, { DREC_DIR "/Pensylvania", 0, 0 }, { DREC_DIR "/RhodeIsland", 0, 0 }, // state? { DREC_DIR "/south-carolina", 0, 0 }, { DREC_DIR "/Tennesee", 0, 0 }, { DREC_DIR "/texas", 0, 0 }, { DREC_DIR "/utah", 0, 0 }, { DREC_DIR "/Vermont", 0, 0 }, { DREC_DIR "/virginia", 0, 0 }, { DREC_DIR "/washington", 0, 0 }, { DREC_DIR "/Wyoming", 0, 0 }, { DREC_DIR "/40", 0, 0 }, { DREC_DIR "/41", 0, 0 }, { DREC_DIR "/42", 0, 0 }, { DREC_DIR "/43", 0, 0 }, { DREC_DIR "/44", 0, 0 }, { DREC_DIR "/45", 0, 0 }, { DREC_DIR "/46", 0, 0 }, { DREC_DIR "/47", 0, 0 }, { DREC_DIR "/48", 0, 0 }, { DREC_DIR "/49", 0, 0 }, { DREC_DIR "/50", 0, 0 }, { DREC_DIR "/51", 0, 0 }, { DREC_DIR "/52", 0, 0 }, }; struct chunk_s { int ratio; int high; int low; }; const struct chunk_s chunk[DREC_CHUNKS] = { { 15, 65536, 32768 }, { 15, 32768, 16384 }, { 25, 16384, 8192 }, { 20, 8192, 4096 }, { 15, 4096, 2048 }, { 10, 2048, 0 } }; int i, j, n, num_files; int num, pct; int size_used, size_max, size_chunk; int size_chunk_used, size_chunk_low, size_chunk_high; num = (p0 == 0 ? 11 : p0); pct = (p1 == 0 ? 50 : p1); tffs_mkdir("/north-america"); tffs_mkdir("/north-america/usa"); size_max = pct * param.bytes_avail / 100; size_used = 0; num_files = sizeof(files) / sizeof(struct test_file_s); n = 0; tw(tr(TR_FUNC, TrTestHigh, "pct = %d%% = %dk\n", pct, size_max/1024)); for (i = 0; i < DREC_CHUNKS; i++) { size_chunk = chunk[i].ratio * size_max / 100; size_chunk_low = chunk[i].low / 256 * fs.filesize_max / 256; size_chunk_high = chunk[i].high / 256 * fs.filesize_max / 256; tw(tr(TR_FUNC, TrTestHigh, "%4dk of [%d..%d]: ", size_chunk/1024, size_chunk_high, size_chunk_low)); ttw(ttr(TTrTest, "%4dk of [%d..%d]: ", size_chunk/1024, size_chunk_high, size_chunk_low)); size_chunk_used = 0; // If the total chunk size can guaranteeable be contained... if (size_chunk >= chunk[i].high) { for (j = 0; j < size_chunk / size_chunk_high; j++) { if (n >= num_files) { tw(tr(TR_FUNC, TrTestHigh, "j too big\n")); ttw(str(TTrTest, "j too big" NL)); break; } files[n].size = size_chunk_low + rand() % (size_chunk_high - size_chunk_low); #ifdef WIN32 /* due to limitation in MS Visual C */ if( files[n].size > 65535 ) files[n].size = 65535; #endif //WIN32 files[n].data = (char *) tdata[TDATA_HUGE] + n; tw(tr(TR_NULL, TrTestHigh, "%d:%s ", files[n].size, &files[n].name[strlen(DREC_DIR)+1])); ttw(ttr(TTrTest, "%d:%s " NL, files[n].size, &files[n].name[strlen(DREC_DIR)+1])); size_chunk_used += files[n].size; n++; // FIXME: We should let all the unused bytes from this // chunk spill over into the next chunk so we use the // full percentage specified by p1. } size_used += size_chunk_used; } tw(tr(TR_NULL, TrTestHigh, "(%dk)\n", size_chunk_used/1024)); ttw(ttr(TTrTest, "(%dk)" NL, size_chunk_used/1024)); } tw(tr(TR_FUNC, TrTestHigh, "pct = %d%% = %dk\n", 100 * size_used/param.bytes_avail, size_used/1024)); ttw(ttr(TTrTest, "pct = %d%% = %dk" NL, 100 * size_used/param.bytes_avail, size_used/1024)); for (j = 0; j < num; j++) { ttw(ttr(TTrTest, "drec: %d. write" NL, j+1)); tw(tr(TR_FUNC, TrTestHigh, "drec: %d. write\n", j+1)); for (i = 0; i < n; i++) { error = tffs_fwrite(files[i].name, files[i].data, files[i].size); expect(error, EFFS_OK); } } for (i = 0; i < n; i++) { error = test_expect_file(files[i].name, files[i].data, files[i].size); if (error) return 1; error = tffs_remove(files[i].name); expect(error, EFFS_OK); } tffs_remove("/north-america/usa"); tffs_remove("/north-america"); return 0; } #else // Test data reclaim. Use up to maximum half the total space available. // drec params: percentage of avail to use, numupdates. We must not have too // big files due to fragmentation problems int case_drec(int p0, int p1) { struct blabla_s { int ratio; int high; int low; }; struct blabla_s sizes[4] = { { 10, 65536, 32768 }, { 40, 21800, 16384 }, { 45, 16384, 2048 }, { 5, 2048, 0 } }; static struct test_file_s files[] = { { DRECDIR "/alaska", 0, 0 }, { DRECDIR "/arkansas", 0, 0 }, { DRECDIR "/california", 0, 0 }, { DRECDIR "/colorado", 0, 0 }, { DRECDIR "/dakota", 0, 0 }, // north, south? { DRECDIR "/DistrictOfColumbia", 0, 0 }, // state? { DRECDIR "/florida", 0, 0 }, { DRECDIR "/Georgia", 0, 0 }, { DRECDIR "/hawaii", 0, 0 }, { DRECDIR "/Idaho", 0, 0 }, { DRECDIR "/Illinois", 0, 0 }, { DRECDIR "/Iowa", 0, 0 }, { DRECDIR "/kentucky", 0, 0 }, { DRECDIR "/maine", 0, 0 }, { DRECDIR "/Massachusettes", 0, 0 }, // spelling? { DRECDIR "/michigan", 0, 0 }, { DRECDIR "/minnesota", 0, 0 }, { DRECDIR "/mississippi", 0, 0 }, { DRECDIR "/missouri", 0, 0 }, { DRECDIR "/Montana", 0, 0 }, { DRECDIR "/Nevada", 0, 0 }, { DRECDIR "/NewHampshire", 0, 0 }, { DRECDIR "/NewJersey", 0, 0 }, { DRECDIR "/NewMexico", 0, 0 }, { DRECDIR "/NewYork", 0, 0 }, // state? { DRECDIR "/north-carolina", 0, 0 }, { DRECDIR "/ohio", 0, 0 }, { DRECDIR "/oklahoma", 0, 0 }, { DRECDIR "/Oregon", 0, 0 }, { DRECDIR "/Pensylvania", 0, 0 }, { DRECDIR "/RhodeIsland", 0, 0 }, // state? { DRECDIR "/south-carolina", 0, 0 }, { DRECDIR "/Tennesee", 0, 0 }, { DRECDIR "/texas", 0, 0 }, { DRECDIR "/utah", 0, 0 }, { DRECDIR "/Vermont", 0, 0 }, { DRECDIR "/virginia", 0, 0 }, { DRECDIR "/washington", 0, 0 }, { DRECDIR "/Wyoming", 0, 0 }, { DRECDIR "/40", 0, 0 }, { DRECDIR "/41", 0, 0 }, { DRECDIR "/42", 0, 0 }, { DRECDIR "/43", 0, 0 }, { DRECDIR "/44", 0, 0 }, { DRECDIR "/45", 0, 0 }, { DRECDIR "/46", 0, 0 }, { DRECDIR "/47", 0, 0 }, { DRECDIR "/48", 0, 0 }, { DRECDIR "/49", 0, 0 }, { DRECDIR "/50", 0, 0 }, { DRECDIR "/51", 0, 0 }, { DRECDIR "/52", 0, 0 }, }; int i, j, n, size, size_target; int num, percentage; error = tffs_mkdir("/north-america"); percentage = (p1 == 0 ? 50 : p1); n = sizeof(files)/sizeof(struct test_file_s); size_target = percentage * param.bytes_avail / 100; size = 0; for (i = 0; i < 4; i++) { sizes[i].ratio = sizes[i].ratio * size_target / 100; sizes[i].high = sizes[i].high / 256 * fs.filesize_max / 256; sizes[i].low = sizes[i].low / 256 * fs.filesize_max / 256; size += sizes[i].ratio; } ttw(ttr(TTrTest, "%4dk in total" NL, size / 1024)); tw(tr(TR_FUNC, TrTestHigh, "%4dk in total\n", size / 1024)); j = 0; size = size_target = 0; for (i = 0; i < 4; i++) { ttw(ttr(TTrTest, "%4dk: %d..%d = ", sizes[i].ratio / 1024, sizes[i].high, sizes[i].low)); tw(tr(TR_FUNC, TrTestHigh, "%4dk: %d..%d = ", sizes[i].ratio / 1024, sizes[i].high, sizes[i].low)); size_target += sizes[i].ratio; while (size < size_target) { files[j].data = (char *) tdata[TDATA_HUGE] + j; files[j].size = rand() % (sizes[i].high - sizes[i].low) + sizes[i].low; size += files[j].size; ttw(ttr(TTrTest, "%d:%s, ", files[j].size, &files[j].name[strlen(DRECDIR)+1])); tw(tr(TR_NULL, TrTestHigh, "%d:%s, ", files[j].size, &files[j].name[strlen(DRECDIR)+1])); j++; if (j >= n) { // TODO: better error handling? ttw(str(TTrTest, "j too big" NL)); tw(tr(TR_FUNC, TrTestHigh, "j too big\n")); return -1; } } ttw(str(TTrTest, "" NL)); tw(tr(TR_NULL, TrTestHigh, "\n")); } n = j; error = tffs_mkdir(DRECDIR); num = (p0 == 0 ? 5 : p0); for (j = 0; j < num; j++) { ttw(ttr(TTrTest, "drec: %d. write" NL, j+1)); tw(tr(TR_FUNC, TrTestHigh, "drec: %d. write\n", j+1)); for (i = 0; i < n; i++) { error = tffs_fwrite(files[i].name, files[i].data, files[i].size); expect(error, EFFS_OK); } // error = test_run("ri"); // if (error) return 1; } for (i = 0; i < n; i++) { error = test_expect_file(files[i].name, files[i].data, files[i].size); if (error) return 1; error = tffs_remove(files[i].name); expect(error, EFFS_OK); } return 0; } #endif void save_block_ages(int *age) { int i; struct block_header_s *bhp; for (i = 0; i < dev.numblocks; i++) { bhp = (struct block_header_s *) offset2addr(dev.binfo[i].offset); age[i] = bhp->age; tw(tr(TR_FUNC, TrTestHigh, "b,age(%d,%d)\n", i, age[i])); } } // NOTE: This chech includes the inode block thus the inode block also needs // to be reclaimed before this functions returns ok int has_all_block_ages_changed(int *age) { int i; struct block_header_s *bhp; for (i = 0; i < dev.numblocks; i++) { bhp = (struct block_header_s *) offset2addr(dev.binfo[i].offset); if (age[i] == bhp->age) return 0; // Age have not changed } return 1; } #define chunkalign(size) (((size) + fs.chunk_size_max-1) & ~(fs.chunk_size_max-1)) // Fill a data blocks with the maximum possible numbers of objects int fill_block_with_max_objects(void) { static int n = 1000; // Avoid white spaces in the filename int fsize, i, b, nobjects, bfree_space; char myname[] = "Fb_max-xxxx"; char *mydata = (char *) tdata[TDATA_HUGE]; // Find an approximate objects size fsize = dev.blocksize / fs.block_files_max - sizeof(myname) - 1; // Fill approximate 75% of a data block for (i = 0; i < fs.block_files_max / 4 * 3; i++, n++) { sprintf(myname, "/Fb_max-%4d", n); error = tffs_file_write(myname,(char*) mydata, fsize, FFS_O_CREATE); expect_ok(error); } // Find the block we currently are filling with objects. error = tffs_xlstat(myname, &xstat); expect_ok(error); b = xstat.block; bfree_space = dev.blocksize - bstat[b].used; tw(tr(TR_FUNC, TrTestHigh, "Block: %d, Free space:%dkB\n", b ,bfree_space>>10)); // Calculate the exact file size to fill a block with max number of files nobjects = fs.block_files_max - bstat[b].objects - fs.block_files_reserved; fsize = bfree_space / nobjects - sizeof(myname); fsize &= ~dev.atomnotmask; // Avoid padding tw(tr(TR_FUNC, TrTestHigh, "Exact file size (%d)\n", fsize)); // Fill the rest of the block for (i = 0; i < nobjects; i++, n++) { sprintf(myname, "/Fb_max-%4d", n); error = tffs_file_write(myname, (char*) mydata, fsize, FFS_O_CREATE); expect_ok(error); } tw(tr(TR_FUNC, TrTest, "Filled block %d done\n", b)); return EFFS_OK; } // Stress test data reclaim and test wearleveling. // NOTE: This test can not run in less than 7 blocks int case_adrec(int nwrites, int nage_change) { int i, j, n, inodes_reserved, nobjects, bytes_max; int blocks, fsize; int dynamic_fsize = 1000; char static_name[] = "/Static-xxxx"; char dynamic_name[] = "/Dynamic"; int *age = (int*) smallbuf; /* Flow: Format ffs. Fill N number of blocks with the maximum possible numbers of objects. Fill the rest of FFS except from a few kbytes. Use almost all the inodes. Trigger data reclaim by continue to update a file. Test if all flash blocks are used for wearleveling. */ if (nwrites == 0) // Number of updates of the dynamic file (timeout) nwrites = 1000000; if (nage_change == 0) // nage_change = 2; error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs", 0x2BAD); expect(error, EFFS_OK); // Major hack, disable journal and reserved space or else there will be // created new journals inside the blocks where we wish to have only // static objects fs.ijournal = 0; fs.reserved_space = 0; // Minimum amount of inodes to fill FFS: ffs_query(Q_BYTES_FREE, &bytes_max); inodes_reserved = bytes_max / fs.chunk_size_max; // We need to calculate the amount of inodes we can use and still // garanti that we have enough inodes to fill all data blocks with data. nobjects = fs.objects_max - inodes_reserved; // How many blocks can we fill? blocks = nobjects / fs.block_files_max; // Fill N number of data blocks with the maximum possible numbers of objects tw(tr(TR_FUNC, TrTest, "Fill %d blocks with max obj \n", blocks)); n = 1000; // Avoid white spaces in the filename for (j = 0; j < blocks; j++) { error = fill_block_with_max_objects(); expect_ok(error); } // Restore journal and reserved space error = tffs_initialize(); expect_ok(error); // Fill the rest of FFS except from 3 times the dynamic size, futhermore // keep 4 objects 'free' ffs_query(Q_OBJECTS_FREE, &nobjects); tw(tr(TR_FUNC, TrTest, "Fill almost the rest of FFS\n")); n = 1000; // Avoid white spaces in the filename while (nobjects - 4 > 0) { ffs_query(Q_BYTES_FREE, &bytes_max); fsize = (bytes_max - dynamic_fsize * 3) / (nobjects - 4); tw(tr(TR_FUNC, TrTestHigh, "space,obj,fsize (%dk,%d,%d)\n", bytes_max/1024, nobjects, fsize)); if (fsize > fs.chunk_size_max) { // Roundup to the closet complete chunk or else we use to many inodes fsize = chunkalign(fsize); tw(tr(TR_FUNC, TrTestHigh, "Chunkalign %d\n", fsize)); } sprintf(static_name, "/Static-%4d", n++); error = tffs_file_write(static_name, (char*)tdata[TDATA_HUGE], fsize, FFS_O_CREATE); if (error == EFFS_NOSPACE) { tw(tr(TR_FUNC, TrTest, "WARNING: Fill failed with out of data space\n")); break; } else expect_ok(error); ffs_query(Q_OBJECTS_FREE, &nobjects); } tw(tr(TR_FUNC, TrTest, "Fill done, all static objects are written\n")); case_debug_help(2,0); // bstat trace // Save the age of all blocks save_block_ages(age); tw(tr(TR_FUNC, TrTest, "Update the dynamic file max %d times\n", nwrites)); for (i = 0, j = 0; i < nwrites; i++) { error = tffs_file_write(dynamic_name, (char*)tdata[TDATA_HUGE], dynamic_fsize, FFS_O_CREATE | FFS_O_TRUNC); expect_ok(error); if (i%1000 == 0) tw(tr(TR_FUNC, TrTest, "Write number %d\n", i)); if (has_all_block_ages_changed(age) == 1) { tw(tr(TR_FUNC, TrTest,"All block ages have changed %d\n", j)); j++; save_block_ages(age); } if (j >= nage_change) break; } test_statistics_print(); case_debug_help(2,0); // bstat trace if (j >= nage_change) return 0; return 1; //Error } /****************************************************************************** * Journalling and Recover Test Cases ******************************************************************************/ // Test journalling. Both for normal objects but also for journal file // itself! The following operations are tested for proper recovery: fcreate, // fupdate, relocate, delete, clean, fcontrol int case_journal(int p0, int p1) { struct test_file_s tf; struct ffs_state_s old, new; struct xstat_s stat_old, stat_new; int i, myspace = 0, mytmpspace, offset; char mytmpname[] = "/test-journal-tmp"; int testflags[] = { JOURNAL_TEST_EMPTY, // object not committed JOURNAL_TEST_WRITING, // object not committed JOURNAL_TEST_READY, // object ok JOURNAL_TEST_COMMITTING, // object ok JOURNAL_TEST_COMMITTED, // object ok JOURNAL_TEST_DONE, // object ok }; tf.name = "/test-journal"; tf.data = (char *) tdata[2]; tf.size = sdata[2]; #if 1 // NOTEME: Workaround, the below use of state_get can not handle if we // create a new journal while we run the test. Instead we create a // new journal before the test if we are close to full. if (fs.journal_pos >= fs.journal_size - (FFS_JOURNAL_MARGIN + 12) * sizeof(struct journal_s)) { tw(tr(TR_FUNC, TrTest, "Journal file (near) full!\n")); journal_create(fs.ijournal); } // NOTEME: Workaround, this test is unabel to handle a // block_alloc(). The 'extra' amount of lost space caused by a // block_alloc() and its block header dos cheat the test to fail. offset = data_prealloc(1024); expect_gt(offset, 0); #endif // init #if 1 error = tffs_fwrite(mytmpname, tf.data, tf.size); expect(error, EFFS_OK); error = tffs_xlstat(mytmpname, &stat_old); expect(error, EFFS_OK); mytmpspace = stat_old.space; ttw(ttr(TTrTest, "file '%s' uses %d bytes" NL, mytmpname, mytmpspace)); tw(tr(TR_FUNC, TrTestHigh, "file '%s' uses %d bytes\n", mytmpname, mytmpspace)); #endif error = tffs_fcreate(tf.name, tf.data, tf.size); expect(error, EFFS_OK); error = tffs_xlstat(tf.name, &stat_old); expect(error, EFFS_OK); myspace = stat_old.space; error = tffs_fcreate("/dummy", 0, 0); expect(error, EFFS_OK); ttw(ttr(TTrTest, "file '%s' uses %d bytes" NL, tf.name, myspace)); tw(tr(TR_FUNC, TrTestHigh, "file '%s' uses %d bytes\n", tf.name, myspace)); ttw(str(TTrTest, "fcreate:" NL)); tw(tr(TR_BEGIN, TrTestHigh, "fcreate:\n")); stat_old.inode = 0; for (i = 0; i < sizeof(testflags)/sizeof(int); i++) { // clean up from operation being tested tffs_remove(tf.name); ttw(ttr(TTrTest, "fs.testflags = 0x%x" NL, testflags[i])); tw(tr(TR_FUNC, TrTestHigh, "fs.testflags = 0x%x\n", testflags[i])); test_ffs_state_get(&old); error = tffs_fcontrol("/dummy", OC_FS_TESTFLAGS, testflags[i]); expect(error, EFFS_OK); error = tffs_fcreate(tf.name, tf.data, tf.size); expect(error, EFFS_OK); error = tffs_initialize(); expect(error, EFFS_OK); test_ffs_state_get(&new); tw(tr(TR_FUNC, TrTestHigh, "bytes_used new = %7d, old = %7d, diff = %d\n", new.bytes_used, old.bytes_used, new.bytes_used - old.bytes_used)); tw(tr(TR_FUNC, TrTestHigh, "bytes_lost new = %7d, old = %7d, diff = %d\n", new.bytes_lost, old.bytes_lost, new.bytes_lost - old.bytes_lost)); expect_eq(new.bytes_used, old.bytes_used + myspace); error = tffs_xlstat(tf.name, &stat_new); if (i < 2) { // object was not committed, so object must not exist expect(error, EFFS_NOTFOUND); expect_eq(new.objects_total, old.objects_total); expect_eq(new.bytes_lost, old.bytes_lost + myspace); } else { // object was committed, so object must exist expect(error, EFFS_OK); expect_ne(stat_old.inode, stat_new.inode); expect_eq(new.objects_total, old.objects_total + 1); expect_eq(new.bytes_lost, old.bytes_lost); } } ttw(str(TTrTest, "" NL)); tw(tr(TR_END, TrTestHigh, "")); ttw(str(TTrTest, "fupdate:" NL)); tw(tr(TR_BEGIN, TrTestHigh, "fupdate:\n")); error = tffs_xlstat(tf.name, &stat_old); expect(error, EFFS_OK); for (i = 0; i < sizeof(testflags)/sizeof(int); i++) { ttw(ttr(TTrTest, "fs.testflags = 0x%x" NL, testflags[i])); tw(tr(TR_FUNC, TrTestHigh, "fs.testflags = 0x%x\n", testflags[i])); test_ffs_state_get(&old); error = tffs_fcontrol("/dummy", OC_FS_TESTFLAGS, testflags[i]); expect(error, EFFS_OK); error = tffs_fupdate(tf.name, tf.data, tf.size); error = tffs_initialize(); expect(error, EFFS_OK); error = tffs_xlstat(tf.name, &stat_new); expect(error, EFFS_OK); if (i < 2) { // object was not committed, so stats must be equal error = test_expect_data(&stat_new, &stat_old, sizeof(stat)); if (error) return 1; } else { // object was committed, so inodes must not be equal expect_ne(stat_old.inode, stat_new.inode); } test_ffs_state_get(&new); tw(tr(TR_FUNC, TrTestHigh, "bytes_used new = %7d, old = %7d, diff = %d\n", new.bytes_used, old.bytes_used, new.bytes_used - old.bytes_used)); tw(tr(TR_FUNC, TrTestHigh, "bytes_lost new = %7d, old = %7d, diff = %d\n", new.bytes_lost, old.bytes_lost, new.bytes_lost - old.bytes_lost)); expect_eq(new.objects_total, old.objects_total); expect_eq(new.bytes_used, old.bytes_used + myspace); expect_eq(new.bytes_lost, old.bytes_lost + myspace); } ttw(str(TTrTest, "" NL)); tw(tr(TR_END, TrTestHigh, "")); ttw(str(TTrTest, "rename:" NL)); tw(tr(TR_BEGIN, TrTestHigh, "rename:\n")); error = tffs_fwrite(tf.name, tf.data, tf.size); expect(error, EFFS_OK); error = tffs_xlstat(tf.name, &stat_old); expect(error, EFFS_OK); for (i = 0; i < sizeof(testflags)/sizeof(int); i++) { ttw(ttr(TTrTest, "fs.testflags = 0x%x" NL, testflags[i])); tw(tr(TR_FUNC, TrTestHigh, "fs.testflags = 0x%x\n", testflags[i])); error = tffs_fwrite(mytmpname, tf.data, tf.size); expect_ok(error); error = tffs_fcontrol("/dummy", OC_FS_TESTFLAGS, testflags[i]); expect(error, EFFS_OK); test_ffs_state_get(&old); error = tffs_rename(mytmpname, tf.name); expect_ok(error); error = tffs_initialize(); expect(error, EFFS_OK); test_ffs_state_get(&new); // tf.name MUST always exist error = tffs_xlstat(tf.name, &stat_new); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTestHigh, "bytes_used new = %7d, old = %7d, diff = %d\n", new.bytes_used, old.bytes_used, new.bytes_used - old.bytes_used)); tw(tr(TR_FUNC, TrTestHigh, "bytes_lost new = %7d, old = %7d, diff = %d\n", new.bytes_lost, old.bytes_lost, new.bytes_lost - old.bytes_lost)); error = tffs_xlstat(mytmpname, &stat_new); if (i < 2) { // Journal not READY, so test-journal-tmp must exist expect(error, EFFS_OK); expect_eq(new.objects_total, old.objects_total); expect_eq(new.bytes_used, old.bytes_used + myspace); expect_eq(new.bytes_lost, old.bytes_lost + myspace); } else { // Journal ready, so test-journal-tmp must not exist expect(error, EFFS_NOTFOUND); expect_eq(new.objects_total, old.objects_total - 1); expect_eq(new.bytes_used, old.bytes_used + myspace); expect_eq(new.bytes_lost, old.bytes_lost + myspace + mytmpspace); expect_ne(stat_old.inode, stat_new.inode); } } ttw(str(TTrTest, "" NL)); tw(tr(TR_END, TrTestHigh, "")); // cleanup tffs_remove(tf.name); tffs_remove("/dummy"); return 0; } // Test block recovery int case_brecover(int p0, int p1) { struct test_file_s tf; struct ffs_state_s old, new; int j, inodes_block_old = 0, inodes_block_new = 0; int i, error; uint16 free_blocks; int testflags[] = { BLOCK_COMMIT_BEFORE, BLOCK_COMMIT_NO_VALID, BLOCK_COMMIT_OLD_FREE, BLOCK_COMMIT_AFTER }; int testflags_2[] = { BLOCK_RECLAIM_ALLOC, BLOCK_RECLAIM_CLEANING, BLOCK_RECLAIM_NO_CLEAN, }; tf.name = "/dummy4fctrl"; tf.data = (char *) tdata[0]; tf.size = sdata[0]; error = tffs_fwrite(tf.name, tf.data, tf.size); expect(error, EFFS_OK); for (j = 0; j < 3; j++) { test_ffs_state_get(&old); //case_lsr(0, 0); // temp error = ffs_query(Q_FS_INODES, (uint16 *) &inodes_block_old); expect(error, EFFS_OK); // Trigger block_commit() to fail error = tffs_fcontrol(tf.name, OC_FS_TESTFLAGS, testflags[j]); expect(error, EFFS_OK); inodes_reclaim(); error = tffs_initialize(); expect(error, EFFS_OK); error = ffs_query(Q_FS_INODES, (uint16 *) &inodes_block_new); expect(error, EFFS_OK); ttw(ttr(TTrTest, "inodes block %d -> %d" NL, inodes_block_old, inodes_block_new)); tw(tr(TR_FUNC, TrTestHigh, "inodes block %d -> %d\n", inodes_block_old, inodes_block_new)); switch (j) { case 0: expect(inodes_block_old, inodes_block_new); break; case 1: expect_ne(inodes_block_old, inodes_block_new); break; case 2: expect_ne(inodes_block_old, inodes_block_new); break; case 3: expect_ne(inodes_block_old, inodes_block_new); break; } test_ffs_state_get(&new); if (test_expect_objects(&old, &new)) return 1; } // FIXME: The below test often fails (recalaim data block and create new // jouranl file makes it to fail) if (p0 == 9) { // Test BLOCK_RECLAIM Fill up all data blocks until only one is // free. tw(tr(TR_FUNC, TrTest, "Test Block reclaim\n")); ttw(ttr(TTrTest, "Test Block reclaim..." )); tffs_mkdir("/rand"); ffs_query(Q_BLOCKS_FREE, (uint16 *) &free_blocks); while (free_blocks > 1) { error = case_mk_rand_file("/rand", 10000, 0); if (error < 0) { tw(tr(TR_FUNC, TrTest, "ERROR: case_mk_rand_file failed\n")); return 1; } ffs_query(Q_BLOCKS_FREE, (uint16 *) &free_blocks); } // case_mk_rand_file() will trigger a block_reclaim which will be // interruptet by the activated testflags. This will cause the FFS to // use the last free block! After a new init of ffs must it have // recovered. ttw(ttr(TTrTest, "use last block...")); for (i = 0; i < sizeof(testflags_2)/sizeof(int); i++) { error = tffs_fcontrol(tf.name, OC_FS_TESTFLAGS, testflags_2[i]); expect(error, EFFS_OK); do { case_cleanup(20000); error = case_mk_rand_file("/rand", 7000, 0); } while (error >= 0); expect(error, EFFS_NOSPACE); ffs_query(Q_BLOCKS_FREE, (uint16 *) &free_blocks); if (free_blocks != 0) { tw(tr(TR_FUNC, TrTestHigh, "ERROR: We still have free blocks!\n")); return 1; } error = tffs_initialize();// NOTE: error "WARNING: block_alloc failed" is okay. expect_ok(error); ffs_query(Q_BLOCKS_FREE, (uint16 *) &free_blocks); if (free_blocks != 1) { tw(tr(TR_FUNC, TrTestHigh, "ERROR: Wrong number of blocks free: %d\n", free_blocks)); return 1; } } // NOTE: We have to make this clenaup because we can not run if FFS is // near full case_cleanup(dev.blocksize); // This test will make data_block_reclaim() to fail before it has // relocated all objects ttw(ttr(TTrTest, "interrupt block_reclaim()...")); for (i = 0; i < 4; i++) { error = tffs_fcontrol(tf.name, OC_FS_TESTFLAGS, BLOCK_RECOVER_OBJECTS); expect(error, EFFS_OK); do { case_cleanup(20000); error = case_mk_rand_file("/rand", 7000, 0); } while (error >= 0); expect(error, EFFS_NOSPACE); // NOTE: error "WARNING: block_alloc failed" is okay. error = tffs_initialize(); expect_ok(error); } // Free up some space to the next test case. case_cleanup(dev.blocksize * 4); ttw(ttr(TTrTest, "Done" NL)); } return 0; } /****************************************************************************** * Specific/Special Test Cases ******************************************************************************/ // FIXME: Is this test case still valid after the new ffs_file_write() which // makes seveal chunks instead of on big? // Test filling a block to the full block size. We write N files of // decreasing powers of two, starting at half the block size. At the end we // are guaranteed to have at least one completelt full block. Also, we know // we have used at least <block_size> bytes of storage. int case_bfull(int p0, int p1) { struct ffs_state_s old, new; char myname[40] = "/antarctica/ice-"; int i, size, mysize, dirlen, space; if (param.data_blocks <= 1) { ttw(ttr(TTrTest, "WARNING: Too few blocks to run. Skip test" NL)); tw(tr(TR_FUNC, TrTest, "WARNING: Too few blocks to run. Skip test\n")); return 0; } error = tffs_mkdir("/antarctica"); error = case_cleanup(param.block_size + 10000); expect_ok(error); test_ffs_state_get(&old); dirlen = strlen(myname); space = 0; size = param.block_size/2; for (i = 0; size > 0; i++) { mysize = size - param.atomsize; if (mysize < 0) mysize = size; sprintf(&myname[dirlen], "%d", size); error = tffs_fwrite(myname, (char *) tdata[TDATA_HUGE], mysize); expect(error, EFFS_OK); error = tffs_xlstat(myname, &xstat); expect(error, EFFS_OK); ttw(ttr(TTrTest, "%6d: %s" NL, xstat.space, myname)); tw(tr(TR_FUNC, TrTestHigh, "%6d: %s\n", xstat.space, myname)); space += xstat.space; size >>= 1; } test_ffs_state_get(&new); // Check space used. We have used a little more than <space> because we // have probably allocated one or two new blocks, and that is // dev.atomsize more bytes per block. tw(tr(TR_FUNC, TrTestHigh, "old.free = %d, new.free = %d, space used in test = %d\n", old.bytes_free, new.bytes_free, space)); expect_gt(old.bytes_free, new.bytes_free + space - 1); // remove half of the files size = param.block_size/2; for (i = 0; size > 0; i++) { if (i & 1) { sprintf(&myname[dirlen], "%d", size); error = tffs_remove(myname); expect(error, EFFS_OK); } size >>= 1; } return 0; } int case_list(int p0, int p1) { const struct testcase_s *p; tw(tr(TR_FUNC, TrTestHigh, "bigbuf = 0x%X, %d\n", bigbuf, bigbuf_size)); tw(tr(TR_FUNC, TrTestHigh, "smallbuf = 0x%X, %d\n", smallbuf, smallbuf_size)); ttw(ttr(TTrTest, "bigbuf = 0x%X, %d" NL, bigbuf, bigbuf_size)); ttw(ttr(TTrTest, "smallbuf = 0x%X, %d" NL, smallbuf, smallbuf_size)); ttw(str(TTrTest, "Test Cases:" NL)); for (p = testcase; p->name != 0; p++) { tw(tr(TR_FUNC, TrTest, "%8s: %s\n", p->name, p->comment)); ttw(ttr(TTrTest, "%8s: %s" NL, p->name, p->comment)); } return 0; } // Stress test the target. // p0 is number of times to run. Default is 10. // p1 is number of milli-seconds to wait between each file update. int case_stress(int p0, int p1) { char myname[20]; int i, mysize; char *mydata; mydata = (char *) tdata[TDATA_HUGE]; mysize = 9 * param.block_size / 16; if (p0 == 0) p0 = 10; if (p1 == 0) p1 = 1000; for (i = 0; i < p0; i++) { sprintf(myname, "/stress-%d", i); ttw(ttr(TTrTest, "/stress %d" NL, i)); error = tffs_fcreate(myname, (char *)mydata, mysize); expect(error, EFFS_OK); tffs_delay(p1); error = tffs_remove(myname); expect(error, EFFS_OK); } return 0; } // Test ffs with many (small) files. p0 is number of times to write all the // files. Default is two e.g. one create and one update int case_mfiles(int p0, int p1) { char myname[40]; int i, j; static struct test_file_s tf[] = { { MFDIR "/MSCAP", 0, 6 }, { MFDIR "/IMEI", 0, 8 }, { MFDIR "/CLASS2", 0, 3 }, { MFDIR "/CLASS3", 0, 2 }, { MFDIR "/MSSUP", 0, 5 }, { MFDIR "/MSSET", 0, 83 }, { MFDIR "/UPN", 0, 165 }, { MFDIR "/CTIM", 0, 92 }, { MFDIR "/CCNT", 0, 52 }, { MFDIR "/ECC", 0, 15 }, { MFDIR "/ORG", 0, 2650 }, { MFDIR "/BCCHINF", 0, 54 }, { MFDIR "/CCP", 0, 7 }, { MFDIR "/EXT1", 0, 13 }, { MFDIR "/SIMLCK", 0, 62 }, { MFDIR "/SIMLCKEXT",0, 172 }, { MFDIR "/SECURITY", 0, 8 }, { MFDIR "/MAIN", 0, 8 }, { MFDIR "/SFK", 0, 8 }, { MFDIR "/FAULT", 0, 8 }, { MFDIR "/DEBUG", 0, 8 }, { MFDIR "/POWER", 0, 8 }, { MFDIR "/KEYB", 0, 64 }, { MFDIR "/RADIO", 0, 8 }, { MFDIR "/CGMI", 0, 20 }, { MFDIR "/CGMM", 0, 20 }, { MFDIR "/CGMR", 0, 20 }, { MFDIR "/CGSN", 0, 20 }, { MFDIR "/SMSPRFL", 0, 206 }, { MFDIR "/PLMN", 0, 68 }, { MFDIR "/ATRADIO", 0, 2700 }, { MFDIR "/GROUP", 0, 1122 } }; error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/ffs/i2048o1024", 0x2BAD); expect(error, EFFS_OK); error = tffs_mkdir(MFDIR); expect(error, EFFS_OK); if (p0 == 0) p0 = 2; for (j = 0; j < p0; j++) { tw(tr(TR_FUNC, TrTestHigh, "mf: Writing many files, loop %d\n", j)); tw(tr(TR_FUNC, TrTestHigh, "SMS000..SMS099\n")); for (i = 0; i < 100; i++) { sprintf(myname, MFDIR "/SMS%03d", i); error = tffs_fwrite(myname, (char *) tdata[TDATA_HUGE], 176); expect(error, EFFS_OK); } tw(tr(TR_FUNC, TrTestHigh, "LDN/LRN/LMN000..LDN/LRN/LMN009\n")); for (i = 0; i < 10; i++) { sprintf(myname, MFDIR "/LDN%03d", i); error = tffs_fwrite(myname, (char *) tdata[TDATA_HUGE], 31); expect(error, EFFS_OK); sprintf(myname, MFDIR "/LRN%03d", i); error = tffs_fwrite(myname, (char *) tdata[TDATA_HUGE], 32); expect(error, EFFS_OK); sprintf(myname, MFDIR "/LMN%03d", i); error = tffs_fwrite(myname, (char *) tdata[TDATA_HUGE], 30); expect(error, EFFS_OK); } tw(tr(TR_FUNC, TrTestHigh, "ADN000..ADN149\n")); for (i = 0; i < 150; i++) { sprintf(myname, MFDIR "/ADN%03d", i); error = tffs_fwrite(myname, (char *) tdata[TDATA_HUGE], 43); expect(error, EFFS_OK); } tw(tr(TR_FUNC, TrTestHigh, "PCM Files...\n")); for (i = 0; i < sizeof(tf)/sizeof(struct test_file_s); i++) { error = tffs_fwrite(tf[i].name, (char *) tdata[TDATA_HUGE], tf[i].size); expect(error, EFFS_OK); } } error = tffs_exit(); expect(error, EFFS_OK); return 0; } int case_open(int p0, int p1) { fd_t fdi; char myname[20]; int i, size; #define RDONLY FFS_O_RDONLY #define WRONLY FFS_O_WRONLY #define APPEND FFS_O_APPEND #define CREATE FFS_O_CREATE #define EXCL FFS_O_EXCL #define TRUNC FFS_O_TRUNC ffs_options_t non_exist_invalid[] = { ( 0 ), ( RDONLY | CREATE ), ( RDONLY | CREATE | EXCL ), ( RDONLY | CREATE | TRUNC ), ( RDONLY | CREATE | EXCL | TRUNC ), ( RDONLY | EXCL ), ( RDONLY | EXCL | TRUNC ), ( RDONLY | TRUNC ), ( EXCL ), ( EXCL | TRUNC ), ( TRUNC ), ( CREATE | TRUNC ), ( CREATE | EXCL | TRUNC ), ( CREATE | EXCL ), ( CREATE ), ( APPEND ), ( APPEND | EXCL ), ( APPEND | EXCL | TRUNC ), ( APPEND | TRUNC ), ( APPEND | CREATE | TRUNC ), ( APPEND | CREATE | EXCL ), ( APPEND | CREATE | EXCL ), ( APPEND | CREATE ), ( RDONLY | APPEND ), ( RDONLY | APPEND | EXCL ), ( RDONLY | APPEND | EXCL | TRUNC ), ( RDONLY | APPEND | TRUNC ), ( RDONLY | APPEND | CREATE | TRUNC ), ( RDONLY | APPEND | CREATE | EXCL | TRUNC ), ( RDONLY | APPEND | CREATE | EXCL ), ( RDONLY | APPEND | CREATE ) }; ffs_options_t non_exist_notfound[] = { ( WRONLY | APPEND ), ( WRONLY | APPEND | EXCL ), ( WRONLY | APPEND | EXCL | TRUNC ), ( WRONLY | APPEND | TRUNC ), ( WRONLY ), ( WRONLY | EXCL ), ( WRONLY | EXCL | TRUNC ), ( WRONLY | TRUNC ), ( RDONLY | WRONLY ), ( RDONLY | WRONLY | EXCL ), ( RDONLY | WRONLY | EXCL | TRUNC ), ( RDONLY | WRONLY | TRUNC ), ( RDONLY | WRONLY | APPEND ), ( RDONLY | WRONLY | APPEND | EXCL ), ( RDONLY | WRONLY | APPEND | EXCL | TRUNC ), ( RDONLY | WRONLY | APPEND | TRUNC ), ( RDONLY ) }; ffs_options_t non_exist_fd_offset[] = { ( WRONLY | APPEND | CREATE | TRUNC ), ( WRONLY | APPEND | CREATE | EXCL | TRUNC ), ( WRONLY | APPEND | CREATE | EXCL ), ( WRONLY | APPEND | CREATE ), ( WRONLY | CREATE | TRUNC ), ( WRONLY | CREATE | EXCL | TRUNC ), ( WRONLY | CREATE | EXCL ), ( WRONLY | CREATE ), ( RDONLY | WRONLY | CREATE | TRUNC ), ( RDONLY | WRONLY | CREATE | EXCL | TRUNC ), ( RDONLY | WRONLY | CREATE | EXCL ), ( RDONLY | WRONLY | CREATE ), ( RDONLY | WRONLY | APPEND | CREATE | TRUNC ), ( RDONLY | WRONLY | APPEND | CREATE | EXCL | TRUNC ), ( RDONLY | WRONLY | APPEND | CREATE | EXCL ), ( RDONLY | WRONLY | APPEND | CREATE ) }; ffs_options_t exist_invalid[] = { ( EXCL ), ( EXCL | TRUNC ), ( TRUNC ), ( CREATE | TRUNC ), ( CREATE | EXCL | TRUNC ), ( CREATE | EXCL ), ( CREATE ), ( APPEND ), ( APPEND | EXCL ), ( APPEND | EXCL | TRUNC ), ( APPEND | TRUNC ), ( APPEND | CREATE | TRUNC ), ( APPEND | CREATE | EXCL | TRUNC ), ( APPEND | CREATE | EXCL ), ( APPEND | CREATE ), ( RDONLY | APPEND ), ( RDONLY | APPEND | EXCL ), ( RDONLY | APPEND | EXCL | TRUNC ), ( RDONLY | APPEND | TRUNC ), ( RDONLY | APPEND | CREATE | TRUNC ), ( RDONLY | APPEND | CREATE | EXCL | TRUNC ), ( RDONLY | APPEND | CREATE | EXCL ), ( RDONLY | APPEND | CREATE ), ( RDONLY | EXCL ), ( RDONLY | EXCL | TRUNC ), ( RDONLY | TRUNC ), ( RDONLY | CREATE | TRUNC ), ( RDONLY | CREATE | EXCL | TRUNC ), ( RDONLY | CREATE | EXCL ), ( RDONLY | CREATE ) }; ffs_options_t exist_exist[] = { ( WRONLY | APPEND | CREATE | EXCL | TRUNC ), ( WRONLY | APPEND | CREATE | EXCL ), ( WRONLY | CREATE | EXCL | TRUNC ), ( WRONLY | CREATE | EXCL ), ( RDONLY | WRONLY | CREATE | EXCL | TRUNC ), ( RDONLY | WRONLY | CREATE | EXCL ), ( RDONLY | WRONLY | APPEND | CREATE | EXCL | TRUNC ), ( RDONLY | WRONLY | APPEND | CREATE | EXCL ) }; ffs_options_t exist_fd_offset[] = { ( WRONLY | APPEND ), ( WRONLY | APPEND | EXCL ), ( WRONLY | APPEND | EXCL | TRUNC ), ( WRONLY | APPEND | TRUNC ), ( WRONLY | APPEND | CREATE | TRUNC ), ( WRONLY | APPEND | CREATE ), ( RDONLY ), ( RDONLY | WRONLY ), ( RDONLY | WRONLY | EXCL ), ( RDONLY | WRONLY | EXCL | TRUNC ), ( RDONLY | WRONLY | TRUNC ), ( RDONLY | WRONLY | CREATE | TRUNC ), ( RDONLY | WRONLY | CREATE ), ( RDONLY | WRONLY | APPEND ), ( RDONLY | WRONLY | APPEND | EXCL ), ( RDONLY | WRONLY | APPEND | EXCL | TRUNC ), ( RDONLY | WRONLY | APPEND | TRUNC ), ( RDONLY | WRONLY | APPEND | CREATE | TRUNC ), ( RDONLY | WRONLY | APPEND | CREATE ), ( WRONLY ), ( WRONLY | EXCL ), ( WRONLY | EXCL | TRUNC ), ( WRONLY | TRUNC ), ( WRONLY | CREATE | TRUNC ), ( WRONLY | CREATE ) }; // Cleanup for (i = 0; i < 20; i++) { sprintf(myname, "/stream%d", i); tffs_remove(myname); } tw(tr(TR_FUNC, TrTestHigh, "Various open options, file non-existing\n")); ttw(str(TTrTest, "Various open options, file non-existing" NL)); for (i = 0; i < sizeof(non_exist_invalid)/sizeof(ffs_options_t); i++) { sprintf(myname, "/stream%d", i - i/20 * 20); fdi = tffs_open(myname, non_exist_invalid[i]); expect(fdi, EFFS_INVALID); } for (i = 0; i < sizeof(non_exist_notfound)/sizeof(ffs_options_t); i++) { sprintf(myname, "/stream%d", i - i/20 * 20); fdi = tffs_open(myname, non_exist_notfound[i]); expect(fdi, EFFS_NOTFOUND); } for (i = 0; i < sizeof(non_exist_fd_offset)/sizeof(ffs_options_t); i++) { sprintf(myname, "/stream%d", i - i/20 * 20); fdi = tffs_open(myname, non_exist_fd_offset[i]); expect(fdi, FFS_FD_OFFSET); tffs_close(fdi); } // Make FFS_FD_MAX number of files and write some data for later use tw(tr(TR_FUNC, TrTestHigh, "Create %d files with data \n", fs.fd_max)); for (i = 0; i < fs.fd_max; i++) { sprintf(myname, "/stream%d", i); fdi = tffs_open(myname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC); expect(fdi, i + FFS_FD_OFFSET); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 31); expect(size, 31); } // Try to open one more file, this will make a error! sprintf(myname, "/stream%d", i++); fdi = tffs_open(myname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC); expect(fdi, EFFS_NUMFD); for (i = 0; i < fs.fd_max; i++) { error = tffs_close(i + FFS_FD_OFFSET); expect(error, EFFS_OK); } tw(tr(TR_FUNC, TrTestHigh, "Open same file multiple times in RDONLY mode\n")); for (i = 0; i < fs.fd_max; i++) { fdi = tffs_open("/stream2", FFS_O_RDONLY); expect(fdi, i + FFS_FD_OFFSET); } for (i = 0; i < fs.fd_max; i++) { error = tffs_close(i + FFS_FD_OFFSET); expect(error, 0); } tw(tr(TR_FUNC, TrTestHigh, "Open in WRONLY and try to open same file in WR or RD\n")); fdi = tffs_open("/stream1", FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); fdi = tffs_open("/stream1", FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, EFFS_LOCKED); fdi = tffs_open("/stream1", FFS_O_RDONLY ); expect(fdi, EFFS_LOCKED); error = tffs_close(FFS_FD_OFFSET); expect(error, 0); tw(tr(TR_FUNC, TrTestHigh, "Open in READONLY and try to open same file WRONLY\n")); fdi = tffs_open("/stream1", FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); fdi = tffs_open("/stream1", FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, EFFS_LOCKED); error = tffs_close(FFS_FD_OFFSET); expect(error, 0); tw(tr(TR_FUNC, TrTestHigh, "Various open options, file exists\n")); ttw(str(TTrTest, "Various open options, file exists" NL)); for (i = 0; i < sizeof(exist_invalid)/sizeof(ffs_options_t); i++) { sprintf(myname, "/stream%d", i - i/20 * 20); fdi = tffs_open(myname, exist_invalid[i]); expect(fdi, EFFS_INVALID); } for (i = 0; i < sizeof(exist_exist)/sizeof(ffs_options_t); i++) { sprintf(myname, "/stream%d", i - i/20 * 20); fdi = tffs_open(myname, exist_exist[i]); expect(fdi, EFFS_EXISTS); tffs_close(fdi); } for (i = 0; i < sizeof(exist_fd_offset)/sizeof(ffs_options_t); i++) { sprintf(myname, "/stream%d", i - i/10 * 10); fdi = tffs_open(myname, exist_fd_offset[i]); expect(fdi, FFS_FD_OFFSET); tffs_close(fdi); } tw(tr(TR_FUNC, TrTestHigh, "Try to create a file with a dir name\n")); error = tffs_mkdir("/streams"); fdi = tffs_open("/streams", FFS_O_WRONLY | FFS_O_APPEND | FFS_O_CREATE); expect(fdi, EFFS_NOTAFILE); tw(tr(TR_FUNC, TrTestHigh, "Try to use fwrite, fcreate, fread and remove on a open file\n")); fdi = tffs_open("/stream1", FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); error = tffs_fcreate("/stream1", TDATA(2)); expect(error, EFFS_EXISTS); error = tffs_file_write("/stream1", (char *) tdata[TDATA_HUGE], 31, FFS_O_CREATE | FFS_O_TRUNC); expect(error, EFFS_LOCKED); error = tffs_file_read("/stream1", bigbuf, 31); expect(error, EFFS_LOCKED); error = tffs_remove("/stream1"); expect(error, EFFS_LOCKED); error = tffs_close(fdi); expect(error, 0); #undef RDONLY #undef WRONLY #undef APPEND #undef CREATE #undef EXCL #undef TRUNC return 0; } int twrite_seek(fd_t fdi, offset_t offset, int whence) { int size; size = tffs_write(fdi, (char*) tdata[TDATA_HUGE] + 10, 100); if (test_expect(size, 100) < 0) return -1; offset = tffs_seek(fdi, offset, whence); if (test_expect_ok(offset)) return -1; return offset; } int tseek_read(fd_t fdi, offset_t offset, int whence, offset_t src_offset) { int size; offset = tffs_seek(fdi, offset, whence); if (test_expect_ok(offset)) return -1; size = tffs_read(fdi, bigbuf, 100); if (test_expect(size, 100) < 0) return -1; error = test_expect_data((char*)tdata[TDATA_HUGE] + src_offset, bigbuf, size); if (error) return error; return offset; } int tseek_write(fd_t fdi, offset_t offset, int whence, offset_t src_offset) { int size; offset = tffs_seek(fdi, offset, whence); if (test_expect_ok(offset)) return -1; size = tffs_write(fdi, (char*) tdata[TDATA_HUGE] + src_offset + offset, fs.chunk_size_max / 2); if (test_expect(size, fs.chunk_size_max / 2) < 0) return -1; return offset; } int case_rw(int p0, int p1) { int i, size, offset; fd_t fdi; const char *dirs[] = { "/streams", "/streams/rw", "/streams/rw/multi", "/streams/rw/multi/write" }; error = case_cleanup(5 * fs.chunk_size_max); expect_ok(error); for (i = 0; i < sizeof(dirs)/sizeof(char *); i++) { error = tffs_mkdir(dirs[i]); } tw(tr(TR_FUNC, TrTestHigh, "Test create and append of file, small and huge\n")); ttw(str(TTrTest, "Test create and append of file, small and huge" NL)); fdi = tffs_open("/streams/rw_test", FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 31); expect(size, 31); error = tffs_close(fdi); expect(error, EFFS_OK); fdi = tffs_open("/streams/rw_test", FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE] + 31, 2 * fs.chunk_size_max); expect(size, 2 * fs.chunk_size_max); error = tffs_close(fdi); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); ttw(ttr(TTrTest, "Validate data" NL)); size = tffs_fread("/streams/rw_test", bigbuf, bigbuf_size); expect(size, 31 + 2 * fs.chunk_size_max); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; // Provoke EFFS_FILETOOBIG error = tffs_fread("/streams/rw_test", bigbuf, size - 1); expect(error, EFFS_FILETOOBIG); error = tffs_fread("/streams/rw_test", bigbuf, size + 1); expect_ok(error); error = tffs_fread("/streams/rw_test", bigbuf, size); expect_ok(error); // Update file from zero, middle (between to chunks) and end. File with // and without data in seghead. Update the first 100 bytes (new data // have a offset of 10 so the data not is identical with the existing // ones). fdi = tffs_open("/streams/rw_test", FFS_O_RDWR); expect(fdi, FFS_FD_OFFSET); // write 100 bytes and seek to 50 bytes before end of the first chunk expect_ok(twrite_seek(fdi, fs.chunk_size_max - 50, FFS_SEEK_SET)); // write 100 bytes, seek to 50 bytes from the end of the file expect_ok(twrite_seek(fdi, -50, FFS_SEEK_END)); // Write 100 bytes. This will update the last 19 bytes of second chunk, // update and append data to the last chunk. Read the last data but // only 80 bytes because they are in the buffer expect_ok(twrite_seek(fdi, -80, FFS_SEEK_CUR)); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); ttw(ttr(TTrTest, "Validate data" NL)); size = tffs_read(fdi, bigbuf, 100); expect(size, 80); error = test_expect_data((char *)tdata[TDATA_HUGE] + 10 + 20, bigbuf, size); if (error) return 1; // Seek to start of file and read the first 100 bytes that was updated offset = tseek_read(fdi, 0, FFS_SEEK_SET, 10); expect_ok(offset); // Read 100 bytes more to make sure that the old data still is valid offset = tseek_read(fdi, 0, FFS_SEEK_CUR, 100); expect_ok(offset); // Seek to 50 bytes before end of the first chunk and read 100 bytes offset = tseek_read(fdi, fs.chunk_size_max - 50, FFS_SEEK_SET, 10); expect_ok(offset); // Read 100 bytes more to make sure that the old data still is valid offset = tseek_read(fdi, 0, FFS_SEEK_CUR, offset + 100); expect_ok(offset); // Read the last 100 bytes (updated data from 2 chunks) offset = tseek_read(fdi, -100, FFS_SEEK_END, 10); expect_ok(offset); error = tffs_close(fdi); expect(error, EFFS_OK); // Test where there is data in the seghead size = tffs_file_write("/streams/rw_test", (char*) tdata[TDATA_HUGE], 2 * fs.chunk_size_max, FFS_O_CREATE | FFS_O_TRUNC); fdi = tffs_open("/streams/rw_test", FFS_O_RDWR); expect(fdi, FFS_FD_OFFSET); // Update the last half of the last chunk expect_ok(tseek_write(fdi, -fs.chunk_size_max / 2, FFS_SEEK_END, 10)); // Update the first half of the last chunk expect_ok(tseek_write(fdi, fs.chunk_size_max, FFS_SEEK_SET, 10)); // Update the first half of the seghead expect_ok(tseek_write(fdi, 0, FFS_SEEK_SET, 10)); // Update the last half of the seghead, but flush the buffer so it have // to write and read the previous updated data error = tffs_fdatasync(fdi); expect(error, EFFS_OK); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE] + 10 + fs.chunk_size_max/2, fs.chunk_size_max/2); expect(size, fs.chunk_size_max/2); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); ttw(ttr(TTrTest, "Validate data" NL)); // Read the complete file, all the data should have a offset of 10 bytes now! offset = tffs_seek(fdi, 0, FFS_SEEK_SET); expect_ok(offset); size = tffs_read(fdi, bigbuf, bigbuf_size); expect(size, 2 * fs.chunk_size_max); error = test_expect_data((char *)tdata[TDATA_HUGE] + 10, bigbuf, size); if (error) return 1; error = tffs_close(fdi); expect(error, 0); // Test bad fdi, write in RDONLY and read from WRONLY fdi = tffs_open("/streams/rw_test", FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); size = tffs_read(fdi + 1, bigbuf, 31); expect(size, EFFS_BADFD); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 31); tw(tr(TR_END, TrApi, "} %d\n", size)); // Avoid wrong indent expect(size, EFFS_INVALID); error = tffs_close(fdi); expect(error, 0); fdi = tffs_open("/streams/rw_test", FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); size = tffs_read(fdi, bigbuf, 31); expect(size, EFFS_INVALID); error = tffs_close(fdi); expect(error, 0); // Test for bad file name fdi = tffs_open("/stream&!'*", FFS_O_WRONLY | FFS_O_CREATE); expect(fdi, EFFS_BADNAME); // Test for bad file name fdi = tffs_open("/%$#.+-_,Z19", FFS_O_WRONLY | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); tffs_close(fdi); return 0; } int case_multi_open(int p0, int p1) { int i, j, size; fd_t fdi; const char *dirs[] = { "/streams", "/streams/rw", "/streams/rw/multi", "/streams/rw/multi/write" }; const char *file[] = { "/x-rw", "/streams/rw/x-rw", "/streams/rw/multi/x-rw", "/streams/rw/multi/write/x-rw" }; error = case_cleanup(fs.fd_max * 50 * 10 + 5000); expect_ok(error); for (i = 0; i < sizeof(dirs)/sizeof(char *); i++) { error = tffs_mkdir(dirs[i]); } // Open multiply files from difference subdirectorys // and write/read X times to/from file (this also test truncate option) for (i = 0; i < fs.fd_max; i++) { fdi = tffs_open( file[i], FFS_O_WRONLY | FFS_O_APPEND | FFS_O_CREATE | FFS_O_TRUNC); expect(fdi, i + FFS_FD_OFFSET); } for (i = 0; i < fs.fd_max; i++) { for (j = 0; j < 10; j++) { size = tffs_write(i + FFS_FD_OFFSET, (char *) tdata[TDATA_HUGE], 50); expect(size, 50); } } // Run test with p0 != 0 to stress the test if (p0) { error = test_run("irec;drec;"); if (error > 0) return 1; } for (i = 0; i < fs.fd_max; i++) { error = tffs_close(i + FFS_FD_OFFSET); expect(error, 0); } for (i = 0; i < fs.fd_max; i++) { fdi = tffs_open( file[i], FFS_O_RDONLY); expect(fdi, i + FFS_FD_OFFSET); } if(p0) { error = test_run("irec;drec;"); if (error > 0) return 1; } for (i = 0; i < fs.fd_max; i++) { for (j = 0; j < 10; j++) { size = tffs_read(i + FFS_FD_OFFSET, bigbuf, 50); expect(size, 50); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; } } for (i = 0; i < fs.fd_max; i++) { error = tffs_close(i + FFS_FD_OFFSET); expect(error, 0); } return 0; } int case_seek(int p0, int p1) { fd_t fdi; int size, offset; error = case_cleanup(3 * fs.chunk_size_max); expect_ok(error); // The seek function itself don't care if the file is open in read or // write mode. error = tffs_mkdir("/streams"); offset = tffs_seek(FFS_FD_OFFSET, 0, FFS_SEEK_SET); expect(offset, EFFS_BADFD); /* Make file to seek on */ fdi = tffs_open("/streams/seek", FFS_O_RDWR | FFS_O_CREATE | FFS_O_TRUNC); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 3 * fs.chunk_size_max); expect(size, 3 * fs.chunk_size_max); /* Simple test in RDWR without any writes. Test good and bad offset */ tw(tr(TR_FUNC, TrTestHigh, "seek and RDWR \n")); ttw(str(TTrTest, "seek and RDWR" NL)); offset = ffs_seek(fdi, 0, 5); expect(offset, EFFS_INVALID ); tw(tr(TR_FUNC, TrTestHigh, "ffs_seek(FFS_SEEK_SET)\n")); ttw(str(TTrTest, "ffs_seek(FFS_SEEK_SET)" NL)); offset = tffs_seek(fdi, -1, FFS_SEEK_SET); expect(offset, EFFS_INVALID); offset = tffs_seek(fdi, 3 * fs.chunk_size_max + 1, FFS_SEEK_SET); expect(offset, EFFS_INVALID); offset = tffs_seek(fdi, 15, FFS_SEEK_SET); expect(offset, 15); size = tffs_read(fdi, bigbuf, fs.chunk_size_max/10); expect(size, fs.chunk_size_max/10); error = test_expect_data((char *)tdata[TDATA_HUGE] + 15, bigbuf, fs.chunk_size_max/10); if (error) return 1; offset = tffs_seek(fdi, 0, FFS_SEEK_SET); expect(offset, 0); size = tffs_read(fdi, bigbuf, bigbuf_size); error = test_expect_data((char*)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; tw(tr(TR_FUNC, TrTestHigh, "ffs_seek(FFS_SEEK_CUR)\n")); ttw(str(TTrTest, "ffs_seek(FFS_SEEK_CUR)" NL)); offset = tffs_seek(fdi, -(3 * fs.chunk_size_max + 1), FFS_SEEK_CUR); expect(offset, EFFS_INVALID); offset = tffs_seek(fdi, 1, FFS_SEEK_CUR); expect(offset, EFFS_INVALID); offset = tffs_seek(fdi, 0, FFS_SEEK_CUR); expect(offset, 3 * fs.chunk_size_max); offset = tffs_seek(fdi, -30, FFS_SEEK_CUR); expect(offset, 3 * fs.chunk_size_max - 30); offset = tffs_seek(fdi, 20, FFS_SEEK_CUR); expect(offset, 3 * fs.chunk_size_max - 30 + 20); size = tffs_read(fdi, bigbuf, 3 * fs.chunk_size_max); expect(size, 10); error = test_expect_data((char *)tdata[TDATA_HUGE] + (3 * fs.chunk_size_max - 10), bigbuf, 10); if (error) return 1; tw(tr(TR_FUNC, TrTestHigh, "ffs_seek(FFS_SEEK_END)\n")); ttw(str(TTrTest, "ffs_seek(FFS_SEEK_END)" NL)); offset = tffs_seek(fdi, -(3 * fs.chunk_size_max + 1), FFS_SEEK_END); expect(offset, EFFS_INVALID); offset = tffs_seek(fdi, 1, FFS_SEEK_END); expect(offset, EFFS_INVALID); offset = tffs_seek(fdi, 0, FFS_SEEK_END); expect(offset, 3 * fs.chunk_size_max); offset = tffs_seek(fdi, -15, FFS_SEEK_END); expect(offset, 3 * fs.chunk_size_max - 15); size = tffs_read(fdi, bigbuf, 20); expect(size, 15); error = test_expect_data((char *)tdata[TDATA_HUGE] + (3 * fs.chunk_size_max - 15), bigbuf, size); if (error) return 1; error = tffs_close(fdi); expect(error, 0); // Seek in write-mode with write in different places of the file. Test // on the dirty flag to se if the buffer is flushed ad the rigth time tw(tr(TR_FUNC, TrTestHigh, "seek and WRONLY \n")); ttw(str(TTrTest, "seek and WRONLY" NL)); fdi = tffs_open("/streams/seek", FFS_O_WRONLY); // Write and seek in the last chunk, buffer must be dirty so long the fp // not have been moved away from that chunk! offset = tffs_seek(fdi, -20, FFS_SEEK_END); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 0); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 10); expect(size, 10); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); offset = tffs_seek(fdi, -100, FFS_SEEK_CUR); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 10); expect(size, 10); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); offset = tffs_seek(fdi, -fs.chunk_size_max/4, FFS_SEEK_CUR); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 10); expect(size, 10); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); offset = tffs_seek(fdi, offset + 10, FFS_SEEK_SET); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 10); expect(size, 10); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); // Write and seek across several chunks, the buffer must be flushed after // leaving a 'dirty' chunk. offset = tffs_seek(fdi, 10, FFS_SEEK_SET); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 0); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 10); expect(size, 10); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); offset = tffs_seek(fdi, -50, FFS_SEEK_END); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 0); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 100); expect(size, 100); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); offset = tffs_seek(fdi, -fs.chunk_size_max, FFS_SEEK_CUR); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 0); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], fs.chunk_size_max / 2); expect(size, fs.chunk_size_max / 2); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); offset = tffs_seek(fdi, fs.chunk_size_max, FFS_SEEK_SET); expect_ok(offset); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 0); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 50); expect(size, 50); expect(fs.fd[fdi - FFS_FD_OFFSET].dirty, 1); error = tffs_close(fdi); expect(error, 0); return 0; } // NOTEME: move to other place later int case_trunc(int p0, int p1) { fd_t fdi; int size, f_size, trunc_size, i; T_FFS_STAT stat; char fname[] = "/truncate/normal_file"; char sname[] = "/truncate/stream_file"; char imeifile[] = "/truncate/IMEI"; tffs_remove(fname); tffs_remove(sname); error = case_cleanup(5 * fs.chunk_size_max); expect_ok(error); tffs_mkdir("/truncate"); // Try to truncate a non existing file error = tffs_truncate(fname, 0); expect(error, EFFS_NOTFOUND); // Try to truncate a directory error = tffs_truncate("/truncate", 0); expect(error, EFFS_NOTAFILE); f_size = fs.chunk_size_max + 100; error = tffs_file_write(fname, (char *)tdata[TDATA_HUGE], f_size, FFS_O_TRUNC | FFS_O_WRONLY | FFS_O_CREATE); expect(error, EFFS_OK); /* Test truncate from file size + 3 to file size - 3 */ for (i = 0; i < 6; i++) { trunc_size = f_size + 3 - i; tw(tr(TR_FUNC, TrTestLow, "TRUNC file to size %d \n", trunc_size)); error = tffs_truncate(fname, trunc_size); expect(error, EFFS_OK); error = tffs_lstat(fname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, (f_size < trunc_size ? f_size : trunc_size)); } tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread(fname, bigbuf, f_size); expect(size, trunc_size); // ++ because of the last -- in for() error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; tw(tr(TR_FUNC, TrTestLow, "Truncate file while it is open \n")); fdi = tffs_open(fname, FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char *) &tdata[TDATA_HUGE][trunc_size], 31); expect(size, 31); f_size = trunc_size + 31; // Truncate to a size less than fp, this will fail error = tffs_ftruncate(fdi, f_size - 1); expect(error, EFFS_INVALID); // Move fp otherwise it will not be possible to truncate error = ffs_seek(fdi, 0, FFS_SEEK_SET); expect(error, 0); // Test truncate from file size + 3 to file size - 3 for (i = 0; i < 6; i++) { trunc_size = f_size + 3 - i; tw(tr(TR_FUNC, TrTestLow, "TRUNC file to size %d \n", trunc_size)); error = tffs_ftruncate(fdi, trunc_size); expect(error, EFFS_OK); error = tffs_lstat(fname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, (f_size < trunc_size ? f_size : trunc_size)); } error = tffs_close(fdi); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread(fname, bigbuf, f_size); expect(size, trunc_size); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; // We need to test ftruncate with a size less than the first chunk fdi = tffs_open(fname, FFS_O_WRONLY); expect(fdi, FFS_FD_OFFSET); error = tffs_ftruncate(fdi, 10); expect(error, EFFS_OK); error = tffs_ftruncate(fdi, 10); // same size twice to complicate it expect(error, EFFS_OK); error = tffs_lstat(fname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, 10); error = tffs_ftruncate(fdi, 0); expect(error, EFFS_OK); error = tffs_ftruncate(fdi, 0); expect(error, EFFS_OK); error = tffs_lstat(fname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, 0); error = tffs_close(fdi); expect(error, EFFS_OK); // Stream file test, make 2 segments and test around the end of segment // 1 (file closed) fdi = tffs_open(sname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], fs.chunk_size_max + 1); expect(size, fs.chunk_size_max + 1); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 31); expect(size, 31); error = tffs_close(fdi); expect(error, EFFS_OK); f_size = fs.chunk_size_max + 1 + 31; for (i = 0; i < 6; i++) { trunc_size = fs.chunk_size_max + 1 + 3 - i; tw(tr(TR_FUNC, TrTestLow, "TRUNC stream to size %d \n", trunc_size)); error = tffs_truncate(sname, trunc_size); expect(error, EFFS_OK); error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, trunc_size); } tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread(sname, bigbuf, f_size); expect(size, trunc_size); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; // Try to enlarge the file. tw(tr(TR_FUNC, TrTestLow, "Try to enlarge the file \n")); error = tffs_truncate(sname, trunc_size + 1); expect(error, EFFS_OK); error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, trunc_size); fdi = tffs_open(sname, FFS_O_WRONLY); expect(fdi, FFS_FD_OFFSET); error = tffs_ftruncate(fdi, trunc_size + 1); expect(error, EFFS_OK); error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, trunc_size); error = tffs_close(fdi); expect(error, EFFS_OK); // Make a file with 2 segments + some data in the buffer, test around // end of segment 2 and the begining of segment 1 (file open). tw(tr(TR_FUNC, TrTestLow, "Truncate stream while it is open \n")); // Make segment 1 fdi = tffs_open(sname, FFS_O_WRONLY | FFS_O_APPEND | FFS_O_TRUNC); expect(fdi, FFS_FD_OFFSET); f_size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 2 * fs.chunk_size_max + 1); expect(f_size, 2 * fs.chunk_size_max + 1); // Move fp otherwise it will not be possible to truncate error = ffs_seek(fdi, 0, FFS_SEEK_SET); expect(error, 0); // Test around end of segment 2 for (i = 0; i < 16; i++) { trunc_size = f_size + 3 - i; tw(tr(TR_FUNC, TrTestLow, "TRUNC stream to size %d \n", trunc_size)); error = tffs_ftruncate(fdi, trunc_size); expect(error, EFFS_OK); error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, (f_size < trunc_size ? f_size : trunc_size)); } // Test around begining of segment 1 for (i = 0; i < 6; i++) { trunc_size = 3 - i; tw(tr(TR_FUNC, TrTestLow, "TRUNC stream to size %d \n", trunc_size)); error = tffs_ftruncate(fdi, trunc_size); if (trunc_size >= 0) { expect(error, EFFS_OK); } else { expect(error, EFFS_INVALID); break; } error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, (trunc_size)); } error = tffs_close(fdi); expect(error, EFFS_OK); // Make, truncate and append to a file (use ffs_ftruncate()) fdi = tffs_open(sname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); f_size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], fs.chunk_size_max); expect(f_size, fs.chunk_size_max ); tw(tr(TR_FUNC, TrTestLow, "write 1/2 buf size, trunc 1/4 buf size\n")); // write 1/2 buf size and truncate 1/4 buf size x times for (i = 0; i < 8; i++) { size = tffs_write(fdi, (char *) &tdata[TDATA_HUGE][f_size], fs.chunk_size_max / 2); if (size < 0) return size; f_size += size; size = tffs_seek(fdi, - fs.chunk_size_max / 4, FFS_SEEK_CUR); expect(size, f_size - fs.chunk_size_max / 4); error = tffs_ftruncate(fdi, f_size - fs.chunk_size_max / 4); expect(error, EFFS_OK); f_size -= fs.chunk_size_max / 4; error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, f_size); } error = tffs_close(fdi); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread(sname, bigbuf, bigbuf_size); expect(size, f_size); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, f_size); if (error) return 1; // backwards trunc 1/2 write 1/4 x times tw(tr(TR_FUNC, TrTestLow, "trunc 1/2 buf size, write 1/4 buf size\n")); fdi = tffs_open(sname, FFS_O_WRONLY | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); for (i = 0; i < 8; i++) { size = tffs_seek(fdi, - fs.chunk_size_max / 2, FFS_SEEK_CUR); expect(size, f_size - fs.chunk_size_max / 2); error = tffs_ftruncate(fdi, f_size - fs.chunk_size_max / 2); expect(error, EFFS_OK); f_size -= fs.chunk_size_max / 2; f_size += tffs_write(fdi, (char *) &tdata[TDATA_HUGE][f_size], fs.chunk_size_max / 4); error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, f_size); } if (p0) { error = test_run("irec"); if (error > 0) return 1; } error = tffs_close(fdi); expect(error, EFFS_OK); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread(sname, bigbuf, bigbuf_size); expect(size, f_size); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, f_size); if (error) return 1; tw(tr(TR_FUNC, TrTestLow, "Make 2 segments, truncate to zero and append\n")); fdi = tffs_open(sname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); size = tffs_write(fdi, (char*) tdata[TDATA_HUGE], 2 * fs.chunk_size_max + 10); expect(size, 2 * fs.chunk_size_max + 10); // Move fp otherwise it will not be possible to truncate error = ffs_seek(fdi, 0, FFS_SEEK_SET); expect(error, 0); error = tffs_ftruncate(fdi, 0); expect(error, EFFS_OK); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 150); expect(size, 150 ); error = tffs_close(fdi); expect(error, EFFS_OK); if (p0) { error = test_run("irec;brec"); if (error > 0) return 1; } tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); f_size = tffs_fread(sname, bigbuf, bigbuf_size); expect(f_size, size); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, f_size); if (error) return 1; // Truncate through a symlink error = tffs_symlink("/str", sname); expect(error, EFFS_OK); error = tffs_truncate("/str", 100); expect(error, EFFS_OK); f_size = tffs_fread(sname, bigbuf, bigbuf_size); expect(f_size, 100); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, f_size); if (error) return 1; tffs_remove("/str"); // truncate a file which only have data in the stream buffer fdi = tffs_open(sname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); f_size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], 3); expect(f_size, 3); // Move fp otherwise it will not be possible to truncate error = ffs_seek(fdi, 0, FFS_SEEK_SET); expect(error, 0); tw(tr(TR_FUNC, TrTestLow, "Truncate in buffer\n")); for (i = 0; i < 4; i++) { error = tffs_ftruncate(fdi, --f_size); if (f_size >= 0) { expect(error, EFFS_OK); error = tffs_lstat(sname, &stat); expect(error, EFFS_OK); expect_eq(stat.size, f_size); } else { expect(error, EFFS_INVALID); } } error = tffs_close(fdi); expect(error, EFFS_OK); // Try to truncate a read-only file error = tffs_fwrite(imeifile, TDATA(0)); // The file is maybe already created! error = tffs_fcontrol(imeifile, OC_FLAGS, OF_READONLY); error = tffs_truncate(imeifile, 0); expect(error, EFFS_ACCESS); // Use ffs_truncate() on a file open in read mode fdi = tffs_open(fname, FFS_O_RDONLY); error = tffs_truncate(fname, 0); expect(error, EFFS_LOCKED); error = tffs_ftruncate(fdi, 0); expect(error, EFFS_INVALID); error = tffs_close(fdi); expect_ok(error); return 0; } int case_append(int p0, int p1) { fd_t fdi; int size, fsize; char sname[] = "/stream/append1"; char fname[] = "/stream/append2"; int offset = 0; if (param.data_blocks <= 1) { ttw(ttr(TTrTest, "WARNING: Too few blocks to run. Skip test" NL)); tw(tr(TR_FUNC, TrTest, "WARNING: Too few blocks to run. Skip test\n")); return 0; } case_cleanup(fs.chunk_size_max * 5); tffs_mkdir("/stream"); fdi = tffs_open(sname, FFS_O_RDWR | FFS_O_APPEND | FFS_O_TRUNC | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); // Write data in buf and seek in the buf size = tffs_write(fdi, (char *) tdata[TDATA_HUGE], fs.chunk_size_max / 2); expect_gt(size, 0); offset = ffs_seek(fdi, -fs.chunk_size_max / 4, FFS_SEEK_CUR); expect_gt(offset, 0); expect(1, fs.fd[fdi - FFS_FD_OFFSET].dirty); // Append data and seek in the buffer ad the end of the last chunk size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + fs.fd[fdi - FFS_FD_OFFSET].size, fs.chunk_size_max); expect_gt(size, 0); offset = ffs_seek(fdi, -fs.chunk_size_max / 4, FFS_SEEK_END); expect_gt(offset, 0); size = tffs_read(fdi, bigbuf, fs.chunk_size_max / 4); expect(size, fs.chunk_size_max / 4); expect(1, fs.fd[fdi - FFS_FD_OFFSET].dirty); // Append data and seek away from buf size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + fs.fd[fdi - FFS_FD_OFFSET].size, fs.chunk_size_max * 1.5); expect_gt(size, 0); offset = ffs_seek(fdi, -fs.chunk_size_max * 2, FFS_SEEK_END); expect_gt(offset, 0); size = tffs_read(fdi, bigbuf, fs.chunk_size_max / 4); expect(size, fs.chunk_size_max / 4); // Append again and validate the data size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + fs.fd[fdi - FFS_FD_OFFSET].size, fs.chunk_size_max / 2); expect_gt(size, 0); offset = ffs_seek(fdi, 0, FFS_SEEK_SET); expect(0, offset); size = tffs_read(fdi, bigbuf, fs.fd[fdi - FFS_FD_OFFSET].size); expect_gt(size, 0); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; error = tffs_close(fdi); expect_ok(error); // Append on a file with data in the seghead fsize = fs.chunk_size_max / 4; error = tffs_file_write(fname, (char *)tdata[TDATA_HUGE], fsize, FFS_O_TRUNC | FFS_O_WRONLY | FFS_O_CREATE); expect(error, EFFS_OK); fdi = tffs_open(fname, FFS_O_RDWR | FFS_O_APPEND); expect(fdi, FFS_FD_OFFSET); // Write data in buf and seek in the buf size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + fsize, fs.chunk_size_max / 2); expect_gt(size, 0); fsize += size; offset = ffs_seek(fdi, 0, FFS_SEEK_SET); expect(offset, 0); expect(1, fs.fd[fdi - FFS_FD_OFFSET].dirty); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + fsize, fs.chunk_size_max / 2); expect_gt(size, 0); fsize += size; error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, fsize); if (error) return 1; error = tffs_close(fdi); expect_ok(error); return 0; } int case_datasync(int p0, int p1) { fd_t fdi; int size, fsize, offset, i; char myname[] = "/datasync"; error = case_cleanup(4 * fs.chunk_size_max); expect_ok(error); fdi = tffs_open(myname, FFS_O_RDWR | FFS_O_TRUNC | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); fsize = 0; for (i = 0; i < 10; i++) { // Write data in buf and use datasync size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + fsize, fs.chunk_size_max / 3); expect_gt(size, 0); fsize += size; error = tffs_fdatasync(fdi); expect(error, EFFS_OK); expect(0, fs.fd[fdi - FFS_FD_OFFSET].dirty); } error = tffs_fdatasync(fdi + 1); expect(error, EFFS_BADFD); // Seek to chunk 2, write data, seek, use fdatasync offset = tffs_seek(fdi, fs.chunk_size_max * 2.5, FFS_SEEK_SET); expect_gt(offset, 0); size = tffs_write(fdi, (char *) tdata[TDATA_HUGE] + offset, fs.chunk_size_max / 4); expect_gt(size, 0); offset = tffs_seek(fdi, -fs.chunk_size_max / 2, FFS_SEEK_CUR); expect_gt(offset, 0); error = tffs_fdatasync(fdi); expect(error, EFFS_OK); expect(0, fs.fd[fdi - FFS_FD_OFFSET].dirty); error = tffs_close(fdi); expect_ok(error); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread(myname, bigbuf, fsize); tw(tr(TR_FUNC, TrTestLow, "size: %d fsize: %d \n", size, fsize)); expect(size, fsize); error = test_expect_data((char *)tdata[TDATA_HUGE], bigbuf, size); if (error) return 1; return 0; } int case_fw_flags(int p0, int p1) { int i; char myname[] = "/file_write"; tffs_remove(myname); tw(tr(TR_FUNC, TrTestHigh, "Test ffs_file_write() flags, file non-exist\n")); for (i = 0; i < 0xFE; i++) { error = tffs_file_write(myname, 0,0, i); if (is_open_option(i, FFS_O_CREATE)) { expect_ok(error); error = tffs_remove(myname); expect_ok(error); } else expect(EFFS_NOTFOUND, error); } tw(tr(TR_FUNC, TrTestHigh, "Test ffs_file_write() flags, file exist\n")); error = tffs_file_write(myname, 0,0, FFS_O_CREATE); expect_ok(error); for (i = 0; i < 0xFF; i++) { error = tffs_file_write(myname, 0,0, i); if (is_open_option(i, FFS_O_EXCL) && is_open_option(i, FFS_O_CREATE)) { expect(error, EFFS_EXISTS); } else expect_ok(error); } return 0; } drv_Return_Type pcm_init(void); int case_pcm(int p0, int p1) { UBYTE version; UBYTE imei[] = "12345678"; int size; ffs_mkdir("/pcm"); // Write imei file to ffs. Run init (this reads the imei file), remove // the file from flash, write the imei to flash from pcm and validate // data. error = ffs_file_write("/pcm/IMEI", &imei, sizeof(imei), FFS_O_CREATE); expect_ok(error); error = pcm_init(); expect(PCM_INITIALIZED, error); error = pcm_ReadFile((UBYTE *) EF_IMEI_ID, SIZE_EF_IMEI, imei, &version); expect(PCM_OK, error); error = ffs_remove("/pcm/IMEI"); expect_ok(error); error = pcm_WriteFile((UBYTE *) EF_IMEI_ID, SIZE_EF_IMEI, imei); expect(PCM_OK, error); tw(tr(TR_FUNC, TrTestLow, "Validate data \n")); size = tffs_fread("/pcm/IMEI", bigbuf, bigbuf_size); expect(size, SIZE_EF_IMEI); error = test_expect_data(imei, bigbuf, size); if (error) return 1; // TODO: test pcm_WriteRecord and pcm_ReadRecord. return 0; } int case_api_exceptions(int p0, int p1) { int i; T_FFS_DIR dir; T_FFS_FD myfdi; char myname[] = "/BAD"; char mydir[] = "/tdir"; char mylink[] = "/tlink"; char mytfile[] = "/tfile"; char buf[] = "foobar"; // Create test file and dir error = tffs_file_write(mytfile, buf, sizeof(buf), FFS_O_CREATE); expect_ok(error); error = tffs_mkdir(mydir); if (error < 0 && error != EFFS_EXISTS) return 1; error = tffs_symlink(mytfile, mylink); if (error < 0 && error != EFFS_EXISTS) return 1; error = tffs_opendir(mydir, &dir); expect_ok(error); myfdi = tffs_open("/tsream", FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC); expect(myfdi, FFS_FD_OFFSET); expect(tffs_write(myfdi, buf, sizeof(buf)), sizeof(buf)); // Test all API functions for pathname with null pointers. for (i = 0; i < 17; i++) { switch (i) { case 0: error = tffs_open(0, FFS_O_WRONLY | FFS_O_CREATE); break; case 1: error = tffs_truncate(0, 1); break; case 2: error = tffs_file_write(0, buf, sizeof(buf), FFS_O_CREATE); break; case 3: error = tffs_stat(0, &stat); break; case 4: error = tffs_lstat(0, &stat); break; case 5: error = tffs_xlstat(0, &xstat); break; case 6: error = tffs_remove(0); break; case 7: error = tffs_mkdir(0); break; case 8: error = tffs_opendir(0, &dir); break; case 9: error = tffs_symlink(0, myname); break; case 10: error = tffs_symlink("/link", 0); break; case 11: error = tffs_readlink(0, buf, sizeof(buf)); break; case 12: error = tffs_rename(0, "/test"); break; case 13: error = tffs_rename(mytfile, 0); break; case 14: error = tffs_file_write(0, buf, sizeof(buf), FFS_O_CREATE); break; case 15: error = tffs_file_read(0, buf, sizeof(buf)); break; case 16: error = tffs_fcontrol(0, OC_FLAGS, OF_READONLY); break; default: error = EFFS_INVALID; } expect(error, EFFS_BADNAME); } // Test all API functions with buffer null pointers. for (i = 0; i < 13; i++) { switch (i) { case 0: error = tffs_file_write(myname, 0, 100, FFS_O_CREATE); break; case 1: error = tffs_file_read(mytfile, 0, 40); break; case 2: error = tffs_opendir(mydir, 0); break; case 3: error = ffs_readdir (0, buf, sizeof(buf)); break; case 4: error = ffs_readdir (&dir, 0, 100); break; case 5: error = tffs_readlink(mylink, 0, 1); break; case 6: error = tffs_stat(mytfile, 0); break; case 7: error = tffs_lstat(mylink, 0); break; case 8: error = tffs_xlstat(mylink, 0); break; case 9: error = tffs_query(Q_BYTES_FREE, 0); break; case 10: error = tffs_fstat(myfdi, 0); break; case 11: error = tffs_write(myfdi, 0, 10); break; case 12: error = tffs_read(myfdi, 0, 40); break; default: error = 0; } expect(error, EFFS_INVALID); } expect_ok(tffs_close(myfdi)); // Make sure we still can create empty files! error = tffs_file_write("/empty_file", 0, 0, FFS_O_CREATE); expect_ok(error); // Open file for test of negative size myfdi = tffs_open(myname, FFS_O_RDWR | FFS_O_CREATE | FFS_O_TRUNC | FFS_O_APPEND); expect(myfdi, FFS_FD_OFFSET); // Test all API 'size' parameters with a negative size for (i = 0; i < 11; i++) { switch (i) { // Try modify case 0: error = tffs_write(myfdi, buf, -1); break; case 1: error = tffs_truncate(myname, -1); break; case 2: error = tffs_fwrite(myname, buf, -2); break; case 3: error = tffs_fcreate(myname, buf, -3); break; case 4: error = tffs_fupdate(myname, buf, -56); break; case 5: error = tffs_file_write(myname, buf, -100, FFS_O_CREATE); break; // Try read case 6: error = tffs_fread(mytfile, bigbuf, -1); break; case 7: error = tffs_file_read(mytfile, bigbuf, -2); break; case 8: error = tffs_readdir(&dir, bigbuf, -3); break; case 9: error = tffs_readlink(mytfile, bigbuf, -4); break; case 10: error = tffs_read(myfdi, bigbuf, -5); break; default: error = 0; } expect(error, EFFS_INVALID); } error = tffs_close(myfdi); expect_ok(error); return EFFS_OK; } int case_api_notformated(int p0, int p1) { int i; struct dir_s dir; fd_t fdi; char buf[20]; uint16 q_out; struct xstat_s xstat; error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); for(i=1; i<24; i++) { switch(i) { case 1: error = tffs_open("apinf", FFS_O_WRONLY); break; case 2: error = tffs_close(fdi); break; case 3: error = tffs_write(fdi, "abcdefghij", 10); break; case 4: error = tffs_read(fdi, buf, 2); break; case 5: error = tffs_seek(fdi, 0, FFS_SEEK_SET); break; case 6: error = tffs_truncate("apinf", 0); break; case 7: error = tffs_fdatasync(fdi); break; case 8: error = tffs_stat("apinf", &stat); break; case 9: error = tffs_fstat(fdi, &stat); break; case 10: error = tffs_lstat("apinf", &stat); break; case 11: error = tffs_xlstat("apinf", &xstat); break; case 12: error = ffs_remove("/apinf"); break; case 13: error = ffs_mkdir("/apinf"); break; case 14: error = ffs_opendir("/apinf", &dir); break; case 15: error = tffs_readdir(&dir, "/ffs", 21); break; case 16: error = tffs_symlink("/europe/imie", "imei"); break; case 17: error = ffs_rename("/Pluto", "/Dog"); break; case 18: error = ffs_file_write("/non-exist", "1234567890", 10, FFS_O_TRUNC); break; case 19: error = tffs_file_read("/stream1", buf, 10); break; case 20: error = tffs_fcreate("/", "apinf", 3); break; case 21: error = tffs_fupdate("/", "bar", 3); break; case 22: error = tffs_fwrite("/europe/imie","0123456789", 10); break; case 23: error = tffs_fcontrol("/apinf", OC_FLAGS, OF_READONLY); break; } expect(error, EFFS_NOFORMAT); } /* query must return information even if the device isnt formated */ error = tffs_query(Q_FS_INODES, (uint16 *) &q_out); expect(error, EFFS_OK); return 0; } int case_query(int p0, int p1) { int i; uint32 ret_val32; uint16 ret_val16; for(i=1; i<27; i++) { switch(i) { case 1: error = ffs_query(Q_TM_BUFADDR, (uint32 *)&ret_val32); break; case 2: error = ffs_query(Q_TM_BUFSIZE, (uint32 *)&ret_val32); break; case 3: error = ffs_query(Q_DEV_BASE, (uint32 *)&ret_val32); break; case 4: error = ffs_query(Q_FFS_API_VERSION, (uint16 *)&ret_val16); break; case 5: error = ffs_query(Q_FFS_DRV_VERSION, (uint16 *)&ret_val16); break; case 6: error = ffs_query(Q_FFS_REVISION, (uint16 *)&ret_val16); break; case 7: error = ffs_query(Q_FFS_FORMAT_WRITE, (uint16 *)&ret_val16); break; case 8: error = ffs_query(Q_FFS_FORMAT_READ, (uint16 *)&ret_val16); break; case 9: error = ffs_query(Q_FFS_LASTERROR, (uint16 *)&ret_val16); break; case 10: error = ffs_query(Q_FFS_TM_VERSION, (uint16 *)&ret_val16); break; case 11: error = ffs_query(Q_OBJECTS_MAX, (uint16 *)&ret_val16); break; case 12: error = ffs_query(Q_CHUNK_SIZE_MAX, (uint16 *)&ret_val16); break; case 13: error = ffs_query(Q_FD_BUF_SIZE, (uint32 *)&ret_val32); break; case 14: error = ffs_query(Q_FD_MAX, (uint16 *)&ret_val16); break; case 15: error = ffs_query(Q_DEV_MANUFACTURER, (uint16 *)&ret_val16); break; case 16: error = ffs_query(Q_DEV_DEVICE, (uint16 *)&ret_val16); break; case 17: error = ffs_query(Q_DEV_DRIVER, (uint16 *)&ret_val16); break; case 18: error = ffs_query(Q_LOST_HIGH, (uint16 *)&ret_val16); break; case 19: error = ffs_query(Q_FS_FLAGS, (uint16 *)&ret_val16); break; case 20: error = ffs_query(Q_FS_ROOT, (uint16 *)&ret_val16); break; case 21: error = ffs_query(Q_STATS_DRECLAIMS, (uint32 *)&ret_val32); break; case 22: error = ffs_query(Q_STATS_IRECLAIMS, (uint32 *)&ret_val32); break; case 23: error = ffs_query(Q_STATS_DATA_RECLAIMED, (uint32 *)&ret_val32); break; case 24: error = ffs_query(Q_STATS_INODES_RECLAIMED, (uint32 *)&ret_val32); break; case 25: error = ffs_query(Q_STATS_DATA_ALLOCATED, (uint32 *)&ret_val32); break; case 26: error = ffs_query(Q_REQUEST_ID_LAST, (uint32 *)&ret_val32); break; } expect(error, EFFS_OK); } return 0; } extern uint16 ffs_flash_device; extern uint16 ffs_flash_manufact; int case_octrl(int p0, int p1) { int org_fsflags = fs.flags, org_manufact = ffs_flash_manufact; int org_device = ffs_flash_device, org_testf = fs.testflags; error = object_control(0, OC_FS_FLAGS, 0x02); expect(error, EFFS_OK); error = object_control(0, OC_DEV_MANUFACT, 'T'); expect(error, EFFS_OK); error = object_control(0, OC_DEV_DEVICE, 0x0F12); expect(error, EFFS_OK); error = object_control(0, OC_DEBUG_0, 0xAFFE); expect(error, EFFS_OK); error = object_control(0, OC_DEBUG_LAST+1, 0); expect(error, EFFS_INVALID); // Restore the values or else it mess up the other tests fs.flags = org_fsflags; fs.testflags = org_testf; ffs_flash_manufact = org_manufact; ffs_flash_device = org_device; return 0; } /***************************************************************************/ /* Examples from RIV111 */ /***************************************************************************/ #if (TARGET == 0) int case_examples(int p0, int p1) { return 0; } #else static req_id_t my_request_id; static int removed_greeting; static void my_callback(T_FFS_FILE_CNF *confirm) { ttw(ttr(TTrTest, "my_callback(%d, %s, %d)" NL, confirm->error, confirm->path, confirm->request_id)); if (my_request_id != confirm->request_id) { ttw(ttr(TTrFatal, "error (%d, %d)" NL, confirm->request_id, my_request_id)); return; } if (confirm->error < 0) { ttw(ttr(TTrTest, "FATAL: my_callback error: %d" NL, confirm->error)); } else { ttw(ttr(TTrTest, "Remove 'greeting'" NL)); ffs_remove_nb("/greeting", 0); removed_greeting = 1; } error = rvf_free_buf(confirm); if (error < 0) ttr(TTrFatal, "FFS FATAL: os_free() %d" NL, error); } // Using Function Callback T_RV_RETURN my_fc_cnfpath = {0, (CALLBACK_FUNC)my_callback}; // Using Mail Callback #define MY_TASK_ID (MAX_RVF_TASKS - 1 - 1) // same as FFS_TEST_TASK_ID T_RV_RETURN my_mail_cnfpath = {MY_TASK_ID, 0}; int case_examples(int p0, int p1) { if(1) { // MMI Configuration scenario struct { int volume; uint32 ringtype; } mmi; int temp; ttw(ttr(TTrTest, "Run MMI conf." NL)); mmi.volume = temp = 3; mmi.ringtype = 1111; ffs_mkdir("/mmi"); ffs_fwrite("/mmi/volume", &mmi.volume, sizeof(mmi.volume)); error = ffs_file_read("/mmi/volume", &mmi.volume, sizeof(mmi.volume)); if (error < 0) return 1; error = test_expect_data(&temp, &mmi.volume, sizeof(mmi.volume)); if (error < 0) return 1; } if(1) { // Using Mail Callback T_FFS_FILE_CNF *confirm_file; T_FFS_STREAM_CNF *confirm_stream; T_FFS_RET error; T_FFS_FD fdi; char hello[] = "Hello, world"; T_FFS_SIZE size; ttw(ttr(TTrTest, "Use Mail Callback" NL)); if ((my_request_id = ffs_open_nb("/mmi/sound", FFS_O_WRONLY | FFS_O_CREATE | FFS_O_APPEND | FFS_O_TRUNC, &my_mail_cnfpath)) < 0) return my_request_id; ttw(ttr(TTrTest, "Wait..." NL)); rvf_wait(RVF_TASK_MBOX_0_EVT_MASK, 0); ttw(ttr(TTrTest, "Get mail" NL)); confirm_file = (T_FFS_FILE_CNF *) rvf_read_mbox(RVF_TASK_MBOX_0); fdi = confirm_file->error; expect(FFS_FD_OFFSET, fdi); expect(my_request_id, confirm_file->request_id); expect(FFS_MESSAGE_OFFSET, confirm_file->header.msg_id); ttw(ttr(TTrTest, "Path '%s'" NL, confirm_file->path)); error = rvf_free_buf(confirm_file); if (error < 0) ttr(TTrFatal, "FFS FATAL: os_free() %d" NL, error); if ((my_request_id = ffs_write_nb(fdi, hello, strlen(hello) , &my_mail_cnfpath)) < 0) return my_request_id; ttw(ttr(TTrTest, "Wait..." NL)); rvf_wait(RVF_TASK_MBOX_0_EVT_MASK, 0); ttw(ttr(TTrTest, "Get mail" NL)); confirm_stream = (T_FFS_STREAM_CNF *) rvf_read_mbox(RVF_TASK_MBOX_0); size = confirm_stream->error; expect(strlen(hello), size); expect(my_request_id, confirm_stream->request_id); ttw(ttr(TTrTest, "fdi: %d" NL, confirm_stream->fdi)); error = rvf_free_buf(confirm_stream); if (error < 0) ttr(TTrFatal, "FFS FATAL: os_free() %d" NL, error); ttw(ttr(TTrTest, "Close file" NL)); if ((error = ffs_close(fdi)) < 0) return error; } if(1) { // Using Function Callback char hello[] = "Hello, world"; T_FFS_STAT stat; ttw(ttr(TTrTest, "Use Function Callback" NL)); if ((my_request_id = ffs_fcreate_nb("/greeting", hello, strlen(hello), &my_fc_cnfpath)) < 0) return my_request_id; // Wait until my_callback() has removed the file "greeting" removed_greeting = 0; while (removed_greeting == 0) tffs_delay(10); // NOTE: This is importent because if the test brec runs immediately // after his one will the brec test fail because the "greeting" file // is removed inside the brec test which don't expect that files is // removed. } return 0; } #endif /****************************************************************************** * Non blocking test ffs_xx_nb() ******************************************************************************/ #if (TARGET == 0) int case_nonblock(int p0, int p1) { tw(tr(TR_FUNC, TrTest, "WARNING: This test can only run in target. Skip test\n")); return 0; } #else extern int request_id_last; // from task.c #define WAIT_GET_TEST ttw(ttr(TTrTest, "Wait..." NL)); rvf_wait(RVF_TASK_MBOX_0_EVT_MASK, 0); ttw(ttr(TTrTest, "Get mail" NL)); confirm_file = (T_FFS_FILE_CNF *) rvf_read_mbox(RVF_TASK_MBOX_0); ttw(ttr(TTrTest, "Path '%s'" NL, confirm_file->path)); expect(FFS_MESSAGE_OFFSET, confirm_file->header.msg_id); expect(my_id, confirm_file->request_id); #define WAIT_GET_TEST_FDI ttw(ttr(TTrTest, "Wait..." NL)); rvf_wait(RVF_TASK_MBOX_0_EVT_MASK, 0); confirm_stream = (T_FFS_STREAM_CNF *) rvf_read_mbox(RVF_TASK_MBOX_0); ttw(ttr(TTrTest, "Get mail" NL)); ttw(ttr(TTrTest, "fdi: %d" NL, confirm_stream->fdi)); expect(FFS_MESSAGE_OFFSET, confirm_stream->header.msg_id); expect(FFS_FD_OFFSET, confirm_stream->fdi); expect(my_id, confirm_stream->request_id); #define FREE error = rvf_free_buf(confirm_file); if (error < 0) { ttr(TTrFatal, "FFS FATAL: os_free() %d" NL, error); return 1;} #define FREE_FDI error = rvf_free_buf(confirm_stream); if (error < 0) { ttr(TTrFatal, "FFS FATAL: os_free() %d" NL, error); return 1;} int case_nonblock(int p0, int p1) { T_FFS_FILE_CNF *confirm_file; T_FFS_STREAM_CNF *confirm_stream; T_FFS_RET error; T_FFS_FD fdi; char hello[] = "Hello, world"; char myfname[] = "/non_block/file"; char mysname[] = "/non_block/stream"; T_FFS_SIZE size; int i; T_FFS_REQ_ID my_id; // mail confirm T_RV_RETURN cnfpath = {MY_TASK_ID, 0}; // Test wrap arround in request_id_get(). Set id to 5 from max, the following // function calls will trigger the wrap arround request_id_last = 0x7FFFFFFF - 5; ttw(ttr(TTrTest, "request :%d" NL, request_id_last)); // preformat, format, mkdir // open, write, ftruncate, close, remove, // file_write, symlink, rename, truncate, fcontrol my_id = ffs_preformat_nb(0xDEAD, &cnfpath); expect_gt(my_id, -1); WAIT_GET_TEST; FREE; my_id = ffs_format_nb("/ffs", 0x2BAD, &cnfpath); expect_gt(my_id, -1); WAIT_GET_TEST; FREE; my_id = ffs_mkdir_nb("/non_block", &cnfpath); expect_gt(my_id, -1); WAIT_GET_TEST; FREE; if ((my_id = ffs_open_nb(mysname, FFS_O_WRONLY | FFS_O_CREATE, &cnfpath)) < 0) return my_id; WAIT_GET_TEST; fdi = confirm_file->error; expect(FFS_FD_OFFSET, fdi); FREE; if ((my_id = ffs_write_nb(fdi, hello, strlen(hello), &cnfpath)) < 0) return my_id; WAIT_GET_TEST_FDI; FREE_FDI; expect(1, fs.fd[fdi - FFS_FD_OFFSET].dirty); if ((my_id = ffs_fdatasync_nb(fdi, &cnfpath)) < 0) return my_id; WAIT_GET_TEST_FDI; FREE_FDI; expect(0, fs.fd[fdi - FFS_FD_OFFSET].dirty); if ((my_id = ffs_seek_nb(fdi, 2, FFS_SEEK_SET, &cnfpath)) < 0) return my_id; WAIT_GET_TEST_FDI; FREE_FDI; expect(2, fs.fd[fdi - FFS_FD_OFFSET].fp); if ((my_id = ffs_ftruncate_nb(fdi, 3, &cnfpath)) < 0) return my_id; WAIT_GET_TEST_FDI; expect(EFFS_OK, confirm_stream->error); FREE_FDI; if ((my_id = ffs_close_nb(fdi, &cnfpath)) < 0) return my_id; WAIT_GET_TEST_FDI; expect(EFFS_OK, confirm_stream->error); FREE_FDI; if ((my_id = ffs_remove_nb(mysname, &cnfpath)) < 0) return my_id; WAIT_GET_TEST; expect(EFFS_OK, confirm_file->error); FREE; if ((my_id = ffs_file_write_nb(myfname, hello, strlen(hello), FFS_O_WRONLY | FFS_O_CREATE, &cnfpath)) < 0) return my_id; WAIT_GET_TEST; expect(EFFS_OK, confirm_file->error); FREE; if ((my_id = ffs_symlink_nb("/nb_file", myfname, &cnfpath)) < 0) return my_id; WAIT_GET_TEST; expect(EFFS_OK, confirm_file->error); FREE; if ((my_id = ffs_rename_nb("/nb_file", "/nbf", &cnfpath)) < 0) return my_id; WAIT_GET_TEST; expect(EFFS_OK, confirm_file->error); FREE; if ((my_id = ffs_truncate_nb(myfname, 0, &cnfpath)) < 0) return my_id; WAIT_GET_TEST; expect(EFFS_OK, confirm_file->error); FREE; if ((my_id = ffs_fcontrol_nb(myfname, OC_FLAGS, OF_READONLY, &cnfpath)) < 0) return my_id; WAIT_GET_TEST; expect(EFFS_OK, confirm_file->error); FREE; ttw(ttr(TTrTest, "request :%d" NL, request_id_last)); // This should make a EFFS_MEMORY fail for(i = 0; i < 10000; i++) { my_id = ffs_fwrite_nb("/mem_test", TDATA(0), 0); if (my_id < 0) break; } ttw(ttr(TTrTest, "write nr: %d my_id: %d" NL, i, my_id)); expect(my_id, EFFS_MEMORY); // All memory has been used so wait a while (let some of the writes finish) tffs_delay(50); // Make a call to a blocking function will make it possible for ffs to // finish all the non blocking writes. error = ffs_fwrite("/mem_test", TDATA(0)); expect_ok(error); return 0; } #endif /****************************************************************************** * Blocking test ******************************************************************************/ #if (TARGET == 0) int case_blocking(int p0, int p1) { tw(tr(TR_FUNC, TrTest, "WARNING: This test can only run in target. Skip test\n")); return 0; } #else #include "ffs/board/task.h" void my_mb_callback(T_RVF_MB_ID id) { // Dummy callback function } // Test that a FFS API blocking call that not is able to send a mail to FFS fails as expected and don't lock the system (it must unlock and delete the mutex). int case_blocking(int p0, int p1) { UINT32 mem_unused; T_RVF_MB_STATUS status; char *buf; extern T_OS_MB_ID ffs_mb_id; // Later when all the memory is used the memory bank will be marked as // RED by Riviera. The RED flag is erased when the buffer is freed but // only if a callback function is applied. rvf_set_callback_func(ffs_mb_id, my_mb_callback); // Ignore error mem_unused = rvf_get_mb_unused_mem(ffs_mb_id); ttw(ttr(TTrTest,"Unused memory: %d" NL, mem_unused)); // When all the memory is used the blocking functions will not be able // to allocate memory to the FFS mail thus it must fail. After the // buffer has been freed the block call must again be fully functional. if ((buf = (char *) target_malloc(mem_unused)) == 0) return EFFS_MEMORY; error = tffs_fwrite("/blocking_test", (char*)tdata[TDATA_HUGE], 20); target_free(buf); expect(error, EFFS_MEMORY); error = tffs_fwrite("/blocking_test", (char*)tdata[TDATA_HUGE], 20); expect_ok(error); return EFFS_OK; } #endif /****************************************************************************** * Test erase_suspend and erase_resume ****************************************************************************** * Test erase suspend and resume by making a call to ffs_begin() followed by * an ffs_read() while we are erasing. We trigger the erase by sending * several write mails to FFS. We let FFS get the CPU for less time than an * erase, which will guarantee that we interrupt the erase when we call * ffs_begin(). ******************************************************************************/ #if (TARGET == 1) static int write_mails_left; static void driver_callback(T_FFS_FILE_CNF *confirm) { if (confirm->error < 0) { ttw(ttr(TTrTest, "FATAL: driver_callback error: %d" NL, confirm->error)); } error = rvf_free_buf(confirm); if (error < 0) ttr(TTrFatal, "FFS FATAL: os_free() %d" NL, error); write_mails_left--; } #endif #if (TARGET == 0) int case_erase_suspend(int p0, int p1) { tw(tr(TR_FUNC, TrTestHigh, "Erase_suspend test not supported on PC sim\n")); return 1; } #else // TEST erase_suspend and erase_resume int case_erase_suspend(int p0, int p1) { int i, j, error, nsuspend; char myname[] = "/ffs/b4"; // Run test with only 4 block. char myfname[] = "/driver_test"; int file_size = 1024; T_RV_RETURN cnfpath = {0, (CALLBACK_FUNC)driver_callback}; ttw(ttr(TTrTest, "Erase suspend-resume test" NL)); // To minimize the numbers of required writes before we are guarantied an // erase we run this test in only 4 blocks error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format(myname, 0x2BAD); expect(error, EFFS_OK); if (p0 == 0) p0 = 1; for (j = 0; j < p0; j++) { write_mails_left = 0; // Write 2 blocks of data (must trigger an erase) for (i = 0; i < 2 * (dev.blocksize/file_size); i++) { // Non blockin write if ((error = ffs_file_write_nb(myfname, (char *) tdata[TDATA_HUGE], file_size, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC, &cnfpath)) < 0) return error; write_mails_left++; } ttw(ttr(TTrTest, "Finish write calls(%d)" NL, i)); // NOTE only tested with AMD MB driver switch (dev.driver) { case FFS_DRIVER_AMD: case FFS_DRIVER_SST: case FFS_DRIVER_INTEL: case FFS_DRIVER_AMD_SB: ttw(ttr(TTrTest, "Start read test" NL)); nsuspend = 0; do { // NOTEME: We can also test if we get the CPU and not is delay // by the erase // This will change state to ERASE_SUSPEND if we are in // ERASE mode (only valid for MB driver) error = ffs_begin(); expect(error, EFFS_OK); if (dev.state == DEV_ERASE_SUSPEND) { nsuspend++; // Read... Note it will also resume the erase (MB driver). error = tffs_file_read(myfname, bigbuf, file_size/4); expect(error, EFFS_FILETOOBIG); } rvf_delay(5); // Let FFS get the CPU for some time } while (write_mails_left > 0); if (nsuspend <= 0) { ttw(ttr(TTrTest, "Arg no erase suspend" NL)); return 1; } ttw(ttr(TTrTest, "Erase suspended %d times" NL, nsuspend)); break; case FFS_DRIVER_SST_SB: case FFS_DRIVER_INTEL_SB: default: ttw(ttr(TTrTest, "Driver not supported by this test" NL)); // Do not return before the cnfpath resource not is used anymore while (write_mails_left > 0) rvf_delay(100); return 1; } } return 0; } #endif // TARGET == 0 /****************************************************************************** * The 3 below testh_.. function is helper function used for the low level * driver tests. ******************************************************************************/ #if (TARGET == 1) uint32 int_disable(void); void int_enable(uint32 tmp); // Only start an erase, don't wait until it is finish // Note to use this function with SB drivers require a multi bank flash device int testh_nb_erase(uint8 block) { volatile char *flash = dev.base; volatile char *addr; uint32 cpsr; ttw(ttr(TTrTest, "e(%d)" NL, block)); addr = block2addr(block); cpsr = int_disable(); switch (dev.driver) { case FFS_DRIVER_AMD: case FFS_DRIVER_AMD_SB: case FFS_DRIVER_AMD_PSEUDO_SB: flash[0xAAAA] = 0xAA; // AMD unlock cycle 1 flash[0x5555] = 0x55; // AMD unlock cycle 2 flash[0xAAAA] = 0x80; flash[0xAAAA] = 0xAA; // AMD unlock cycle 1 flash[0x5555] = 0x55; // AMD unlock cycle 2 *addr = 0x30; // AMD erase sector command int_enable(cpsr); break; default: return -1; // Not yet supported } return 0; } // Note to use this function with SB drivers require a multi bank flash device int testh_erase_suspend(uint8 block) { volatile char *flash = dev.base; volatile char *addr; ttw(ttr(TTrTest, "esusp(%d)" NL, block)); addr = block2addr(block); switch (dev.driver) { case FFS_DRIVER_AMD: case FFS_DRIVER_AMD_SB: case FFS_DRIVER_AMD_PSEUDO_SB: *addr = 0xB0; // AMD erase suspend command break; default: return -1; // Not yet supported } return 0; } int testh_get_free_block(void) { uint8 b; for (b = 0; b < dev.numblocks; b++) { if (is_block(b, BF_IS_FREE) || is_block(b, BF_IS_EMPTY)) return b; } return -1; } #endif // TARGET == 1 // NOTEME: The below test have not been used in this FFS version // NOTEME: We don't have an AMD init function /****************************************************************************** * Test drv.init (low level driver test) ****************************************************************************** * Test drv.init by start an erase, call drv.init and make sure that the * erase is finish after we return from drv.init. * * Test drv.init by start erase, suspend the erase and make sure that the * erase is resumed and finish before we return from drv.init. The test is * valid for both AMD SB and MB driver ******************************************************************************/ #if (TARGET == 0) int case_drv_init(int p0, int p1) { tw(tr(TR_FUNC, TrTestHigh, "drv_init test not supported on PC sim\n")); return 1; } #else int case_drv_init(int p0, int p1) { volatile char *flash = dev.base; volatile char *addr; char myname[] = "/drv_init"; uint32 cpsr; uint8 block; int free_b, i, j, command; enum command { ERASE, WRITE, READ }; char action[6][3] = { { WRITE, READ, ERASE }, { WRITE, ERASE, READ }, { READ, WRITE, ERASE }, { READ, ERASE, WRITE }, { ERASE, READ, WRITE }, { ERASE, WRITE, READ } }; ttw(ttr(TTrTest, "Test ffsdrv.init()" NL)); // Test drv.init 6 times there the flash is in erase mode and 6 times // there the flash is in erase suspend mode. for (i = 0; i < 12; i++) { free_b = testh_get_free_block(); expect_ok(free_b); fs.debug[0] = 0; fs.debug[1] = 0; // Start erase, run driver init. error = testh_nb_erase(free_b); expect_ok(error); rvf_delay(20); // Make sure that the erase has started if (i > 6) { error = testh_erase_suspend(free_b); expect_ok(error); rvf_delay(20); // Make sure that the erase is suspended } error = ffsdrv.init(); // Call ffsdrv_init() instead? (inc. autodetect) expect_ok(error); if (i > 6) { expect(fs.debug[0], 1); // Test that the erase was resumed expect_gt(fs.debug[1], 0); // Test what we did wait for erase to finish } else { expect(fs.debug[0], 0); // Erase not resumed expect_gt(fs.debug[1], 0); // We did wait for an erase to finish } // To test that any combination of erase, read or write can be // performed after drv.init do we make the test below. The first // complete drv.init test will be like this: Start an erase, call // drv.init(), write a file, read the file and erase a free block. for (j = 0; j < 3; j++) { if (i < 6) command = action[i][j]; else command = action[i-6][j]; switch (command) { case ERASE: free_b = testh_get_free_block(); expect_ok(free_b); ffsdrv.erase(free_b); break; case READ: error = test_expect_file(myname, TDATA(2)); if (error) return 1; break; case WRITE: error = tffs_fwrite(myname, TDATA(2)); expect(error, EFFS_OK); break; default: return 1; } } } return 0; } #endif uint32 get_cpsr(void); // Test disable and enable of interrupt (Note only valid for MB drivers) int case_interrupt(int p0, int p1) { extern void int_enable(uint32 tmp); extern uint32 int_disable(void); uint32 cpsr; int i; uint32 xcpsr; switch (dev.driver) { case FFS_DRIVER_AMD: case FFS_DRIVER_INTEL: break; default: return 1; // Not supported } p0 *= 100; ttw(ttr(TTrTest, "Test interrupt code" NL)); for (i = 0; i < p0; i++) { cpsr = int_disable(); xcpsr = get_cpsr(); if ((xcpsr & 0xC0) != 0xC0) { ttw(ttr(TTrTest, "FATAL int not disablet!" NL)); return 1; } int_enable(cpsr); xcpsr = get_cpsr(); if ((xcpsr & 0xC0) != 0) { ttw(ttr(TTrTest, "FATAL int not enablet!" NL)); return 1; } if ((i % 1000) == 0) ttw(ttr(TTrTest, "loop (%d)" NL, i)); } return 0; } /****************************************************************************** * Benchmarking ******************************************************************************/ // Below define only for PC compile #if (TARGET == 0) #define UINT32 int #define tffs_timer_begin() 0 #define tffs_timer_end(time) 5000 - time #endif const struct results_s old_results[] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; // NOTEME: Bencmark_results() not finished. int bencmark_results(int manufact, int driver, int sample, struct results_s *meas_res) { switch(manufact) { case MANUFACT_AMD: switch(driver) { // case FFS_DRIVER_AMD: case FFS_DRIVER_AMD_SB: default: return 0; } case MANUFACT_FUJITSU: switch(driver) { // case FFS_DRIVER_AMD: case FFS_DRIVER_AMD_SB: default: return 0; } default: return 0; } } // Benchmark write and read operation. Measure write throughput as kBps in // idle and loopback mode. Both for an empty ffs where we don't have to // reclaim blocks and for a full ffs where block reclaims/erasures affect // the overall throughput. For SB flash driver as well as for DB driver. For // files of size:16, 256, 4096, bytes. int case_bm_stream_rw(int p0, int p1) { #if (TARGET == 1) UINT32 time_begin, elapsed, bytes_max; char myname[] = "/Benchmark0"; int i, j, k, nm_files, file_size; int size, write_size[] = {16, 256, 4096}; fd_t fdi; //ttw(ttr(TTrTest, "RVF_POOL_0_SIZE: %d" NL, RVF_POOL_0_SIZE)); // 1 block is used for inodes, 1 have to be free, 1/4 is used for // journal and 1/2 is used as a margin // FIXME: changed to 4.75 because the SW TCS2.1 dos make some files and // because of this is there not enough data space left to this test. // NOTEME: Instead of hardwire this value then query it bytes_max = (dev.numblocks - 4.75) * dev.blocksize; file_size = bytes_max / dev.numblocks; ttw(ttr(TTrTest, "bytes_max: %d(- 2.75 block)" NL, bytes_max)); // Format, measure write of 16B. // Format, measure write of 256B. // Format, measure write of 4096B. for (j = 0; j < 3; j++) { ttw(ttr(TTrTest, "Format, measure write of %dB" NL, write_size[j])); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); time_begin = tffs_timer_begin(); for (i = 0; i < dev.numblocks; i++) { myname[10] = i + (i <= 9 ? '0': 'A' -10); fdi = tffs_open(myname, FFS_O_WRONLY | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); size = 0; do { size += error = tffs_write(fdi, (char*)tdata[TDATA_HUGE], write_size[j]); if (error < 0) { if (error == EFFS_NOSPACE) { ttw(ttr(TTrTest, "Aarg out of space" NL)); bytes_max = size; break; } else expect_ok(error); } } while (size < file_size); error = tffs_close(fdi); expect_ok(error); } elapsed = tffs_timer_end(time_begin); // FFS has used the CPU for a long time so let the trace task get // the CPU for a while tffs_delay(50); ttw(ttr(TTrTest, "Write %dBytes %d times in %dms, %dkBps" NL NL, write_size[j], file_size/write_size[j], elapsed, (bytes_max * 1000) / (elapsed * 1024))); } ttw(ttr(TTrTest, "Rm all files, measure write of 4kB" NL)); for (i = 0; i < dev.numblocks; i++) { myname[10] = i + (i <= 9 ? '0': 'A' -10); error = tffs_remove(myname); expect_ok(error); } time_begin = tffs_timer_begin(); for (i = 0; i < dev.numblocks; i++) { myname[10] = i + (i <= 9 ? '0': 'A' -10); fdi = tffs_open(myname, FFS_O_WRONLY | FFS_O_CREATE); expect(fdi, FFS_FD_OFFSET); size = 0; do { size += tffs_write(fdi, (char*)tdata[TDATA_HUGE], write_size[2]); } while (size < file_size); error = tffs_close(fdi); expect_ok(error); } elapsed = tffs_timer_end(time_begin); tffs_delay(50); ttw(ttr(TTrTest, "Write %dBytes %d times in %dms, %dkBps" NL NL, write_size[2], file_size/write_size[2], elapsed, (bytes_max * 1000) / (elapsed * 1024))); // Read all files with 16B at a time for (j = 0; j < 3; j++) { ttw(ttr(TTrTest, "Read all files with %dB at a time" NL, write_size[j])); time_begin = tffs_timer_begin(); for (i = 0; i < dev.numblocks; i++) { myname[10] = i + (i <= 9 ? '0': 'A' -10); fdi = tffs_open(myname, FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); size = 0; do { size += tffs_read(fdi, bigbuf, write_size[j]); } while (size < file_size); error = tffs_close(fdi); expect(error, 0); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Read %d files of %dkB in %dms, %dkBps," NL NL, dev.numblocks, size/1024, elapsed, (dev.numblocks * size * 1000) / (elapsed * 1024))); } // Measure write and read of one big file ttw(ttr(TTrTest, "Format, Write and read one big file" NL)); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); time_begin = tffs_timer_begin(); fdi = tffs_open(myname, FFS_O_WRONLY | FFS_O_CREATE); size = 0; do { size += tffs_write(fdi, bigbuf, write_size[2]); } while (size < bytes_max); error = tffs_close(fdi); expect(error, 0); elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Write %dkB in %dms, %dkBps" NL, size/1024 , elapsed, (size * 1000) / (elapsed * 1024))); time_begin = tffs_timer_begin(); // read one big file (reading one big file take more time than reading // many files of the same total size because there is a lot more inode // to look through) fdi = tffs_open(myname, FFS_O_RDONLY); expect(fdi, FFS_FD_OFFSET); size = 0; do { size += tffs_read(fdi, bigbuf, write_size[2]); } while (size < bytes_max); error = tffs_close(fdi); expect(error, 0); elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Read %dkB in %dms, %dkBps," NL, size/1024, elapsed, (size * 1000) / (elapsed * 1024))); // Make a data base of old measurements her? compare with old data and // make a warning if the performance has decreased? return 0; #else return 1; #endif } // Benchmark ffs_file_write() and ffs_file_read() int case_bm_file_rw(int p0, int p1) { UINT32 time_begin, elapsed; char myname[] = "/Benchmark"; int i, j; struct test_info_s { int fsize; int fnumber; }; struct test_info_s tinfo[] = { { 16, 500 }, { 256, 200 }, { 4096, 200 }, {32768, 10 } }; for (j = 0; j < 4; j++) { ttw(ttr(TTrTest, "Format, measure write of %dB" NL, tinfo[j].fsize)); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); time_begin = tffs_timer_begin(); for (i = 0; i < tinfo[j].fnumber; i++) { error = tffs_file_write(myname, (char*)tdata[TDATA_HUGE], tinfo[j].fsize, FFS_O_CREATE); expect_ok(error); } elapsed = tffs_timer_end(time_begin); // FFS has used the CPU for a long time so let the trace task get // the CPU for a while tffs_delay(50); ttw(ttr(TTrTest, "Write %dBytes %d times in %dms, %dkBps" NL NL, tinfo[j].fsize, i, elapsed, (i * tinfo[j].fsize * 1000) / (elapsed * 1024))); time_begin = tffs_timer_begin(); for (i = 0; i < tinfo[j].fnumber; i++) { error = tffs_file_read(myname, bigbuf, tinfo[j].fsize); expect_ok(error); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Read %dBytes %d times in %dms, %dkBps" NL NL, tinfo[j].fsize, i, elapsed, (i * tinfo[j].fsize * 1000) / (elapsed * 1024))); } tffs_delay(3000); return 0; } // We banchmark lookups by using ffs_stat() int case_bm_lookup(int p0, int p1) { //format, update file x times, measure lookup with ffs_stat() //format, make x difference files, measure lookup with ffs_stat() //format, make a file in max directory depth, measure lookup with ffs_stat() //format, make one file, measure lookup with ffs_stat() (no traverse) //format, make x dirs with x files, measure lookup of last file in last dir #if (TARGET == 1) UINT32 time_begin, elapsed; int i, j; char myname[] = "/benchmark"; char mydir[100] = ""; char depth[] = "/depth99"; char mypath[100] = ""; //format, update file 100 times, measure lookup with ffs_stat() error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); for (i = 0; i < 100; i++) { error = tffs_file_write(myname, 0,0, FFS_O_CREATE); expect_ok(error); } time_begin = tffs_timer_begin(); for (i = 0; i < 100; i++) { error = tffs_stat(myname, &stat); expect_ok(error); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Lookup through 100 deleted objects 100 times in %dms(%d obj/s)" NL, elapsed, 100 * 1000 / elapsed)); //format, make 100 difference files, measure lookup with ffs_stat() error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); for (i = 0; i < 100; i++) { sprintf(myname, "/benchm%d", i); error = tffs_file_write(myname, 0,0, FFS_O_CREATE); expect_ok(error); } time_begin = tffs_timer_begin(); for (i = 0; i < 100; i++) { error = tffs_stat(myname, &stat); expect_ok(error); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Lookup through 100 difference objects 100 times in %dms(%d obj/s)" NL, elapsed, 100 * 1000 / elapsed)); //format, make a file in max directory depth, measure lookup with ffs_stat() error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); for (i = 0; i < fs.path_depth_max - 1; i++) { sprintf(depth, "/depth%d", i + 1); strcat(mydir, depth); error = tffs_mkdir(mydir); expect_ok(error); } strcat(mydir, myname); error = tffs_file_write(mydir, 0,0, FFS_O_CREATE); expect_ok(error); time_begin = tffs_timer_begin(); for (i = 0; i < 1000; i++) { error = tffs_stat(mydir, &stat); expect_ok(error); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Lookup througt %d directorys 1000 times in %dms(%d obj/s)" NL, fs.path_depth_max, elapsed, 1000 * 1000 / elapsed)); //format, make x files in each directory, measure lookup with ffs_stat() error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); mydir[0] = 0; // Reset/empty buffer for (i = 0; i < fs.path_depth_max - 1; i++) { sprintf(depth, "/depth%d", i + 1); strcat(mydir, depth); error = tffs_mkdir(mydir); expect_ok(error); for (j = 0; j < 5; j++){ sprintf(myname, "/benchm%d", j); strcpy(mypath, mydir); strcat(mypath, myname); error = tffs_file_write(mypath, 0,0, FFS_O_CREATE); expect_ok(error); } } time_begin = tffs_timer_begin(); for (i = 0; i < 100; i++) { error = tffs_stat(mypath, &stat); expect_ok(error); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Lookup througt %d directorys with %d files in each times in %dms(%d obj/s)" NL, fs.path_depth_max, j, elapsed, 100 * 1000 / elapsed)); //format, make one file, measure lookup with ffs_stat() (no traverse) error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); error = tffs_file_write(myname, 0,0, FFS_O_CREATE); expect_ok(error); time_begin = tffs_timer_begin(); for (i = 0; i < 10000; i++) { error = tffs_stat(myname, &stat); expect_ok(error); } elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Lookup one file 10000 times in %dms(%d obj/s)" NL, elapsed, 1000 * 10000 / elapsed)); //format, make x dirs with x files, measure lookup of last file in last dir error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); return 0; #else return 1; #endif } int case_bm_seek(int p0, int p1) { UINT32 time_begin, elapsed; int i, pos, fsize = 20000; char myname[] = "/benchmark"; fd_t fdi; //format, update file 100 times, measure lookup with ffs_stat() error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); error = tffs_format("/foo", 0x2BAD); expect(error, EFFS_OK); // Create test file error = tffs_file_write(myname, (char*)tdata[TDATA_HUGE], fsize, FFS_O_CREATE); expect_ok(error); fdi = tffs_open(myname, FFS_O_RDWR); expect(fdi, FFS_FD_OFFSET); tw(tr(TR_FUNC, TrTest, "Run read reference\n")); ttw(ttr(TTrTest, "Run read reference" NL)); // Reference time_begin = tffs_timer_begin(); for (i = 0; i < fsize/4; i++) { error = tffs_read(fdi, bigbuf, 4); expect_ok(error); } elapsed = tffs_timer_end(time_begin); tw(tr(TR_FUNC, TrTest, "Read %d entries of 4 bytes (bytes/s %d)\n", fsize/4, (fsize * 1000 / elapsed))); ttw(ttr(TTrTest, "Read %d entries of 4 bytes in %dms (bytes/s %d)" NL, fsize/4, elapsed, (fsize * 1000 / elapsed))); tw(tr(TR_FUNC, TrTest, "Benchmark seek\n")); ttw(ttr(TTrTest, "Benchmark seek" NL)); error = tffs_seek(fdi, 0, FFS_SEEK_SET); expect_ok(error); pos = 0; // Benchmark seek time_begin = tffs_timer_begin(); for (i = 0; i < fsize/4; i++) { error = tffs_seek(fdi, pos, FFS_SEEK_SET); expect_ok(error); pos += 4; error = tffs_read(fdi, bigbuf, 4); expect_ok(error); } elapsed = tffs_timer_end(time_begin); tw(tr(TR_FUNC, TrTest, "Seek and Read %d entries of 4 bytes (bytes/s %d)\n", fsize/4, (fsize * 1000 / elapsed))); ttw(ttr(TTrTest, "Seek and Read %d entries of 4 bytes in %dms (bytes/s %d)" NL, fsize/4, elapsed, (fsize * 1000 / elapsed))); error = tffs_close(fdi); expect_ok(error); return EFFS_OK; } // Benchmark the blocking 'system'. Make n number of calls to ffs_format() // with bad magic. int case_bm_blocking(int p0, int p1) { int i; int time_begin, elapsed; time_begin = tffs_timer_begin(); for (i = 0; i < 10000; i++) tffs_format("/ffs", 0); elapsed = tffs_timer_end(time_begin); ttw(ttr(TTrTest, "Made %d blocking calls in %dms (ms/call %d)" NL, i, elapsed, elapsed / i)); // Make sure that it did fail as we expected error = tffs_format("/ffs", 0); expect(error, EFFS_INVALID); return EFFS_OK; } /****************************************************************************** * Core test case ******************************************************************************/ int case_seekfile(int p0, int p1) { // NOTEME: this is only a visual test add some expect() to finish it. char *name; iref_t i, dir, i_out; int length, flength; int segment_offset; fd_t fdi; error = tffs_fwrite("/File_seg", (char*)tdata[TDATA_HUGE], 50); expect(error, EFFS_OK); if ((i = object_lookup("/File_seg", &name, &dir)) < 0) return i; for (length = 52; length > 48; length--) { flength = segfile_seek(i, length, &i_out, &segment_offset); tw(tr(TR_FUNC, TrTest, "*** Length %d, Flength %d, Seg_offset %2d, i %d ***\n", length, flength, segment_offset, i_out)); } fdi = tffs_open("/Stream_seg", FFS_O_WRONLY | FFS_O_CREATE); tffs_write(fdi, (char*)tdata[TDATA_HUGE], 55); tffs_close(fdi); fdi = tffs_open("/Stream_seg", FFS_O_WRONLY | FFS_O_APPEND); tffs_write(fdi, (char*)tdata[TDATA_HUGE], 50); tffs_close(fdi); fdi = tffs_open("/Stream_seg", FFS_O_WRONLY | FFS_O_APPEND); tffs_write(fdi, (char*)tdata[TDATA_HUGE], 45); tffs_close(fdi); if ((i = object_lookup("/Stream_seg", &name, &dir)) < 0) return i; for (length = 153; length > 148; length--) { flength = segfile_seek(i, length, &i_out, &segment_offset); tw(tr(TR_FUNC, TrTest, "*** Length %d, Flength %d, Seg_offset %2d, i %d ***\n", length, flength, segment_offset, i_out)); } for (length = 107; length > 103; length--) { flength = segfile_seek(i, length, &i_out, &segment_offset); tw(tr(TR_FUNC, TrTest, "*** Length %d, Flength %d, Seg_offset %2d, i %d ***\n", length, flength, segment_offset, i_out)); } for (length = 58; length > 53; length--) { flength = segfile_seek(i, length, &i_out, &segment_offset); tw(tr(TR_FUNC, TrTest, "*** Length %d, Flength %d, Seg_offset %2d, i %d ***\n", length, flength, segment_offset, i_out)); } return 0; } void display_fs_params(void) { int numdatablocks; numdatablocks = dev.numblocks - fs.blocks_free_min - 1; // Note expect only one free block! tw(tr(TR_FUNC, TrTest, "Data blocks %d, size %dkB\n", numdatablocks, dev.blocksize / 1024)); tw(tr(TR_FUNC, TrTest, " User data: %dkB\n", (numdatablocks * dev.blocksize - fs.reserved_space) / 1024)); tw(tr(TR_FUNC, TrTest, " reserved_space: %dkB\n", fs.reserved_space/ 1024)); tw(tr(TR_FUNC, TrTest, " inodes_max: %d\n", fs.inodes_max)); tw(tr(TR_FUNC, TrTest, " journal_size: %d\n", fs.journal_size)); tw(tr(TR_FUNC, TrTest, " objects_max: %d\n", fs.objects_max)); tw(tr(TR_FUNC, TrTest, " block_files_max: %d\n", fs.block_files_max)); tw(tr(TR_FUNC, TrTest, " chunk_size: %d\n\n", fs.chunk_size_max)); } // NOTE only visual test int case_param_init(int p0, int p1) { char myname[] = "/ffs/xx/xx/xx"; int numblocks[] = { 8, 3, 7, 15, 31, 63, 96, 127, 61, 127}; int i; for (i = 0; i < sizeof(numblocks) / sizeof(int); i++) { dev.numblocks = numblocks[i]; if (i == 0) dev.blocksize = 1024 * 8; else if (i > 0 && i < 8) dev.blocksize = 1024 * 64; else dev.blocksize = 1024 * 128; fs_params_init(myname); display_fs_params(); // TODO add some checks, test format("/ffs/bx/ox") etc. } error = tffs_initialize(); // Use the 'real' blocksize expect(error, EFFS_OK); // Test param input from the format string error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); tffs_format("/ffs/i4000/o3000", 0x2BAD); expect(error, EFFS_OK); display_fs_params(); // Test small conf. tw(tr(TR_FUNC, TrTest, "Test small conf\n")); error = tffs_preformat(0xDEAD); expect(error, EFFS_OK); tffs_format("/ffs/b3/j8/r24/f100/o100", 0x2BAD); expect(error, EFFS_OK); display_fs_params(); return 0; } /****************************************************************************** * Trace mask ******************************************************************************/ // This is not a test case but a way to change the trace mask on the fly int case_trace_mask(int p0, int p1) { unsigned int temp; // Because p0 and p1 only is of the type int and trace_mask is a // unsigned int is p0 bit 1-28 and p1 is bit 29-32 temp = p1 << 28; temp |= p0; #if (TARGET == 0) tr_init(temp, 2, NULL ); tw(tr(TR_FUNC, TrAll, "trace mask = %x\n", temp)); #else ttr_init(temp); ttw(ttr(TTrTest, "trase mask = %x" NL, temp)); #endif return 0; } /****************************************************************************** * Helper function ******************************************************************************/ #define CLEANUP_MIN_SIZE 1 int ignore_file(char *pathname) { int i; const char *ignore[] = { "/.journal", "/.ffs-stats", "/.ffs-bstat.old","/dummy4fctrl" , "/truncate/IMEI", "/truncate/stream_file", "/gsm/com/rfcap", "/var/dbg/dar" }; const char readonly[] = "/READONLY"; for (i = 0; i < sizeof(ignore)/sizeof(char *); i++) { if (ffs_strcmp(pathname, ignore[i]) == 0) { return 0; } } // Special case, we have one directory where we don't delete any objects if (memcmp(pathname, readonly, sizeof(readonly) - 1) == 0) return 0; return 1; } // Remove files in FFS until there is min_space left. Ignore all files that // are listed in the ignore string above. int case_cleanup(int min_space) { int free, i, n, error; struct object_s *plist, *plist_start; char *pnames, *pnames_start; tw(tr(TR_FUNC, TrTestLow, "case_cleanup(%d)?\n", min_space)); ffs_query(Q_BYTES_FREE, (uint32 *) &free); if (free > min_space) { tw(tr(TR_FUNC, TrTestLow, "Total data free: %d\n", free)); return free; // Nothing to do } plist = plist_start = (struct object_s *) bigbuf; pnames = pnames_start = bigbuf + bigbuf_size; n = case_dir_list("/", &plist, &pnames); plist = plist_start; for (i = 0; i < n; i++, plist++) { ffs_query(Q_BYTES_FREE, (uint32 *) &free); if (free > min_space) { tw(tr(TR_FUNC, TrTestLow, "Total data free: %d\n", free)); return free; } if (plist->stat.size > CLEANUP_MIN_SIZE && ignore_file(plist->name)) { if ((error = ffs_remove(plist->name)) < 0) return error; } } return EFFS_NOSPACE; } // Make a random file. Random filename length, random filename, random filesize. // Note it is up to the caller that the directory exists. int case_mk_rand_file(char *dirname, int max_size, int min_size) { int size, diff, filenamelen, pathlen = 0, i, error; char pathname[(FFS_FILENAME_MAX + 1) * FFS_PATH_DEPTH_MAX]; // All valid filename chars char tabel[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrtsuvwyz_-.,+%$#"; diff = max_size - min_size; if (diff < 0) return EFFS_INVALID; else if (diff == 0) size = max_size; else size = (rand() % diff) + min_size; filenamelen = (rand() % (FFS_FILENAME_MAX - 1)) + 1; // We need min one char tw(tr(TR_FUNC, TrTestLow, "size:%5d, filenamelen:%3d,",size, filenamelen)); if (dirname != 0) { strcpy(pathname, dirname); pathlen = strlen(pathname); } pathname[pathlen++] = '/'; for (i = 0; i < filenamelen; pathlen++, i++) { pathname[pathlen] = tabel[rand() % strlen(tabel)]; } pathname[pathlen] = 0; // Zero terminate tw(tr(TR_FUNC, TrTestLow, "pathname:'%s'\n", pathname, size)); error = tffs_file_write(pathname, (char *) tdata[TDATA_HUGE], size, FFS_O_CREATE | FFS_O_TRUNC); return error; } // Various helper functions for debug int case_debug_help(int p0, int p1) { int tr_mask; switch(p0) { case 1: { // Write rfcap file char rfcap_data[] = { 0, 0xf, 0x41, 0x10, 0, 0, 0, 0, 0x50, 0, 0, 0xa5, 0x5, 0, 0xc0, 0 }; ttw(ttr(TTrTest, "Write rfcap file...")); tffs_mkdir("/gsm"); // ignore error tffs_mkdir("/gsm/com"); error = tffs_file_write("/gsm/com/rfcap", rfcap_data, sizeof(rfcap_data), FFS_O_CREATE | FFS_O_TRUNC); expect_ok(error); ttw(ttr(TTrTest, "done" NL)); } break; case 2: tr_mask = tr_query(INT_MAX); #if (TARGET == 0) tr_init(TrBstat, 2, NULL ); tr_bstat(); tr_init(tr_mask, 2, NULL ); #else ttr_init(TTrBstat); tr_bstat(); ttr_init(tr_mask); #endif break; case 3: // Make mem dump to screen #if (TARGET == 1) rvf_dump_mem(); break; #else tw(tr(TR_FUNC, TrTestLow, "Not supported on PC\n")); return 1; #endif case 4: #if (TARGET == 1) rvf_dump_tasks(); break; #else tw(tr(TR_FUNC, TrTestLow, "Not supported on PC\n")); return 1; #endif case 5: #if (TARGET == 1) rvf_dump_pool(); break; #else tw(tr(TR_FUNC, TrTestLow, "Not supported on PC\n")); return 1; #endif default: tw(tr(TR_FUNC, TrTest, "Available helper functions:\n")); tw(tr(TR_FUNC, TrTest, " 1: Write rfcap file\n")); tw(tr(TR_FUNC, TrTest, " 2: Display bstat trace\n")); ttw(ttr(TTrTest, "Available helper functions:" NL)); ttw(ttr(TTrTest, " 1: Write rfcap file" NL)); ttw(ttr(TTrTest, " 2: Display bstat trace" NL)); ttw(ttr(TTrTest, " 3: Memory dump to screen" NL)); ttw(ttr(TTrTest, " 4: Tasks dump to screen" NL)); ttw(ttr(TTrTest, " 5: Pool dump to screen" NL)); return 0; } return 0; } /****************************************************************************** * Hexdumping ******************************************************************************/ #if 0 void hexdump(const char *buf, int size, unsigned int address, int unitsize) { char string[(8+1+1) + (1+16+1+1) + (3*16) + 1]; int n, i; char *s; while (size > 0) { s = string; s += sprintf(s, "%8x: ", address); // print offset n = (size > 16 ? 16 : size); // print the textual representation for (i = 0; i < n; i++) { if (buf[i] >= ' ' && buf[i] < 127) *s++ = buf[i]; else *s++ = '.'; } // pad textual representation with spaces for (i = 0; i < 16 - n; i++) { *s++ = ' '; } *s++ = ' '; // print hexedecimal representation for (i = 0; i < n; i += unitsize) { switch (unitsize) { case 1: s += sprintf(s, "%02x ", *(unsigned char *) (buf+i)); break; case 2: s += sprintf(s, "%04x ", *(unsigned short *) (buf+i)); break; case 4: s += sprintf(s, "%08x ", *(unsigned int *) (buf+i)); break; } } buf += 16; address += 16; size -= 16; puts(string); } } #endif /****************************************************************************** * Test Cases ******************************************************************************/ const struct testcase_s testcase[] = { // Collective test cases { "all", PC , case_all, "All" }, { "alot", PC , case_alot, "Almost all" }, { "tall", IT , case_tall, "Target all" }, { "aall", PC , case_aall, "Agressive all" }, { "list", PC|IT , case_list, "List all testcases" }, { "lsr", PC|IT , case_lsr, "Recursively list all objects" }, { "test", PC|IT , case_test, "Ad hoc test" }, { "rand", PC|IT , case_rand, "Random tests (NOT FINISHED)" }, { "fail", PC|IT , case_fail, "Fail (just fail)" }, { "okay", PC|IT , case_okay, "Okay (just succeed)" }, // Atomic/small test cases { "s", PC|IT , case_status, "Status: ffs_init(), lsr" }, { "r", PC|IT , case_reset, "preformat(), format(), init(), exit()" }, { "i", PC|IT , case_init, "ffs_init()" }, { "x", PC|IT , case_exit, "ffs_exit()" }, { "p", PC|IT , case_only_preformat, "ffs_preformat()" }, { "f", PC|IT , case_only_format, "ffs_format()" }, // Populate test cases { "world", PC|IT , case_world, "Make world" }, { "eu", PC|IT , case_europe, "Make europe" }, { "dk", PC|IT , case_denmark, "Make denmark" }, // Special test cases { "mf", PC|IT , case_mfiles, "Test FFS with many (small) files" }, { "threeb", PC|IT , case_threeb, "Test ffs in only three blocks" }, { "twob", PC|IT , case_twob, "Test ffs in only two blocks" }, { "pcm", PC|IT , case_pcm, "Test PCM interface" }, { "stress", IT , case_stress, "Stress test erase/write" }, { "find", PC|IT|RND, case_find, "Recursively traverse all objects" }, { "nb", IT , case_nonblock, "Test non block functions" }, { "bf", IT|RND , case_blocking, "Test blocking function" }, { "cust", PC , case_customer, "Test customer typical use"}, { "esus", IT , case_erase_suspend, "Test erase suspend and resume" }, // Driver test cases { "int", IT , case_interrupt, "Test INT disable and enable" }, // Benchmark test cases { "bmsrw", IT , case_bm_stream_rw, "Benchmark stream read and writes" }, { "bmfrw", IT , case_bm_file_rw, "Benchmark file read and writes" }, { "bmlu", IT , case_bm_lookup, "Benchmark lookups" }, { "bmsk", PC|IT , case_bm_seek, "Benchmark ffs_seek()" }, { "bmbl", PC|IT , case_bm_blocking, "Benchmark blocking 'system'" }, // Normal test cases { "format", PC , case_format, "Test ffs_pre/format()" }, { "lu", PC|IT , case_lookup, "Test object_lookup()" }, { "fc", PC|IT , case_fcontrol, "Test fcontrol() and read-only" }, { "root", PC|IT|RND, case_root, "Test root inode is non-modifiable" }, { "dirs", PC|IT|RND, case_dirs, "Test Directories" }, { "frd", PC|IT|RND, case_fread, "Test fread() with varying sizes/names" }, { "bfull", PC|IT , case_bfull, "Test filling a block completely" }, { "ffull", PC|IT , case_ffull, "Test filling ffs completely" }, { "bigf", PC , case_bigfile, "Test big files" }, { "stat", PC|IT|RND, case_stat, "Test ffs_stat()" }, { "rm", PC|IT , case_remove, "Test ffs_remove()" }, { "ren", PC|IT|RND, case_rename, "Test ffs_rename()" }, { "renext", PC|IT , case_rename_extended, "Extended test of ffs_rename()" }, { "irec", PC|IT , case_irec, "Test inodes reclaim" }, { "drec", PC , case_drec, "Test data reclaim" }, { "adrec", PC , case_adrec, "Test xxxx reclaim" }, { "jnl", PC|IT , case_journal, "Test journalling" }, { "brec", PC|IT|RND, case_brecover, "Test block recovery" }, { "ssym", PC|IT|RND, case_ssym, "Test Simple Symlinks" }, { "fsym", PC|IT , case_fsym, "Test Full Symlinks (NOT IMPLEMENTED)" }, { "ri", PC|IT|RND, case_reinit, "Test re-ffs_init()" }, { "open", PC|IT|RND, case_open, "Test open flags" }, { "rw", PC|IT|RND, case_rw, "Test read and write files" }, { "mopen", PC|IT|RND, case_multi_open, "Test open of multiply files" }, { "seek", PC|IT|RND, case_seek, "Test seek on files " }, { "trunc", PC|IT|RND, case_trunc, "Test ffs_truncate() " }, { "append", PC|IT|RND, case_append, "Test append on a open file" }, { "dsync", PC|IT|RND, case_datasync, "Test ffs_fdatasync" }, { "ex", IT|RND, case_examples, "Test examples from RIV111 " }, { "fwflags",PC|IT|RND, case_fw_flags, "Test ffs_file_write() flags" }, { "pcm" ,PC|IT|RND, case_pcm, "Test pcm interface" }, { "apiexc" ,PC|IT, case_api_exceptions, "Test API exceptions" }, { "ninit" ,PC|IT, case_api_notformated, "Test API on a nformated flash"}, { "query" ,PC|IT|RND, case_query, "Test query function" }, { "octrl" ,PC|IT, case_octrl, "Test of object control" }, // Core test cases { "seekf", PC, case_seekfile, "Test core segfile_seek" }, { "param", PC, case_param_init,"Test core param_init" }, // Helper test case { "tr", PC|IT , case_trace_mask, "Change trace mask on the fly " }, { "dh", PC|IT , case_debug_help, "Difference help functions " }, { 0, 0, 0, 0 } }; #if (TARGET == 1) // Note like the interrupt functions in drv.c this asm needs to be places ad // the buttom of the file or else it crach! uint32 get_cpsr(void) { asm(" .state16"); asm(" adr A2, get_cpsr32"); asm(" bx A2"); asm(" .state32"); asm("get_cpsr32"); asm(" mrs A1,cpsr ; get current CPSR"); } #else uint32 get_cpsr(void){return 0;} #endif