summaryrefslogtreecommitdiff
path: root/test/buildtool/execution_api/local_tree_map.test.cpp
blob: 9b9f2da484f795b521256ca59a123b09cad00d22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <string>
#include <thread>
#include <vector>

#include "catch2/catch.hpp"
#include "src/buildtool/execution_api/common/local_tree_map.hpp"
#include "src/utils/cpp/atomic.hpp"

namespace {

[[nodiscard]] auto ToDigest(std::string const& s) {
    return static_cast<bazel_re::Digest>(
        ArtifactDigest{s, 0, /*is_tree=*/false});
}

[[nodiscard]] auto ToInfo(std::string const& s) {
    return Artifact::ObjectInfo{ArtifactDigest{s, 0, /*is_tree=*/false},
                                ObjectType::File};
}

}  // namespace

TEST_CASE("LocalTree: empty tree", "[execution_api]") {
    LocalTreeMap tree_map{};

    auto tree = tree_map.CreateTree();
    CHECK(tree.size() == 0);
    CHECK(std::all_of(
        tree.begin(), tree.end(), [](auto /*unused*/) { return false; }));
}

TEST_CASE("LocalTree: first wins", "[execution_api]") {
    LocalTreeMap tree_map{};

    auto tree = tree_map.CreateTree();
    CHECK(tree.AddInfo("foo", ToInfo("bar")));
    CHECK(tree.AddInfo("foo", ToInfo("baz")));
    CHECK(tree.size() == 1);
    for (auto const& [path, oid] : tree) {
        CHECK(oid->digest.hash() == "bar");
    }
}

TEST_CASE("LocalTreeMap: first wins", "[execution_api]") {
    LocalTreeMap tree_map{};

    auto tree_1 = tree_map.CreateTree();
    CHECK(tree_1.AddInfo("foo", ToInfo("bar")));

    auto tree_2 = tree_map.CreateTree();
    CHECK(tree_2.AddInfo("foo", ToInfo("baz")));

    auto tree_id = ToDigest("tree");
    CHECK(tree_map.AddTree(tree_id, std::move(tree_1)));  // NOLINT
    CHECK(tree_map.AddTree(tree_id, std::move(tree_2)));  // NOLINT

    CHECK(tree_map.HasTree(tree_id));

    auto const* tree = tree_map.GetTree(tree_id);
    REQUIRE(tree != nullptr);
    CHECK(tree->size() == 1);
    for (auto const& [path, oid] : *tree) {
        CHECK(oid->digest.hash() == "bar");
    }
}

TEST_CASE("LocalTreeMap: thread-safety", "[execution_api]") {
    constexpr auto kNumThreads = 100;
    constexpr auto kQ = 10;

    atomic<bool> starting_signal{false};
    std::vector<std::thread> threads{};
    threads.reserve(kNumThreads);

    LocalTreeMap tree_map{};

    for (int id{}; id < kNumThreads; ++id) {
        threads.emplace_back(
            [&tree_map, &starting_signal](int tid) {
                auto entry_id = std::to_string(tid);
                auto tree = tree_map.CreateTree();
                REQUIRE(tree.AddInfo(entry_id, ToInfo(entry_id)));

                auto tree_id = ToDigest(std::to_string(tid / kQ));
                starting_signal.wait(false);

                // kQ-many threads try to add tree with same id
                REQUIRE(tree_map.AddTree(tree_id, std::move(tree)));  // NOLINT
            },
            id);
    }

    starting_signal = true;
    starting_signal.notify_all();
    for (auto& thread : threads) {
        thread.join();
    }

    for (int id{}; id <= (kNumThreads - 1) / kQ; ++id) {
        auto tree_id = ToDigest(std::to_string(id));
        CHECK(tree_map.HasTree(tree_id));

        auto const* tree = tree_map.GetTree(tree_id);
        REQUIRE(tree != nullptr);
        CHECK(tree->size() == 1);
        for (auto const& [path, oid] : *tree) {
            auto entry_id = std::stoi(oid->digest.hash());
            CHECK(entry_id >= id * kQ);
            CHECK(entry_id < (id + 1) * kQ);
        }
    }
}