1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 package org.openjdk.jmc.common.util; 34 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertFalse; 37 import static org.junit.Assert.fail; 38 39 import java.util.Iterator; 40 import java.util.NoSuchElementException; 41 42 import org.junit.Test; 43 import org.junit.experimental.categories.Category; 44 import org.openjdk.jmc.common.collection.BoundedList; 45 import org.openjdk.jmc.common.test.SlowTests; 46 47 public class BoundedListTest { 48 49 private static class ProducerThread implements Runnable { 50 private final BoundedList<Long> list; 51 private volatile boolean shouldStop = false; 52 private long counter; 53 54 public ProducerThread(BoundedList<Long> list) { 55 this.list = list; 56 } 57 58 @Override 59 public void run() { 60 while (!shouldStop) { 61 for (int i = 0; i < 100000; i++) { 62 list.add(counter++); 63 } 64 try { 65 Thread.sleep(1000); 66 } catch (InterruptedException e) { 67 e.printStackTrace(); 68 } 69 Thread.yield(); 70 } 71 } 72 73 public void stop() { 74 shouldStop = true; 75 } 76 } 77 78 private static class ValidationThread implements Runnable { 79 private final BoundedList<Long> list; 80 private volatile boolean shouldStop = false; 81 private volatile long countError = -1; 82 private volatile long sequenceError = -1; 83 private volatile long maxNum; 84 85 public ValidationThread(BoundedList<Long> list) { 86 this.list = list; 87 } 88 89 @Override 90 public void run() { 91 while (!shouldStop) { 92 validate(); 93 } 94 } 95 96 private void validate() { 97 int count = 0; 98 long lastNumber = -1; 99 for (Long l : list) { 100 if (lastNumber != -1 && (l - lastNumber != 1)) { 101 sequenceError = l - lastNumber; 102 } 103 count++; 104 maxNum = Math.max(l, maxNum); 105 } 106 if (count > list.getMaxSize()) { 107 countError = count; 108 } 109 } 110 111 public void stop() { 112 shouldStop = true; 113 } 114 } 115 116 @Test 117 public void testAdd() { 118 BoundedList<Integer> bl = new BoundedList<>(10); 119 bl.add(1); 120 bl.add(2); 121 assertEquals(2, bl.getSize()); 122 assertEquals(10, bl.getMaxSize()); 123 } 124 125 @Test 126 public void testBasicIterator() { 127 BoundedList<Integer> bl = new BoundedList<>(10); 128 bl.add(1); 129 bl.add(2); 130 bl.add(3); 131 132 int val = 1; 133 for (Integer iterVal : bl) { 134 assertEquals(val++, iterVal.intValue()); 135 } 136 assertEquals(4, val); 137 } 138 139 @Test 140 public void testWrappingIterator() { 141 BoundedList<Integer> bl = new BoundedList<>(10); 142 for (int i = 1; i <= 20; i++) { 143 bl.add(i); 144 } 145 146 int val = 11; 147 for (Integer iterVal : bl) { 148 assertEquals(val++, iterVal.intValue()); 149 } 150 } 151 152 @Test 153 public void testEmptyIterator() { 154 BoundedList<Integer> bl = new BoundedList<>(10); 155 assertFalse("Empty list should have no elements!", bl.iterator().hasNext()); //$NON-NLS-1$ 156 try { 157 bl.iterator().next(); 158 fail("next should have generated an exception!"); //$NON-NLS-1$ 159 } catch (NoSuchElementException el) { 160 // Fall through... 161 } 162 } 163 164 @Test 165 public void testMultipleIterators() { 166 // Shows that we can leak memory if we add faster than we can consume... 167 BoundedList<Integer> bl = new BoundedList<>(10); 168 for (int i = 1; i <= 10; i++) { 169 bl.add(i); 170 } 171 Iterator<Integer> iter1 = bl.iterator(); 172 for (int i = 11; i <= 20; i++) { 173 bl.add(i); 174 } 175 Iterator<Integer> iter2 = bl.iterator(); 176 177 int val1 = 1; 178 while (iter1.hasNext()) { 179 assertEquals(val1++, iter1.next().intValue()); 180 } 181 182 int val2 = 11; 183 while (iter2.hasNext()) { 184 assertEquals(val2++, iter2.next().intValue()); 185 } 186 // Iterated through all values, even though we've wrapped. 187 assertEquals(11, val1); 188 assertEquals(21, val2); 189 } 190 191 @Category(value = SlowTests.class) 192 @Test 193 public void testMultiThreadedConsumption() throws InterruptedException { 194 final BoundedList<Long> bl = new BoundedList<>(20); 195 ProducerThread t = new ProducerThread(bl); 196 ValidationThread[] validators = new ValidationThread[10]; 197 new Thread(t, "Producer").start(); //$NON-NLS-1$ 198 for (int i = 0; i < validators.length; i++) { 199 validators[i] = new ValidationThread(bl); 200 new Thread(validators[i], "Validator " + i).start(); //$NON-NLS-1$ 201 } 202 Thread.sleep(30000); 203 for (ValidationThread validator : validators) { 204 validator.stop(); 205 } 206 t.stop(); 207 long maxNo = 0; 208 for (ValidationThread validator : validators) { 209 assertEquals("Failed count validation!", -1, validator.countError); //$NON-NLS-1$ 210 assertEquals("Failed sequence validation!", -1, validator.sequenceError); //$NON-NLS-1$ 211 maxNo = Math.max(maxNo, validator.maxNum); 212 } 213 System.out.println("Allocated up to " + t.counter); //$NON-NLS-1$ 214 System.out.println("Max no was " + maxNo); //$NON-NLS-1$ 215 } 216 217 // FIXME: This test has been commented out for a long time. Check if it is still relevant and either remove or reintroduce it. 218 // @Category(value = SlowTests.class) 219 // @Test 220 // public void testNoLeak() { 221 // BoundedList<Long> bl = new BoundedList<Long>(10); 222 // // Adding a few billion numbers, just to show that we do not leak under normal circumstances... 223 // for (long i = 1; i <= Integer.MAX_VALUE; i++) { 224 // if (i % (Integer.MAX_VALUE / 20) == 0) { 225 // System.out.println(String.format("Passed %.0f%%", (i * 100f) / Integer.MAX_VALUE)); //$NON-NLS-1$ 226 // } 227 // bl.add(i); 228 // } 229 // // On a 32-bit platform we should either crash or pass... 230 // } 231 232 }