1 // Copyright 2021 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gmock/gmock.h>
16
17 #include "common/RefBase.h"
18
19 namespace {
20 using Id = uint32_t;
21
22 enum class Action {
23 kReference,
24 kRelease,
25 kAssign,
26 kMarker,
27 };
28
29 struct Event {
30 Action action;
31 Id thisId = 0;
32 Id otherId = 0;
33 };
34
operator <<(std::ostream & os,const Event & event)35 std::ostream& operator<<(std::ostream& os, const Event& event) {
36 switch (event.action) {
37 case Action::kReference:
38 os << "Reference " << event.thisId;
39 break;
40 case Action::kRelease:
41 os << "Release " << event.thisId;
42 break;
43 case Action::kAssign:
44 os << "Assign " << event.thisId << " <- " << event.otherId;
45 break;
46 case Action::kMarker:
47 os << "Marker " << event.thisId;
48 break;
49 }
50 return os;
51 }
52
operator ==(const Event & a,const Event & b)53 bool operator==(const Event& a, const Event& b) {
54 return a.action == b.action && a.thisId == b.thisId && a.otherId == b.otherId;
55 }
56
57 using Events = std::vector<Event>;
58
59 struct RefTracker {
RefTracker__anon8a55f6c90111::RefTracker60 explicit constexpr RefTracker(nullptr_t) : mId(0), mEvents(nullptr) {
61 }
62
63 constexpr RefTracker(const RefTracker& other) = default;
64
RefTracker__anon8a55f6c90111::RefTracker65 RefTracker(Id id, Events* events) : mId(id), mEvents(events) {
66 }
67
Reference__anon8a55f6c90111::RefTracker68 void Reference() const {
69 mEvents->emplace_back(Event{Action::kReference, mId});
70 }
71
Release__anon8a55f6c90111::RefTracker72 void Release() const {
73 mEvents->emplace_back(Event{Action::kRelease, mId});
74 }
75
operator =__anon8a55f6c90111::RefTracker76 RefTracker& operator=(const RefTracker& other) {
77 if (mEvents || other.mEvents) {
78 Events* events = mEvents ? mEvents : other.mEvents;
79 events->emplace_back(Event{Action::kAssign, mId, other.mId});
80 }
81 mId = other.mId;
82 mEvents = other.mEvents;
83 return *this;
84 }
85
operator ==__anon8a55f6c90111::RefTracker86 bool operator==(const RefTracker& other) const {
87 return mId == other.mId;
88 }
89
operator !=__anon8a55f6c90111::RefTracker90 bool operator!=(const RefTracker& other) const {
91 return mId != other.mId;
92 }
93
94 Id mId;
95 Events* mEvents;
96 };
97
98 struct RefTrackerTraits {
99 static constexpr RefTracker kNullValue{nullptr};
100
Reference__anon8a55f6c90111::RefTrackerTraits101 static void Reference(const RefTracker& handle) {
102 handle.Reference();
103 }
104
Release__anon8a55f6c90111::RefTrackerTraits105 static void Release(const RefTracker& handle) {
106 handle.Release();
107 }
108 };
109
110 constexpr RefTracker RefTrackerTraits::kNullValue;
111
112 using Ref = RefBase<RefTracker, RefTrackerTraits>;
113 } // namespace
114
TEST(RefBase,Acquire)115 TEST(RefBase, Acquire) {
116 Events events;
117 RefTracker tracker1(1, &events);
118 RefTracker tracker2(2, &events);
119 Ref ref(tracker1);
120
121 events.clear();
122 { ref.Acquire(tracker2); }
123 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kRelease, 1}, // release ref
124 Event{Action::kAssign, 1, 2} // acquire tracker2
125 ));
126 }
127
TEST(RefBase,Detach)128 TEST(RefBase, Detach) {
129 Events events;
130 RefTracker tracker(1, &events);
131 Ref ref(tracker);
132
133 events.clear();
134 { DAWN_UNUSED(ref.Detach()); }
135 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kAssign, 1, 0} // nullify ref
136 ));
137 }
138
TEST(RefBase,Constructor)139 TEST(RefBase, Constructor) {
140 Ref ref;
141 EXPECT_EQ(ref.Get(), RefTrackerTraits::kNullValue);
142 }
143
TEST(RefBase,ConstructDestruct)144 TEST(RefBase, ConstructDestruct) {
145 Events events;
146 RefTracker tracker(1, &events);
147
148 events.clear();
149 {
150 Ref ref(tracker);
151 events.emplace_back(Event{Action::kMarker, 10});
152 }
153 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kReference, 1}, // reference tracker
154 Event{Action::kMarker, 10}, //
155 Event{Action::kRelease, 1} // destruct ref
156 ));
157 }
158
TEST(RefBase,CopyConstruct)159 TEST(RefBase, CopyConstruct) {
160 Events events;
161 RefTracker tracker(1, &events);
162 Ref refA(tracker);
163
164 events.clear();
165 {
166 Ref refB(refA);
167 events.emplace_back(Event{Action::kMarker, 10});
168 }
169 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kReference, 1}, // reference tracker
170 Event{Action::kMarker, 10}, //
171 Event{Action::kRelease, 1} // destruct ref
172 ));
173 }
174
TEST(RefBase,RefCopyAssignment)175 TEST(RefBase, RefCopyAssignment) {
176 Events events;
177 RefTracker tracker1(1, &events);
178 RefTracker tracker2(2, &events);
179 Ref refA(tracker1);
180 Ref refB(tracker2);
181
182 events.clear();
183 {
184 Ref ref;
185 events.emplace_back(Event{Action::kMarker, 10});
186 ref = refA;
187 events.emplace_back(Event{Action::kMarker, 20});
188 ref = refB;
189 events.emplace_back(Event{Action::kMarker, 30});
190 ref = refA;
191 events.emplace_back(Event{Action::kMarker, 40});
192 }
193 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kMarker, 10}, //
194 Event{Action::kReference, 1}, // reference tracker1
195 Event{Action::kAssign, 0, 1}, // copy tracker1
196 Event{Action::kMarker, 20}, //
197 Event{Action::kReference, 2}, // reference tracker2
198 Event{Action::kRelease, 1}, // release tracker1
199 Event{Action::kAssign, 1, 2}, // copy tracker2
200 Event{Action::kMarker, 30}, //
201 Event{Action::kReference, 1}, // reference tracker1
202 Event{Action::kRelease, 2}, // release tracker2
203 Event{Action::kAssign, 2, 1}, // copy tracker1
204 Event{Action::kMarker, 40}, //
205 Event{Action::kRelease, 1} // destruct ref
206 ));
207 }
208
TEST(RefBase,RefMoveAssignment)209 TEST(RefBase, RefMoveAssignment) {
210 Events events;
211 RefTracker tracker1(1, &events);
212 RefTracker tracker2(2, &events);
213 Ref refA(tracker1);
214 Ref refB(tracker2);
215
216 events.clear();
217 {
218 Ref ref;
219 events.emplace_back(Event{Action::kMarker, 10});
220 ref = std::move(refA);
221 events.emplace_back(Event{Action::kMarker, 20});
222 ref = std::move(refB);
223 events.emplace_back(Event{Action::kMarker, 30});
224 }
225 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kMarker, 10}, //
226 Event{Action::kAssign, 1, 0}, // nullify refA
227 Event{Action::kAssign, 0, 1}, // move into ref
228 Event{Action::kMarker, 20}, //
229 Event{Action::kRelease, 1}, // release tracker1
230 Event{Action::kAssign, 2, 0}, // nullify refB
231 Event{Action::kAssign, 1, 2}, // move into ref
232 Event{Action::kMarker, 30}, //
233 Event{Action::kRelease, 2} // destruct ref
234 ));
235 }
236
TEST(RefBase,RefCopyAssignmentSelf)237 TEST(RefBase, RefCopyAssignmentSelf) {
238 Events events;
239 RefTracker tracker(1, &events);
240 Ref ref(tracker);
241 Ref& self = ref;
242
243 events.clear();
244 {
245 ref = self;
246 ref = self;
247 ref = self;
248 }
249 EXPECT_THAT(events, testing::ElementsAre());
250 }
251
TEST(RefBase,RefMoveAssignmentSelf)252 TEST(RefBase, RefMoveAssignmentSelf) {
253 Events events;
254 RefTracker tracker(1, &events);
255 Ref ref(tracker);
256 Ref& self = ref;
257
258 events.clear();
259 {
260 ref = std::move(self);
261 ref = std::move(self);
262 ref = std::move(self);
263 }
264 EXPECT_THAT(events, testing::ElementsAre());
265 }
266
TEST(RefBase,TCopyAssignment)267 TEST(RefBase, TCopyAssignment) {
268 Events events;
269 RefTracker tracker(1, &events);
270 Ref ref;
271
272 events.clear();
273 {
274 ref = tracker;
275 ref = tracker;
276 ref = tracker;
277 }
278 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kReference, 1}, //
279 Event{Action::kAssign, 0, 1}));
280 }
281
TEST(RefBase,TMoveAssignment)282 TEST(RefBase, TMoveAssignment) {
283 Events events;
284 RefTracker tracker(1, &events);
285 Ref ref;
286
287 events.clear();
288 { ref = std::move(tracker); }
289 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kReference, 1}, //
290 Event{Action::kAssign, 0, 1}));
291 }
292
TEST(RefBase,TCopyAssignmentAlternate)293 TEST(RefBase, TCopyAssignmentAlternate) {
294 Events events;
295 RefTracker tracker1(1, &events);
296 RefTracker tracker2(2, &events);
297 Ref ref;
298
299 events.clear();
300 {
301 ref = tracker1;
302 events.emplace_back(Event{Action::kMarker, 10});
303 ref = tracker2;
304 events.emplace_back(Event{Action::kMarker, 20});
305 ref = tracker1;
306 events.emplace_back(Event{Action::kMarker, 30});
307 }
308 EXPECT_THAT(events, testing::ElementsAre(Event{Action::kReference, 1}, // reference tracker1
309 Event{Action::kAssign, 0, 1}, // copy tracker1
310 Event{Action::kMarker, 10}, //
311 Event{Action::kReference, 2}, // reference tracker2
312 Event{Action::kRelease, 1}, // release tracker1
313 Event{Action::kAssign, 1, 2}, // copy tracker2
314 Event{Action::kMarker, 20}, //
315 Event{Action::kReference, 1}, // reference tracker1
316 Event{Action::kRelease, 2}, // release tracker2
317 Event{Action::kAssign, 2, 1}, // copy tracker1
318 Event{Action::kMarker, 30}));
319 }
320