mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-25 18:40:19 -04:00
Refactors the regression test.
This commit is contained in:
parent
0f6867ba4e
commit
91c3de2182
6 changed files with 263 additions and 236 deletions
216
Makefile.am
216
Makefile.am
|
|
@ -110,8 +110,8 @@ maintainer-clean-local:
|
|||
|
||||
# Automatic testing
|
||||
|
||||
CHECKFLAGS = --test-skip-device --test-skip-self -q
|
||||
CHECKCOUNT = 2000
|
||||
CHECKFLAGS = --test-skip-device --test-skip-self --test-force-order-alpha -q
|
||||
CHECKCOUNT = 1000
|
||||
CHECKSIZE = 4000
|
||||
CONF = $(srcdir)/test-par6.conf
|
||||
HOLE = $(srcdir)/test-par6-hole.conf
|
||||
|
|
@ -135,8 +135,10 @@ check-local:
|
|||
mkdir test/pool
|
||||
echo --- Help
|
||||
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -h
|
||||
echo --- SelfTest
|
||||
echo --- Run the self test only this time
|
||||
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -c $(CONF) status
|
||||
echo --- Run the speed test
|
||||
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -T
|
||||
echo --- As empty, sync and check forcing murmur3
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -v -c $(CONF) diff
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -v -c $(CONF) dup
|
||||
|
|
@ -168,15 +170,15 @@ endif
|
|||
echo --- Check the --gen-conf command
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --gen-conf test/content
|
||||
echo --- Delete some files and sync
|
||||
rm test/disk4/9*
|
||||
rm test/disk5/9*
|
||||
rm test/disk6/9*
|
||||
rm test/disk4/a/9*
|
||||
rm test/disk5/a/9*
|
||||
rm test/disk6/a/9*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Create some more files, hardlinks and empty directories, delete others, sync PAR1 and check
|
||||
rm test/disk4/8*
|
||||
rm test/disk5/8*
|
||||
rm test/disk6/8*
|
||||
rm test/disk4/a/8*
|
||||
rm test/disk5/a/8*
|
||||
rm test/disk6/a/8*
|
||||
$(TESTENV) ./mktest$(EXEEXT) generate 2 6 $(CHECKCOUNT) $(CHECKSIZE)
|
||||
mkdir test/disk1/empty1
|
||||
mkdir test/disk2/empty2
|
||||
|
|
@ -216,9 +218,9 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-scrub 100000 scrub
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
|
||||
echo --- Corrupt some files, scrub and fix filtering for error
|
||||
echo --- Silently corrupt some files, scrub and fix filtering for error
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 1 10 1 test/disk1/a*
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 1 10 1 test/disk1/a/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-expect-recoverable --test-force-scrub 100000 scrub
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -e
|
||||
|
|
@ -226,11 +228,9 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -o 0 scrub
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -p 100 scrub
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Corrupt some files, and sync with error presents
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 2 10 1 test/disk1/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 2 10 1 test/disk1/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 2 10 1 test/disk2/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 2 10 1 test/disk2/_a/_b/*
|
||||
echo --- Silently corrupt some files, and sync with error presents
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 1 500 test/disk1/a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) damage 1 1 500 test/disk2/a/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-expect-recoverable sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -e
|
||||
|
|
@ -242,8 +242,8 @@ endif
|
|||
echo --- Use with the hole
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) check
|
||||
rm -r test/disk1/_a
|
||||
rm -r test/disk3/_a
|
||||
rm -r test/disk1/a
|
||||
rm -r test/disk3/a
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(HOLE) dry
|
||||
|
|
@ -253,18 +253,18 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete some files, create some new, sync and check
|
||||
rm test/disk1/1*
|
||||
rm test/disk2/2*
|
||||
rm test/disk3/3*
|
||||
rm test/disk4/4*
|
||||
rm test/disk1/a/1*
|
||||
rm test/disk2/a/2*
|
||||
rm test/disk3/a/3*
|
||||
rm test/disk4/a/4*
|
||||
$(TESTENV) ./mktest$(EXEEXT) generate 3 6 $(CHECKCOUNT) $(CHECKSIZE)
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Move some files, sync and check
|
||||
mv test/disk1/7* test/disk1/_a/
|
||||
mv test/disk2/7* test/disk2/_a/
|
||||
mv test/disk3/7* test/disk3/_a/
|
||||
mv test/disk4/7* test/disk4/_a/
|
||||
mv test/disk1/a/7* test/disk1/b/
|
||||
mv test/disk2/a/7* test/disk2/b/
|
||||
mv test/disk3/a/7* test/disk3/b/
|
||||
mv test/disk4/a/7* test/disk4/b/
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete one disk, fix and check with PAR1
|
||||
|
|
@ -345,10 +345,10 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Delete some files and create some new, sync and check in multiple steps using text content
|
||||
rm test/disk1/4*
|
||||
rm test/disk2/5*
|
||||
rm test/disk3/6*
|
||||
rm test/disk4/6*
|
||||
rm test/disk1/a/4*
|
||||
rm test/disk2/a/5*
|
||||
rm test/disk3/a/6*
|
||||
rm test/disk4/a/6*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -t 10 sync --test-force-content-text
|
||||
cp test/content test/content.txt
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) test-rewrite
|
||||
|
|
@ -371,116 +371,100 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete files from three disks and fix with import in PAR2
|
||||
rm -r test/disk1/_a
|
||||
rm -r test/disk1/_b
|
||||
rm -r test/disk2/_a
|
||||
rm -r test/disk2/_b
|
||||
mv test/disk3/_a test/_a
|
||||
rm -r test/disk1/a
|
||||
rm -r test/disk2/a
|
||||
mv test/disk3/a test/a
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -i test/_a -c $(PAR2) fix -l test.log
|
||||
rm -r test/_a
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -i test/a -c $(PAR2) fix -l test.log
|
||||
rm -r test/a
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Abort sync late with additions, delete them and recover with PAR2
|
||||
echo --- This triggers the recovering with q using p to check the validity
|
||||
cp -pR test/disk1/_a test/disk1/_c
|
||||
cp -pR test/disk1/_a test/disk1/_d
|
||||
cp -pR test/disk1/a test/disk1/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-kill-after-sync -c $(CONF) sync
|
||||
rm -r test/disk1/_c
|
||||
rm -r test/disk1/_d
|
||||
rm -r test/disk1/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
rm -r test/disk1/_c
|
||||
rm -r test/disk1/_d
|
||||
rm -r test/disk1/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Abort sync early with additions, delete them and recover with PAR2
|
||||
echo --- This triggers the recovering with q using p to check the validity, but failing for new block being filled with zero
|
||||
cp -pR test/disk1/_a test/disk1/_c
|
||||
cp -pR test/disk1/_a test/disk1/_d
|
||||
cp -pR test/disk1/a test/disk1/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -t 1 --test-kill-after-sync -c $(CONF) sync
|
||||
rm -r test/disk1/_c
|
||||
rm -r test/disk1/_d
|
||||
rm -r test/disk1/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR2) fix -l test.log
|
||||
rm -r test/disk1/_c
|
||||
rm -r test/disk1/_d
|
||||
rm -r test/disk1/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Abort sync late with additions and then delete the new additions and sync again
|
||||
cp -pR test/disk1/_a test/disk1/_c
|
||||
cp -pR test/disk2/_a test/disk2/_c
|
||||
cp -pR test/disk3/_a test/disk3/_c
|
||||
cp -pR test/disk1/a test/disk1/a_copy
|
||||
cp -pR test/disk2/a test/disk2/a_copy
|
||||
cp -pR test/disk3/a test/disk3/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-kill-after-sync -c $(CONF) sync
|
||||
rm -r test/disk1/_c
|
||||
rm -r test/disk2/_c
|
||||
rm -r test/disk3/_c
|
||||
rm -r test/disk1/a_copy
|
||||
rm -r test/disk2/a_copy
|
||||
rm -r test/disk3/a_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Abort sync late with additions and then delete some unchanged stuff and fix with PAR1
|
||||
cp -pR test/disk1/_b test/disk1/_c
|
||||
cp -pR test/disk1/b test/disk1/b_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) --test-kill-after-sync sync
|
||||
rm -r test/disk2
|
||||
mkdir test/disk2
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
|
||||
echo --- Fixes again to restore all the parity
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -l test.log
|
||||
rm -r test/disk1/_c
|
||||
rm -r test/disk1/b_copy
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
echo --- Abort sync late with more deletions than additions and then delete some unchanged stuff and fix with PAR1
|
||||
cp -pR test/disk1/_a test/disk1/_c
|
||||
mv test/disk1/_a test/_a
|
||||
mv test/disk1/_b test/_b
|
||||
cp -pR test/disk1/a test/disk1/a_copy
|
||||
mv test/disk1/a test/a
|
||||
mv test/disk1/b test/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-kill-after-sync sync
|
||||
rm -r test/disk2
|
||||
mkdir test/disk2
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
|
||||
echo --- Fixes again to restore all the parity
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -l test.log
|
||||
-rm -r test/disk1/_a
|
||||
-rm -r test/disk1/_b
|
||||
rm -r test/disk1/_c
|
||||
mv test/_a test/disk1/_a
|
||||
mv test/_b test/disk1/_b
|
||||
-rm -r test/disk1/a
|
||||
-rm -r test/disk1/b
|
||||
rm -r test/disk1/a_copy
|
||||
mv test/a test/disk1/a
|
||||
mv test/b test/disk1/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Abort sync early with more deletions than additions and then delete some unchanged stuff and fix with PAR2
|
||||
cp -pR test/disk1/_a test/disk1/_c
|
||||
mv test/disk1/_a test/_a
|
||||
mv test/disk1/_b test/_b
|
||||
cp -pR test/disk1/a test/disk1/a_copy
|
||||
mv test/disk1/a test/a
|
||||
mv test/disk1/b test/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -t 1 --test-kill-after-sync sync
|
||||
rm -r test/disk2
|
||||
mkdir test/disk2
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
-rm -r test/disk1/_a
|
||||
-rm -r test/disk1/_b
|
||||
rm -r test/disk1/_c
|
||||
mv test/_a test/disk1/_a
|
||||
mv test/_b test/disk1/_b
|
||||
-rm -r test/disk1/a
|
||||
-rm -r test/disk1/b
|
||||
rm -r test/disk1/a_copy
|
||||
mv test/a test/disk1/a
|
||||
mv test/b test/disk1/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete some files, fix with -m and check with PAR1
|
||||
rm test/disk1/8*
|
||||
rm test/disk1/a/8*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) -m fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete some dirs in six disk, fix with -m and check
|
||||
rm -r test/disk1/_a
|
||||
rm -r test/disk1/_b
|
||||
rm -r test/disk2/_a
|
||||
rm -r test/disk2/_b
|
||||
rm -r test/disk3/_a
|
||||
rm -r test/disk3/_b
|
||||
rm -r test/disk4/_a
|
||||
rm -r test/disk4/_b
|
||||
rm -r test/disk5/_a
|
||||
rm -r test/disk5/_b
|
||||
rm -r test/disk6/_a
|
||||
rm -r test/disk6/_b
|
||||
rm -r test/disk1/b
|
||||
rm -r test/disk2/b
|
||||
rm -r test/disk3/b
|
||||
rm -r test/disk4/b
|
||||
rm -r test/disk5/b
|
||||
rm -r test/disk6/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR5) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(CONF) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -m fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete some dirs in two disks, fix and check with PAR2 using the -d option for each disk
|
||||
rm -r test/disk2/_a
|
||||
rm -r test/disk2/_b
|
||||
rm -r test/disk5/_a
|
||||
rm -r test/disk5/_b
|
||||
rm -r test/disk2/b
|
||||
rm -r test/disk5/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) -d disk2 fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) -d disk5 fix -l test.log
|
||||
|
|
@ -497,82 +481,58 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete the parity and a disk, fix and check with PAR2
|
||||
rm test/parity
|
||||
rm -r test/disk2/_a
|
||||
rm -r test/disk2/_b
|
||||
rm -r test/disk2/b
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Delete the 2-parity and a disk, fix and check with PAR2
|
||||
rm test/2-parity
|
||||
rm -r test/disk3/_a
|
||||
rm -r test/disk3/_b
|
||||
rm -r test/disk3/a
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt the parity, fix and check with PAR1
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 1 100 10000 test/parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) write 1 100 10000 test/parity
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) -a check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt the parity and the 2-parity, fix and check with PAR2
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 2 100 10000 test/parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 2 100 10000 test/2-parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) write 2 100 10000 test/parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) write 2 100 10000 test/2-parity
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt some files, fix and check with PAR1
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 3 1 500 test/disk2/b*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 3 500 test/disk2/c*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 3 500 test/disk2/d*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 3 500 test/disk2/b/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) -a check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt some files, fix and check with PAR2
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 4 1 500 test/disk2/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 4 1 500 test/disk3/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 4 1 500 test/disk2/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 4 1 500 test/disk3/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 4 500 test/disk2/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 4 500 test/disk3/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 4 500 test/disk2/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 4 500 test/disk3/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 4 500 test/disk2/b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 4 500 test/disk3/b/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) -a check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt some files, fix and check with PAR2
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 5 2 50 test/disk2/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 5 2 50 test/disk3/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 5 500 test/disk2/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 5 500 test/disk3/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 5 2 50 test/disk2/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 5 2 50 test/disk3/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 5 1500 test/disk2/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 5 1500 test/disk3/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 5 1500 test/disk2/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 5 1500 test/disk3/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 5 500 test/disk2/b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 5 500 test/disk3/b/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt the parity and some files, fix and check with PAR2
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 6 100 10000 test/parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 6 1 500 test/disk1/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 6 1 500 test/disk1/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 6 500 test/disk1/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 6 500 test/disk1/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) write 6 100 10000 test/parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 6 500 test/disk1/a/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
|
||||
echo --- Corrupt the 2-parity and some files, fix and check with PAR2
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 7 100 10000 test/2-parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 7 1 500 test/disk1/_a/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 7 1 500 test/disk1/_a/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) append 7 500 test/disk1/_b/_a/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) truncate 7 500 test/disk1/_b/_b/*
|
||||
$(TESTENV) ./mktest$(EXEEXT) write 7 100 10000 test/2-parity
|
||||
$(TESTENV) ./mktest$(EXEEXT) change 7 500 test/disk1/a/*
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR1) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(PAR2) check -l test.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR2) fix -l test.log
|
||||
|
|
|
|||
266
mktest.c
266
mktest.c
|
|
@ -50,6 +50,59 @@ void rnd_name(char* file)
|
|||
*file = 0;
|
||||
}
|
||||
|
||||
void create_file(const char* path, int size)
|
||||
{
|
||||
FILE* f;
|
||||
int count;
|
||||
|
||||
/* remove the existing file if any */
|
||||
if (remove(path) != 0) {
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "Error removing file %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* don't recreate a 0 file to avoid ZERO protection */
|
||||
++size;
|
||||
}
|
||||
|
||||
f = fopen(path, "wb");
|
||||
if (!f) {
|
||||
fprintf(stderr, "Error writing file %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
count = size;
|
||||
while (count) {
|
||||
/* We don't write zero bytes because we want to test */
|
||||
/* the recovering of new files, after an aborted sync */
|
||||
/* If the files contains full blocks at zero */
|
||||
/* this is an impossible condition to recover */
|
||||
/* because we cannot differentiate between an unused block */
|
||||
/* and a file filled with 0 */
|
||||
fputc(rndnotzero(256), f);
|
||||
--count;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void create_symlink(const char* path, const char* linkto)
|
||||
{
|
||||
/* remove the existing file if any */
|
||||
if (remove(path) != 0) {
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "Error removing file %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (symlink(linkto, path) != 0) {
|
||||
fprintf(stderr, "Error writing symlink %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void generate(int disk, int size)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
|
@ -59,38 +112,18 @@ void generate(int disk, int size)
|
|||
file = path + strlen(path);
|
||||
|
||||
/* add a directory */
|
||||
if (rnd(2) == 0) {
|
||||
*file++ = '_';
|
||||
*file++ = 'a' + rnd(2);
|
||||
*file = 0;
|
||||
*file++ = 'a' + rnd(2);
|
||||
*file = 0;
|
||||
|
||||
/* create it */
|
||||
if (mkdir(path, 0777) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
fprintf(stderr, "Error creating directory %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* create it */
|
||||
if (mkdir(path, 0777) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
fprintf(stderr, "Error creating directory %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
*file++ = '/';
|
||||
}
|
||||
|
||||
/* add another directory */
|
||||
if (rnd(2) == 0) {
|
||||
*file++ = '_';
|
||||
*file++ = 'a' + rnd(2);
|
||||
*file = 0;
|
||||
|
||||
/* create it */
|
||||
if (mkdir(path, 0777) != 0) {
|
||||
if (errno != EEXIST) {
|
||||
fprintf(stderr, "Error creating directory %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
*file++ = '/';
|
||||
}
|
||||
*file++ = '/';
|
||||
|
||||
while (1) {
|
||||
/* add a random file */
|
||||
|
|
@ -113,53 +146,19 @@ void generate(int disk, int size)
|
|||
break;
|
||||
}
|
||||
|
||||
/* remove the existing file if any */
|
||||
if (remove(path) != 0) {
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "Error removing file %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WIN32 /* Windows XP doesn't support symlinks */
|
||||
if (rnd(32) == 0) {
|
||||
/* symlink */
|
||||
char linkto[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
rnd_name(linkto);
|
||||
|
||||
ret = symlink(linkto, path);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error writing symlink %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
create_symlink(path, linkto);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* file */
|
||||
FILE* f;
|
||||
int count;
|
||||
|
||||
f = fopen(path, "wb");
|
||||
if (!f) {
|
||||
fprintf(stderr, "Error writing file %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
count = size;
|
||||
while (count) {
|
||||
/* We don't write zero bytes because we want to test */
|
||||
/* the recovering of new files, after an aborted sync */
|
||||
/* If the files contains full blocks at zero */
|
||||
/* this is an impossible condition to recover */
|
||||
/* because we cannot differentiate between an unused block */
|
||||
/* and a file filled with 0 */
|
||||
fputc(rndnotzero(256), f);
|
||||
--count;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
create_file(path, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +210,19 @@ void fallback(int f, struct stat* st)
|
|||
}
|
||||
}
|
||||
|
||||
void change(const char* path, int size, int silent_error)
|
||||
void duplicate_name(const char* path, char* newpath)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
strcpy(newpath, path);
|
||||
|
||||
len = strlen(newpath);
|
||||
|
||||
newpath[len] = 'a' + rnd('z' - 'a');
|
||||
newpath[len+1] = 0;
|
||||
}
|
||||
|
||||
void writ(const char* path, int size, int silent_error)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
|
|
@ -225,18 +236,7 @@ void change(const char* path, int size, int silent_error)
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
if (!silent_error) {
|
||||
/* symlink */
|
||||
int ret;
|
||||
|
||||
ret = remove(path);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error removing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
/* file */
|
||||
FILE* f;
|
||||
off_t start;
|
||||
|
|
@ -290,18 +290,7 @@ void append(const char* path, int size)
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
/* symlink */
|
||||
int ret;
|
||||
|
||||
ret = remove(path);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error removing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* file */
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
FILE* f;
|
||||
int count;
|
||||
|
||||
|
|
@ -336,17 +325,7 @@ void truncat(const char* path, int size)
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
/* symlink */
|
||||
int ret;
|
||||
|
||||
ret = remove(path);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Error removing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* file */
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
FILE* f;
|
||||
off_t start;
|
||||
int count;
|
||||
|
|
@ -362,6 +341,10 @@ void truncat(const char* path, int size)
|
|||
if (count > st.st_size)
|
||||
count = st.st_size;
|
||||
start = st.st_size - count;
|
||||
|
||||
/* don't truncate to 0 to avoid ZERO protection */
|
||||
if (start == 0)
|
||||
start = 1;
|
||||
|
||||
if (ftruncate(fileno(f), start) != 0) {
|
||||
fprintf(stderr, "Error truncating %s\n", path);
|
||||
|
|
@ -372,6 +355,64 @@ void truncat(const char* path, int size)
|
|||
}
|
||||
}
|
||||
|
||||
void change(const char* path, int size)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
if (lstat(path, &st) != 0) {
|
||||
if (errno == ENOENT)
|
||||
return; /* it may be already deleted */
|
||||
fprintf(stderr, "Error accessing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
/* symlink */
|
||||
if (rnd(2) == 0) {
|
||||
/* delete */
|
||||
if (remove(path) != 0) {
|
||||
fprintf(stderr, "Error removing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* recreate */
|
||||
char linkto[PATH_MAX];
|
||||
|
||||
if (remove(path) != 0) {
|
||||
fprintf(stderr, "Error removing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rnd_name(linkto);
|
||||
|
||||
create_symlink(path, linkto);
|
||||
}
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
int r;
|
||||
|
||||
r = rnd(4);
|
||||
|
||||
if (r == 0) {
|
||||
/* write */
|
||||
writ(path, size, 0);
|
||||
} else if (r == 1) {
|
||||
/* append */
|
||||
append(path, size);
|
||||
} else if (r == 2) {
|
||||
/* truncate */
|
||||
truncat(path, size);
|
||||
} else {
|
||||
/* delete */
|
||||
if (remove(path) != 0) {
|
||||
fprintf(stderr, "Error removing %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void help(void)
|
||||
{
|
||||
|
|
@ -379,7 +420,8 @@ void help(void)
|
|||
printf("Usage:\n");
|
||||
printf("\tmktest generate SEED DISK_NUM FILE_NUM FILE_SIZE\n");
|
||||
printf("\tmktest damage SEED FAIL_NUM FAIL_SIZE FILE\n");
|
||||
printf("\tmktest change SEED FAIL_NUM FAIL_SIZE FILE\n");
|
||||
printf("\tmktest write SEED FAIL_NUM FAIL_SIZE FILE\n");
|
||||
printf("\tmktest change SEED FAIL_SIZE FILE\n");
|
||||
printf("\tmktest append SEED FAIL_SIZE FILE\n");
|
||||
printf("\tmktest truncate SEED FAIL_SIZE FILE\n");
|
||||
}
|
||||
|
|
@ -421,7 +463,7 @@ int main(int argc, char* argv[])
|
|||
generate(i+1, rnd(size));
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[1], "damage") == 0 || strcmp(argv[1], "change") == 0) {
|
||||
} else if (strcmp(argv[1], "damage") == 0 || strcmp(argv[1], "write") == 0) {
|
||||
int fail, size;
|
||||
int silent_error = strcmp(argv[1], "damage") == 0;
|
||||
|
||||
|
|
@ -440,7 +482,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
for(i=b;i<argc;++i) {
|
||||
for(j=0;j<fail;++j) {
|
||||
change(argv[i], rndnotzero(size), silent_error);
|
||||
writ(argv[i], rndnotzero(size), silent_error);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[1], "append") == 0) {
|
||||
|
|
@ -479,6 +521,24 @@ int main(int argc, char* argv[])
|
|||
for(i=b;i<argc;++i) {
|
||||
truncat(argv[i], rnd(size));
|
||||
}
|
||||
} else if (strcmp(argv[1], "change") == 0) {
|
||||
int size;
|
||||
|
||||
if (argc < 5) {
|
||||
help();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
seed = atoi(argv[2]);
|
||||
size = atoi(argv[3]);
|
||||
b = 4;
|
||||
|
||||
/* sort the file names */
|
||||
qsort(&argv[b], argc - b, sizeof(argv[b]), qsort_strcmp);
|
||||
|
||||
for(i=b;i<argc;++i) {
|
||||
change(argv[i], rnd(size));
|
||||
}
|
||||
} else {
|
||||
help();
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
|
|||
|
|
@ -279,6 +279,10 @@ int raid_test_par(int mode, int nd, size_t size)
|
|||
if (!v)
|
||||
return -1;
|
||||
|
||||
/* check memory */
|
||||
if (raid_mtest_vector(nv, size, v) != 0)
|
||||
return -1;
|
||||
|
||||
/* fill with random */
|
||||
raid_mrand_vector(nv, size, v);
|
||||
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ int main(int argc, char* argv[])
|
|||
const char* log;
|
||||
int lock;
|
||||
const char* gen_conf;
|
||||
int period;
|
||||
|
||||
os_init();
|
||||
raid_init();
|
||||
|
|
@ -251,6 +252,7 @@ int main(int argc, char* argv[])
|
|||
blockcount = 0;
|
||||
tommy_list_init(&filterlist_file);
|
||||
tommy_list_init(&filterlist_disk);
|
||||
period = 1000;
|
||||
filter_missing = 0;
|
||||
filter_error = 0;
|
||||
percentage = -1;
|
||||
|
|
@ -370,7 +372,7 @@ int main(int argc, char* argv[])
|
|||
version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'T' :
|
||||
speed();
|
||||
speed(period);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'C' :
|
||||
gen_conf = optarg;
|
||||
|
|
@ -398,6 +400,7 @@ int main(int argc, char* argv[])
|
|||
break;
|
||||
case OPT_TEST_SKIP_DEVICE :
|
||||
opt.skip_device = 1;
|
||||
period = 100; /* reduce period of the speed test */
|
||||
break;
|
||||
case OPT_TEST_FORCE_MURMUR3 :
|
||||
opt.force_murmur3 = 1;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
/****************************************************************************/
|
||||
/* snapraid */
|
||||
|
||||
void speed(void);
|
||||
void speed(int period);
|
||||
void selftest(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
6
speed.c
6
speed.c
|
|
@ -58,11 +58,11 @@ static int64_t diffgettimeofday(struct timeval *start, struct timeval *stop)
|
|||
#define SPEED_STOP \
|
||||
count += delta; \
|
||||
gettimeofday(&stop, 0); \
|
||||
} while (diffgettimeofday(&start, &stop) < 1000000LL); \
|
||||
} while (diffgettimeofday(&start, &stop) < period * 1000LL); \
|
||||
ds = size * (int64_t)count * nd; \
|
||||
dt = diffgettimeofday(&start, &stop);
|
||||
|
||||
void speed(void)
|
||||
void speed(int period)
|
||||
{
|
||||
struct timeval start;
|
||||
struct timeval stop;
|
||||
|
|
@ -74,7 +74,7 @@ void speed(void)
|
|||
int id[RAID_PARITY_MAX];
|
||||
int ip[RAID_PARITY_MAX];
|
||||
int count;
|
||||
int delta = 10;
|
||||
int delta = period >= 1000 ? 10 : 1;
|
||||
int size = TEST_SIZE;
|
||||
int nd = 8;
|
||||
int nv;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue