PageRenderTime 175ms CodeModel.GetById 11ms app.highlight 151ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/newview/tests/llviewerassetstats_test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 984 lines | 717 code | 195 blank | 72 comment | 69 complexity | 1492ce71f40e5d6fe50b38e4776f3cdd MD5 | raw file
  1/** 
  2 * @file llviewerassetstats_tut.cpp
  3 * @date 2010-10-28
  4 * @brief Test cases for some of newview/llviewerassetstats.cpp
  5 *
  6 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28#include "linden_common.h"
 29
 30#include <tut/tut.hpp>
 31#include <iostream>
 32
 33#include "lltut.h"
 34#include "../llviewerassetstats.h"
 35#include "lluuid.h"
 36#include "llsdutil.h"
 37#include "llregionhandle.h"
 38
 39static const char * all_keys[] = 
 40{
 41	"duration",
 42	"fps",
 43	"get_other",
 44	"get_texture_temp_http",
 45	"get_texture_temp_udp",
 46	"get_texture_non_temp_http",
 47	"get_texture_non_temp_udp",
 48	"get_wearable_udp",
 49	"get_sound_udp",
 50	"get_gesture_udp"
 51};
 52
 53static const char * resp_keys[] = 
 54{
 55	"get_other",
 56	"get_texture_temp_http",
 57	"get_texture_temp_udp",
 58	"get_texture_non_temp_http",
 59	"get_texture_non_temp_udp",
 60	"get_wearable_udp",
 61	"get_sound_udp",
 62	"get_gesture_udp"
 63};
 64
 65static const char * sub_keys[] =
 66{
 67	"dequeued",
 68	"enqueued",
 69	"resp_count",
 70	"resp_max",
 71	"resp_min",
 72	"resp_mean"
 73};
 74
 75static const char * mmm_resp_keys[] = 
 76{
 77	"fps"
 78};
 79
 80static const char * mmm_sub_keys[] =
 81{
 82	"count",
 83	"max",
 84	"min",
 85	"mean"
 86};
 87
 88static const LLUUID region1("4e2d81a3-6263-6ffe-ad5c-8ce04bee07e8");
 89static const LLUUID region2("68762cc8-b68b-4e45-854b-e830734f2d4a");
 90static const U64 region1_handle(0x0000040000003f00ULL);
 91static const U64 region2_handle(0x0000030000004200ULL);
 92static const std::string region1_handle_str("0000040000003f00");
 93static const std::string region2_handle_str("0000030000004200");
 94
 95#if 0
 96static bool
 97is_empty_map(const LLSD & sd)
 98{
 99	return sd.isMap() && 0 == sd.size();
100}
101
102static bool
103is_single_key_map(const LLSD & sd, const std::string & key)
104{
105	return sd.isMap() && 1 == sd.size() && sd.has(key);
106}
107#endif
108
109static bool
110is_double_key_map(const LLSD & sd, const std::string & key1, const std::string & key2)
111{
112	return sd.isMap() && 2 == sd.size() && sd.has(key1) && sd.has(key2);
113}
114
115static bool
116is_no_stats_map(const LLSD & sd)
117{
118	return is_double_key_map(sd, "duration", "regions");
119}
120
121static bool
122is_single_slot_array(const LLSD & sd, U64 region_handle)
123{
124	U32 grid_x(0), grid_y(0);
125	grid_from_region_handle(region_handle, &grid_x, &grid_y);
126	
127	return (sd.isArray() &&
128			1 == sd.size() &&
129			sd[0].has("grid_x") &&
130			sd[0].has("grid_y") &&
131			sd[0]["grid_x"].isInteger() &&
132			sd[0]["grid_y"].isInteger() &&
133			grid_x == sd[0]["grid_x"].asInteger() &&
134			grid_y == sd[0]["grid_y"].asInteger());
135}
136
137static bool
138is_double_slot_array(const LLSD & sd, U64 region_handle1, U64 region_handle2)
139{
140	U32 grid_x1(0), grid_y1(0);
141	U32 grid_x2(0), grid_y2(0);
142	grid_from_region_handle(region_handle1, &grid_x1, &grid_y1);
143	grid_from_region_handle(region_handle2, &grid_x2, &grid_y2);
144	
145	return (sd.isArray() &&
146			2 == sd.size() &&
147			sd[0].has("grid_x") &&
148			sd[0].has("grid_y") &&
149			sd[0]["grid_x"].isInteger() &&
150			sd[0]["grid_y"].isInteger() &&
151			sd[1].has("grid_x") &&
152			sd[1].has("grid_y") &&
153			sd[1]["grid_x"].isInteger() &&
154			sd[1]["grid_y"].isInteger() &&
155			((grid_x1 == sd[0]["grid_x"].asInteger() &&
156			  grid_y1 == sd[0]["grid_y"].asInteger() &&
157			  grid_x2 == sd[1]["grid_x"].asInteger() &&
158			  grid_y2 == sd[1]["grid_y"].asInteger()) ||
159			 (grid_x1 == sd[1]["grid_x"].asInteger() &&
160			  grid_y1 == sd[1]["grid_y"].asInteger() &&
161			  grid_x2 == sd[0]["grid_x"].asInteger() &&
162			  grid_y2 == sd[0]["grid_y"].asInteger())));
163}
164
165static LLSD
166get_region(const LLSD & sd, U64 region_handle1)
167{
168	U32 grid_x(0), grid_y(0);
169	grid_from_region_handle(region_handle1, &grid_x, &grid_y);
170
171	for (LLSD::array_const_iterator it(sd["regions"].beginArray());
172		 sd["regions"].endArray() != it;
173		 ++it)
174	{
175		if ((*it).has("grid_x") &&
176			(*it).has("grid_y") &&
177			(*it)["grid_x"].isInteger() &&
178			(*it)["grid_y"].isInteger() &&
179			(*it)["grid_x"].asInteger() == grid_x &&
180			(*it)["grid_y"].asInteger() == grid_y)
181		{
182			return *it;
183		}
184	}
185	return LLSD();
186}
187
188namespace tut
189{
190	struct tst_viewerassetstats_index
191	{};
192	typedef test_group<tst_viewerassetstats_index> tst_viewerassetstats_index_t;
193	typedef tst_viewerassetstats_index_t::object tst_viewerassetstats_index_object_t;
194	tut::tst_viewerassetstats_index_t tut_tst_viewerassetstats_index("tst_viewerassetstats_test");
195
196	// Testing free functions without global stats allocated
197	template<> template<>
198	void tst_viewerassetstats_index_object_t::test<1>()
199	{
200		// Check that helpers aren't bothered by missing global stats
201		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));
202
203		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
204
205		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
206
207		LLViewerAssetStatsFF::record_response_main(LLViewerAssetType::AT_GESTURE, false, false, 12300000ULL);
208	}
209
210	// Create a non-global instance and check the structure
211	template<> template<>
212	void tst_viewerassetstats_index_object_t::test<2>()
213	{
214		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));
215
216		LLViewerAssetStats * it = new LLViewerAssetStats();
217
218		ensure("Global gViewerAssetStatsMain should still be NULL", (NULL == gViewerAssetStatsMain));
219
220		LLSD sd_full = it->asLLSD(false);
221
222		// Default (NULL) region ID doesn't produce LLSD results so should
223		// get an empty map back from output
224		ensure("Stat-less LLSD initially", is_no_stats_map(sd_full));
225
226		// Once the region is set, we will get a response even with no data collection
227		it->setRegion(region1_handle);
228		sd_full = it->asLLSD(false);
229		ensure("Correct single-key LLSD map root", is_double_key_map(sd_full, "duration", "regions"));
230		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd_full["regions"], region1_handle));
231		
232		LLSD sd = sd_full["regions"][0];
233
234		delete it;
235			
236		// Check the structure of the LLSD
237		for (int i = 0; i < LL_ARRAY_SIZE(all_keys); ++i)
238		{
239			std::string line = llformat("Has '%s' key", all_keys[i]);
240			ensure(line, sd.has(all_keys[i]));
241		}
242
243		for (int i = 0; i < LL_ARRAY_SIZE(resp_keys); ++i)
244		{
245			for (int j = 0; j < LL_ARRAY_SIZE(sub_keys); ++j)
246			{
247				std::string line = llformat("Key '%s' has '%s' key", resp_keys[i], sub_keys[j]);
248				ensure(line, sd[resp_keys[i]].has(sub_keys[j]));
249			}
250		}
251
252		for (int i = 0; i < LL_ARRAY_SIZE(mmm_resp_keys); ++i)
253		{
254			for (int j = 0; j < LL_ARRAY_SIZE(mmm_sub_keys); ++j)
255			{
256				std::string line = llformat("Key '%s' has '%s' key", mmm_resp_keys[i], mmm_sub_keys[j]);
257				ensure(line, sd[mmm_resp_keys[i]].has(mmm_sub_keys[j]));
258			}
259		}
260	}
261
262	// Create a non-global instance and check some content
263	template<> template<>
264	void tst_viewerassetstats_index_object_t::test<3>()
265	{
266		LLViewerAssetStats * it = new LLViewerAssetStats();
267		it->setRegion(region1_handle);
268		
269		LLSD sd = it->asLLSD(false);
270		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
271		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
272		sd = sd[0];
273		
274		delete it;
275
276		// Check a few points on the tree for content
277		ensure("sd[get_texture_temp_http][dequeued] is 0", (0 == sd["get_texture_temp_http"]["dequeued"].asInteger()));
278		ensure("sd[get_sound_udp][resp_min] is 0", (0.0 == sd["get_sound_udp"]["resp_min"].asReal()));
279	}
280
281	// Create a global instance and verify free functions do something useful
282	template<> template<>
283	void tst_viewerassetstats_index_object_t::test<4>()
284	{
285		gViewerAssetStatsMain = new LLViewerAssetStats();
286		LLViewerAssetStatsFF::set_region_main(region1_handle);
287
288		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
289		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
290
291		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
292		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
293
294		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
295		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
296		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
297		sd = sd["regions"][0];
298		
299		// Check a few points on the tree for content
300		ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
301		ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger()));
302		ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger()));
303		ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger()));
304		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
305
306		// Reset and check zeros...
307		// Reset leaves current region in place
308		gViewerAssetStatsMain->reset();
309		sd = gViewerAssetStatsMain->asLLSD(false)["regions"][region1_handle_str];
310		
311		delete gViewerAssetStatsMain;
312		gViewerAssetStatsMain = NULL;
313
314		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
315		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
316	}
317
318	// Create two global instances and verify no interactions
319	template<> template<>
320	void tst_viewerassetstats_index_object_t::test<5>()
321	{
322		gViewerAssetStatsThread1 = new LLViewerAssetStats();
323		gViewerAssetStatsMain = new LLViewerAssetStats();
324		LLViewerAssetStatsFF::set_region_main(region1_handle);
325
326		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
327		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
328
329		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
330		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
331
332		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
333		ensure("Other collector is empty", is_no_stats_map(sd));
334		sd = gViewerAssetStatsMain->asLLSD(false);
335		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
336		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
337		sd = sd["regions"][0];
338		
339		// Check a few points on the tree for content
340		ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
341		ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger()));
342		ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger()));
343		ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger()));
344		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
345
346		// Reset and check zeros...
347		// Reset leaves current region in place
348		gViewerAssetStatsMain->reset();
349		sd = gViewerAssetStatsMain->asLLSD(false)["regions"][0];
350		
351		delete gViewerAssetStatsMain;
352		gViewerAssetStatsMain = NULL;
353		delete gViewerAssetStatsThread1;
354		gViewerAssetStatsThread1 = NULL;
355
356		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
357		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
358	}
359
360    // Check multiple region collection
361	template<> template<>
362	void tst_viewerassetstats_index_object_t::test<6>()
363	{
364		gViewerAssetStatsMain = new LLViewerAssetStats();
365
366		LLViewerAssetStatsFF::set_region_main(region1_handle);
367
368		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
369		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
370
371		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
372		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
373
374		LLViewerAssetStatsFF::set_region_main(region2_handle);
375
376		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
377		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
378		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
379		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
380
381		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
382
383		// std::cout << sd << std::endl;
384		
385		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
386		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
387		LLSD sd1 = get_region(sd, region1_handle);
388		LLSD sd2 = get_region(sd, region2_handle);
389		ensure("Region1 is present in results", sd1.isMap());
390		ensure("Region2 is present in results", sd2.isMap());
391		
392		// Check a few points on the tree for content
393		ensure_equals("sd1[get_texture_non_temp_udp][enqueued] is 1", sd1["get_texture_non_temp_udp"]["enqueued"].asInteger(), 1);
394		ensure_equals("sd1[get_texture_temp_udp][enqueued] is 0", sd1["get_texture_temp_udp"]["enqueued"].asInteger(), 0);
395		ensure_equals("sd1[get_texture_non_temp_http][enqueued] is 0", sd1["get_texture_non_temp_http"]["enqueued"].asInteger(), 0);
396		ensure_equals("sd1[get_texture_temp_http][enqueued] is 0", sd1["get_texture_temp_http"]["enqueued"].asInteger(), 0);
397		ensure_equals("sd1[get_gesture_udp][dequeued] is 0", sd1["get_gesture_udp"]["dequeued"].asInteger(), 0);
398
399		// Check a few points on the tree for content
400		ensure("sd2[get_gesture_udp][enqueued] is 4", (4 == sd2["get_gesture_udp"]["enqueued"].asInteger()));
401		ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger()));
402		ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger()));
403
404		// Reset and check zeros...
405		// Reset leaves current region in place
406		gViewerAssetStatsMain->reset();
407		sd = gViewerAssetStatsMain->asLLSD(false);
408		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
409		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
410		sd2 = sd["regions"][0];
411		
412		delete gViewerAssetStatsMain;
413		gViewerAssetStatsMain = NULL;
414
415		ensure("sd2[get_texture_non_temp_udp][enqueued] is reset", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger()));
416		ensure("sd2[get_gesture_udp][enqueued] is reset", (0 == sd2["get_gesture_udp"]["enqueued"].asInteger()));
417	}
418
419    // Check multiple region collection jumping back-and-forth between regions
420	template<> template<>
421	void tst_viewerassetstats_index_object_t::test<7>()
422	{
423		gViewerAssetStatsMain = new LLViewerAssetStats();
424
425		LLViewerAssetStatsFF::set_region_main(region1_handle);
426
427		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
428		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
429
430		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
431		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
432
433		LLViewerAssetStatsFF::set_region_main(region2_handle);
434
435		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
436		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
437		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
438		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
439
440		LLViewerAssetStatsFF::set_region_main(region1_handle);
441
442		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, true, true);
443		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, true, true);
444
445		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
446		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
447
448		LLViewerAssetStatsFF::set_region_main(region2_handle);
449
450		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
451		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
452		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
453		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
454
455		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
456
457		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
458		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
459		LLSD sd1 = get_region(sd, region1_handle);
460		LLSD sd2 = get_region(sd, region2_handle);
461		ensure("Region1 is present in results", sd1.isMap());
462		ensure("Region2 is present in results", sd2.isMap());
463		
464		// Check a few points on the tree for content
465		ensure("sd1[get_texture_non_temp_udp][enqueued] is 1", (1 == sd1["get_texture_non_temp_udp"]["enqueued"].asInteger()));
466		ensure("sd1[get_texture_temp_udp][enqueued] is 0", (0 == sd1["get_texture_temp_udp"]["enqueued"].asInteger()));
467		ensure("sd1[get_texture_non_temp_http][enqueued] is 0", (0 == sd1["get_texture_non_temp_http"]["enqueued"].asInteger()));
468		ensure("sd1[get_texture_temp_http][enqueued] is 1", (1 == sd1["get_texture_temp_http"]["enqueued"].asInteger()));
469		ensure("sd1[get_gesture_udp][dequeued] is 0", (0 == sd1["get_gesture_udp"]["dequeued"].asInteger()));
470
471		// Check a few points on the tree for content
472		ensure("sd2[get_gesture_udp][enqueued] is 8", (8 == sd2["get_gesture_udp"]["enqueued"].asInteger()));
473		ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger()));
474		ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger()));
475
476		// Reset and check zeros...
477		// Reset leaves current region in place
478		gViewerAssetStatsMain->reset();
479		sd = gViewerAssetStatsMain->asLLSD(false);
480		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
481		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
482		sd2 = get_region(sd, region2_handle);
483		ensure("Region2 is present in results", sd2.isMap());
484		
485		delete gViewerAssetStatsMain;
486		gViewerAssetStatsMain = NULL;
487
488		ensure_equals("sd2[get_texture_non_temp_udp][enqueued] is reset", sd2["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0);
489		ensure_equals("sd2[get_gesture_udp][enqueued] is reset", sd2["get_gesture_udp"]["enqueued"].asInteger(), 0);
490	}
491
492	// Non-texture assets ignore transport and persistence flags
493	template<> template<>
494	void tst_viewerassetstats_index_object_t::test<8>()
495	{
496		gViewerAssetStatsThread1 = new LLViewerAssetStats();
497		gViewerAssetStatsMain = new LLViewerAssetStats();
498		LLViewerAssetStatsFF::set_region_main(region1_handle);
499
500		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
501		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
502
503		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
504		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
505
506		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, true);
507		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, true);
508
509		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, false);
510		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, false);
511
512		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, true);
513		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, true);
514
515		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, false);
516
517		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, true);
518
519		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, false);
520
521		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
522
523		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
524		ensure("Other collector is empty", is_no_stats_map(sd));
525		sd = gViewerAssetStatsMain->asLLSD(false);
526		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
527		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
528		sd = get_region(sd, region1_handle);
529		ensure("Region1 is present in results", sd.isMap());
530		
531		// Check a few points on the tree for content
532		ensure("sd[get_gesture_udp][enqueued] is 0", (0 == sd["get_gesture_udp"]["enqueued"].asInteger()));
533		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
534
535		ensure("sd[get_wearable_udp][enqueued] is 4", (4 == sd["get_wearable_udp"]["enqueued"].asInteger()));
536		ensure("sd[get_wearable_udp][dequeued] is 4", (4 == sd["get_wearable_udp"]["dequeued"].asInteger()));
537
538		ensure("sd[get_other][enqueued] is 4", (4 == sd["get_other"]["enqueued"].asInteger()));
539		ensure("sd[get_other][dequeued] is 0", (0 == sd["get_other"]["dequeued"].asInteger()));
540
541		// Reset and check zeros...
542		// Reset leaves current region in place
543		gViewerAssetStatsMain->reset();
544		sd = get_region(gViewerAssetStatsMain->asLLSD(false), region1_handle);
545		ensure("Region1 is present in results", sd.isMap());
546		
547		delete gViewerAssetStatsMain;
548		gViewerAssetStatsMain = NULL;
549		delete gViewerAssetStatsThread1;
550		gViewerAssetStatsThread1 = NULL;
551
552		ensure_equals("sd[get_texture_non_temp_udp][enqueued] is reset", sd["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0);
553		ensure_equals("sd[get_gesture_udp][dequeued] is reset", sd["get_gesture_udp"]["dequeued"].asInteger(), 0);
554	}
555
556
557	// LLViewerAssetStats::merge() basic functions work
558	template<> template<>
559	void tst_viewerassetstats_index_object_t::test<9>()
560	{
561		LLViewerAssetStats s1;
562		LLViewerAssetStats s2;
563
564		s1.setRegion(region1_handle);
565		s2.setRegion(region1_handle);
566
567		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 5000000);
568		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 6000000);
569		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 8000000);
570		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 7000000);
571		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 9000000);
572		
573		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 2000000);
574		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 3000000);
575		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 4000000);
576
577		s2.merge(s1);
578
579		LLSD s2_llsd = get_region(s2.asLLSD(false), region1_handle);
580		ensure("Region1 is present in results", s2_llsd.isMap());
581		
582		ensure_equals("count after merge", s2_llsd["get_texture_temp_http"]["resp_count"].asInteger(), 8);
583		ensure_approximately_equals("min after merge", s2_llsd["get_texture_temp_http"]["resp_min"].asReal(), 2.0, 22);
584		ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_max"].asReal(), 9.0, 22);
585		ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_mean"].asReal(), 5.5, 22);
586	}
587
588	// LLViewerAssetStats::merge() basic functions work without corrupting source data
589	template<> template<>
590	void tst_viewerassetstats_index_object_t::test<10>()
591	{
592		LLViewerAssetStats s1;
593		LLViewerAssetStats s2;
594
595		s1.setRegion(region1_handle);
596		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
597		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
598		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
599		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
600
601		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
602		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
603		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
604		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
605
606		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200);
607		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900);
608
609		
610		s2.setRegion(region2_handle);
611		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
612		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
613		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
614		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
615		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
616		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
617		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
618		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
619
620		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
621		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
622		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
623		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
624		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
625		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
626		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
627
628		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000);
629		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000);
630
631		{
632			s2.merge(s1);
633			
634			LLSD src = s1.asLLSD(false);
635			LLSD dst = s2.asLLSD(false);
636
637			ensure_equals("merge src has single region", src["regions"].size(), 1);
638			ensure_equals("merge dst has dual regions", dst["regions"].size(), 2);
639			
640			// Remove time stamps, they're a problem
641			src.erase("duration");
642			src["regions"][0].erase("duration");
643			dst.erase("duration");
644			dst["regions"][0].erase("duration");
645			dst["regions"][1].erase("duration");
646
647			LLSD s1_llsd = get_region(src, region1_handle);
648			ensure("Region1 is present in src", s1_llsd.isMap());
649			LLSD s2_llsd = get_region(dst, region1_handle);
650			ensure("Region1 is present in dst", s2_llsd.isMap());
651
652			ensure("result from src is in dst", llsd_equals(s1_llsd, s2_llsd));
653		}
654
655		s1.setRegion(region1_handle);
656		s2.setRegion(region1_handle);
657		s1.reset();
658		s2.reset();
659		
660		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
661		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
662		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
663		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
664
665		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
666		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
667		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
668		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
669
670		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200);
671		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900);
672
673		
674		s2.setRegion(region1_handle);
675		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
676		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
677		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
678		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
679		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
680		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
681		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
682		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
683
684		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
685		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
686		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
687		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
688		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
689		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
690		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
691
692		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000);
693		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000);
694
695		{
696			s2.merge(s1);
697			
698			LLSD src = s1.asLLSD(false);
699			LLSD dst = s2.asLLSD(false);
700
701			ensure_equals("merge src has single region (p2)", src["regions"].size(), 1);
702			ensure_equals("merge dst has single region (p2)", dst["regions"].size(), 1);
703
704			// Remove time stamps, they're a problem
705			src.erase("duration");
706			src["regions"][0].erase("duration");
707			dst.erase("duration");
708			dst["regions"][0].erase("duration");
709			
710			LLSD s1_llsd = get_region(src, region1_handle);
711			ensure("Region1 is present in src", s1_llsd.isMap());
712			LLSD s2_llsd = get_region(dst, region1_handle);
713			ensure("Region1 is present in dst", s2_llsd.isMap());
714
715			ensure_equals("src counts okay (enq)", s1_llsd["get_other"]["enqueued"].asInteger(), 4);
716			ensure_equals("src counts okay (deq)", s1_llsd["get_other"]["dequeued"].asInteger(), 4);
717			ensure_equals("src resp counts okay", s1_llsd["get_other"]["resp_count"].asInteger(), 2);
718			ensure_approximately_equals("src respmin okay", s1_llsd["get_other"]["resp_min"].asReal(), 0.2829, 20);
719			ensure_approximately_equals("src respmax okay", s1_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20);
720			
721			ensure_equals("dst counts okay (enq)", s2_llsd["get_other"]["enqueued"].asInteger(), 12);
722			ensure_equals("src counts okay (deq)", s2_llsd["get_other"]["dequeued"].asInteger(), 11);
723			ensure_equals("dst resp counts okay", s2_llsd["get_other"]["resp_count"].asInteger(), 4);
724			ensure_approximately_equals("dst respmin okay", s2_llsd["get_other"]["resp_min"].asReal(), 0.010, 20);
725			ensure_approximately_equals("dst respmax okay", s2_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20);
726		}
727	}
728
729
730    // Maximum merges are interesting when one side contributes nothing
731	template<> template<>
732	void tst_viewerassetstats_index_object_t::test<11>()
733	{
734		LLViewerAssetStats s1;
735		LLViewerAssetStats s2;
736
737		s1.setRegion(region1_handle);
738		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
739		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
740		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
741		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
742
743		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
744		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
745		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
746		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
747
748		// Want to test negative numbers here but have to work in U64
749		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
750		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
751		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
752
753		s2.setRegion(region1_handle);
754		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
755		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
756		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
757		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
758		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
759		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
760		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
761		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
762
763		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
764		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
765		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
766		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
767		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
768		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
769		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
770
771		{
772			s2.merge(s1);
773			
774			LLSD src = s1.asLLSD(false);
775			LLSD dst = s2.asLLSD(false);
776
777			ensure_equals("merge src has single region", src["regions"].size(), 1);
778			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
779			
780			// Remove time stamps, they're a problem
781			src.erase("duration");
782			src["regions"][0].erase("duration");
783			dst.erase("duration");
784			dst["regions"][0].erase("duration");
785
786			LLSD s2_llsd = get_region(dst, region1_handle);
787			ensure("Region1 is present in dst", s2_llsd.isMap());
788			
789			ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
790
791			ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum",
792										s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20);
793		}
794
795		// Other way around
796		s1.setRegion(region1_handle);
797		s2.setRegion(region1_handle);
798		s1.reset();
799		s2.reset();
800
801		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
802		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
803		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
804		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
805
806		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
807		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
808		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
809		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
810
811		// Want to test negative numbers here but have to work in U64
812		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
813		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
814		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
815
816		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
817		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
818		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
819		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
820		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
821		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
822		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
823		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
824
825		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
826		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
827		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
828		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
829		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
830		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
831		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
832
833		{
834			s1.merge(s2);
835			
836			LLSD src = s2.asLLSD(false);
837			LLSD dst = s1.asLLSD(false);
838
839			ensure_equals("merge src has single region", src["regions"].size(), 1);
840			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
841			
842			// Remove time stamps, they're a problem
843			src.erase("duration");
844			src["regions"][0].erase("duration");
845			dst.erase("duration");
846			dst["regions"][0].erase("duration");
847
848			LLSD s2_llsd = get_region(dst, region1_handle);
849			ensure("Region1 is present in dst", s2_llsd.isMap());
850
851			ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
852
853			ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum (flipped)",
854										s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20);
855		}
856	}
857
858    // Minimum merges are interesting when one side contributes nothing
859	template<> template<>
860	void tst_viewerassetstats_index_object_t::test<12>()
861	{
862		LLViewerAssetStats s1;
863		LLViewerAssetStats s2;
864
865		s1.setRegion(region1_handle);
866		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
867		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
868		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
869		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
870
871		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
872		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
873		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
874		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
875
876		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000);
877		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000);
878		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000);
879
880		s2.setRegion(region1_handle);
881		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
882		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
883		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
884		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
885		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
886		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
887		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
888		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
889
890		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
891		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
892		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
893		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
894		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
895		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
896		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
897
898		{
899			s2.merge(s1);
900			
901			LLSD src = s1.asLLSD(false);
902			LLSD dst = s2.asLLSD(false);
903
904			ensure_equals("merge src has single region", src["regions"].size(), 1);
905			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
906			
907			// Remove time stamps, they're a problem
908			src.erase("duration");
909			src["regions"][0].erase("duration");
910			dst.erase("duration");
911			dst["regions"][0].erase("duration");
912
913			LLSD s2_llsd = get_region(dst, region1_handle);
914			ensure("Region1 is present in dst", s2_llsd.isMap());
915
916			ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
917
918			ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum",
919										s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20);
920		}
921
922		// Other way around
923		s1.setRegion(region1_handle);
924		s2.setRegion(region1_handle);
925		s1.reset();
926		s2.reset();
927
928		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
929		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
930		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
931		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
932
933		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
934		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
935		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
936		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
937
938		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000);
939		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000);
940		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000);
941
942		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
943		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
944		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
945		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
946		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
947		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
948		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
949		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
950
951		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
952		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
953		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
954		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
955		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
956		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
957		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
958
959		{
960			s1.merge(s2);
961			
962			LLSD src = s2.asLLSD(false);
963			LLSD dst = s1.asLLSD(false);
964
965			ensure_equals("merge src has single region", src["regions"].size(), 1);
966			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
967			
968			// Remove time stamps, they're a problem
969			src.erase("duration");
970			src["regions"][0].erase("duration");
971			dst.erase("duration");
972			dst["regions"][0].erase("duration");
973
974			LLSD s2_llsd = get_region(dst, region1_handle);
975			ensure("Region1 is present in dst", s2_llsd.isMap());
976
977			ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
978
979			ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum (flipped)",
980										s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20);
981		}
982	}
983
984}