FreeCalypso > hg > fc-selenite
view src/cs/drivers/drv_app/ffs/board/tcases.c @ 193:6a53de2c4fc2
R2D sync with Magnetite
R2D is never compiled in Selenite, thus the present change has absolutely
no impact on anything - but this sync is being done in order to keep the
overall diff between Magnetite and Selenite to a minimum.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sat, 23 May 2020 07:13:36 +0000 |
parents | b6a5e36de839 |
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