From ba6a2e3fd2ca937b1027345c1638a0c2c0527110 Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Sun, 13 Jun 2004 20:03:21 +0000
Subject: [PATCH] unit test for the bandwidth limiting functionality
 (TrivialBandwidthLimiter, BandwidthLimiter,
 BandwidthLimited{In,Out}putStream)

---
 .../transport/BandwidthLimiterTest.java       | 209 ++++++++++++++++++
 .../i2p/router/transport/FakeInputStream.java |  27 +++
 .../router/transport/NullOutputStream.java    |  11 +
 3 files changed, 247 insertions(+)
 create mode 100644 router/java/test/net/i2p/router/transport/BandwidthLimiterTest.java
 create mode 100644 router/java/test/net/i2p/router/transport/FakeInputStream.java
 create mode 100644 router/java/test/net/i2p/router/transport/NullOutputStream.java

diff --git a/router/java/test/net/i2p/router/transport/BandwidthLimiterTest.java b/router/java/test/net/i2p/router/transport/BandwidthLimiterTest.java
new file mode 100644
index 0000000000..23ae110454
--- /dev/null
+++ b/router/java/test/net/i2p/router/transport/BandwidthLimiterTest.java
@@ -0,0 +1,209 @@
+package net.i2p.router.transport;
+
+import net.i2p.router.RouterContext;
+import net.i2p.util.Log;
+
+import java.io.IOException;
+
+import java.util.Random;
+import java.util.Properties;
+
+/**
+ * Stress out the bandwidth limiter by running a series of push and pull tests
+ * through bandwidth limited streams.  This includes pushing data through 
+ * unthrottled streams, through streams throttled at 4KBps, 32KBps, and 256KBps, 
+ * pulling data through those same rates, as well as doing so with 10 concurrent
+ * threads (and, in turn, 10 concurrent streams all using the same BandwidthLimiter).
+ *
+ * Note: this takes a long time to run (~1 hour) since the 4KBps push/pull of 1MB with
+ * 10 concurrent threads is, well, slow.
+ *
+ */
+public class BandwidthLimiterTest {
+    private RouterContext _context;
+    private Log _log;
+    private final static int NUM_MB = 1;
+    
+    public BandwidthLimiterTest() {
+        _context = new RouterContext(null);
+        _log = _context.logManager().getLog(BandwidthLimiterTest.class);
+        //_context.jobQueue().runQueue(1);
+    }
+    
+    public void prepareLimiter(int inKBps, int outKBps, int inBurst, int outBurst) {
+        Properties props = new Properties();
+        props.setProperty(TrivialBandwidthLimiter.PROP_INBOUND_BANDWIDTH, ""+inKBps);
+        props.setProperty(TrivialBandwidthLimiter.PROP_OUTBOUND_BANDWIDTH, ""+outKBps);
+        props.setProperty(TrivialBandwidthLimiter.PROP_INBOUND_BANDWIDTH_PEAK, ""+inBurst);
+        props.setProperty(TrivialBandwidthLimiter.PROP_OUTBOUND_BANDWIDTH_PEAK, ""+outBurst);
+        //props.setProperty(TrivialBandwidthLimiter.PROP_REPLENISH_FREQUENCY, ""+10*1000);
+        System.setProperties(props);
+        ((TrivialBandwidthLimiter)_context.bandwidthLimiter()).reinitialize();
+    }
+    
+    /**
+     * Using the configured limiter, determine how long it takes to shove 
+     * numBytes through a BandwidthLimitedOutputStream (broken up into numBytesPerWrite)
+     * chunks.
+     *
+     */
+    public long testOutboundThrottle(int numBytes, int numBytesPerWrite) {
+        byte source[] = new byte[numBytesPerWrite];
+        new Random().nextBytes(source);
+        NullOutputStream target = new NullOutputStream();
+        BandwidthLimitedOutputStream out = new BandwidthLimitedOutputStream(_context, target, null);
+        long before = System.currentTimeMillis();
+        try {
+            for (int i = 0; i < numBytes; i += numBytesPerWrite) {
+                int num = numBytesPerWrite;
+                if (numBytesPerWrite + i >= numBytes)
+                    num = numBytes - i;
+                //_log.info("** Writing " + num + " bytes starting at " + i);
+                out.write(source, 0, num);
+            }
+        } catch (IOException ioe) {}
+        long after = System.currentTimeMillis();
+        return after-before;
+    }
+    
+    /**
+     * Using the configured limiter, determine how long it takes to read 
+     * numBytes through a BandwidthLimitedInputStream (broken up into numBytesPerRead)
+     * chunks.
+     *
+     */
+    public long testInboundThrottle(int numBytes, int numBytesPerRead) {
+        FakeInputStream source = new FakeInputStream(numBytes);
+        BandwidthLimitedInputStream in = new BandwidthLimitedInputStream(_context, source, null);
+        long before = System.currentTimeMillis();
+        try {
+            byte buf[] = new byte[numBytesPerRead];
+            int read = 0;
+            while ( (read = in.read(buf)) != -1) {
+                //_log.info("** Read " + read + " bytes");
+                // gobble the data.  who cares
+            }
+        } catch (IOException ioe) {}
+        long after = System.currentTimeMillis();
+        return after-before;
+    }
+    
+    /**
+     * Run a series of tests on outbound throttling (shoving lots of data through pipes 
+     * with various limits) and log the times.
+     *
+     */
+    public void testOutbound() {
+        prepareLimiter(-1, -1, -1, -1);
+        long ms = testOutboundThrottle(NUM_MB*1024*1024, 32*1024);
+        _log.info("** Unlimited pushed " + NUM_MB + "MB in " + ms + "ms");
+        prepareLimiter(-1, 4, -1, 5*1024*1024);
+        ms = testOutboundThrottle(NUM_MB*1024*1024, 32*1024);
+        _log.info("** 4KBps pushed " + NUM_MB + "MB in " + ms + "ms");
+        prepareLimiter(-1, 32, -1, 5*1024*1024);
+        ms = testOutboundThrottle(NUM_MB*1024*1024, 32*1024);
+        _log.info("** 32KBps pushed " + NUM_MB + "MB in " + ms + "ms");
+        prepareLimiter(-1, 256, -1, 5*1024*1024);
+        ms = testOutboundThrottle(NUM_MB*1024*1024, 256*1024);
+        _log.info("** 256KBps pushed " + NUM_MB + "MB in " + ms + "ms");
+    }
+    
+    /**
+     * Run a series of tests on inbound throttling (pulling lots of data through pipes 
+     * with various limits) and log the times.
+     *
+     */
+    public void testInbound() {
+        prepareLimiter(-1, -1, -1, -1);
+        long ms = testInboundThrottle(NUM_MB*1024*1024, 32*1024);
+        _log.info("** Unlimited pulled " + NUM_MB + "MB in " + ms + "ms");
+        prepareLimiter(4, -1, 5*1024*1024, -1);
+        ms = testInboundThrottle(NUM_MB*1024*1024, 32*1024);
+        _log.info("** 4KBps pulled " + NUM_MB + "MB in " + ms + "ms");
+        prepareLimiter(32, -1, 5*1024*1024, -1);
+        ms = testInboundThrottle(NUM_MB*1024*1024, 32*1024);
+        _log.info("** 32KBps pulled " + NUM_MB + "MB in " + ms + "ms");
+        prepareLimiter(256, -1, 5*1024*1024, -1);
+        ms = testInboundThrottle(NUM_MB*1024*1024, 256*1024);
+        _log.info("** 256KBps pulled " + NUM_MB + "MB in " + ms + "ms");
+    }
+    
+    
+    public void testOutboundContention() {
+        prepareLimiter(-1, -1, -1, -1);
+        long start = System.currentTimeMillis();
+        long runningTimes[] = testOutboundContention(10, NUM_MB*1024*1024);
+        long end = System.currentTimeMillis();
+        _log.info("** Done with unlimited " + NUM_MB + "MB test with 10 concurrent threads after " + (end-start) + "ms: " + displayTimes(runningTimes));
+        prepareLimiter(-1, 4, -1, 5*1024*1024);
+        start = System.currentTimeMillis();
+        runningTimes = testOutboundContention(10, NUM_MB*1024*1024);
+        end = System.currentTimeMillis();
+        _log.info("** Done with 4KBps " + NUM_MB + "MB test with 10 concurrent threads after " + (end-start) + "ms: " + displayTimes(runningTimes));
+        prepareLimiter(-1, 32, -1, 5*1024*1024);
+        start = System.currentTimeMillis();
+        runningTimes = testOutboundContention(10, NUM_MB*1024*1024);
+        end = System.currentTimeMillis();
+        _log.info("** Done with 32KBps " + NUM_MB + "MB test with 10 concurrent threads after " + (end-start) + "ms: " + displayTimes(runningTimes));
+        prepareLimiter(-1, 256, -1, 5*1024*1024);
+        start = System.currentTimeMillis();
+        runningTimes = testOutboundContention(10, NUM_MB*1024*1024);
+        end = System.currentTimeMillis();
+        _log.info("** Done with 256KBps " + NUM_MB + "MB test with 10 concurrent threads after " + (end-start) + "ms: " + displayTimes(runningTimes));
+    }
+    
+    private String displayTimes(long times[]) {
+        StringBuffer rv = new StringBuffer();
+        for (int i = 0; i < times.length; i++) {
+            rv.append(times[i]);
+            if (i + 1 <= times.length)
+                rv.append(' ');
+        }
+        return rv.toString();
+    }
+    
+    private long[] testOutboundContention(int numConcurrent, int numBytes) {
+        OutboundRunner threads[] = new OutboundRunner[numConcurrent];
+        for (int i = 0; i < numConcurrent; i++) {
+            threads[i] = new OutboundRunner(numBytes);
+        }
+        _log.debug("Starting up outbound contention test for " + numBytes + " with " + numConcurrent + " runners");
+        for (int i = 0; i < numConcurrent; i++)
+            threads[i].start();
+        for (int i = 0; i < numConcurrent; i++) {
+            try {
+                threads[i].join();
+            } catch (InterruptedException ie) {}
+        }
+        long rv[] = new long[numConcurrent];
+        for (int i = 0; i < numConcurrent; i++)
+            rv[i] = threads[i].getRunningTime();
+        return rv;
+    }
+
+    private static int __runnerNum = 0;
+    private class OutboundRunner extends Thread {
+        private int _numBytes;
+        private int _runnerNum;
+        private long _runningTime;
+        public OutboundRunner(int numBytes) {
+            _numBytes = numBytes;
+            _runnerNum = ++__runnerNum;
+        }
+        public void run() {
+            Thread.currentThread().setName("Out" + _runnerNum);
+            _runningTime = testOutboundThrottle(_numBytes, 8*1024);
+            _log.debug("Outbound runner " + _runnerNum + " pushed " + _numBytes + " in " + _runningTime + "ms");
+        }
+        public long getRunningTime() { return _runningTime; }
+    }
+        
+    
+    public static void main(String args[]) {
+        BandwidthLimiterTest test = new BandwidthLimiterTest();
+        test.testOutbound();
+        test.testInbound();
+        test.testOutboundContention();
+        System.exit(0);
+    }
+}
\ No newline at end of file
diff --git a/router/java/test/net/i2p/router/transport/FakeInputStream.java b/router/java/test/net/i2p/router/transport/FakeInputStream.java
new file mode 100644
index 0000000000..e08ddb1481
--- /dev/null
+++ b/router/java/test/net/i2p/router/transport/FakeInputStream.java
@@ -0,0 +1,27 @@
+package net.i2p.router.transport;
+
+import java.io.InputStream;
+
+/**
+ * Read up to a specified number of bytes, then EOF.
+ * Uses pretty much no memory.
+ *
+ */
+public class FakeInputStream extends InputStream {
+    private volatile int _numRead;
+    private int _size;
+    
+    public FakeInputStream(int size) {
+        _size = size;
+        _numRead = 0;
+    }
+    public int read() {
+        int rv = 0;
+        if (_numRead >= _size) 
+            rv = -1;
+        else
+            rv = 42;
+        _numRead++;
+        return rv;
+    }
+}
\ No newline at end of file
diff --git a/router/java/test/net/i2p/router/transport/NullOutputStream.java b/router/java/test/net/i2p/router/transport/NullOutputStream.java
new file mode 100644
index 0000000000..d878113c36
--- /dev/null
+++ b/router/java/test/net/i2p/router/transport/NullOutputStream.java
@@ -0,0 +1,11 @@
+package net.i2p.router.transport;
+
+import java.io.OutputStream;
+
+/**
+ * Output stream for when we don't care whats written
+ *
+ */
+public class NullOutputStream extends OutputStream {
+    public void write(int param) {}
+}
\ No newline at end of file
-- 
GitLab