def test_od_oi(self): """ A past opperations is an object delete which gets applied to this object insert. """ array_path = ['a', 'b', 3, 4] op1 = Op('od', array_path, offset='X') cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op('oi', ['a', 'b', 3, 5], offset='Y', val=['XYZ']) op2.ot(cs1) assert op2.t_path == ['a', 'b', 3, 5] assert op2.t_offset == 'Y' assert op2.t_val == ['XYZ'] assert not op2.is_noop() op1.hazards = [] # op3 happens at path, but differant offset, no change op3 = Op('oi', array_path, offset='W', val=['XYZ']) op3.ot(cs1) assert op3.t_path == array_path assert op3.t_offset == 'W' assert not op3.is_noop() op1.hazards = [] # op4 happens within deletion op4_path = array_path + ['X'] op4 = Op('oi', op4_path, offset='R', val=['XYZ']) op4.ot(cs1) assert op4.is_noop() op1.hazards = [] # op5 inserts right where there was a deletion. Some previous conflict, # so noop op5 = Op('oi', array_path, offset='X', val='XYZ') op5.ot(cs1) assert op5.t_path == array_path assert op5.t_offset == 'X' assert op5.t_val == 'XYZ' assert op5.is_noop() op1.hazards = [] # op6 is at a partial path along deletion, no change op6_path = ['a'] op6 = Op('oi', op6_path, offset='c', val=['XYZ']) op6.ot(cs1) assert op6.t_path == ['a'] assert op6.t_offset == 'c' assert op6.t_val == ['XYZ'] assert not op6.is_noop() op1.hazards = []
def test_si_sd(self): op1 = Op('si', [], offset=3, val="ABC") cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # insertion was at a later index than this delete. No change op2 = Op('sd', [], offset=0, val=3) op2.ot(cs1) assert op2.t_offset == 0 assert op2.t_val == 3 op1.remove_old_hazards(purge=True) # this deletion should expand to delete inserted text as well. op3 = Op('sd', [], offset=2, val=2) op3.ot(cs1) assert op3.t_offset == 2 assert op3.t_val == 5 op1.remove_old_hazards(purge=True) # edge case, don't delete text if don't have have to. Shift # delete range. op4 = Op('sd', [], offset=3, val=2) op4.ot(cs1) assert op4.t_offset == 6 assert op4.t_val == 2 op1.remove_old_hazards(purge=True) # insertion was at lower index. shift delete range forward. op5 = Op('sd', [], offset=4, val=2) op5.ot(cs1) assert op5.t_offset == 7 assert op5.t_val == 2
def test_sd_si(self): op1 = Op('sd', [], offset=3, val=3) cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # delete range has greater index than this insert. Do nothing op2 = Op('si', [], offset=2, val="ABC") op2.ot(cs1) assert op2.t_offset == 2 assert op2.t_val == "ABC" op1.remove_old_hazards(purge=True) # edge case. avoid deleting op3 = Op('si', [], offset=3, val="ABC") op3.ot(cs1) assert op3.t_offset == 3 assert op3.t_val == "ABC" op1.remove_old_hazards(purge=True) # text was put into delete range, so get rid of it. op4 = Op('si', [], offset=4, val="ABC") op4.ot(cs1) assert op4.t_offset == 3 assert op4.t_val == "" op1.remove_old_hazards(purge=True) # text is at edge after delete range op5 = Op('si', [], offset=6, val="ABC") op5.ot(cs1) assert op5.t_offset == 3 assert op5.t_val == "ABC"
def test_oi_oi(self): """ A past opperation is an Object insert which gets applied to this object insert. The only opportunity for conflict is if the later insert happens along a path that is no longer valid. """ array_path = ['a', 'b', 3, 4] op1 = Op('oi', array_path, offset='X', val=['Y', 'Z']) cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op('oi', ['a', 'b', 3, 5], offset='c', val=["XYZ"]) op2.ot(cs1) assert op2.t_path == ['a', 'b', 3, 5] op1.hazards = [] # op3 happens along path, so it will just overwrite past data op3 = Op('oi', ['a'], offset='b', val="XYZ") op3.ot(cs1) assert op3.t_path == ['a'] assert op3.t_offset == 'b' assert op3.t_val == 'XYZ' op1.hazards = [] # op4 is at same path, different offset, so no conflict op4 = Op('oi', array_path, offset='W', val="WWW") op4.ot(cs1) assert op4.t_path == array_path assert op4.t_offset == 'W' assert op4.t_val == 'WWW' assert not op4.is_noop() op1.hazards = [] # op5 is at same path and offset, so previous op takes precedence op5 = Op('oi', array_path, offset='X', val=["XXX"]) op5.ot(cs1) assert op5.t_path == array_path assert op5.t_offset == 'X' assert op5.t_val == ['XXX'] assert op5.is_noop() op1.hazards = [] # op6 path is deep within a previous object insert op6_path = array_path + ['X', 9, 'c'] op6 = Op('oi', op6_path, offset=8, val=["XYZ"]) op6.ot(cs1) assert op6.is_noop() op1.hazards = []
def test_sequence_of_set_ops(self): op1 = Op("set", ["k", 3, "j"], val="ABC") cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) op2 = Op("set", ["k", 3, "j"], val="XYZ") op2.ot(cs1) assert op2.is_noop() == True assert op1.is_noop() == False cs2 = Changeset("doc_id", "author", [cs1]) cs2.add_op(op2) op3 = Op("set", ["k", 3, "j"], val="XYZ") op3.ot(cs2) assert op2.is_noop() == True assert op3.is_noop() == False
def test_set_root_after(self): """ The set root occurs after other unknown operations. They should have no effect. """ op1 = Op("si", [], offset=0, val="ABC") cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) op2 = Op("set", [], val="XYZ") op2.ot(cs1) assert op2.is_noop() == False assert op1.is_noop() == False # op3 happens not at root. still should be affected op3 = Op("set", ["k", 3, "j"], offset=3, val="XYZ") op3.ot(cs1) assert op3.is_noop() == False
def test_set_root_preceding(self): """ A preceding operation did a set at the root. Any following operations should be transformed to a noop. """ op1 = Op("set", [], val="ABC") cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) # op2 happens at root so should be affected op2 = Op("si", [], offset=2, val="XYZ") op2.ot(cs1) assert op2.is_noop() == True # op3 happens not at root. still should be affected op3 = Op("si", ["k", 3, "j"], offset=3, val="XYZ") op3.ot(cs1) assert op3.is_noop() == True
def test_complex_path(self): op1 = Op("set", ["k", 3, "j"], val="ABC") cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) op2 = Op("si", [], val="XYZ") op2.ot(cs1) assert op2.is_noop() == False assert op1.is_noop() == False op3 = Op("si", ["k", 3, "j"], offset=3, val="XYZ") op3.ot(cs1) assert op3.is_noop() == True assert op1.is_noop() == False op4 = Op("si", ["k", 3, "j", "h"], offset=3, val="XYZ") op4.ot(cs1) assert op4.is_noop() == True assert op1.is_noop() == False
def test_si_si(self): op1 = Op('si', [], offset=3, val="ABC") cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # op2 happens at a lower offset, so should not be affected op2 = Op('si', [], offset=2, val="XYZ") op2.ot(cs1) assert op2.t_offset == 2 op1.remove_old_hazards(purge=True) # op3 happens at an equal offset, so should be pushed forward op2 = Op('si', [], offset=3, val="XYZ") op2.ot(cs1) assert op2.t_offset == 6 op1.remove_old_hazards(purge=True) # op4 happens at a later offset, so should be pushed forward op2 = Op('si', [], offset=5, val="XYZ") op2.ot(cs1) assert op2.t_offset == 8
def test_ad_ai(self): """ A past opperations is an array delete which gets applied to this op. """ array_path = ["a", "b", 3, 4] op1 = Op("ad", array_path, offset=3, val=3) cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op("ai", ["a", "b", 3, 5], offset=2, val=["XYZ"]) op2.ot(cs1) assert op2.t_path == ["a", "b", 3, 5] assert op2.t_offset == 2 op1.remove_old_hazards(purge=True) # op3 happens at path, but lower offset, no change (edge case) op3 = Op("ai", array_path, offset=2, val=["XYZ"]) op3.ot(cs1) assert op3.t_path == array_path assert op3.t_offset == 2 assert not op3.is_noop() op1.remove_old_hazards(purge=True) # op4 happens at path and in deletion range (edge case) op4 = Op("ai", array_path, offset=4, val=["XYZ"]) op4.ot(cs1) assert op4.t_path == array_path assert op4.t_offset == 3 assert op4.is_noop() op1.remove_old_hazards(purge=True) # op5 happens at path and after deletion range (edge case) op5 = Op("ai", array_path, offset=6, val=["XYZ"]) op5.ot(cs1) assert op5.t_path == array_path assert op5.t_offset == 3 assert not op5.is_noop() op1.remove_old_hazards(purge=True) # op5 happens within an array element being deleted (edge case) op5_path = array_path + [3] op5 = Op("ai", op5_path, offset=8, val=["XYZ"]) op5.ot(cs1) assert op5.is_noop() op1.remove_old_hazards(purge=True) # op6 is within an array element being deleted (other edge case) op6_path = array_path + [5] op6 = Op("ai", op6_path, offset=8, val=["XYZ"]) op6.ot(cs1) assert op6.is_noop() op1.remove_old_hazards(purge=True) # op7 path is far in an array element being deleted op7_path = array_path + [4, 9, "c"] op7 = Op("ai", op7_path, offset=8, val=["XYZ"]) op7.ot(cs1) assert op7.is_noop() op1.remove_old_hazards(purge=True) # op8 path is in an array element being pulled back (edge case) op8_path = array_path + [6, 9, "c"] op8 = Op("ai", op8_path, offset=8, val=["XYZ"]) op8.ot(cs1) assert op8.t_path == array_path + [3, 9, "c"] op1.remove_old_hazards(purge=True) # op9 path is in an array element NOT being pulled back (edge case) op9_path = array_path + [2, 9, "c"] op9 = Op("ai", op9_path, offset=8, val=["XYZ"]) op9.ot(cs1) assert op9.t_path == array_path + [2, 9, "c"] op1.remove_old_hazards(purge=True) # op10 path is shorter than past path, so no change op10_path = ["a", "b", 3] op10 = Op("ai", op10_path, offset=8, val=["XYZ"]) op10.ot(cs1) assert op10.t_path == ["a", "b", 3] assert op10.t_offset == 8 op1.remove_old_hazards(purge=True)
def test_ad_ad_same_arrays(self): """ A past opperation is an array delete which gets applied to these array deletes. In these cases, they ops are working on the same arrays, so their paths should be unaffected. Only their offsets and vals should shift because of potential overlapping delete ranges. """ path = ["a", "b", 3, 4] op1 = Op("ad", path, offset=5, val=10) cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) # |-- prev op --| # |-- self --| # op2 happens at lower offset, so should not be affected op2 = Op("ad", path, offset=1, val=3) op2.ot(cs1) assert op2.t_path == path assert op2.t_offset == 1 assert op2.t_val == 3 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # op3 happens at lower offset, so should not be affected (edge case) op3 = Op("ad", path, offset=2, val=3) op3.ot(cs1) assert op3.t_path == path assert op3.t_offset == 2 assert op3.t_val == 3 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # op4 is above delete range op4 = Op("ad", path, offset=20, val=5) op4.ot(cs1) assert op4.t_offset == 10 assert op4.t_val == 5 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # op5 is above delete range (edge case) op5 = Op("ad", path, offset=15, val=3) op5.ot(cs1) assert op5.t_offset == 5 assert op5.t_val == 3 assert not op5.is_noop() op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # ops partially overlap op6 = Op("ad", path, offset=3, val=7) op6.ot(cs1) assert op6.t_offset == 3 assert op6.t_val == 2 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # ops partially overlap op9 = Op("ad", path, offset=11, val=10) op9.ot(cs1) assert op9.t_offset == 5 assert op9.t_val == 6 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # ops perfectly overlap op10 = Op("ad", path, offset=5, val=10) op10.ot(cs1) assert op10.t_offset == 5 assert op10.t_val == 0 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # prev encompases self op11 = Op("ad", path, offset=7, val=5) op11.ot(cs1) assert op11.t_offset == 5 assert op11.t_val == 0 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # prev encompases self (edge case) op12 = Op("ad", path, offset=5, val=6) op12.ot(cs1) assert op12.t_offset == 5 assert op12.t_val == 0 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # prev encompases self (other edge case) op12 = Op("ad", path, offset=10, val=5) op12.ot(cs1) assert op12.t_offset == 5 assert op12.t_val == 0 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # self deletes one more than prev (edge case) op13 = Op("ad", path, offset=10, val=6) op13.ot(cs1) assert op13.t_offset == 5 assert op13.t_val == 1 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # self encompases prev op14 = Op("ad", path, offset=3, val=20) op14.ot(cs1) assert op14.t_offset == 3 assert op14.t_val == 10 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # self encompases prev (edge case) oop15 = Op("ad", path, offset=5, val=12) oop15.ot(cs1) assert oop15.t_offset == 5 assert oop15.t_val == 2 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # self encompases prev (other edge case) op16 = Op("ad", path, offset=4, val=11) op16.ot(cs1) assert op16.t_offset == 4 assert op16.t_val == 1 op1.remove_old_hazards(purge=True) # |-- prev op --| # |-- self --| # self encompases prev (off by one on bother sides) op17 = Op("ad", path, offset=4, val=12) op17.ot(cs1) assert op17.t_offset == 4 assert op17.t_val == 2 op1.remove_old_hazards(purge=True)
def test_ai_ai(self): """ A past opperations is an array insert which gets applied to this op. """ array_path = ["a", "b", 3, 4] op1 = Op("ai", array_path, offset=3, val=["X", "Y"]) cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op("ai", ["a", "b", 3, 5], offset=2, val=["XYZ"]) op2.ot(cs1) assert op2.t_path == ["a", "b", 3, 5] op1.remove_old_hazards(purge=True) # op3 happens at same path, but lower offset, no change op3 = Op("ai", array_path, offset=2, val=["XYZ"]) op3.ot(cs1) assert op3.t_path == array_path assert op3.t_offset == 2 op1.remove_old_hazards(purge=True) # op4 is at same path with offset to get pushed forward (edge case) op4 = Op("ai", array_path, offset=3, val=["XYZ"]) op4.ot(cs1) assert op4.t_path == array_path assert op4.t_offset == 5 op1.remove_old_hazards(purge=True) # op5 is at same path with offset to get pushed forward (not edge case) op5 = Op("ai", array_path, offset=8, val=["XYZ"]) op5.ot(cs1) assert op5.t_path == array_path assert op5.t_offset == 10 op1.remove_old_hazards(purge=True) # op6 path is in an array element being pushed forward (edge case) op6_path = array_path + [3, 9, "c"] op6 = Op("ai", op6_path, offset=8, val=["XYZ"]) op6.ot(cs1) assert op6.t_path == array_path + [5, 9, "c"] assert op6.t_offset == 8 op1.remove_old_hazards(purge=True) # op7 path is in an array element being pushed forward (not edge case) op7_path = array_path + [5, 9, "c"] op7 = Op("ai", op7_path, offset=8, val=["XYZ"]) op7.ot(cs1) assert op7.t_path == array_path + [7, 9, "c"] op1.remove_old_hazards(purge=True) # op8 path is shorter then array's path, so no change op8_path = ["a", "b", 3] op8 = Op("ai", op8_path, offset=8, val=["XYZ"]) op8.ot(cs1) assert op8.t_path == op8_path assert op8.t_offset == 8 op1.remove_old_hazards(purge=True)
def test_ad_ad_different_arrays(self): """ A past opperation is an array delete which gets applied to these array deletes. In each case, the are at different arrays, so there should be no changes to offset or val -- only path. """ array_path = ["a", "b", 3, 4] op1 = Op("ad", array_path, offset=3, val=3) cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op("ad", ["a", "b", 3, 5], offset=2, val=3) op2.ot(cs1) assert op2.t_path == ["a", "b", 3, 5] assert op2.t_offset == 2 assert op2.t_val == 3 op1.remove_old_hazards(purge=True) # op3 happens along path of previous delete but in an unaffected # element (edge case) op3_path = array_path + [2] op3 = Op("ad", op3_path, offset=6, val=5) op3.ot(cs1) assert op3.t_path == array_path + [2] assert op3.t_offset == 6 assert op3.t_val == 5 op1.remove_old_hazards(purge=True) # op4 happens along path of previous delete in an element that gets # shifted back (edge case) op4_path = array_path + [6] op4 = Op("ad", op4_path, offset=12, val=13) op4.ot(cs1) assert op4.t_path == array_path + [3] assert op4.t_val == 13 assert op4.t_offset == 12 op1.remove_old_hazards(purge=True) # op5 happens along path of previous delete and in an affected element # (edge case) op5_path = array_path + [3] op5 = Op("ad", op5_path, offset=1, val=3) op5.ot(cs1) assert op5.is_noop() op1.remove_old_hazards(purge=True) # op6 happens along path of previous delete and in an affected element # (other edge case) op6_path = array_path + [5] op6 = Op("ad", op6_path, offset=1, val=2) op6.ot(cs1) assert op6.is_noop() op1.remove_old_hazards(purge=True) # op9 path is shorter then array's path, so no change op9_path = ["a", "b", 3] op9 = Op("ad", op9_path, offset=8, val=3) op9.ot(cs1) assert op9.t_path == op9_path assert op9.t_offset == 8 assert op9.t_val == 3 op1.remove_old_hazards(purge=True)
def test_od_od(self): """ A past opperation is an object delete which gets applied to these object deletes. """ array_path = ['a', 'b', 3, 4] op1 = Op('od', array_path, offset='X') cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op('od', ['a', 'b', 3, 5], offset='Y') op2.ot(cs1) assert op2.t_path == ['a', 'b', 3, 5] assert op2.t_offset == 'Y' assert not op2.is_noop() op1.hazards = [] # op3 happens at same path but different offset op3 = Op('od', array_path, offset='R') op3.ot(cs1) assert op3.t_path == array_path assert op3.t_offset == 'R' assert not op3.is_noop() op1.hazards = [] # op4 tries to delete the same key as op1 op4 = Op('od', array_path, offset='X') op4.ot(cs1) assert op4.is_noop() op1.hazards = [] # op5 tries to delete something within what was already deleted op5_path = array_path + ['X'] op5 = Op('od', op5_path, offset='R') op5.ot(cs1) assert op5.is_noop() op1.hazards = [] # op6 is at a shorter, different path. No change op6_path = ['a'] op6 = Op('od', op6_path, offset='c') op6.ot(cs1) assert op6.t_path == ['a'] assert op6.t_offset == 'c' assert not op6.is_noop() op1.hazards = [] # op9 is at shorter, same path. No change op9_path = ['a'] op9 = Op('od', op9_path, offset='b') op9.ot(cs1) assert op9.t_path == op9_path assert op9.t_offset == 'b' assert not op9.is_noop() op1.hazards = []
def test_sd_sd(self): op1 = Op('sd', [], offset=3, val=3) cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # op1 deletes a range after op2, so should not affect it # |-- op1 --| # |-- op2 --| op2 = Op('sd', [], offset=1, val=2) op2.ot(cs1) assert op2.t_offset == 1 assert op2.t_val == 2 op1.remove_old_hazards(purge=True) # The end of op3 overlaps the start of op 1 # |-- op1 --| # |-- op3 --| op3 = Op('sd', [], offset=2, val=2) op3.ot(cs1) assert op3.t_offset == 2 assert op3.t_val == 1 op1.remove_old_hazards(purge=True) # op1 range is encompased by op 4 range # |-- op1 --| # |---- op4 ----| op4 = Op('sd', [], offset=2, val=6) op4.ot(cs1) assert op4.t_offset == 2 assert op4.t_val == 3 op1.remove_old_hazards(purge=True) # op5 range is encompased by op1 range # |---- op1 ----| # |-- op5 --| op5 = Op('sd', [], offset=4, val=1) op5.ot(cs1) assert op5.t_offset == 3 assert op5.t_val == 0 op1.remove_old_hazards(purge=True) # start of op6 range overlaps end of op1 range # |-- op1 --| # |-- op6 --| op6 = Op('sd', [], offset=5, val=3) op6.ot(cs1) assert op6.t_offset == 3 assert op6.t_val == 2 op1.remove_old_hazards(purge=True) # start of op7 range is after start of op1 range # |-- op1 --| # |-- op7 --| op7 = Op('sd', [], offset=8, val=3) op7.ot(cs1) assert op7.t_offset == 5 assert op7.t_val == 3
def test_ai_ad(self): """ A past opperation is an array insert which gets applied to these array deletes. """ array_path = ["a", "b", 3, 4] op1 = Op("ai", array_path, offset=3, val=["X", "Y"]) cs1 = Changeset("doc_id", "author", []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op("ad", ["a", "b", 3, 5], offset=2, val=3) op2.ot(cs1) assert op2.t_path == ["a", "b", 3, 5] assert op2.t_offset == 2 assert op2.t_val == 3 op1.remove_old_hazards(purge=True) # op3 happens at same path, but past insert is before delete range, so # delete moves. (edge case) op3 = Op("ad", array_path, offset=3, val=3) op3.ot(cs1) assert op3.t_path == array_path assert op3.t_offset == 5 op1.remove_old_hazards(purge=True) # op4 is at same path and will expand delete range to include past # op. (edge case) op4 = Op("ad", array_path, offset=2, val=3) op4.ot(cs1) assert op4.t_path == array_path assert op4.t_val == 5 assert op4.t_offset == 2 op1.remove_old_hazards(purge=True) # op5 is at same path and will expand delete range to include past # op. (other edge case) op5 = Op("ad", array_path, offset=1, val=3) op5.ot(cs1) assert op5.t_path == array_path assert op5.t_val == 5 assert op5.t_offset == 1 op1.remove_old_hazards(purge=True) # op6 is at same path with delete range at lower index than # insert. (edge case) op6 = Op("ad", array_path, offset=1, val=2) op6.ot(cs1) assert op6.t_path == array_path assert op6.t_val == 2 assert op6.t_offset == 1 op1.remove_old_hazards(purge=True) # op7 path is in an array element being pushed forward (edge case) op7_path = array_path + [3, 9, "c"] op7 = Op("ad", op7_path, offset=8, val=3) op7.ot(cs1) assert op7.t_path == array_path + [5, 9, "c"] assert op7.t_offset == 8 op1.remove_old_hazards(purge=True) # op8 path is in an array element being pushed forward (not edge case) op8_path = array_path + [5, 9, "c"] op8 = Op("ad", op8_path, offset=8, val=3) op8.ot(cs1) assert op8.t_path == array_path + [7, 9, "c"] op1.remove_old_hazards(purge=True) # op9 path is shorter then array's path, so no change op9_path = ["a", "b", 3] op9 = Op("ad", op9_path, offset=8, val=3) op9.ot(cs1) assert op9.t_path == op9_path assert op9.t_offset == 8 op1.remove_old_hazards(purge=True)
def test_oi_od(self): """ A past opperation is an object insert which gets applied to these object deletes. """ array_path = ['a', 'b', 3, 4] op1 = Op('oi', array_path, offset='X', val=['X', 'Y']) cs1 = Changeset('doc_id', 'author', []) cs1.add_op(op1) # op2 happens at a different path, so should not be affected op2 = Op('od', ['a', 'b', 3, 5], offset='R') op2.ot(cs1) assert op2.t_path == ['a', 'b', 3, 5] assert op2.t_offset == 'R' assert not op2.is_noop() op1.hazards = [] # op3 deletes a key were unknown op had inserted one. Could not have # intended to delete what it did not know, so noop op3 = Op('od', array_path, offset='X') op3.ot(cs1) assert op3.t_path == array_path assert op3.t_offset == 'X' assert op3.is_noop() op1.hazards = [] # same as above, but well within the inserted value op4_path = array_path + ['X'] op4 = Op('od', op4_path, offset='Y') op4.ot(cs1) assert op4.t_path == array_path + ['X'] assert op4.t_offset == 'Y' assert op4.is_noop() op1.hazards = [] # op5 is at same path, differant offset. No change op5 = Op('od', array_path, offset='R') op5.ot(cs1) assert op5.t_path == array_path assert op5.t_offset == 'R' assert not op5.is_noop() op1.hazards = [] # op6 is at shorter path. No change op6 = Op('od', ['a'], offset='c') op6.ot(cs1) assert op6.t_path == ['a'] assert op6.t_offset == 'c' assert not op6.is_noop() op1.hazards = [] # op7 deletes whatever was previously changed op7 = Op('od', ['a'], offset='b') op7.ot(cs1) assert op7.t_path == ['a'] assert op7.t_offset == 'b' assert not op7.is_noop() op1.hazards = []