1 /** @file
2 
3    Catch-based unit tests for HdrHeap
4 
5    @section license License
6 
7    Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
8    See the NOTICE file distributed with this work for additional information regarding copyright
9    ownership.  The ASF licenses this file to you under the Apache License, Version 2.0 (the
10    "License"); you may not use this file except in compliance with the License.  You may obtain a
11    copy of the License at
12 
13       http://www.apache.org/licenses/LICENSE-2.0
14 
15    Unless required by applicable law or agreed to in writing, software distributed under the License
16    is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
17    or implied. See the License for the specific language governing permissions and limitations under
18    the License.
19  */
20 
21 #include "catch.hpp"
22 
23 #include "HdrHeap.h"
24 #include "URL.h"
25 
26 /**
27   This test is designed to test numerous pieces of the HdrHeaps including allocations,
28   demotion of rw heaps to ronly heaps, and finally the coalesce and evacuate behaviours.
29  */
30 TEST_CASE("HdrHeap", "[proxy][hdrheap]")
31 {
32   // The amount of space we will need to overflow the StrHdrHeap is HdrStrHeap::DEFAULT_SIZE - sizeof(HdrStrHeap)
33   size_t next_rw_heap_size           = HdrStrHeap::DEFAULT_SIZE;
34   size_t next_required_overflow_size = next_rw_heap_size - sizeof(HdrStrHeap);
35   char buf[next_required_overflow_size];
36   for (unsigned int i = 0; i < sizeof(buf); ++i) {
37     buf[i] = ('a' + (i % 26));
38   }
39 
40   HdrHeap *heap = new_HdrHeap();
41   URLImpl *url  = url_create(heap);
42 
43   // Checking that we have no rw heap
44   CHECK(heap->m_read_write_heap.get() == nullptr);
45   url_path_set(heap, url, buf, next_required_overflow_size, true);
46 
47   // Checking that we've completely consumed the rw heap
48   CHECK(heap->m_read_write_heap->m_free_size == 0);
49 
50   // Checking ronly_heaps are empty
51   for (unsigned i = 0; i < HDR_BUF_RONLY_HEAPS; ++i) {
52     CHECK(heap->m_ronly_heap[i].m_heap_start == nullptr);
53   }
54 
55   // Now we have no ronly heaps in use and a completely full rwheap, so we will test that
56   // we demote to ronly heaps HDR_BUF_RONLY_HEAPS times.
57   for (unsigned ronly_heap = 0; ronly_heap < HDR_BUF_RONLY_HEAPS; ++ronly_heap) {
58     next_rw_heap_size           = 2 * heap->m_read_write_heap->m_heap_size;
59     next_required_overflow_size = next_rw_heap_size - sizeof(HdrStrHeap);
60     char buf2[next_required_overflow_size];
61     for (unsigned int i = 0; i < sizeof(buf2); ++i) {
62       buf2[i] = ('a' + (i % 26));
63     }
64 
65     URLImpl *url2 = url_create(heap);
66     url_path_set(heap, url2, buf2, next_required_overflow_size, true);
67 
68     // Checking the current rw heap is next_rw_heap_size bytes
69     CHECK(heap->m_read_write_heap->m_heap_size == (uint32_t)next_rw_heap_size);
70     // Checking that we've completely consumed the rw heap
71     CHECK(heap->m_read_write_heap->m_free_size == 0);
72     // Checking that we properly demoted the previous rw heap
73     CHECK(heap->m_ronly_heap[ronly_heap].m_heap_start != nullptr);
74 
75     for (unsigned i = ronly_heap + 1; i < HDR_BUF_RONLY_HEAPS; ++i) {
76       // Checking ronly_heap[i] is empty
77       CHECK(heap->m_ronly_heap[i].m_heap_start == nullptr);
78     }
79   }
80 
81   // We will rerun these checks after we introduce a non-copied string to make sure we didn't already coalesce
82   for (unsigned i = 0; i < HDR_BUF_RONLY_HEAPS; ++i) {
83     // Pre non-copied string: Checking ronly_heaps[i] is NOT empty
84     CHECK(heap->m_ronly_heap[i].m_heap_start != nullptr);
85   }
86 
87   // Now if we add a url object that contains only non-copied strings it shouldn't affect the size of the rwheap
88   // since it doesn't require allocating any storage on this heap.
89   char buf3[next_required_overflow_size];
90   for (unsigned int i = 0; i < sizeof(buf3); ++i) {
91     buf3[i] = ('a' + (i % 26));
92   }
93 
94   URLImpl *aliased_str_url = url_create(heap);
95   url_path_set(heap, aliased_str_url, buf3, next_required_overflow_size, false); // don't copy this string
96   // Checking that the aliased string shows having proper length
97   CHECK(aliased_str_url->m_len_path == next_required_overflow_size);
98   // Checking that the aliased string is correctly pointing at buf
99   CHECK(aliased_str_url->m_ptr_path == &buf3[0]);
100 
101   // Post non-copied string: Checking ronly_heaps are NOT empty
102   for (unsigned i = 0; i < HDR_BUF_RONLY_HEAPS; ++i) {
103     CHECK(heap->m_ronly_heap[i].m_heap_start != nullptr);
104   }
105   // Checking that we've completely consumed the rw heap
106   CHECK(heap->m_read_write_heap->m_free_size == 0);
107   // Checking that we dont have any chained heaps
108   CHECK(heap->m_next == nullptr);
109 
110   // Now at this point we have a completely full rw_heap and no ronly heap slots, so any allocation would have to result
111   // in a coalesce, and to validate that we don't reintroduce TS-2766 we have an aliased string, so when it tries to
112   // coalesce it used to sum up the size of the ronly heaps and the rw heap which is incorrect because we never
113   // copied the above string onto the heap. The new behaviour fixed in TS-2766 will make sure that this non copied
114   // string is accounted for, in the old implementation it would result in an allocation failure.
115 
116   char *str = heap->allocate_str(1); // this will force a coalesce.
117   // Checking that 1 byte allocated string is not nullptr
118   CHECK(str != nullptr);
119 
120   // Now we need to validate that aliased_str_url has a path that isn't nullptr, if it's nullptr then the
121   // coalesce is broken and didn't properly determine the size, if it's not nullptr then everything worked as expected.
122 
123   // Checking that the aliased string shows having proper length
124   CHECK(aliased_str_url->m_len_path == next_required_overflow_size);
125   // Checking that the aliased string was properly moved during coalesce and evacuation
126   CHECK(aliased_str_url->m_ptr_path != nullptr);
127   // Checking that the aliased string was properly moved during coalesce and evacuation (not pointing at buf3)
128   CHECK(aliased_str_url->m_ptr_path != &buf3[0]);
129 
130   // Clean up
131   heap->destroy();
132 }
133