1431 }; |
1431 }; |
1432 Ok(Some((node.full_path(map.on_disk)?, debug_tuple))) |
1432 Ok(Some((node.full_path(map.on_disk)?, debug_tuple))) |
1433 })) |
1433 })) |
1434 } |
1434 } |
1435 } |
1435 } |
|
1436 #[cfg(test)] |
|
1437 mod tests { |
|
1438 use super::*; |
|
1439 |
|
1440 /// Shortcut to return tracked descendants of a path. |
|
1441 /// Panics if the path does not exist. |
|
1442 fn tracked_descendants(map: &OwningDirstateMap, path: &[u8]) -> u32 { |
|
1443 let path = dbg!(HgPath::new(path)); |
|
1444 let node = map.get_map().get_node(path); |
|
1445 node.unwrap().unwrap().tracked_descendants_count() |
|
1446 } |
|
1447 |
|
1448 /// Shortcut to return descendants with an entry. |
|
1449 /// Panics if the path does not exist. |
|
1450 fn descendants_with_an_entry(map: &OwningDirstateMap, path: &[u8]) -> u32 { |
|
1451 let path = dbg!(HgPath::new(path)); |
|
1452 let node = map.get_map().get_node(path); |
|
1453 node.unwrap().unwrap().descendants_with_entry_count() |
|
1454 } |
|
1455 |
|
1456 fn assert_does_not_exist(map: &OwningDirstateMap, path: &[u8]) { |
|
1457 let path = dbg!(HgPath::new(path)); |
|
1458 let node = map.get_map().get_node(path); |
|
1459 assert!(node.unwrap().is_none()); |
|
1460 } |
|
1461 |
|
1462 /// Shortcut for path creation in tests |
|
1463 fn p(b: &[u8]) -> &HgPath { |
|
1464 HgPath::new(b) |
|
1465 } |
|
1466 |
|
1467 /// Test the very simple case a single tracked file |
|
1468 #[test] |
|
1469 fn test_tracked_descendants_simple() -> Result<(), DirstateError> { |
|
1470 let mut map = OwningDirstateMap::new_empty(vec![]); |
|
1471 assert_eq!(map.len(), 0); |
|
1472 |
|
1473 map.set_tracked(p(b"some/nested/path"))?; |
|
1474 |
|
1475 assert_eq!(map.len(), 1); |
|
1476 assert_eq!(tracked_descendants(&map, b"some"), 1); |
|
1477 assert_eq!(tracked_descendants(&map, b"some/nested"), 1); |
|
1478 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0); |
|
1479 |
|
1480 map.set_untracked(p(b"some/nested/path"))?; |
|
1481 assert_eq!(map.len(), 0); |
|
1482 assert!(map.get_map().get_node(p(b"some"))?.is_none()); |
|
1483 |
|
1484 Ok(()) |
|
1485 } |
|
1486 |
|
1487 /// Test the simple case of all tracked, but multiple files |
|
1488 #[test] |
|
1489 fn test_tracked_descendants_multiple() -> Result<(), DirstateError> { |
|
1490 let mut map = OwningDirstateMap::new_empty(vec![]); |
|
1491 |
|
1492 map.set_tracked(p(b"some/nested/path"))?; |
|
1493 map.set_tracked(p(b"some/nested/file"))?; |
|
1494 // one layer without any files to test deletion cascade |
|
1495 map.set_tracked(p(b"some/other/nested/path"))?; |
|
1496 map.set_tracked(p(b"root_file"))?; |
|
1497 map.set_tracked(p(b"some/file"))?; |
|
1498 map.set_tracked(p(b"some/file2"))?; |
|
1499 map.set_tracked(p(b"some/file3"))?; |
|
1500 |
|
1501 assert_eq!(map.len(), 7); |
|
1502 assert_eq!(tracked_descendants(&map, b"some"), 6); |
|
1503 assert_eq!(tracked_descendants(&map, b"some/nested"), 2); |
|
1504 assert_eq!(tracked_descendants(&map, b"some/other"), 1); |
|
1505 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1); |
|
1506 assert_eq!(tracked_descendants(&map, b"some/nested/path"), 0); |
|
1507 |
|
1508 map.set_untracked(p(b"some/nested/path"))?; |
|
1509 assert_eq!(map.len(), 6); |
|
1510 assert_eq!(tracked_descendants(&map, b"some"), 5); |
|
1511 assert_eq!(tracked_descendants(&map, b"some/nested"), 1); |
|
1512 assert_eq!(tracked_descendants(&map, b"some/other"), 1); |
|
1513 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1); |
|
1514 |
|
1515 map.set_untracked(p(b"some/nested/file"))?; |
|
1516 assert_eq!(map.len(), 5); |
|
1517 assert_eq!(tracked_descendants(&map, b"some"), 4); |
|
1518 assert_eq!(tracked_descendants(&map, b"some/other"), 1); |
|
1519 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1); |
|
1520 assert_does_not_exist(&map, b"some_nested"); |
|
1521 |
|
1522 map.set_untracked(p(b"some/other/nested/path"))?; |
|
1523 assert_eq!(map.len(), 4); |
|
1524 assert_eq!(tracked_descendants(&map, b"some"), 3); |
|
1525 assert_does_not_exist(&map, b"some/other"); |
|
1526 |
|
1527 map.set_untracked(p(b"root_file"))?; |
|
1528 assert_eq!(map.len(), 3); |
|
1529 assert_eq!(tracked_descendants(&map, b"some"), 3); |
|
1530 assert_does_not_exist(&map, b"root_file"); |
|
1531 |
|
1532 map.set_untracked(p(b"some/file"))?; |
|
1533 assert_eq!(map.len(), 2); |
|
1534 assert_eq!(tracked_descendants(&map, b"some"), 2); |
|
1535 assert_does_not_exist(&map, b"some/file"); |
|
1536 |
|
1537 map.set_untracked(p(b"some/file2"))?; |
|
1538 assert_eq!(map.len(), 1); |
|
1539 assert_eq!(tracked_descendants(&map, b"some"), 1); |
|
1540 assert_does_not_exist(&map, b"some/file2"); |
|
1541 |
|
1542 map.set_untracked(p(b"some/file3"))?; |
|
1543 assert_eq!(map.len(), 0); |
|
1544 assert_does_not_exist(&map, b"some/file3"); |
|
1545 |
|
1546 Ok(()) |
|
1547 } |
|
1548 |
|
1549 /// Check with a mix of tracked and non-tracked items |
|
1550 #[test] |
|
1551 fn test_tracked_descendants_different() -> Result<(), DirstateError> { |
|
1552 let mut map = OwningDirstateMap::new_empty(vec![]); |
|
1553 |
|
1554 // A file that was just added |
|
1555 map.set_tracked(p(b"some/nested/path"))?; |
|
1556 // This has no information, the dirstate should ignore it |
|
1557 map.reset_state(p(b"some/file"), false, false, false, false, None)?; |
|
1558 assert_does_not_exist(&map, b"some/file"); |
|
1559 |
|
1560 // A file that was removed |
|
1561 map.reset_state( |
|
1562 p(b"some/nested/file"), |
|
1563 false, |
|
1564 true, |
|
1565 false, |
|
1566 false, |
|
1567 None, |
|
1568 )?; |
|
1569 assert!(!map.get(p(b"some/nested/file"))?.unwrap().tracked()); |
|
1570 // Only present in p2 |
|
1571 map.reset_state(p(b"some/file3"), false, false, true, false, None)?; |
|
1572 assert!(!map.get(p(b"some/file3"))?.unwrap().tracked()); |
|
1573 // A file that was merged |
|
1574 map.reset_state(p(b"root_file"), true, true, true, false, None)?; |
|
1575 assert!(map.get(p(b"root_file"))?.unwrap().tracked()); |
|
1576 // A file that is added, with info from p2 |
|
1577 // XXX is that actually possible? |
|
1578 map.reset_state(p(b"some/file2"), true, false, true, false, None)?; |
|
1579 assert!(map.get(p(b"some/file2"))?.unwrap().tracked()); |
|
1580 // A clean file |
|
1581 // One layer without any files to test deletion cascade |
|
1582 map.reset_state( |
|
1583 p(b"some/other/nested/path"), |
|
1584 true, |
|
1585 true, |
|
1586 false, |
|
1587 false, |
|
1588 None, |
|
1589 )?; |
|
1590 assert!(map.get(p(b"some/other/nested/path"))?.unwrap().tracked()); |
|
1591 |
|
1592 assert_eq!(map.len(), 6); |
|
1593 assert_eq!(tracked_descendants(&map, b"some"), 3); |
|
1594 assert_eq!(descendants_with_an_entry(&map, b"some"), 5); |
|
1595 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1); |
|
1596 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1); |
|
1597 assert_eq!(tracked_descendants(&map, b"some/other/nested/path"), 0); |
|
1598 assert_eq!( |
|
1599 descendants_with_an_entry(&map, b"some/other/nested/path"), |
|
1600 0 |
|
1601 ); |
|
1602 assert_eq!(tracked_descendants(&map, b"some/nested"), 1); |
|
1603 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2); |
|
1604 |
|
1605 // might as well check this |
|
1606 map.set_untracked(p(b"path/does/not/exist"))?; |
|
1607 assert_eq!(map.len(), 6); |
|
1608 |
|
1609 map.set_untracked(p(b"some/other/nested/path"))?; |
|
1610 // It is set untracked but not deleted since it held other information |
|
1611 assert_eq!(map.len(), 6); |
|
1612 assert_eq!(tracked_descendants(&map, b"some"), 2); |
|
1613 assert_eq!(descendants_with_an_entry(&map, b"some"), 5); |
|
1614 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1); |
|
1615 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1); |
|
1616 assert_eq!(tracked_descendants(&map, b"some/nested"), 1); |
|
1617 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2); |
|
1618 |
|
1619 map.set_untracked(p(b"some/nested/path"))?; |
|
1620 // It is set untracked *and* deleted since it was only added |
|
1621 assert_eq!(map.len(), 5); |
|
1622 assert_eq!(tracked_descendants(&map, b"some"), 1); |
|
1623 assert_eq!(descendants_with_an_entry(&map, b"some"), 4); |
|
1624 assert_eq!(tracked_descendants(&map, b"some/nested"), 0); |
|
1625 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 1); |
|
1626 assert_does_not_exist(&map, b"some/nested/path"); |
|
1627 |
|
1628 map.set_untracked(p(b"root_file"))?; |
|
1629 // Untracked but not deleted |
|
1630 assert_eq!(map.len(), 5); |
|
1631 assert!(map.get(p(b"root_file"))?.is_some()); |
|
1632 |
|
1633 map.set_untracked(p(b"some/file2"))?; |
|
1634 assert_eq!(map.len(), 5); |
|
1635 assert_eq!(tracked_descendants(&map, b"some"), 0); |
|
1636 assert!(map.get(p(b"some/file2"))?.is_some()); |
|
1637 |
|
1638 map.set_untracked(p(b"some/file3"))?; |
|
1639 assert_eq!(map.len(), 5); |
|
1640 assert_eq!(tracked_descendants(&map, b"some"), 0); |
|
1641 assert!(map.get(p(b"some/file3"))?.is_some()); |
|
1642 |
|
1643 Ok(()) |
|
1644 } |
|
1645 |
|
1646 /// Check that copies counter is correctly updated |
|
1647 #[test] |
|
1648 fn test_copy_source() -> Result<(), DirstateError> { |
|
1649 let mut map = OwningDirstateMap::new_empty(vec![]); |
|
1650 |
|
1651 // Clean file |
|
1652 map.reset_state(p(b"files/clean"), true, true, false, false, None)?; |
|
1653 // Merged file |
|
1654 map.reset_state(p(b"files/from_p2"), true, true, true, false, None)?; |
|
1655 // Removed file |
|
1656 map.reset_state(p(b"removed"), false, true, false, false, None)?; |
|
1657 // Added file |
|
1658 map.reset_state(p(b"files/added"), true, false, false, false, None)?; |
|
1659 // Add copy |
|
1660 map.copy_map_insert(p(b"files/clean"), p(b"clean_copy_source"))?; |
|
1661 assert_eq!(map.copy_map_len(), 1); |
|
1662 |
|
1663 // Copy override |
|
1664 map.copy_map_insert(p(b"files/clean"), p(b"other_clean_copy_source"))?; |
|
1665 assert_eq!(map.copy_map_len(), 1); |
|
1666 |
|
1667 // Multiple copies |
|
1668 map.copy_map_insert(p(b"removed"), p(b"removed_copy_source"))?; |
|
1669 assert_eq!(map.copy_map_len(), 2); |
|
1670 |
|
1671 map.copy_map_insert(p(b"files/added"), p(b"added_copy_source"))?; |
|
1672 assert_eq!(map.copy_map_len(), 3); |
|
1673 |
|
1674 // Added, so the entry is completely removed |
|
1675 map.set_untracked(p(b"files/added"))?; |
|
1676 assert_does_not_exist(&map, b"files/added"); |
|
1677 assert_eq!(map.copy_map_len(), 2); |
|
1678 |
|
1679 // Removed, so the entry is kept around, so is its copy |
|
1680 map.set_untracked(p(b"removed"))?; |
|
1681 assert!(map.get(p(b"removed"))?.is_some()); |
|
1682 assert_eq!(map.copy_map_len(), 2); |
|
1683 |
|
1684 // Clean, so the entry is kept around, but not its copy |
|
1685 map.set_untracked(p(b"files/clean"))?; |
|
1686 assert!(map.get(p(b"files/clean"))?.is_some()); |
|
1687 assert_eq!(map.copy_map_len(), 1); |
|
1688 |
|
1689 map.copy_map_insert(p(b"files/from_p2"), p(b"from_p2_copy_source"))?; |
|
1690 assert_eq!(map.copy_map_len(), 2); |
|
1691 |
|
1692 // Info from p2, so its copy source info is kept around |
|
1693 map.set_untracked(p(b"files/from_p2"))?; |
|
1694 assert!(map.get(p(b"files/from_p2"))?.is_some()); |
|
1695 assert_eq!(map.copy_map_len(), 2); |
|
1696 |
|
1697 Ok(()) |
|
1698 } |
|
1699 |
|
1700 /// Test with "on disk" data. For the sake of this test, the "on disk" data |
|
1701 /// does not actually come from the disk, but it's opaque to the code being |
|
1702 /// tested. |
|
1703 #[test] |
|
1704 fn test_on_disk() -> Result<(), DirstateError> { |
|
1705 // First let's create some data to put "on disk" |
|
1706 let mut map = OwningDirstateMap::new_empty(vec![]); |
|
1707 |
|
1708 // A file that was just added |
|
1709 map.set_tracked(p(b"some/nested/added"))?; |
|
1710 map.copy_map_insert(p(b"some/nested/added"), p(b"added_copy_source"))?; |
|
1711 |
|
1712 // A file that was removed |
|
1713 map.reset_state( |
|
1714 p(b"some/nested/removed"), |
|
1715 false, |
|
1716 true, |
|
1717 false, |
|
1718 false, |
|
1719 None, |
|
1720 )?; |
|
1721 // Only present in p2 |
|
1722 map.reset_state( |
|
1723 p(b"other/p2_info_only"), |
|
1724 false, |
|
1725 false, |
|
1726 true, |
|
1727 false, |
|
1728 None, |
|
1729 )?; |
|
1730 map.copy_map_insert( |
|
1731 p(b"other/p2_info_only"), |
|
1732 p(b"other/p2_info_copy_source"), |
|
1733 )?; |
|
1734 // A file that was merged |
|
1735 map.reset_state(p(b"merged"), true, true, true, false, None)?; |
|
1736 // A file that is added, with info from p2 |
|
1737 // XXX is that actually possible? |
|
1738 map.reset_state( |
|
1739 p(b"other/added_with_p2"), |
|
1740 true, |
|
1741 false, |
|
1742 true, |
|
1743 false, |
|
1744 None, |
|
1745 )?; |
|
1746 // One layer without any files to test deletion cascade |
|
1747 // A clean file |
|
1748 map.reset_state( |
|
1749 p(b"some/other/nested/clean"), |
|
1750 true, |
|
1751 true, |
|
1752 false, |
|
1753 false, |
|
1754 None, |
|
1755 )?; |
|
1756 |
|
1757 let (packed, metadata, _should_append) = map.pack_v2(false)?; |
|
1758 let packed_len = packed.len(); |
|
1759 assert!(packed_len > 0); |
|
1760 |
|
1761 // Recreate "from disk" |
|
1762 let mut map = OwningDirstateMap::new_v2( |
|
1763 packed, |
|
1764 packed_len, |
|
1765 metadata.as_bytes(), |
|
1766 )?; |
|
1767 |
|
1768 // Check that everything is accounted for |
|
1769 assert!(map.contains_key(p(b"some/nested/added"))?); |
|
1770 assert!(map.contains_key(p(b"some/nested/removed"))?); |
|
1771 assert!(map.contains_key(p(b"merged"))?); |
|
1772 assert!(map.contains_key(p(b"other/p2_info_only"))?); |
|
1773 assert!(map.contains_key(p(b"other/added_with_p2"))?); |
|
1774 assert!(map.contains_key(p(b"some/other/nested/clean"))?); |
|
1775 assert_eq!( |
|
1776 map.copy_map_get(p(b"some/nested/added"))?, |
|
1777 Some(p(b"added_copy_source")) |
|
1778 ); |
|
1779 assert_eq!( |
|
1780 map.copy_map_get(p(b"other/p2_info_only"))?, |
|
1781 Some(p(b"other/p2_info_copy_source")) |
|
1782 ); |
|
1783 assert_eq!(tracked_descendants(&map, b"some"), 2); |
|
1784 assert_eq!(descendants_with_an_entry(&map, b"some"), 3); |
|
1785 assert_eq!(tracked_descendants(&map, b"other"), 1); |
|
1786 assert_eq!(descendants_with_an_entry(&map, b"other"), 2); |
|
1787 assert_eq!(tracked_descendants(&map, b"some/other"), 1); |
|
1788 assert_eq!(descendants_with_an_entry(&map, b"some/other"), 1); |
|
1789 assert_eq!(tracked_descendants(&map, b"some/other/nested"), 1); |
|
1790 assert_eq!(descendants_with_an_entry(&map, b"some/other/nested"), 1); |
|
1791 assert_eq!(tracked_descendants(&map, b"some/nested"), 1); |
|
1792 assert_eq!(descendants_with_an_entry(&map, b"some/nested"), 2); |
|
1793 assert_eq!(map.len(), 6); |
|
1794 assert_eq!(map.get_map().unreachable_bytes, 0); |
|
1795 assert_eq!(map.copy_map_len(), 2); |
|
1796 |
|
1797 // Shouldn't change anything since it's already not tracked |
|
1798 map.set_untracked(p(b"some/nested/removed"))?; |
|
1799 assert_eq!(map.get_map().unreachable_bytes, 0); |
|
1800 |
|
1801 match map.get_map().root { |
|
1802 ChildNodes::InMemory(_) => { |
|
1803 panic!("root should not have been mutated") |
|
1804 } |
|
1805 _ => (), |
|
1806 } |
|
1807 // We haven't mutated enough (nothing, actually), we should still be in |
|
1808 // the append strategy |
|
1809 assert!(map.get_map().write_should_append()); |
|
1810 |
|
1811 // But this mutates the structure, so there should be unreachable_bytes |
|
1812 assert!(map.set_untracked(p(b"some/nested/added"))?); |
|
1813 let unreachable_bytes = map.get_map().unreachable_bytes; |
|
1814 assert!(unreachable_bytes > 0); |
|
1815 |
|
1816 match map.get_map().root { |
|
1817 ChildNodes::OnDisk(_) => panic!("root should have been mutated"), |
|
1818 _ => (), |
|
1819 } |
|
1820 |
|
1821 // This should not mutate the structure either, since `root` has |
|
1822 // already been mutated along with its direct children. |
|
1823 map.set_untracked(p(b"merged"))?; |
|
1824 assert_eq!(map.get_map().unreachable_bytes, unreachable_bytes); |
|
1825 |
|
1826 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() { |
|
1827 NodeRef::InMemory(_, _) => { |
|
1828 panic!("'other/added_with_p2' should not have been mutated") |
|
1829 } |
|
1830 _ => (), |
|
1831 } |
|
1832 // But this should, since it's in a different path |
|
1833 // than `<root>some/nested/add` |
|
1834 map.set_untracked(p(b"other/added_with_p2"))?; |
|
1835 assert!(map.get_map().unreachable_bytes > unreachable_bytes); |
|
1836 |
|
1837 match map.get_map().get_node(p(b"other/added_with_p2"))?.unwrap() { |
|
1838 NodeRef::OnDisk(_) => { |
|
1839 panic!("'other/added_with_p2' should have been mutated") |
|
1840 } |
|
1841 _ => (), |
|
1842 } |
|
1843 |
|
1844 // We have rewritten most of the tree, we should create a new file |
|
1845 assert!(!map.get_map().write_should_append()); |
|
1846 |
|
1847 Ok(()) |
|
1848 } |
|
1849 } |