Refactor stream integration test

Signed-off-by: Andrew DeMaria <lostonamountain@gmail.com>
master
Andrew DeMaria 5 years ago
parent eea9416fbe
commit 10e90beb30
No known key found for this signature in database
GPG Key ID: 0A3F5E91F8364EDF
  1. 122
      integration-test/src/test/java/org/airsonic/test/cucumber/steps/api/StreamStepDef.java
  2. 36
      integration-test/src/test/java/org/airsonic/test/domain/SavedHttpResponse.java
  3. 0
      integration-test/src/test/resources/blobs/stream/piano/input/piano.mp3
  4. BIN
      integration-test/src/test/resources/blobs/stream/piano/responses/1.dat
  5. 7
      integration-test/src/test/resources/features/api/stream-mp3.feature

@ -2,9 +2,12 @@ package org.airsonic.test.cucumber.steps.api;
import cucumber.api.java8.En;
import org.airsonic.test.cucumber.server.AirsonicServer;
import org.airsonic.test.domain.SavedHttpResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.HexDump;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
@ -14,56 +17,117 @@ import org.junit.Assert;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StreamStepDef implements En {
private CloseableHttpResponse response;
private CloseableHttpClient client;
private boolean closed = false;
private byte[] body;
private List<SavedHttpResponse> responses = new ArrayList<>();
private String streamName;
public StreamStepDef(AirsonicServer server) {
this.client = HttpClientBuilder.create().build();
Given("Media file (.*) is added", (String mediaFile) -> {
// TODO fix this
Given("Media file (.*) is added", (String streamName) -> {
this.streamName = streamName;
server.uploadToDefaultMusicFolder(
Paths.get(this.getClass().getResource("/blobs/stream/piano").toURI()),
Paths.get(this.getClass().getResource("/blobs/stream/" + streamName).toURI()),
"");
});
When("A stream request is sent", () -> {
RequestBuilder builder = RequestBuilder.create("GET").setUri(server.getBaseUri() + "/rest/stream");
builder.addParameter("id", "2");
builder.addHeader("Range", "bytes=0-");
builder.addHeader("Accept", "audio/webm,audio/ogg,audio/wav,audio/*;");
server.addRestParameters(builder);
response = client.execute(builder.build());
});
Then("The response bytes are equal", () -> {
ensureBodyRead();
for (int i = 0; i < responses.size(); i++) {
checkBody(responses.get(i), i);
}
});
Then("^Print debug output$", () -> {
for (int i = 0; i < responses.size(); i++) {
System.out.printf("Response %d%n", i + 1);
printDebugInfo(responses.get(i), 2);
}
});
Then("^The length headers are correct$", () -> {
responses.forEach(this::checkLengths);
});
When("^A stream is consumed$", () -> {
while(shouldDoRequest()) {
responses.add(consumeResponse(doRequest(server)));
}
});
FileUtils.writeByteArrayToFile(new File("/tmp/bytearray"), body);
}
byte[] expected = IOUtils.toByteArray(this.getClass().getResource("/blobs/stream/piano/piano.mp3").toURI());
//
private void checkBody(SavedHttpResponse savedHttpResponse, int iter) throws URISyntaxException, IOException {
String expectedBodyResource = String.format("/blobs/stream/"+streamName+"/responses/%d.dat", iter+1);
byte[] expected = IOUtils.toByteArray(
this.getClass()
.getResource(expectedBodyResource)
.toURI());
// TODO if debug...
// FileUtils.writeByteArrayToFile(
// new File(String.format("/tmp/bytearray-%d", iter+1)),
// savedHttpResponse.getBody());
// HexDump.dump(expected, 0, System.out, 0);
Assert.assertArrayEquals(expected, body);
});
Assert.assertArrayEquals(expected, savedHttpResponse.getBody());
}
private void printDebugInfo(SavedHttpResponse savedHttpResponse, int indentLevel) {
String indent = StringUtils.repeat(' ', indentLevel);
System.out.println(indent + "Headers:");
for (Header header : savedHttpResponse.getHeaders()) {
System.out.print(indent + header.getName());
System.out.print(": ");
for (HeaderElement element : header.getElements()) {
System.out.print(element);
System.out.print(", ");
}
System.out.println();
}
}
private void checkLengths(SavedHttpResponse response) {
Header header = response.getHeader("Content-Length");
Assert.assertEquals(response.getBody().length, Integer.parseInt(header.getValue()));
}
private boolean shouldDoRequest() {
return responses.isEmpty() || isUnconsumedContent(responses.get(responses.size() - 1));
}
void ensureBodyRead() throws IOException {
if(closed) {
return;
} else {
this.body = EntityUtils.toByteArray(response.getEntity());
closed = true;
response.close();
private Pattern CONTENT_RANGE_PATTERN = Pattern.compile("^bytes (\\d+)-(\\d+)/(\\d+)$");
private boolean isUnconsumedContent(SavedHttpResponse savedHttpResponse) {
Header header = savedHttpResponse.getHeader("Content-Range");
Matcher matcher = CONTENT_RANGE_PATTERN.matcher(header.getValue());
if(!matcher.matches()) {
throw new RuntimeException("Unexpected Content-Range format");
}
int start = Integer.parseInt(matcher.group(1));
int end = Integer.parseInt(matcher.group(2));
int total = Integer.parseInt(matcher.group(3));
return (total - 1) > end;
}
private CloseableHttpResponse doRequest(AirsonicServer server) throws IOException {
RequestBuilder builder = RequestBuilder.create("GET").setUri(server.getBaseUri() + "/rest/stream");
builder.addParameter("id", "2"); // TODO abstract this out
builder.addHeader("Range", "bytes=0-");
builder.addHeader("Accept", "audio/webm,audio/ogg,audio/wav,audio/*;");
server.addRestParameters(builder);
return client.execute(builder.build());
}
SavedHttpResponse consumeResponse(CloseableHttpResponse response) throws IOException {
byte[] body = EntityUtils.toByteArray(response.getEntity());
List<Header> headers = Arrays.asList(response.getAllHeaders());
response.close();
return new SavedHttpResponse(headers, body);
}
}

@ -0,0 +1,36 @@
package org.airsonic.test.domain;
import org.apache.http.Header;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class SavedHttpResponse {
private final List<Header> headers;
private final byte[] body;
public SavedHttpResponse(List<Header> headers, byte[] body) {
this.headers = headers;
this.body = body;
}
public List<Header> getHeaders() {
return headers;
}
public byte[] getBody() {
return body;
}
public Header getHeader(String name) {
List<Header> matchingHeaders = headers.stream().filter(header ->
Objects.equals(header.getName(), name))
.collect(Collectors.toList());
if(matchingHeaders.size() != 1) {
throw new RuntimeException("Did not find one matching header with name " + name);
}
return matchingHeaders.iterator().next();
}
}

@ -1,11 +1,12 @@
Feature: Stream API for MP3
Background:
Given Media file stream/piano/piano.mp3 is added
Given Media file piano is added
And a scan is done
Scenario: Airsonic sends stream data
When A stream request is sent
When A stream is consumed
Then Print debug output
Then The response bytes are equal
# TODO check length
Then The length headers are correct

Loading…
Cancel
Save