summaryrefslogtreecommitdiffstats
path: root/storage/src/tests/visiting/memory_bounded_trace_test.cpp
blob: e2c44cf0b742f0a6879e8b6abafc13a49954f25e (plain) (blame)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vdstestlib/cppunit/macros.h>
#include <vespa/storage/visiting/memory_bounded_trace.h>

namespace storage {

class MemoryBoundedTraceTest : public CppUnit::TestFixture
{
    CPPUNIT_TEST_SUITE(MemoryBoundedTraceTest);
    CPPUNIT_TEST(noMemoryReportedUsedWhenEmpty);
    CPPUNIT_TEST(memoryUsedIsStringLengthForLeafNode);
    CPPUNIT_TEST(memoryUsedIsAccumulatedRecursivelyForNonLeafNodes);
    CPPUNIT_TEST(traceNodesCanBeMovedAndImplicitlyCleared);
    CPPUNIT_TEST(movedTraceTreeIsMarkedAsStrict);
    CPPUNIT_TEST(canNotAddMoreNodesWhenMemoryUsedExceedsUpperBound);
    CPPUNIT_TEST(movedTreeIncludesStatsNodeWhenNodesOmitted);
    CPPUNIT_TEST_SUITE_END();

public:
    void noMemoryReportedUsedWhenEmpty();
    void memoryUsedIsStringLengthForLeafNode();
    void memoryUsedIsAccumulatedRecursivelyForNonLeafNodes();
    void traceNodesCanBeMovedAndImplicitlyCleared();
    void movedTraceTreeIsMarkedAsStrict();
    void canNotAddMoreNodesWhenMemoryUsedExceedsUpperBound();
    void movedTreeIncludesStatsNodeWhenNodesOmitted();
};

CPPUNIT_TEST_SUITE_REGISTRATION(MemoryBoundedTraceTest);

void
MemoryBoundedTraceTest::noMemoryReportedUsedWhenEmpty()
{
    MemoryBoundedTrace trace(100);
    CPPUNIT_ASSERT_EQUAL(size_t(0), trace.getApproxMemoryUsed());
}

void
MemoryBoundedTraceTest::memoryUsedIsStringLengthForLeafNode()
{
    MemoryBoundedTrace trace(100);
    CPPUNIT_ASSERT(trace.add(mbus::TraceNode("hello world", 0)));
    CPPUNIT_ASSERT_EQUAL(size_t(11), trace.getApproxMemoryUsed());
}

void
MemoryBoundedTraceTest::memoryUsedIsAccumulatedRecursivelyForNonLeafNodes()
{
    MemoryBoundedTrace trace(100);
    mbus::TraceNode innerNode;
    innerNode.addChild("hello world");
    innerNode.addChild("goodbye moon");
    CPPUNIT_ASSERT(trace.add(innerNode));
    CPPUNIT_ASSERT_EQUAL(size_t(23), trace.getApproxMemoryUsed());
}

void
MemoryBoundedTraceTest::traceNodesCanBeMovedAndImplicitlyCleared()
{
    MemoryBoundedTrace trace(100);
    CPPUNIT_ASSERT(trace.add(mbus::TraceNode("hello world", 0)));
    mbus::TraceNode target;
    trace.moveTraceTo(target);
    CPPUNIT_ASSERT_EQUAL(uint32_t(1), target.getNumChildren());
    CPPUNIT_ASSERT_EQUAL(size_t(0), trace.getApproxMemoryUsed());
    
    mbus::TraceNode emptinessCheck;
    trace.moveTraceTo(emptinessCheck);
    CPPUNIT_ASSERT_EQUAL(uint32_t(0), emptinessCheck.getNumChildren());
}

/**
 * We want trace subtrees to be strictly ordered so that the message about
 * omitted traces will remain soundly as the last ordered node. There is no
 * particular performance reason for not having strict mode enabled to the
 * best of my knowledge, since the internal backing data structure is an
 * ordered vector anyhow.
 */
void
MemoryBoundedTraceTest::movedTraceTreeIsMarkedAsStrict()
{
    MemoryBoundedTrace trace(100);
    CPPUNIT_ASSERT(trace.add(mbus::TraceNode("hello world", 0)));
    mbus::TraceNode target;
    trace.moveTraceTo(target);
    CPPUNIT_ASSERT_EQUAL(uint32_t(1), target.getNumChildren());
    CPPUNIT_ASSERT(target.getChild(0).isStrict());
}

void
MemoryBoundedTraceTest::canNotAddMoreNodesWhenMemoryUsedExceedsUpperBound()
{
    // Note: we allow one complete node tree to exceed the bounds, but as soon
    // as the bound is exceeded no further nodes can be added.
    MemoryBoundedTrace trace(10);
    CPPUNIT_ASSERT(trace.add(mbus::TraceNode("hello world", 0)));
    CPPUNIT_ASSERT_EQUAL(size_t(11), trace.getApproxMemoryUsed());

    CPPUNIT_ASSERT(!trace.add(mbus::TraceNode("the quick red fox runs across "
                                              "the freeway", 0)));
    CPPUNIT_ASSERT_EQUAL(size_t(11), trace.getApproxMemoryUsed());

    mbus::TraceNode target;
    trace.moveTraceTo(target);
    // Twice nested node (root -> added trace tree -> leaf with txt).
    CPPUNIT_ASSERT_EQUAL(uint32_t(1), target.getNumChildren());
    CPPUNIT_ASSERT(target.getChild(0).getNumChildren() >= 1);
    CPPUNIT_ASSERT_EQUAL(vespalib::string("hello world"),
                         target.getChild(0).getChild(0).getNote());
}

void
MemoryBoundedTraceTest::movedTreeIncludesStatsNodeWhenNodesOmitted()
{
    MemoryBoundedTrace trace(5);
    CPPUNIT_ASSERT(trace.add(mbus::TraceNode("abcdef", 0)));
    CPPUNIT_ASSERT(!trace.add(mbus::TraceNode("ghijkjlmn", 0)));

    mbus::TraceNode target;
    trace.moveTraceTo(target);
    CPPUNIT_ASSERT_EQUAL(uint32_t(1), target.getNumChildren());
    CPPUNIT_ASSERT_EQUAL(uint32_t(2), target.getChild(0).getNumChildren());
    vespalib::string expected("Trace too large; omitted 1 subsequent trace "
                              "trees containing a total of 9 bytes");
    CPPUNIT_ASSERT_EQUAL(expected, target.getChild(0).getChild(1).getNote());
}

} // storage