Add metadata stream parser and test case.
[sonitus.git] / src / test / java / net / pterodactylus / sonitus / io / MetadataStreamTest.java
1 /*
2  * Sonitus - MetadataStreamTest.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sonitus.io;
19
20 import static org.hamcrest.CoreMatchers.is;
21 import static org.junit.Assert.assertThat;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.util.Arrays;
28 import java.util.Iterator;
29 import java.util.Random;
30
31 import com.google.common.collect.FluentIterable;
32 import com.google.common.io.ByteStreams;
33 import org.testng.annotations.DataProvider;
34 import org.testng.annotations.Test;
35
36 /**
37  * Test for {@link MetadataStream}.
38  *
39  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
40  */
41 public class MetadataStreamTest {
42
43         /**
44          * Returns test data for {@link #testMetadataStream(int, int, String[])}.
45          *
46          * @return Test data for {@link #testMetadataStream(int, int, String[])}
47          */
48         @DataProvider(name = "testData")
49         public Object[][] getStreamTestParameters() {
50                 return new Object[][] {
51                                                                           { 5, 18, new String[] { "Test 1", "Test 2", "Test 3" } },
52                                                                           { 1024, 10240, new String[] { "Test 1", "Test 2" } },
53                                                                           { 1024, 10240, new String[] { "Test 1", "", "", "", "Test 2" } },
54                                                                           { 8192, 10240, new String[] { "Test 1" } },
55                                                                           { 8192, 262144, new String[] { "Test 1", "Test 2" } }
56                 };
57         }
58
59         /**
60          * Returns test data for {@link #testMetadataEncoding(String, String)}.
61          *
62          * @return Test data for {@link #testMetadataEncoding(String, String)}
63          */
64         @DataProvider(name = "encodingTestData")
65         public Object[][] getEncodingTestParameters() {
66                 return new Object[][] {
67                                                                           { "Metadata mit Ümläute!", "UTF-8" },
68                                                                           { "Metadata mit Ümläute!", "ISO8859-1" }
69                 };
70         }
71
72         /**
73          * Tests that the {@link MetadataStream} can successfully separate the payload
74          * from the metadata, and that the stream returns the expected bytes.
75          *
76          * @param metadataInterval
77          *              The interval of the metadata
78          * @param length
79          *              The length of the stream to test
80          * @param metadatas
81          *              The metadata strings to write (empty strings allowed to signify “no
82          *              metadata change”)
83          * @throws IOException
84          *              if an I/O error occurs
85          */
86         @Test(dataProvider = "testData")
87         public void testMetadataStream(int metadataInterval, int length, String[] metadatas) throws IOException {
88                 byte[] randomData = generateData(length);
89                 InputStream testInputStream = generateInputStream(metadataInterval, randomData, "UTF-8", metadatas);
90                 MetadataStream metadataStream = new MetadataStream(testInputStream, metadataInterval);
91                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
92
93                 ByteStreams.copy(metadataStream, outputStream);
94                 assertThat(outputStream.toByteArray().length, is(length));
95                 assertThat(outputStream.toByteArray(), is(randomData));
96         }
97
98         /**
99          * Tests that the metadata is decoded correcty.
100          *
101          * @param metadata
102          *              The string to encode
103          * @param charset
104          *              The charset in which to encode the string
105          * @throws IOException
106          *              if an I/O error occurs
107          */
108         @Test(dataProvider = "encodingTestData")
109         public void testMetadataEncoding(String metadata, String charset) throws IOException {
110                 InputStream testInputStream = generateInputStream(8192, 10240, charset, metadata);
111                 MetadataStream metadataStream = new MetadataStream(testInputStream, 8192);
112
113                 ByteStreams.copy(metadataStream, new ByteArrayOutputStream());
114                 assertThat(metadataStream.getContentMetadata().isPresent(), is(true));
115                 assertThat(metadataStream.getContentMetadata().get().title(), is(metadata));
116         }
117
118         //
119         // PRIVATE METHODS
120         //
121
122         /**
123          * Generates a random amount of data.
124          *
125          * @param length
126          *              The length of the data
127          * @return The generated random data
128          */
129         private static byte[] generateData(int length) {
130                 Random random = new Random();
131                 byte[] buffer = new byte[length];
132                 random.nextBytes(buffer);
133                 return buffer;
134         }
135
136         /**
137          * Generates an input stream of the given length that has the given metadata
138          * strings (cycled) embedded in the given intervals.
139          *
140          * @param metadataInterval
141          *              The interval of the embedded metadata
142          * @param length
143          *              The length of the stream to generate
144          * @param charset
145          *              The charset with which to encode the metadata strings
146          * @param metadatas
147          *              The metadata strings which will be cycled
148          * @return The generated input stream
149          * @throws IOException
150          *              if an I/O error occurs
151          */
152         private static InputStream generateInputStream(int metadataInterval, int length, String charset, String... metadatas) throws IOException {
153                 return generateInputStream(metadataInterval, generateData(length), charset, metadatas);
154         }
155
156         /**
157          * Generates an input stream of the given buffer that has the given metadata
158          * strings (cycled) embedded in the given intervals.
159          *
160          * @param metadataInterval
161          *              The interval of the embedded metadata
162          * @param buffer
163          *              The data to embed the metadata into
164          * @param charset
165          *              The charset with which to encode the metadata strings
166          * @param metadatas
167          *              The metadata strings which will be cycled
168          * @return The generated input stream
169          * @throws IOException
170          *              if an I/O error occurs
171          */
172         private static InputStream generateInputStream(int metadataInterval, byte[] buffer, String charset, String... metadatas) throws IOException {
173                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
174                 Iterator<String> metadataIterator = FluentIterable.from(Arrays.asList(metadatas)).cycle().iterator();
175                 int bufferPosition = 0;
176                 int remaining = buffer.length;
177                 while (remaining > 0) {
178                         int bytesToWrite = Math.min(remaining, metadataInterval);
179                         outputStream.write(buffer, bufferPosition, bytesToWrite);
180                         remaining -= bytesToWrite;
181                         bufferPosition += bytesToWrite;
182                         if (remaining > 0) {
183                                 String nextMetadata = "StreamTitle='" + metadataIterator.next() + "';";
184                                 byte[] metadata = nextMetadata.getBytes(charset);
185                                 outputStream.write((metadata.length + 15) / 16);
186                                 outputStream.write(metadata);
187                                 outputStream.write(new byte[(16 - metadata.length % 16) % 16]);
188                         }
189                 }
190                 return new ByteArrayInputStream(outputStream.toByteArray());
191         }
192
193 }