Show More
@@ -1433,3 +1433,417 b' impl OwningDirstateMap {' | |||||
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 | } |
General Comments 0
You need to be logged in to leave comments.
Login now