diff options
author | mikaelpeltier <mikaelpeltier@google.com> | 2015-06-24 14:31:11 +0200 |
---|---|---|
committer | Mikael Peltier <mikaelpeltier@google.com> | 2015-06-24 14:59:36 +0000 |
commit | 04563874ddaac702d6c715eaa89c29b253f4c54e (patch) | |
tree | c305fa98670c3e80be494cc054a8e31b51bfe7f2 /simple/simple-common/src/test/java/org/simpleframework | |
parent | f1828481ebcfee3bddc323fca178a4502a60ceef (diff) | |
download | toolchain_jack-04563874ddaac702d6c715eaa89c29b253f4c54e.zip toolchain_jack-04563874ddaac702d6c715eaa89c29b253f4c54e.tar.gz toolchain_jack-04563874ddaac702d6c715eaa89c29b253f4c54e.tar.bz2 |
Add simpleframework source files
Change-Id: I18d01df16de2868ca5458f79a88e6070b75db2c3
(cherry picked from commit 3e9f84cf7b22f6970eb8041ca38d12d75c6bb270)
Diffstat (limited to 'simple/simple-common/src/test/java/org/simpleframework')
19 files changed, 1442 insertions, 0 deletions
diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/KeyTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/KeyTest.java new file mode 100644 index 0000000..f9056fe --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/KeyTest.java @@ -0,0 +1,195 @@ +package org.simpleframework.common; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * Test for fast case insensitive mapping for headers that have been taken + * from the request HTTP header or added to the response HTTP header. + * + * @author Niall Gallagher + */ +public class KeyTest extends TestCase { + + public class Index implements Name { + + private final String value; + + public Index(String value) { + this.value = value.toLowerCase(); + } + + public int hashCode() { + return value.hashCode(); + } + + public boolean equals(Object key) { + if(key instanceof Name) { + return key.equals(value); + } + if(key instanceof String) { + return key.equals(value); + } + return false; + } + } + + public interface Name { + + public int hashCode(); + public boolean equals(Object value); + } + + public class ArrayName implements Name { + + private String cache; + private byte[] array; + private int off; + private int size; + private int hash; + + public ArrayName(byte[] array) { + this(array, 0, array.length); + } + + public ArrayName(byte[] array, int off, int size) { + this.array = array; + this.size = size; + this.off = off; + } + + public boolean equals(Object value) { + if(value instanceof String) { + String text = value.toString(); + + return equals(text); + } + return false; + } + + public boolean equals(String value) { + int length = value.length(); + + if(length != size) { + return false; + } + for(int i = 0; i < size; i++) { + int left = value.charAt(i); + int right = array[off + i]; + + if(right >= 'A' && right <= 'Z') { + right = (right - 'A') + 'a'; + } + if(left != right) { + return false; + } + } + return true; + } + + public int hashCode() { + int code = hash; + + if(code == 0) { + int pos = off; + + for(int i = 0; i < size; i++) { + int next = array[pos++]; + + if(next >= 'A' && next <= 'Z') { + next = (next - 'A') + 'a'; + } + code = 31*code + next; + } + hash = code; + } + return code; + } + } + + public class StringName implements Name { + + private final String value; + private final String key; + + public StringName(String value) { + this.key = value.toLowerCase(); + this.value = value; + } + + public int hashCode() { + return key.hashCode(); + } + + public boolean equals(Object value) { + return value.equals(key); + } + } + + public class NameTable<T> { + + private final Map<Name, T> map; + + public NameTable() { + this.map = new HashMap<Name, T>(); + } + + public void put(Name key, T value) { + map.put(key, value); + } + + public void put(String text, T value) { + Name key = new StringName(text); + + map.put(key, value); + } + + public T get(String key) { + Index index = new Index(key); + + return map.get(index); + } + + public T remove(String key) { + Index index = new Index(key); + + return map.remove(index); + } + } + + public void testName() { + Name contentLength = new ArrayName("Content-Length".getBytes()); + Name contentType = new ArrayName("Content-Type".getBytes()); + Name transferEncoding = new ArrayName("Transfer-Encoding".getBytes()); + Name userAgent = new ArrayName("User-Agent".getBytes()); + NameTable<String> map = new NameTable<String>(); + + assertEquals(contentLength.hashCode(), "Content-Length".toLowerCase().hashCode()); + assertEquals(contentType.hashCode(), "Content-Type".toLowerCase().hashCode()); + assertEquals(transferEncoding.hashCode(), "Transfer-Encoding".toLowerCase().hashCode()); + assertEquals(userAgent.hashCode(), "User-Agent".toLowerCase().hashCode()); + + map.put(contentLength, "1024"); + map.put(contentType, "text/html"); + map.put(transferEncoding, "chunked"); + map.put(userAgent, "Mozilla/4.0"); + map.put("Date", "18/11/1977"); + map.put("Accept", "text/plain, text/html, image/gif"); + + assertEquals(map.get("Content-Length"), "1024"); + assertEquals(map.get("CONTENT-LENGTH"), "1024"); + assertEquals(map.get("content-length"), "1024"); + assertEquals(map.get("Content-length"), "1024"); + assertEquals(map.get("Content-Type"), "text/html"); + assertEquals(map.get("Transfer-Encoding"), "chunked"); + assertEquals(map.get("USER-AGENT"), "Mozilla/4.0"); + assertEquals(map.get("Accept"), "text/plain, text/html, image/gif"); + assertEquals(map.get("ACCEPT"), "text/plain, text/html, image/gif"); + assertEquals(map.get("accept"), "text/plain, text/html, image/gif"); + assertEquals(map.get("DATE"), "18/11/1977"); + assertEquals(map.get("Date"), "18/11/1977"); + assertEquals(map.get("date"), "18/11/1977"); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/ArrayBufferTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/ArrayBufferTest.java new file mode 100644 index 0000000..1f6f494 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/ArrayBufferTest.java @@ -0,0 +1,54 @@ +package org.simpleframework.common.buffer; + +import org.simpleframework.common.buffer.ArrayBuffer; +import org.simpleframework.common.buffer.Buffer; + +import junit.framework.TestCase; + +public class ArrayBufferTest extends TestCase { + + public void testBuffer() throws Exception { + Buffer buffer = new ArrayBuffer(1, 2); + + buffer.append(new byte[]{'a'}).append(new byte[]{'b'}); + + assertEquals(buffer.encode(), "ab"); + assertEquals(buffer.encode("ISO-8859-1"), "ab"); + + boolean overflow = false; + + try { + buffer.append(new byte[]{'c'}); + } catch(Exception e) { + overflow = true; + } + assertTrue(overflow); + + buffer.clear(); + + assertEquals(buffer.encode(), ""); + assertEquals(buffer.encode("UTF-8"), ""); + + buffer = new ArrayBuffer(1024, 2048); + buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); + + Buffer alphabet = buffer.allocate(); + alphabet.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); + + Buffer digits = buffer.allocate(); + digits.append("0123456789".getBytes()); + + assertEquals(alphabet.encode(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + assertEquals(digits.encode(), "0123456789"); + assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + + Buffer extra = digits.allocate(); + extra.append("#@?".getBytes()); + + assertEquals(extra.encode(), "#@?"); + assertEquals(digits.encode(), "0123456789#@?"); + assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@?"); + assertEquals(buffer.length(), 65); + } + +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/BufferAllocatorTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/BufferAllocatorTest.java new file mode 100644 index 0000000..9584047 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/BufferAllocatorTest.java @@ -0,0 +1,79 @@ +package org.simpleframework.common.buffer; + +import org.simpleframework.common.buffer.Allocator; +import org.simpleframework.common.buffer.ArrayAllocator; +import org.simpleframework.common.buffer.Buffer; +import org.simpleframework.common.buffer.BufferAllocator; + +import junit.framework.TestCase; + +public class BufferAllocatorTest extends TestCase { + + public void testBuffer() throws Exception { + Allocator allocator = new ArrayAllocator(1, 2); + Buffer buffer = new BufferAllocator(allocator, 1, 2); + + buffer.append(new byte[]{'a'}).append(new byte[]{'b'}); + + assertEquals(buffer.encode(), "ab"); + assertEquals(buffer.encode("ISO-8859-1"), "ab"); + + boolean overflow = false; + + try { + buffer.append(new byte[]{'c'}); + } catch(Exception e) { + overflow = true; + } + assertTrue(overflow); + + buffer.clear(); + + assertEquals(buffer.encode(), ""); + assertEquals(buffer.encode("UTF-8"), ""); + + allocator = new ArrayAllocator(1024, 2048); + buffer = new BufferAllocator(allocator, 1024, 2048); + buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); + + Buffer alphabet = buffer.allocate(); + alphabet.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); + + Buffer digits = buffer.allocate(); + digits.append("0123456789".getBytes()); + + assertEquals(alphabet.encode(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + assertEquals(digits.encode(), "0123456789"); + assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + + Buffer extra = digits.allocate(); + extra.append("#@?".getBytes()); + + assertEquals(extra.encode(), "#@?"); + assertEquals(extra.length(), 3); + assertEquals(digits.encode(), "0123456789#@?"); + assertEquals(digits.length(), 13); + assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@?"); + assertEquals(buffer.length(), 65); + } + + public void testCascadingBufferAllocator() throws Exception { + Allocator allocator = new ArrayAllocator(1024, 2048); + allocator = new BufferAllocator(allocator, 1024, 2048); + allocator = new BufferAllocator(allocator, 1024, 2048); + allocator = new BufferAllocator(allocator, 1024, 2048); + allocator = new BufferAllocator(allocator, 1024, 2048); + + Buffer buffer = allocator.allocate(1024); + + buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); + + assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyz"); + + buffer.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); + + assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + assertEquals(buffer.length(), 52); + } + +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileBufferTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileBufferTest.java new file mode 100644 index 0000000..54cb142 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileBufferTest.java @@ -0,0 +1,45 @@ +package org.simpleframework.common.buffer; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import org.simpleframework.common.buffer.Buffer; +import org.simpleframework.common.buffer.FileBuffer; + +import junit.framework.TestCase; + +public class FileBufferTest extends TestCase { + + public void testFileBuffer() throws Exception { + File tempFile = File.createTempFile(FileBufferTest.class.getSimpleName(), null); + Buffer buffer = new FileBuffer(tempFile); + buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); + + Buffer alphabet = buffer.allocate(); + alphabet.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); + + Buffer digits = buffer.allocate(); + digits.append("0123456789".getBytes()); + + expect(buffer, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes()); + expect(alphabet, "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); + expect(digits, "0123456789".getBytes()); + } + + private void expect(Buffer buffer, byte[] expect) throws IOException { + InputStream result = buffer.open(); + + for(int i =0; i < expect.length; i++) { + byte octet = expect[i]; + int value = result.read(); + + if(value < 0) { + throw new IOException("Buffer exhausted too early"); + } + assertEquals(octet, (byte)value); + } + assertEquals(-1, result.read()); + } + +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileByteQueue.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileByteQueue.java new file mode 100644 index 0000000..b404562 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileByteQueue.java @@ -0,0 +1,109 @@ +package org.simpleframework.common.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.simpleframework.common.buffer.Allocator; +import org.simpleframework.common.buffer.Buffer; +import org.simpleframework.common.buffer.BufferAllocator; + +public class FileByteQueue { + + private BlockingQueue<Block> blocks; + private BlockAllocator allocator; + private Block source; + + public FileByteQueue(Allocator allocator) throws IOException { + this.blocks = new LinkedBlockingQueue<Block>(); + this.allocator = new BlockAllocator(allocator); + } + + public int read(byte[] array, int off, int size) throws Exception { + int left = blocks.size(); + int mark = size; + + for(int i = 0; source != null || i < left; i++) { + if(source == null) { + source = blocks.take(); + } + int remain = source.remaining(); + int read = Math.min(remain, size); + + if(read > 0) { + source.read(array, off, size); + size -= read; + off += read; + } + if(remain == 0) { + source.close(); // clear up file handles + source = null; + } + if(size <= 0) { + return mark; + } + } + return mark - size; + } + + public void write(byte[] array, int off, int size) throws Exception { + Block buffer = allocator.allocate(array, off, size); + + if(size > 0) { + blocks.offer(buffer); + } + } + + private class BlockAllocator { + + private Allocator allocator; + + public BlockAllocator(Allocator allocator) { + this.allocator = new BufferAllocator(allocator); + } + + public Block allocate(byte[] array, int off, int size) throws IOException { + Buffer buffer = allocator.allocate(); + + if(size > 0) { + buffer.append(array, off, size); + } + return new Block(buffer, size); + } + } + + private class Block { + + private InputStream source; + private int remaining; + private int size; + + public Block(Buffer buffer, int size) throws IOException { + this.source = buffer.open(); + this.remaining = size; + this.size = size; + } + + public int read(byte[] array, int off, int size) throws IOException { + int count = source.read(array, off, size); + + if(count > 0) { + remaining -= size; + } + return count; + } + + public void close() throws IOException { + source.close(); + } + + public int remaining() { + return remaining; + } + + public int size() { + return size; + } + } +}
\ No newline at end of file diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileByteQueueTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileByteQueueTest.java new file mode 100644 index 0000000..9699454 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/FileByteQueueTest.java @@ -0,0 +1,22 @@ +package org.simpleframework.common.buffer; + +import junit.framework.TestCase; + +public class FileByteQueueTest extends TestCase { + + public void testQueue() throws Exception { + /* Allocator allocator = new FileAllocator(); + FileByteQueue queue = new FileByteQueue(allocator); + for(int i = 0; i < 26; i++) { + queue.write(new byte[]{(byte)(i+'a')}, 0, 1); + System.err.println("WRITE>>"+(char)(i+'a')); + } + for(int i = 0; i < 26; i++) { + byte[] buffer = new byte[1]; + assertEquals(queue.read(buffer, 0, 1), 1); + System.err.println("READ>>"+((char)buffer[0])); + assertEquals(buffer[0], (byte)(i+'a')); + }*/ + } + +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ArrayByteQueue.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ArrayByteQueue.java new file mode 100644 index 0000000..893ae80 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ArrayByteQueue.java @@ -0,0 +1,100 @@ +package org.simpleframework.common.buffer.queue; + +import java.io.IOException; + +import org.simpleframework.common.buffer.BufferException; + +public class ArrayByteQueue implements ByteQueue { + + private byte[] buffer; + private int limit; + private int count; + private int seek; + private boolean closed; + + public ArrayByteQueue(int limit) { + this.buffer = new byte[16]; + this.limit = limit; + } + + public synchronized void write(byte[] array) throws IOException { + write(array, 0, array.length); + } + + public synchronized void write(byte[] array, int off, int size) throws IOException { + if(closed) { + throw new BufferException("Queue has been closed"); + } + if (size + count > buffer.length) { + expand(count + size); + } + int fragment = buffer.length - seek; // from read pos to end + int space = fragment - count; // space at end + + if(space >= size) { + System.arraycopy(array, off, buffer, seek + count, size); + } else { + int chunk = Math.min(fragment, count); + + System.arraycopy(buffer, seek, buffer, 0, chunk); // adjust downward + System.arraycopy(array, off, buffer, chunk, size); + seek = 0; + } + notify(); + count += size; + } + + public synchronized int read(byte[] array) throws IOException { + return read(array, 0, array.length); + } + + public synchronized int read(byte[] array, int off, int size) throws IOException { + while(count == 0) { + try { + if(closed) { + return -1; + } + wait(); + } catch(Exception e) { + throw new BufferException("Thread interrupted", e); + } + } + int chunk = Math.min(size, count); + + if(chunk > 0) { + System.arraycopy(buffer, seek, array, off, chunk); + seek += chunk; + count -= chunk; + } + return chunk; + } + + private synchronized void expand(int capacity) throws IOException { + if (capacity > limit) { + throw new BufferException("Capacity limit %s exceeded", limit); + } + int resize = buffer.length * 2; + int size = Math.max(capacity, resize); + byte[] temp = new byte[size]; + + System.arraycopy(buffer, seek, temp, 0, count); + buffer = temp; + seek = 0; + } + + public synchronized void reset() throws IOException { + if(closed) { + throw new BufferException("Queue has been closed"); + } + seek = 0; + count = 0; + } + + public synchronized int available() { + return count; + } + + public synchronized void close() { + closed = true; + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ArrayByteQueueTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ArrayByteQueueTest.java new file mode 100644 index 0000000..7d361f3 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ArrayByteQueueTest.java @@ -0,0 +1,119 @@ +package org.simpleframework.common.buffer.queue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import junit.framework.TestCase; + +public class ArrayByteQueueTest extends TestCase { + + public void testArrayByteQueue() throws Exception { + ArrayByteQueue queue = new ArrayByteQueue(10); + + for(int i = 0; i < 9; i++) { + queue.write(new byte[]{(byte)('A'+i)}); + } + for(int i = 0; i < 9; i++) { + byte[] b = new byte[1]; + queue.read(b); + System.err.write(b); + System.err.println(); + } + for(int i = 9; i < 19; i++) { + queue.write(new byte[]{(byte)('A'+i)}); + } + for(int i = 0; i < 9; i++) { + byte[] b = new byte[1]; + queue.read(b); + System.err.write(b); + System.err.println(); + } + } + + public void testRandomReadWrite() throws Exception { + ArrayByteQueue queue = new ArrayByteQueue(1024 * 10); + + for(int i = 0; i < 100; i++) { + String text = "Test: "+i; + queue.write(text.getBytes()); + } + for(int i = 0; i < 100; i++) { + String text = "Test: "+i; + byte[] buffer = new byte[256]; + int size = queue.read(buffer, 0, text.length()); + String result = new String(buffer, 0, size); + System.err.println(result); + assertEquals(result, text); + } + } + /* + public void testStream() throws Exception { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + final ArrayByteQueue queue = new ArrayByteQueue(1024 * 10); + final Thread reader = new Thread(new Runnable() { + public void run() { + try { + for(int i = 0; i < 100; i++) { + byte[] chunk = new byte[(int)Math.round((Math.random() * 100))]; + int size = queue.read(chunk); + output.write(chunk, 0, size); + } + } catch(Exception e) { + e.printStackTrace(); + } + } + }); + final Thread writer = new Thread(new Runnable() { + public void run() { + try { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + ObjectOutputStream objectOutput = new ObjectOutputStream(buffer); + + for(int i = 0; i < 100; i++) { + try { + TestMessage message = new TestMessage(i, "Test Message: " +i); + objectOutput.writeObject(message); + objectOutput.flush(); + byte[] messageBytes = buffer.toByteArray(); + queue.write(messageBytes); + buffer.reset(); // clear out the buffer so toByteArray picks up changes only + } catch(Exception e) { + e.printStackTrace(); + } + } + }catch(Exception e){ + e.printStackTrace(); + } + } + }); + writer.start(); + reader.start(); + writer.join(); + Thread.sleep(5000); + reader.interrupt(); + reader.join(); + + ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + ObjectInputStream objectInput = new ObjectInputStream(input); + + for(int i = 0; i < 100; i++) { + TestMessage message = (TestMessage)objectInput.readObject(); + assertEquals(message.count, i); + assertEquals(message.text, "Test Message: "+i); + } + } +*/ + private static class TestMessage implements Serializable { + + public final int count; + public final String text; + + public TestMessage(int count, String text) { + this.count = count; + this.text = text; + } + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/BufferQueue.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/BufferQueue.java new file mode 100644 index 0000000..5f3e97f --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/BufferQueue.java @@ -0,0 +1,67 @@ +package org.simpleframework.common.buffer.queue; + +import java.io.IOException; +import java.io.InputStream; + +import org.simpleframework.common.buffer.ArrayBuffer; +import org.simpleframework.common.buffer.Buffer; + +public class BufferQueue implements Buffer { + + private final ByteQueue queue; + private final Buffer buffer; + + public BufferQueue(ByteQueue queue) { + this.buffer = new ArrayBuffer(); + this.queue = queue; + } + + public InputStream open() throws IOException { + return new ByteQueueStream(queue); + } + + public Buffer allocate() throws IOException { + return new BufferQueue(queue); + } + + public String encode() throws IOException { + return encode("UTF-8"); + } + + public String encode(String charset) throws IOException { + InputStream source = open(); + byte[] chunk = new byte[512]; + int count = 0; + + while((count = source.read(chunk)) != -1) { + buffer.append(chunk, 0, count); + } + return buffer.encode(charset); + } + + public Buffer append(byte[] array) throws IOException { + if(array.length > 0) { + queue.write(array); + } + return this; + } + + public Buffer append(byte[] array, int off, int len) throws IOException { + if(len > 0) { + queue.write(array, off, len); + } + return this; + } + + public void clear() throws IOException { + queue.reset(); + } + + public void close() throws IOException { + queue.close(); + } + + public long length() { + return buffer.length(); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/BufferQueueTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/BufferQueueTest.java new file mode 100644 index 0000000..22eaba7 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/BufferQueueTest.java @@ -0,0 +1,44 @@ +package org.simpleframework.common.buffer.queue; + +import java.io.InputStream; + +import junit.framework.TestCase; + +public class BufferQueueTest extends TestCase { + + public void testBufferQueue() throws Exception { + final ByteQueue queue = new ArrayByteQueue(1024 * 1000); + final BufferQueue buffer = new BufferQueue(queue); + + Thread reader = new Thread(new Runnable() { + public void run() { + try { + InputStream source = buffer.open(); + for(int i = 0; i < 1000; i++) { + int octet = source.read(); + System.err.write(octet); + System.err.flush(); + } + }catch(Exception e) { + e.printStackTrace(); + } + } + }); + Thread writer = new Thread(new Runnable() { + public void run() { + try { + for(int i = 0; i < 1000; i++) { + buffer.append(("Test message: "+i+"\n").getBytes()); + } + }catch(Exception e) { + e.printStackTrace(); + } + } + }); + reader.start(); + writer.start(); + reader.join(); + writer.join(); + } + +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ByteQueue.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ByteQueue.java new file mode 100644 index 0000000..dc567e9 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ByteQueue.java @@ -0,0 +1,13 @@ +package org.simpleframework.common.buffer.queue; + +import java.io.IOException; + +public interface ByteQueue { + void write(byte[] array) throws IOException; + void write(byte[] array, int off, int size) throws IOException; + int read(byte[] array) throws IOException; + int read(byte[] array, int off, int size) throws IOException; + int available() throws IOException; + void reset() throws IOException; + void close() throws IOException; +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ByteQueueStream.java b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ByteQueueStream.java new file mode 100644 index 0000000..dbf73e1 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/buffer/queue/ByteQueueStream.java @@ -0,0 +1,40 @@ +package org.simpleframework.common.buffer.queue; + +import java.io.IOException; +import java.io.InputStream; + +public class ByteQueueStream extends InputStream { + + private final ByteQueue queue; + + public ByteQueueStream(ByteQueue queue) { + this.queue = queue; + } + + @Override + public int read() throws IOException { + byte[] array = new byte[1]; + int count = read(array) ; + + if(count != -1) { + return array[0] & 0xff; + } + return -1; + } + + public int read(byte[] buffer) throws IOException { + return queue.read(buffer, 0, buffer.length); + } + + public int read(byte[] buffer, int off, int size) throws IOException { + return queue.read(buffer, off, size); + } + + public int available() throws IOException { + return queue.available(); + } + + public void close() throws IOException { + queue.close(); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/lease/ContractQueueTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/lease/ContractQueueTest.java new file mode 100644 index 0000000..6fa55c1 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/lease/ContractQueueTest.java @@ -0,0 +1,57 @@ +package org.simpleframework.common.lease; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.simpleframework.common.lease.Contract; +import org.simpleframework.common.lease.ContractQueue; +import org.simpleframework.common.lease.Expiration; + +public class ContractQueueTest extends TimeTestCase { + + public void testTimeUnits() throws Exception { + ContractQueue<Long> queue = new ContractQueue<Long>(); + List<String> complete = new ArrayList<String>(); + + for(long i = 0; i < 10000; i++) { + long random = (long)(Math.random() * 1000); + Contract<Long> contract = new Expiration(random, random, TimeUnit.NANOSECONDS); + + queue.offer(contract); + } + for(int i = 0; i < 10000; i++) { + Contract<Long> contract = queue.take(); + + assertGreaterThanOrEqual(contract.getDelay(TimeUnit.NANOSECONDS), contract.getDelay(TimeUnit.NANOSECONDS)); + assertGreaterThanOrEqual(contract.getDelay(TimeUnit.MILLISECONDS), contract.getDelay(TimeUnit.MILLISECONDS)); + assertGreaterThanOrEqual(contract.getDelay(TimeUnit.SECONDS), contract.getDelay(TimeUnit.SECONDS)); + + long nanoseconds = contract.getDelay(TimeUnit.NANOSECONDS); + long milliseconds = contract.getDelay(TimeUnit.MILLISECONDS); + + complete.add(String.format("index=[%s] nano=[%s] milli=[%s]", i, nanoseconds, milliseconds)); + } + for(int i = 0; i < 10000; i++) { + System.err.println("expiry=[" + complete.get(i)+ "]"); + } + } + + public void testAccuracy() throws Exception { + ContractQueue<Long> queue = new ContractQueue<Long>(); + + for(long i = 0; i < 10000; i++) { + long random = (long)(Math.random() * 1000); + Contract<Long> contract = new Expiration(random, random, TimeUnit.NANOSECONDS); + + queue.offer(contract); + } + for(int i = 0; i < 10000; i++) { + Contract<Long> contract = queue.take(); + + assertLessThanOrEqual(-2000, contract.getDelay(TimeUnit.MILLISECONDS)); + } + } + +} + diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/lease/ContractTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/lease/ContractTest.java new file mode 100644 index 0000000..1cc40af --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/lease/ContractTest.java @@ -0,0 +1,40 @@ +package org.simpleframework.common.lease; + +import java.util.concurrent.TimeUnit; + +import org.simpleframework.common.lease.Contract; +import org.simpleframework.common.lease.Expiration; + +public class ContractTest extends TimeTestCase { + + public void testContract() throws Exception { + Contract ten = new Expiration(this, 10, TimeUnit.MILLISECONDS); + Contract twenty = new Expiration(this, 20, TimeUnit.MILLISECONDS); + Contract thirty= new Expiration(this, 30, TimeUnit.MILLISECONDS); + + assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.NANOSECONDS), ten.getDelay(TimeUnit.NANOSECONDS)); + assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.NANOSECONDS), twenty.getDelay(TimeUnit.NANOSECONDS)); + + assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.MILLISECONDS), ten.getDelay(TimeUnit.MILLISECONDS)); + assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.MILLISECONDS), twenty.getDelay(TimeUnit.MILLISECONDS)); + + ten.setDelay(0, TimeUnit.MILLISECONDS); + twenty.setDelay(0, TimeUnit.MILLISECONDS); + + assertLessThanOrEqual(ten.getDelay(TimeUnit.MILLISECONDS), 0); + assertLessThanOrEqual(twenty.getDelay(TimeUnit.MILLISECONDS), 0); + + ten.setDelay(10, TimeUnit.MILLISECONDS); + twenty.setDelay(20, TimeUnit.MILLISECONDS); + thirty.setDelay(30, TimeUnit.MILLISECONDS); + + assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.NANOSECONDS), ten.getDelay(TimeUnit.NANOSECONDS)); + assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.NANOSECONDS), twenty.getDelay(TimeUnit.NANOSECONDS)); + + assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.MILLISECONDS), ten.getDelay(TimeUnit.MILLISECONDS)); + assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.MILLISECONDS), twenty.getDelay(TimeUnit.MILLISECONDS)); + + ten.setDelay(0, TimeUnit.MILLISECONDS); + twenty.setDelay(0, TimeUnit.MILLISECONDS); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/lease/LeaseManagerTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/lease/LeaseManagerTest.java new file mode 100644 index 0000000..9ce2b93 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/lease/LeaseManagerTest.java @@ -0,0 +1,227 @@ +package org.simpleframework.common.lease; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.simpleframework.common.lease.Cleaner; +import org.simpleframework.common.lease.Lease; +import org.simpleframework.common.lease.LeaseManager; + +public class LeaseManagerTest extends TimeTestCase { + + private static int ITERATIONS = 1000; + private static int MAXIMUM = 20000; + + static { + String value = System.getProperty("iterations"); + + if (value != null) { + ITERATIONS = Integer.parseInt(value); + } + } + + public void testClock() { + List<Long> timeList = new ArrayList<Long>(); + + for(int i = 0; i < ITERATIONS; i++) { + long time = System.nanoTime(); + long milliseconds = TimeUnit.MILLISECONDS.convert(time, TimeUnit.MILLISECONDS); + + timeList.add(milliseconds); + } + for(int i = 1; i < ITERATIONS; i++) { + assertLessThanOrEqual(timeList.get(i - 1), timeList.get(i)); + } + } + + public void testRandom() { + for(int i = 0; i < ITERATIONS; i++) { + long randomTime = getRandomTime(MAXIMUM); + + assertGreaterThanOrEqual(MAXIMUM, randomTime); + assertGreaterThanOrEqual(randomTime, 0); + } + } + + public void testOrder() throws Exception { + final BlockingQueue<Integer> clean = new LinkedBlockingQueue<Integer>(); + final ConcurrentHashMap<Integer, Long> record = new ConcurrentHashMap<Integer, Long>(); + + Cleaner<Integer> cleaner = new Cleaner<Integer>() { + + long start = System.currentTimeMillis(); + + public void clean(Integer key) { + record.put(key, start - System.currentTimeMillis()); + clean.offer(key); + + } + }; + LeaseManager<Integer> manager = new LeaseManager<Integer>(cleaner); + List<Lease<Integer>> list = new ArrayList<Lease<Integer>>(); + + long start = System.currentTimeMillis(); + + for(int i = 0; i < ITERATIONS; i++) { + long randomTime = getRandomTime(MAXIMUM) + MAXIMUM + i * 50; + + System.err.printf("leasing [%s] for [%s] @ %s%n", i, randomTime, System.currentTimeMillis() - start); + + Lease<Integer> lease = manager.lease(i, randomTime, TimeUnit.MILLISECONDS); + + list.add(lease); + } + start = System.currentTimeMillis(); + + for(int i = 0; i < ITERATIONS; i++) { + try { + System.err.printf("renewing [%s] for [%s] expires [%s] @ %s expired [%s] %n", i, i, list.get(i).getExpiry(TimeUnit.MILLISECONDS), System.currentTimeMillis() - start, record.get(i)); + list.get(i).renew(i, TimeUnit.MILLISECONDS); + }catch(Exception e) { + System.err.printf("Lease %s in error: ", i); + e.printStackTrace(System.err); + } + } + int variation = 20; + int cleaned = 0; + + for(int i = 0; i < ITERATIONS; i++) { + int value = clean.take(); + cleaned++; + + System.err.printf("index=[%s] clean=[%s] expiry[%s]=%s expiry[%s]=%s%n ", i, value, i, record.get(i), value, record.get(value)); + assertLessThanOrEqual(i - variation, value); + } + assertEquals(cleaned, ITERATIONS); + } + + public void testLease() throws Exception { + final BlockingQueue<Expectation> clean = new LinkedBlockingQueue<Expectation>(); + + Cleaner<Expectation> cleaner = new Cleaner<Expectation>() { + public void clean(Expectation key) { + clean.offer(key); + } + }; + final BlockingQueue<Lease<Expectation>> renewalQueue = new LinkedBlockingQueue<Lease<Expectation>>(); + final BlockingQueue<Lease<Expectation>> expiryQueue = new LinkedBlockingQueue<Lease<Expectation>>(); + final CountDownLatch ready = new CountDownLatch(21); + final AtomicInteger renewCount = new AtomicInteger(ITERATIONS); + + for(int i = 0; i < 20; i++) { + new Thread(new Runnable() { + public void run() { + while(renewCount.getAndDecrement() > 0) { + long randomTime = getRandomTime(MAXIMUM); + + try { + ready.countDown(); + ready.await(); + + Lease<Expectation> lease = renewalQueue.take(); + + try { + lease.renew(randomTime, TimeUnit.MILLISECONDS); + lease.getKey().setExpectation(randomTime, TimeUnit.MILLISECONDS); + + assertGreaterThanOrEqual(randomTime, 0); + assertGreaterThanOrEqual(randomTime, lease.getExpiry(TimeUnit.MILLISECONDS)); + } catch(Exception e) { + expiryQueue.offer(lease); + } + } catch(Exception e) { + e.printStackTrace(); + } + } + } + }).start(); + } + final LeaseManager<Expectation> manager = new LeaseManager<Expectation>(cleaner); + final CountDownLatch latch = new CountDownLatch(21); + final AtomicInteger leaseCount = new AtomicInteger(ITERATIONS); + + for(int i = 0; i < 20; i++) { + new Thread(new Runnable() { + public void run() { + while(leaseCount.getAndDecrement() > 0) { + long randomTime = getRandomTime(MAXIMUM); + Expectation expectation = new Expectation(randomTime, TimeUnit.MILLISECONDS); + + try { + latch.countDown(); + latch.await(); + } catch(InterruptedException e) { + e.printStackTrace(); + } + assertGreaterThanOrEqual(randomTime, 0); + + Lease<Expectation> lease = manager.lease(expectation, randomTime, TimeUnit.MILLISECONDS); + renewalQueue.offer(lease); + } + } + }).start(); + } + ready.countDown(); + latch.countDown(); + + for (int i = 0; i < ITERATIONS; i++) { + Expectation expectation = clean.poll(MAXIMUM, TimeUnit.MILLISECONDS); + + if(expectation != null) { + long accuracy = System.nanoTime() - expectation.getExpectation(TimeUnit.NANOSECONDS); + long milliseconds = TimeUnit.MILLISECONDS.convert(accuracy, TimeUnit.NANOSECONDS); + + System.err.printf("index=[%s] accuracy=[%s] queue=[%s]%n", i, milliseconds, clean.size()); + } else { + System.err.printf("index=[%s] queue=[%s]%n", i, clean.size()); + } + + } + System.err.printf("waiting=[%s]%n", clean.size()); + } + + + public static class Expectation { + + private long time; + + public Expectation(long duration, TimeUnit unit) { + setExpectation(duration, unit); + } + + public void setExpectation(long duration, TimeUnit unit) { + long nano = TimeUnit.NANOSECONDS.convert(duration, unit); + long expect = nano + System.nanoTime(); + + this.time = expect; + } + + public long getExpectation(TimeUnit unit) { + return unit.convert(time, TimeUnit.NANOSECONDS); + } + } + + + public static long getRandomTime(long maximum) { + long random = new Random().nextLong() % maximum; + + if(random < 0) { + random *= -1; + } + return random; + } + + public static void main(String[] list) throws Exception { + new LeaseManagerTest().testClock(); + new LeaseManagerTest().testRandom(); + new LeaseManagerTest().testOrder(); + new LeaseManagerTest().testLease(); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/lease/LeaseTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/lease/LeaseTest.java new file mode 100644 index 0000000..d4b73d7 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/lease/LeaseTest.java @@ -0,0 +1,87 @@ +package org.simpleframework.common.lease; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.simpleframework.common.lease.Cleaner; +import org.simpleframework.common.lease.Contract; +import org.simpleframework.common.lease.ContractController; +import org.simpleframework.common.lease.ContractLease; +import org.simpleframework.common.lease.ContractMaintainer; +import org.simpleframework.common.lease.Expiration; +import org.simpleframework.common.lease.Lease; + +public class LeaseTest extends TimeTestCase { + + private static int ITERATIONS = 10000; + + static { + String value = System.getProperty("iterations"); + + if (value != null) { + ITERATIONS = Integer.parseInt(value); + } + } + + public void testLease() throws Exception { + final BlockingQueue<Integer> clean = new LinkedBlockingQueue<Integer>(); + + Cleaner<Integer> cleaner = new Cleaner<Integer>() { + public void clean(Integer key) { + clean.offer(key); + } + }; + Map<Integer, Contract> table = new ConcurrentHashMap<Integer, Contract>(); + List<Lease> list = new ArrayList<Lease>(); + ContractController controller = new ContractMaintainer(cleaner); + + for (int i = 0; i < ITERATIONS; i++) { + long random = (long) (Math.random() * 1000) + 1000L; + Contract<Integer> contract = new Expiration(i, random, TimeUnit.MILLISECONDS); + Lease lease = new ContractLease(controller, contract); + + table.put(i, contract); + list.add(lease); + controller.issue(contract); + } + for (int i = 0; i < ITERATIONS; i++) { + long random = (long) (Math.random() * 1000); + + try { + list.get(i).renew(random, TimeUnit.MILLISECONDS); + } catch (Exception e) { + continue; + // e.printStackTrace(); + } + } + for (int i = 0; i < ITERATIONS; i++) { + try { + System.err.println("delay: " + + list.get(i).getExpiry(TimeUnit.MILLISECONDS)); + } catch (Exception e) { + continue; + // e.printStackTrace(); + } + } + System.err.println("clean: " + clean.size()); + + for (int i = 0; i < ITERATIONS; i++) { + Integer index = clean.take(); + Contract contract = table.get(index); + + // assertLessThanOrEqual(-4000, + // contract.getDelay(TimeUnit.MILLISECONDS)); + System.err.println(String.format("index=[%s] delay=[%s]", index, + contract.getDelay(TimeUnit.MILLISECONDS))); + } + } + + public static void main(String[] list) throws Exception { + new LeaseTest().testLease(); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/lease/TimeTestCase.java b/simple/simple-common/src/test/java/org/simpleframework/common/lease/TimeTestCase.java new file mode 100644 index 0000000..294cc9b --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/lease/TimeTestCase.java @@ -0,0 +1,25 @@ +package org.simpleframework.common.lease; + +import junit.framework.TestCase; + +public class TimeTestCase extends TestCase { + + public void testTime() { + } + + public static void assertLessThan(long a, long b) { + assertTrue(String.format("Value %s is not less than %s", a, b), a < b); + } + + public static void assertLessThanOrEqual(long a, long b) { + assertTrue(String.format("Value %s is not less than or equal to %s", a, b), a <= b); + } + + public static void assertGreaterThan(long a, long b) { + assertTrue(String.format("Value %s is not greater than %s", a, b), a > b); + } + + public static void assertGreaterThanOrEqual(long a, long b) { + assertTrue(String.format("Value %s is not greater than or equal to %s", a, b), a >= b); + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/thread/SchedulerTest.java b/simple/simple-common/src/test/java/org/simpleframework/common/thread/SchedulerTest.java new file mode 100644 index 0000000..78fe802 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/thread/SchedulerTest.java @@ -0,0 +1,65 @@ +package org.simpleframework.common.thread; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.simpleframework.common.thread.ConcurrentScheduler; + +import junit.framework.TestCase; + +public class SchedulerTest extends TestCase { + + private static final int ITERATIONS = 10000; + + public void testScheduler() throws Exception { + ConcurrentScheduler queue = new ConcurrentScheduler(Runnable.class, 10); + LinkedBlockingQueue<Timer> list = new LinkedBlockingQueue<Timer>(); + + for(int i = 0; i < ITERATIONS; i++) { + queue.execute(new Task(list, new Timer(i)), i, TimeUnit.MILLISECONDS); + } + for(Timer timer = list.take(); timer.getValue() < ITERATIONS - 10; timer = list.take()) { + System.err.println("value=["+timer.getValue()+"] delay=["+timer.getDelay()+"] expect=["+timer.getExpectation()+"]"); + } + } + + public class Timer { + + private Integer value; + + private long time; + + public Timer(Integer value) { + this.time = System.currentTimeMillis(); + this.value = value; + } + + public Integer getValue() { + return value; + } + + public long getDelay() { + return System.currentTimeMillis() - time; + } + + public int getExpectation() { + return value.intValue(); + } + } + + public class Task implements Runnable { + + private LinkedBlockingQueue<Timer> queue; + + private Timer timer; + + public Task(LinkedBlockingQueue<Timer> queue, Timer timer) { + this.queue = queue; + this.timer = timer; + } + + public void run() { + queue.offer(timer); + } + } +} diff --git a/simple/simple-common/src/test/java/org/simpleframework/common/thread/TransientApplication.java b/simple/simple-common/src/test/java/org/simpleframework/common/thread/TransientApplication.java new file mode 100644 index 0000000..69941b2 --- /dev/null +++ b/simple/simple-common/src/test/java/org/simpleframework/common/thread/TransientApplication.java @@ -0,0 +1,54 @@ +package org.simpleframework.common.thread; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.simpleframework.common.thread.ConcurrentExecutor; + +public class TransientApplication { + + public static void main(String[] list) throws Exception { + BlockingQueue queue = new LinkedBlockingQueue(); + ConcurrentExecutor pool = new ConcurrentExecutor(TerminateTask.class, 10); + + for(int i = 0; i < 50; i++) { + pool.execute(new LongTask(queue, String.valueOf(i))); + } + pool.execute(new TerminateTask(pool)); + } + + private static class TerminateTask implements Runnable { + + private ConcurrentExecutor pool; + + public TerminateTask(ConcurrentExecutor pool) { + this.pool = pool; + } + + public void run() { + pool.stop(); + } + } + + private static class LongTask implements Runnable { + + private BlockingQueue queue; + + private String name; + + public LongTask(BlockingQueue queue, String name) { + this.queue = queue; + this.name = name; + } + + public void run() { + try { + Thread.sleep(1000); + } catch(Exception e) { + e.printStackTrace(); + } + System.err.println(name); + queue.offer(name); + } + } +} |