def test_version_warning(version): """ This is to test version warnings or even errors (eventually) """ remoteA = 'A' remoteB = 'B' set_debug(False) print(remoteA,remoteB) test = testutils.Tester('ver',remoteA,remoteB) ## Config if version: test.config._syncrclone_version = version test.write_config() # Setup test.write_pre('A/file00.txt','0') test.setup() # This has a sync that will throw the warnings stdout = ''.join(test.synclogs[-1]) if version and version.startswith('20200825.0'): assert 'WARNING Previous behavior of conflict_mode changed. Please update your config' in stdout else: assert 'WARNING Previous behavior of conflict_mode changed. Please update your config' not in stdout # if different_version_match os.chdir(PWD0)
def test_dry_run(): remoteA = 'A' remoteB = 'B' set_debug(False) test = testutils.Tester('backups',remoteA,remoteB) test.config.renamesA = 'mtime' test.write_config() # Expected conflicts test.write_pre('A/mod','0') # 1 test.write_pre('A/move','01') # 2 test.write_pre('A/movemod','012') # 2 test.write_pre('A/del','0123') # 1 test.setup() test.write_post('A/mod','A') shutil.move('A/move','A/moved') shutil.move('A/movemod','A/movedmod');test.write_post('A/movedmod','ABC') os.remove('A/del') test.write_post('A/new','01234') # 1 presync_compare = test.compare_tree() assert len(presync_compare) print('-='*40);print('=-'*40) test.sync(['--dry-run']) assert presync_compare == test.compare_tree() os.chdir(PWD0)
def test_redacted_PW_and_modules_in_config_file(): """ Tests that RCLONE_CONFIG_PASS is redacted in debug mode. (even though it isn't needed. rclone will just ignore it) Also tests when you import modules in the config since that was an issue and has now been fixed. """ test = testutils.Tester('redact','A','B') ## Config test.config.rclone_env.update({'RCLONE_CONFIG_PASS':'******'}) test.write_config() # Add module imports to test a regression with open(test.config._configpath,'at') as file: print('\nimport os,sys,subprocess,math,time',file=file) test.write_pre('A/test','test') # sync with debug and then look at the log test.setup(flags=['--debug']) stdout = ''.join(test.synclogs[-1]) assert 'RCLONE_CONFIG_PASS' in stdout,'not debug' assert '**REDACTED**' in stdout,'redacted not seen' assert 'you_cant_see_me' not in stdout,'I see your password!'
def test_logs(): remoteA = 'cryptA:' remoteB = 'B' set_debug(False) test = testutils.Tester('logs',remoteA,remoteB) test.config.renamesA = 'mtime' test.config.log_dest = 'logs' test.write_config() test.write_pre('A/file','0') test.setup() time.sleep(1.1) # To make sure we don't overwrite logs test.write_post('A/fileA','A') os.remove('A/file') test.write_post('B/fileB','BB') print('-='*40);print('=-'*40) test.sync() stdout = ''.join(test.synclogs[-1]) assert test.compare_tree() == set() # Will includes logs logs = sorted(glob.glob('A/logs/*')) assert len(logs) == 2, "should have two. setup + sync" # We know from all of the other tests that stdout (above) works otherwise # they would fail. So just check that it's the same with open(logs[-1]) as f: f.read().strip() == stdout.strip() os.chdir(PWD0)
def test_reuse_hash(): remoteA = 'A' remoteB = 'B' set_debug(True) print(remoteA,remoteB) test = testutils.Tester('reusehash',remoteA,remoteB) ## Config test.config.reuse_hashesA = True test.config.reuse_hashesB = False test.config.compare = 'hash' test.config.filter_flags = ['--filter','- .*','--filter','- .**/'] test.write_config() # Setup test.write_pre('A/file00.txt','0') test.setup() test.write_post('A/fileA1.txt','A1') test.write_post('B/fileB1.txt','B1') print('-='*40);print('=-'*40) test.sync(['--debug']) stdout = ''.join(test.synclogs[-1]) assert "A: Updated 1. Fetching hashes for 1" in stdout assert "B: Updated 1. Fetching hashes for 1" not in stdout os.chdir(PWD0)
def test_locks(): """ Tests locking and breaking the lock """ remoteA, remoteB = 'A', 'B' test = testutils.Tester('locks', remoteA, remoteB) ## Config test.write_config() test.write_pre('A/file1.txt', 'file1') sync = test.setup() test.write_post('B/file1.txt', 'mod', mode='at') # Set the lock sync.rclone.lock() # Try to sync. Note this is done with --debug so it will raise # errors. Test this even if configured not to set a lock test.config.set_lock = False test.write_config() with pytest.raises(syncrclone.main.LockedRemoteError): test.sync(['--debug']) test.config.set_lock = True test.write_config() # break the lock on both then sync test.sync(['--break-lock', 'both', '--debug']) test.sync(['--debug']) # SHoudl work # Edit the file then set the lock again. Then test breaking each one test.write_post('A/file1.txt', 'mod again', mode='at') # Set the lock sync.rclone.lock() with pytest.raises(syncrclone.main.LockedRemoteError): test.sync(['--debug']) test.sync(['--break-lock', 'A', '--debug']) with pytest.raises(syncrclone.main.LockedRemoteError): test.sync(['--debug']) test.sync(['--break-lock', 'B', '--debug']) test.sync(['--debug']) # Should work # Finally, make sure the error, even with --debug, is appropriate for # "breaking" what isn't locked try: os.rmdir('B/.syncrclone/LOCK/') except OSError: pass test.sync(['--break-lock', 'B', '--debug']) os.chdir(PWD0)
def test_backups(backup): remoteA = 'A' remoteB = 'B' set_debug(False) test = testutils.Tester('backups',remoteA,remoteB) test.config.conflict_mode = 'newer' test.write_config() test.write_pre('A/ModifiedOnA.txt','A') test.write_pre('A/DeletedOnA.txt','A') test.write_pre('A/ModifiedOnB.txt','B') test.write_pre('A/DeletedOnB.txt','B') test.setup() os.remove('A/DeletedOnA.txt') os.remove('B/DeletedOnB.txt') test.write_post('A/ModifiedOnA.txt','A1') test.write_post('B/ModifiedOnB.txt','B1') print('-='*40);print('=-'*40) if backup: test.sync() elif backup is None: # Same as False but set it in the configs test.config.backup = False test.write_config() test.sync() else: test.sync(['--no-backup']) # Compare diffs = test.compare_tree() assert diffs == set() assert exists('A/ModifiedOnA.txt') and test.read('A/ModifiedOnA.txt') == 'A1' assert exists('A/ModifiedOnB.txt') and test.read('A/ModifiedOnB.txt') == 'B1' backedA = glob.glob('A/.*/backups/A_*/*') backedB = glob.glob('B/.*/backups/B_*/*') if backup: assert len(backedA) == len(backedB) == len(backedB) == len(backedB) == 2 # The B files were modified so these should all read B assert all(test.read(f) == 'B' for f in backedA) # not B1 assert all(file.endswith('B.txt') for file in backedA) # The A files were modified so these should all read A assert all(test.read(f) == 'A' for f in backedB) # not A1 assert all(file.endswith('A.txt') for file in backedB) else: # False and None assert len(backedA) == len(backedB) == len(backedB) == len(backedB) == 0 os.chdir(PWD0)
def test_and_demo_exclude_if_present(): """ The --exclude-if-present can lead to issues as the filters cannot be applied symmetrically to both sides and can make an exclude look like a delete Demonstrate (a) it working properly when on both sides and (b) the issues it can cause """ test = testutils.Tester('exclude_present', 'A', 'B') test.config.filter_flags = ['--exclude-if-present', 'ignore'] test.write_config() ## Working properly test.write_pre('A/file1', 'file1') test.write_pre('A/sub/file2', 'file2') test.write_pre('A/sub/ignore', '') test.write_pre('A/sub2/file3', 'file3') test.setup() test.write_post('A/sub/onA', 'onA') test.write_post('B/sub/onB', 'onB') print('-=' * 40) print('=-' * 40) test.sync() diffs = test.compare_tree() assert diffs == {('missing_inB', 'sub/onA'), ('missing_inA', 'sub/onB')}, 'exclude did not work' ## Demonstrate the issue test.write_post('B/sub2/ignore', '') test.sync() stdout = ''.join(test.synclogs[-1]) # This will cause A/sub2/file3 to be deleted on A but remain on B diffs = test.compare_tree() assert { ('missing_inA', 'sub2/ignore'), # The ignore file isn't transfered ('missing_inA', 'sub2/file3'), # It was deleted on A ('missing_inA', 'sub/onB'), # These were from before ('missing_inB', 'sub/onA') } == diffs assert "WARNING '--exclude-if-present' can cause issues. See readme" in stdout
def test_legacy_filelist(legA, legB): remoteA = 'A' remoteB = 'B' set_debug(False) test = testutils.Tester('legacy_list', remoteA, remoteB) test.config.name = 'legacytest' test.write_config() test.write_pre('A/fileADEL.txt', 'ADEL') test.write_pre('A/fileSTAY.txt', 'STAY') test.write_pre('A/fileBDEL.txt', 'BDEL') test.setup() os.remove('A/fileADEL.txt' ) # If we do not have the previous list, then it will copy back! os.remove('B/fileBDEL.txt' ) # If we do not have the previous list, then it will copy back! # Convert the lists def xz2zipjson(xz, zj): HEADER = b'zipjson\x00\x00' with lzma.open(xz) as file: files = json.load(file) with open(zj, 'wb') as file: file.write(HEADER + zlib.compress( json.dumps(files, ensure_ascii=False).encode('utf8'))) os.unlink(xz) if legA: xz2zipjson('A/.syncrclone/A-legacytest_fl.json.xz', 'A/.syncrclone/A-legacytest_fl.zipjson') if legB: xz2zipjson('B/.syncrclone/B-legacytest_fl.json.xz', 'B/.syncrclone/B-legacytest_fl.zipjson') print('-=' * 40) print('=-' * 40) test.sync() # Compare diffs = test.compare_tree() assert not diffs assert not exists('A/fileADEL.txt') assert not exists('B/fileBDEL.txt') #test.sync() # Just to see if the log changed in manual testing os.chdir(PWD0)
def test_conflict_resolution(conflict_mode): remoteA = 'A' remoteB = 'B' set_debug(False) test = testutils.Tester('conflicts', remoteA, remoteB) ## Config test.config.conflict_mode = conflict_mode test.write_config() test.write_pre('A/file.txt', '0') test.setup() test.write_post('A/file.txt', 'A') test.write_post('B/file.txt', 'Bb', add_dt=20) # newer and larger print('-=' * 40) print('=-' * 40) test.sync() stdout = ''.join(test.synclogs[-1]) diffs = test.compare_tree() assert diffs == set() files = [os.path.relpath(f, 'A/') for f in testutils.tree('A/')] A = exists('A/file.txt') and test.read('A/file.txt') == 'A' B = exists('A/file.txt') and test.read('A/file.txt') == 'Bb' tA = any(file.endswith('.A') for file in files) tB = any(file.endswith('.B') for file in files) if conflict_mode in ['A', 'older', 'smaller']: assert (A, B, tA, tB) == (True, False, False, False), f"{conflict_mode} {(A,B,tA,tB)}" elif conflict_mode in ['B', 'newer', 'larger']: assert (A, B, tA, tB) == (False, True, False, False), f"{conflict_mode} {(A,B,tA,tB)}" elif conflict_mode in ['newer_tag']: assert (A, B, tA, tB) == (False, True, True, False), f"{conflict_mode} {(A,B,tA,tB)}" elif conflict_mode in ['tag']: assert (A, B, tA, tB) == (False, False, True, True), f"{conflict_mode} {(A,B,tA,tB)}" else: raise ValueError('Not studied') # Should not be here os.chdir(PWD0)
def test_conflict_resolution(conflict_mode,tag_conflict): remoteA = 'A' remoteB = 'B' set_debug(False) test = testutils.Tester('conflicts',remoteA,remoteB) ## Config test.config.conflict_mode = conflict_mode test.config.tag_conflict = tag_conflict test.write_config() test.write_pre('A/file.txt','0') test.setup() test.write_post('A/file.txt','A') test.write_post('B/file.txt','Bb',add_dt=20) # newer and larger print('-='*40);print('=-'*40) test.sync(['--debug']) stdout = ''.join(test.synclogs[-1]) diffs = test.compare_tree() assert diffs == set() files = [os.path.relpath(f,'A/') for f in testutils.tree('A/')] A = exists('A/file.txt') and test.read('A/file.txt') == 'A' B = exists('A/file.txt') and test.read('A/file.txt') == 'Bb' tA = any(file.endswith('.A.txt') for file in files) tB = any(file.endswith('.B.txt') for file in files) if conflict_mode in ['A','older','smaller']: res = (A,B) == (True,False) tag = (tA,tB) == (False,True) if tag_conflict else (False,False) elif conflict_mode in ['B','newer','larger']: res = (A,B) == False,True tag = (tA,tB) == (True,False) if tag_conflict else (False,False) elif conflict_mode in ['tag']: res = (A,B) == (False,False) tag = (tA,tB) == (True,True) else: raise ValueError('Not studied') # Should not be here assert res, f'Wrong res {A,B}, mode {conflict_mode,tag_conflict}' assert tag, f'Wrong tag {tA,tB} mode {conflict_mode,tag_conflict}' os.chdir(PWD0)
def test_no_hashes(): remoteA = 'A' remoteB = 'cryptB:' # Crypt does not have hashes set_debug(False) print(remoteA,remoteB) test = testutils.Tester('nocommon',remoteA,remoteB) ## Config test.config.compare = 'hash' test.config.hash_fail_fallback = 'mtime' test.write_config() # Setup test.write_pre('A/file00.txt','0') test.setup() # This has a sync that will throw the warnings stdout = ''.join(test.synclogs[-1]) # This will have to change if I change the verbage assert "WARNING No common hashes found and/or one or both remotes do not provide hashes. Falling back to 'mtime'" in stdout os.chdir(PWD0)
def test_local_mode(): """ Tests using local mode """ test = testutils.Tester('local','A','B') # Just test creating the new one os.makedirs('logs') # just so the next doesn't fail with open('logs/bla.txt','wt') as f: f.write('bla') test.sync(flags=['--new'],configpath='.') assert exists('.syncrclone/config.py') os.remove('.syncrclone/config.py') # Remove it for nwp test.config.remoteA = '../' test.config.remoteB = '../../B' test.write_config() os.makedirs('A/.syncrclone') shutil.move('config.py','A/.syncrclone/config.py') # Now make a simple test test.write_pre('A/file1.txt','file1') test.setup(configpath='A',flags=[]) # Specified as a dir test.write_post('A/file1.txt','append',mode='at') test.write_post('B/file2.txt','file2') print('-='*40) print('=-'*40) test.sync(configpath='A',flags=[]) # Specified assert test.compare_tree() == set() assert set(testutils.tree('A')) == {'A/file1.txt', 'A/file2.txt'} assert test.read('A/file1.txt') == 'file1append'
def test_three_way(): """ Test three way. In order to make this work within my own testing framework, I have to switch the configs manually as opposed to having multiple configurations. It isn't ideal but works for testing """ set_debug(False) test = testutils.Tester('three','A','B') # Just use simple comparisons test.config.renamesA = test.config.renamesB = 'hash' test.config.name = 'AB' test.write_config() test.write_pre('A/file1.txt','file1') test.write_pre('A/file2.txt','file1') ## Run test.setup() test.write_post('A/file1.txt','mod',mode='at') test.write_post('B/file3.txt','file3') test.sync() # Modify it to sync A <--> C test.config.remoteB = 'C' test.config.name = 'AC' test.write_config() test.write_pre('C/fileC.txt','this is on C') # This *just* makes sure that we don't have a false positive and we # are hacking it to compare C assert {('missing_inA', 'fileC.txt'), ('missing_inB', 'file1.txt'), ('missing_inB', 'file2.txt'), ('missing_inB', 'file3.txt')} == test.compare_tree(A='A',B='C') test.sync() assert test.compare_tree(A='A',B='C') == set() assert test.compare_tree(A='A',B='B') == {('missing_inB', 'fileC.txt')} # Should still miss that test.config.remoteB = 'B' test.config.name = 'AB' test.write_config() test.sync() assert test.compare_tree(A='A',B='C') == set() assert test.compare_tree(A='A',B='B') == set() # Change it again but this time with B <--> C test.config.remoteB = 'C' test.config.remoteA = 'B' test.config.name = 'BC' test.write_config() test.sync() # Shouldn't do anything test.write_post('B/file3.txt','file3 modified') test.sync() assert test.compare_tree(A='B',B='C') == set() assert test.read('C/file3.txt') == 'file3 modified' assert test.compare_tree(A='A',B='C') == {('disagree', 'file3.txt')} os.chdir(PWD0)
def test_main(remoteA,renamesA,remoteB,renamesB,compare,interactive=False): """ Main test with default settings (if the defaults change, this will need to be updated. A few minor changes from the defaults are also made More edge cases and specific settings are played with later """ set_debug(False) print(remoteA,remoteB) test = testutils.Tester('main',remoteA,remoteB) ## Config test.config.reuse_hashesA = False # Also shouldn't compute them test.config.renamesA = renamesA test.config.reuse_hashesB = True test.config.renamesB = renamesB test.config.rclone_flags = ['--fast-list'] # Will be ignored if not supported but good to have otherwise test.config.filter_flags = ['--filter','+ /yes/**', '--filter','- *.no'] test.config.compare = compare test.config.conflict_mode = 'newer_tag' # Deprecated. Update in the future test.config.log_dest = 'logs/' test.write_config() ## Initial files test.write_pre('A/leave_alone.txt','do not touch') # We use newer_tag so we can confirm that these are *NOT* considered # conflicts. They should be backed up *and* test.write_pre('A/EditOnA.txt','Edit on A') test.write_pre('A/EditOnB.txt','Edit on B') # Give it extra so size is unique test.write_pre('A/MoveOnA.txt','Move on A' + randstr(52)) test.write_pre('A/MoveOnB.txt','Move on B' + randstr(100)) test.write_pre('A/MoveOnAB.txt','Move on Both' + randstr(74)) test.write_pre('A/MoveEditOnA.txt','Move and Edit on A') test.write_pre('A/MoveEditOnB.txt','Move and Edit on B') test.write_pre('A/EditOnBoth_Anewer.txt','A will be newer') test.write_pre('A/EditOnBoth_Bnewer.txt','B will be newer') test.write_pre('A/MoveEditOnBoth_Bnewer.txt','Will move and edit on both sides' + randstr(10)) test.write_pre('A/delA.txt','delete on A') test.write_pre('A/delB.txt','delete on B') test.write_pre('A/delA modB.txt','delA but mod on B') test.write_pre('A/delB modA.txt','delB but mod on A') test.write_pre('A/unic°de and space$.txt','UTF8') test.write_pre('A/common_contentAfter0.txt','abc xyz') test.write_pre('A/common_contentAfter1.txt','abc xy') test.write_pre('A/common_contentBefore0.txt','ABC XYZ') test.write_pre('A/common_contentBefore1.txt','ABC XYZ') ## Run test.setup() ## Modify test.write_post('A/EditOnA.txt','Edited on A',mode='at') test.write_post('B/EditOnB.txt','Edited on B',mode='at') test.move('A/MoveOnA.txt','A/sub/MovedOnA.txt') # move and rename test.move('B/MoveOnB.txt','B/sub2/MoveOnB.txt') # just move test.move('A/MoveOnAB.txt','A/MovedOnAB.txt') test.move('B/MoveOnAB.txt','B/MovedOnAB.txt') test.move('A/MoveEditOnA.txt','A/MovedEditOnA.txt') test.move('B/MoveEditOnB.txt','B/MovedEditOnB.txt') test.write_post('A/MovedEditOnA.txt','Move and Edit on A',mode='at') test.write_post('B/MovedEditOnB.txt','Move and Edit on B',mode='at') # recall when comparing by size, When comparing by size, older --> smaller, newer --> larger test.write_post('A/EditOnBoth_Anewer.txt','AAAa',mode='at',add_dt=50) # larger too test.write_post('A/EditOnBoth_Bnewer.txt','AAA',mode='at',add_dt=0) test.write_post('B/EditOnBoth_Anewer.txt','BBB',mode='at',add_dt=0) test.write_post('B/EditOnBoth_Bnewer.txt','BBBb',mode='at',add_dt=50) # larger too test.move('A/MoveEditOnBoth_Bnewer.txt','A/MovedEditedOnBoth_Bnewer.txt') test.move('B/MoveEditOnBoth_Bnewer.txt','B/MovedEditedOnBoth_Bnewer.txt') test.write_post('A/MovedEditedOnBoth_Bnewer.txt','A', mode='at') test.write_post('B/MovedEditedOnBoth_Bnewer.txt','BB', mode='at',add_dt=50) # larger too os.remove('A/delA.txt') os.remove('B/delB.txt') test.write_post('A/delB modA.txt','mod on A',mode='at') os.remove('B/delB modA.txt') test.write_post('B/delA modB.txt','mod on B',mode='at') os.remove('A/delA modB.txt') test.write_post('A/newA.txt','New on A') test.write_post('B/newB.txt','New on B') test.write_post('A/newA.no','New on A and no') # use new to test exclusions too test.write_post('B/newB.no','New on B and no') test.write_post('A/yes/newA.yes.no','New on A and no but yes') test.write_post('B/yes/newB.yes.no','New on B and no but yes') test.write_post('B/unic°de and space$.txt','works',mode='at') # These don't need to be tested other than not showing a diff test.write_post('A/common_contentAfter1.txt','abc xyz') test.write_post('B/common_contentBefore1.txt','ABC XYZW') print('-='*40) print('=-'*40) args = ['--interactive'] if interactive else [] obj = test.sync(args) ## Confirm! print('-'*100) diffs = test.compare_tree() # Exclusions except when filters to allow! assert {('missing_inA', 'newB.no'), ('missing_inB', 'newA.no')} == diffs stdout = ''.join(test.synclogs[-1]) # Check on A from now on! # Edits -- Should *NOT* tag but *should* backup assert test.read('A/EditOnA.txt') == 'Edit on AEdited on A',"mod did not propogate" assert test.read('A/EditOnB.txt') == 'Edit on BEdited on B',"mod did not propogate" assert not exists('A/EditOnA.txt.*'),'Should NOT have been tagged' assert not exists('A/EditOnB.txt.*'),'Should NOT have been tagged' assert test.globread('B/.syncrclone/backups/B_*/EditOnA.txt') == 'Edit on A','not backed up' assert test.globread('A/.syncrclone/backups/A_*/EditOnB.txt') == 'Edit on B','not backed up' # Moves w/o edit assert not exists('A/MoveOnB.txt') assert exists('A/sub2/MoveOnB.txt') assert "Move on A: 'MoveOnB.txt' --> 'sub2/MoveOnB.txt'" in stdout assert not exists('A/MoveOnA.txt') assert exists('A/sub/MovedOnA.txt') assert "Move on B: 'MoveOnA.txt' --> 'sub/MovedOnA.txt'" in stdout assert not exists('A/MoveOnAB.txt') assert exists('A/MovedOnAB.txt') assert not "move A: 'MoveOnAB.txt' --> 'MovedOnAB.txt'" in stdout assert not "move B: 'MoveOnAB.txt' --> 'MovedOnAB.txt'" in stdout # moves with edit -- should not have been tracked assert not exists('A/MoveEditOnA.txt') assert exists('A/MovedEditOnA.txt') assert 'MovedEditOnA.txt: Copied' in stdout,'Not copied. May be rclone log change' assert not exists('A/MoveEditOnB.txt') assert exists('A/MovedEditOnB.txt') assert 'MovedEditOnB.txt: Copied' in stdout,'Not copied. May be rclone log change' assert test.read('A/EditOnBoth_Anewer.txt') == 'A will be newerAAAa' assert test.read('A/EditOnBoth_Bnewer.txt') == 'B will be newerBBBb' assert exists('A/EditOnBoth_Anewer.*.B.txt'), "Not tagged" assert exists('A/EditOnBoth_Bnewer.*.A.txt'), "Not tagged" assert test.read('B/MovedEditedOnBoth_Bnewer.txt').endswith('BB') assert not exists('A/delA.txt') assert not exists('A/delB.txt') assert exists('A/.syncrclone/backups/A_*/delB.txt'), "did not backup" assert exists('B/.syncrclone/backups/B_*/delA.txt'), "did not backup" assert exists('A/delB modA.txt') # Should not have been deleted assert "DELETE CONFLICT: File 'delB modA.txt' deleted on B but modified on A. Transfering" in stdout assert exists('A/delA modB.txt') assert "DELETE CONFLICT: File 'delA modB.txt' deleted on A but modified on B. Transfering" in stdout assert exists('A/newA.txt') assert exists('A/newB.txt') assert exists('A/yes/newA.yes.no') assert exists('A/yes/newB.yes.no') assert test.read('A/unic°de and space$.txt') == 'UTF8works' assert exists('A/.syncrclone/backups/A_*/uni*.txt'),'did not back up' os.chdir(PWD0)
def test_move_attribs(attrib): """ Test moves over various conditions for each type of move tracking including modified sizes. 7 files: 1,2,3,C,D,4,5 all have different mtimes and are moved - 1,2 are unique content and size - 3,C are unique content but same size - D,4 are the same content and size - 5 is unique content and size but it's modified See Truth Table in the code Only test with local A (as it supports inode). B doesn't matter since moves are only tracked on A. """ remoteA = 'A' remoteB = 'B' print(attrib) set_debug(True) print(remoteA,remoteB) test = testutils.Tester('renames',remoteA,remoteB) ## Config test.config.reuse_hashesA = False test.config.renamesA = attrib if attrib == 'size': # Presumably we do not have mtime test.config.compare = 'size' test.config.dt = 0.001 test.write_config() # Setup test.write_pre('A/file1.txt','1') test.write_pre('A/file2.txt','12') test.write_pre('A/file3.txt','123') test.write_pre('A/fileC.txt','ABC') test.write_pre('A/fileD.txt','ABCD') test.write_pre('A/file4.txt','ABCD') test.write_pre('A/file5.txt','12345') test.setup() for c in '123CD45': shutil.move(f'A/file{c}.txt',f'A/file{c}_moved.txt') test.write_post('A/file5_moved.txt','12345') # Changes mtime but same content print('-='*40) print('=-'*40) test.sync() stdout = ''.join(test.synclogs[-1]) ## Truth Table if not attrib: notmoved = '123CD45' moved = too_many = '' elif attrib == 'size': # Size alone won't care about the mod. Note that compare is 'size' for # this one too since that is likely all you would have moved = '125' too_many = '3CD4' notmoved = '' elif attrib == 'mtime': moved = '1234CD' too_many = '' notmoved = '5' elif attrib == 'inode': moved = '123CD4' too_many = '' notmoved = '5' elif attrib == 'hash': moved = '123C5' too_many = 'D4' notmoved = '' for c in moved: assert f"Move on B: 'file{c}.txt' --> 'file{c}_moved.txt'" in stdout,f"{attrib} file{c} didn't move" for c in too_many: assert f"Too many possible previous files for 'file{c}_moved.txt' on A" in stdout, f"{attrib} file{c} failed multiple" for c in notmoved: assert f"Move on B: 'file{c}.txt' --> 'file{c}_moved.txt'" not in stdout,f"{attrib} file{c} moved" os.chdir(PWD0)