diff --git a/README.md b/README.md index f782024..c09416d 100755 --- a/README.md +++ b/README.md @@ -6,13 +6,13 @@ Likewise, trying to provide a wider spectrum of protocols to support videos stre For more information related to HLS, please refer to the [wikipedia page](https://en.wikipedia.org/wiki/HTTP_Live_Streaming) or to the [RFC](https://tools.ietf.org/html/rfc8216) and, for MPEG DASH, please refer to the [wikipedia page](https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP) or to the [ISO](https://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip). -Currently the project uses the [HLSParserJ](https://github.com/Comcast/hlsparserj) library to parse the HLS playlists and a [fork](https://github.com/Blazemeter/mpd-tools) of [MPD-Tools](https://github.com/carlanton/mpd-tools) for MPEG-DASH manifest and segments. +Currently, the project uses the [HLSParserJ](https://github.com/Comcast/hlsparserj) library to parse the HLS playlists and a [fork](https://github.com/Blazemeter/mpd-tools) of [MPD-Tools](https://github.com/carlanton/mpd-tools) for MPEG-DASH manifest and segments. **NOTICE** In future releases, the plugin will be named "Video Streaming Plugin" instead of "HLS Plugin", following the same desire to cover a wider range of protocols. -#### In a HTTP Live Streaming process: +#### In an HTTP Live Streaming process: - The audio/video to be streamed is reproduced by a media encoder at different quality levels, bitrates and resolutions. Each version is called a variant. - The different variants are split up into smaller Media Segment Files. @@ -20,12 +20,16 @@ In future releases, the plugin will be named "Video Streaming Plugin" instead of - The encoder creates a Master Playlist File with the URLs of each Media Playlist. To play, the client first downloads the Master Playlist, and then the Media Playlists. Then, they play each Media Segment declared within the chosen Media Playlist. The client can reload the Playlist to discover any added segments. This is needed in cases of live events, for example. +Notice that the recognition of the HLS protocol is based on the requirement of the URL extension of the Master playlist link, which must have ".m3u8" on it, as specified on the [ISO regulation](https://tools.ietf.org/html/rfc8216#section-4). + #### In a Dynamic Adaptive Streaming over HTTP Live Streaming process: -- The encoder creates a Manifest that contains all the Periods, among Base URLs and the Adaptation Sets to do the filtering, based on resolution, bandwidth and language selector. +- The encoder creates a Manifest which contains all the Periods, among Base URLs and the Adaptation Sets to do the filtering, based on resolution, bandwidth and language selector. - The plugin is coded so it will download the segments, for each Adaptation Set selected, consecutively, instead of doing it in parallel. - The plugin will update the manifest based on the ```timeShiftBufferDepth``` attribute of MPD. +Notice that, just like is done for HLS, the recognition on this protocol is based on the URL of the Manifest, which should contain ".mpd" on it. In cases, it doesn't meet this requirement, and the url don't contain ".m3a8", it is going to be considered a MPEG-DASH as well. + ## How the plugin works ### Concept @@ -59,7 +63,7 @@ Set the link to the master playlist file #### Duration -Set the playback time to either the whole video or a certain amount of seconds. +Set the playback time to either the whole video, or a certain amount of seconds. ![](docs/duration.png) diff --git a/pom.xml b/pom.xml index cceed9e..7f3c3a0 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.blazemeter.jmeter jmeter-bzm-hls - 3.0.1-SNAPSHOT + 3.0.3 Video Streaming Sampler as JMeter plugin @@ -67,6 +67,12 @@ 4.12 test + + org.testcontainers + testcontainers + 1.14.3 + test + kg.apc jmeter-plugins-cmn-jmeter @@ -103,6 +109,12 @@ 3.8.0 test + + com.github.tomakehurst + wiremock + 2.27.1 + test + @@ -147,6 +159,9 @@ both 10 false + + com.blazemeter.jmeter.MasterSlaveIT + diff --git a/src/main/java/com/blazemeter/jmeter/hls/logic/HlsSampler.java b/src/main/java/com/blazemeter/jmeter/hls/logic/HlsSampler.java index 7cea76b..2fd7f43 100755 --- a/src/main/java/com/blazemeter/jmeter/hls/logic/HlsSampler.java +++ b/src/main/java/com/blazemeter/jmeter/hls/logic/HlsSampler.java @@ -4,7 +4,9 @@ import com.blazemeter.jmeter.videostreaming.core.TimeMachine; import com.blazemeter.jmeter.videostreaming.core.VideoStreamingHttpClient; import com.blazemeter.jmeter.videostreaming.core.VideoStreamingSampler; -import com.blazemeter.jmeter.videostreaming.dash.DashSampler; +import com.helger.commons.annotation.VisibleForTesting; +import java.io.IOException; +import java.io.ObjectInputStream; import java.net.URL; import org.apache.jmeter.engine.event.LoopIterationEvent; import org.apache.jmeter.protocol.http.control.CacheManager; @@ -43,29 +45,45 @@ public class HlsSampler extends HTTPSamplerBase implements Interruptible { private static final String COOKIE_MANAGER = "HLSRequest.cookie_manager"; private static final String CACHE_MANAGER = "HLSRequest.cache_manager"; - private final transient VideoStreamingHttpClient httpClient; - private final transient TimeMachine timeMachine; - private final transient SampleResultProcessor sampleResultProcessor; + private transient VideoStreamingHttpClient httpClient; + private transient TimeMachine timeMachine; + private transient SampleResultProcessor sampleResultProcessor; private transient VideoStreamingSampler sampler; private transient String lastMasterUrl = null; private transient volatile boolean notifyFirstSampleAfterLoopRestart; + private transient VideoStreamingSamplerFactory factory; public HlsSampler() { initHttpSampler(); - httpClient = new VideoStreamingHttpClient(this); - sampleResultProcessor = new SampleResultProcessor(this); - timeMachine = TimeMachine.SYSTEM; + } + + @VisibleForTesting + public HlsSampler(VideoStreamingSamplerFactory factory, VideoStreamingHttpClient client, + SampleResultProcessor processor, TimeMachine timeMachine) { + this.factory = factory; + this.httpClient = client; + this.sampleResultProcessor = processor; + this.timeMachine = timeMachine; } public HlsSampler(VideoStreamingHttpClient httpClient, TimeMachine timeMachine) { - initHttpSampler(); + setInitHttpSamplerConfig(); this.httpClient = httpClient; - sampleResultProcessor = new SampleResultProcessor(this); this.timeMachine = timeMachine; + sampleResultProcessor = new SampleResultProcessor(this); + factory = new VideoStreamingSamplerFactory(); } private void initHttpSampler() { + setInitHttpSamplerConfig(); + factory = new VideoStreamingSamplerFactory(); + httpClient = new VideoStreamingHttpClient(this); + sampleResultProcessor = new SampleResultProcessor(this); + timeMachine = TimeMachine.SYSTEM; + } + + private void setInitHttpSamplerConfig() { setName("Media Sampler"); setFollowRedirects(true); setUseKeepAlive(true); @@ -171,12 +189,8 @@ public SampleResult sample() { String url = getMasterUrl(); if (!url.equals(lastMasterUrl)) { - if (!url.contains(".mpd")) { - sampler = new com.blazemeter.jmeter.videostreaming.hls.HlsSampler(this, httpClient, - timeMachine, sampleResultProcessor); - } else { - sampler = new DashSampler(this, httpClient, timeMachine, sampleResultProcessor); - } + sampler = factory + .getVideoStreamingSampler(url, this, httpClient, timeMachine, sampleResultProcessor); } else if (!this.getResumeVideoStatus()) { sampler.resetVideoStatus(); } @@ -234,4 +248,9 @@ public void testStarted() { timeMachine.reset(); } + private void readObject(ObjectInputStream inputStream) + throws IOException, ClassNotFoundException { + inputStream.defaultReadObject(); + initHttpSampler(); + } } diff --git a/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java b/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java new file mode 100644 index 0000000..846f136 --- /dev/null +++ b/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java @@ -0,0 +1,33 @@ +package com.blazemeter.jmeter.hls.logic; + +import com.blazemeter.jmeter.videostreaming.core.SampleResultProcessor; +import com.blazemeter.jmeter.videostreaming.core.TimeMachine; +import com.blazemeter.jmeter.videostreaming.core.VideoStreamingHttpClient; +import com.blazemeter.jmeter.videostreaming.core.VideoStreamingSampler; +import com.blazemeter.jmeter.videostreaming.dash.DashSampler; + +public class VideoStreamingSamplerFactory { + + public VideoStreamingSampler getVideoStreamingSampler(String url, HlsSampler baseSampler, + VideoStreamingHttpClient httpClient, + TimeMachine timeMachine, SampleResultProcessor sampleResultProcessor) { + //HLS Master Playlist must contain this .m3u8 extension in their URLs + if (url.contains(".m3u8")) { + return createHlsSampler(baseSampler, httpClient, timeMachine, sampleResultProcessor); + } else { + return createDashSampler(baseSampler, httpClient, timeMachine, sampleResultProcessor); + } + } + + private DashSampler createDashSampler(HlsSampler baseSampler, VideoStreamingHttpClient httpClient, + TimeMachine timeMachine, SampleResultProcessor sampleResultProcessor) { + return new DashSampler(baseSampler, httpClient, timeMachine, sampleResultProcessor); + } + + private com.blazemeter.jmeter.videostreaming.hls.HlsSampler createHlsSampler( + HlsSampler baseSampler, VideoStreamingHttpClient httpClient, TimeMachine timeMachine, + SampleResultProcessor sampleResultProcessor) { + return new com.blazemeter.jmeter.videostreaming.hls.HlsSampler(baseSampler, httpClient, + timeMachine, sampleResultProcessor); + } +} diff --git a/src/test/java/com/blazemeter/jmeter/MasterSlaveIT.java b/src/test/java/com/blazemeter/jmeter/MasterSlaveIT.java new file mode 100644 index 0000000..4659cfb --- /dev/null +++ b/src/test/java/com/blazemeter/jmeter/MasterSlaveIT.java @@ -0,0 +1,84 @@ +package com.blazemeter.jmeter; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.RuleChain; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.Container.ExecResult; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; +import org.testcontainers.images.builder.ImageFromDockerfile; + +@Category(MasterSlaveIT.class) +public class MasterSlaveIT { + + private static final Logger LOG = LoggerFactory.getLogger(MasterSlaveIT.class); + private static final long TIMEOUT_MILLI = 120000; + private static final String JMETER_HOME_PATH = "/jmeter/apache-jmeter-5.1.1/bin"; + private static final Network network = Network.newNetwork(); + + public GenericContainer wiremockContainer; + public GenericContainer container; + + @Rule + public RuleChain chain = RuleChain + .outerRule(wiremockContainer = buildWiremockContainerFromDockerfile()) + .around(container = getJavaContainerFromDockerfile()); + + @Test(timeout = TIMEOUT_MILLI * 2) + public void shouldRunHLSMasterSlaveTestWhenStartContainer() + throws IOException, InterruptedException { + ExecResult execResult = container.execInContainer("sh", JMETER_HOME_PATH + "/jmeter", + "-n", "-r", "-t", "/test.jmx", "-l", "/result", "-j", "/master_logs"); + + assertThat(execResult.getStdout()).contains("... end of run"); + } + + private GenericContainer buildWiremockContainerFromDockerfile() { + return new GenericContainer<>( + new ImageFromDockerfile() + //adding files to test-container context + .withFileFromClasspath("mapping.json", "master-slave/mapping.json") + .withFileFromClasspath("Dockerfile", "master-slave/WiremockDockerfile") + .withDockerfilePath("Dockerfile")) + .withLogConsumer(new Slf4jLogConsumer(LOG).withPrefix("WIREMOCK")) + .withExposedPorts(8080) + .withNetwork(network) + .withNetworkAliases("wiremock") + // wait for wiremock to be running + .waitingFor(new LogMessageWaitStrategy() + .withRegEx(".*(/\\$\\$ /\\$\\$ /\\$\\$ " + + " /\\$\\$ /\\$\\$ /\\$\\$ ).*") + .withStartupTimeout(Duration.ofMillis(TIMEOUT_MILLI))); + } + + private GenericContainer getJavaContainerFromDockerfile() { + return new GenericContainer<>( + new ImageFromDockerfile() + //adding files to test-container context + .withFileFromClasspath("master-slave-test.sh", "master-slave/master-slave-test.sh") + .withFileFromClasspath("Dockerfile", "master-slave/Dockerfile") + .withFileFromClasspath("test.jmx", "master-slave/HLSSamplerSlaveRemoteTest.jmx") + .withFileFromFile("jmeter-bzm-hls.jar", + new File("target/jmeter-test/lib/jmeter-bzm-hls.jar")) + .withFileFromFile("hlsparserj.jar", + new File("target/jmeter-test/lib/hlsparserj.jar")) + .withDockerfilePath("Dockerfile")) + .withLogConsumer(new Slf4jLogConsumer(LOG).withPrefix("MAIN")) + .withNetwork(network) + .withNetworkAliases("master") + .waitingFor(new LogMessageWaitStrategy().withRegEx(".*Created\\sremote\\sobject.*") + .withStartupTimeout(Duration.ofMillis(TIMEOUT_MILLI))); + + } + +} diff --git a/src/test/java/com/blazemeter/jmeter/hls/logic/HlsSamplerTest.java b/src/test/java/com/blazemeter/jmeter/hls/logic/HlsSamplerTest.java new file mode 100644 index 0000000..6deff29 --- /dev/null +++ b/src/test/java/com/blazemeter/jmeter/hls/logic/HlsSamplerTest.java @@ -0,0 +1,53 @@ +package com.blazemeter.jmeter.hls.logic; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; + +import com.blazemeter.jmeter.videostreaming.core.MediaSegment; +import com.blazemeter.jmeter.videostreaming.core.SampleResultProcessor; +import com.blazemeter.jmeter.videostreaming.core.TimeMachine; +import com.blazemeter.jmeter.videostreaming.core.VideoStreamingHttpClient; +import com.blazemeter.jmeter.videostreaming.core.VideoStreamingSampler; +import com.blazemeter.jmeter.videostreaming.hls.Playlist; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class HlsSamplerTest { + + @Mock + private VideoStreamingSampler videoStreamingSampler; + @Mock + private VideoStreamingSamplerFactory factory; + @Mock + private VideoStreamingHttpClient client; + @Mock + private TimeMachine timeMachine; + @Mock + private SampleResultProcessor processor; + + private HlsSampler hlsSampler; + + @Before + public void setUp() { + hlsSampler = new HlsSampler(factory, client, processor, timeMachine); + } + + @Test + public void shouldFactoryGetVideoStreamingSamplerWhenSample() { + String masterUrl = "hls_master_playlist.m3u8"; + hlsSampler.setMasterUrl(masterUrl); + + doReturn(videoStreamingSampler) + .when(factory) + .getVideoStreamingSampler(masterUrl, hlsSampler, client, timeMachine, processor); + + hlsSampler.sample(); + verify(factory, only()) + .getVideoStreamingSampler(masterUrl, hlsSampler, client, timeMachine, processor); + } +} \ No newline at end of file diff --git a/src/test/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactoryTest.java b/src/test/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactoryTest.java new file mode 100644 index 0000000..2c78f6c --- /dev/null +++ b/src/test/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactoryTest.java @@ -0,0 +1,43 @@ +package com.blazemeter.jmeter.hls.logic; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.blazemeter.jmeter.videostreaming.core.SampleResultProcessor; +import com.blazemeter.jmeter.videostreaming.core.TimeMachine; +import com.blazemeter.jmeter.videostreaming.core.VideoStreamingHttpClient; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +public class VideoStreamingSamplerFactoryTest { + + @Mock + private HlsSampler proxy; + @Mock + private VideoStreamingHttpClient client; + @Mock + private TimeMachine timeMachine; + @Mock + private SampleResultProcessor processor; + + private VideoStreamingSamplerFactory factory; + + @Before + public void setUp() { + factory = new VideoStreamingSamplerFactory(); + } + + @Test + public void shouldCreateHlsSamplerWhenUrlContainsExtension() { + assertThat(factory + .getVideoStreamingSampler("test.com/master.m3u8", proxy, client, timeMachine, processor)) + .isInstanceOf(com.blazemeter.jmeter.videostreaming.hls.HlsSampler.class); + } + + @Test + public void shouldNotCreateHlsSamplerWhenUrlNotContainsExtension() { + assertThat(factory + .getVideoStreamingSampler("test.com/master.mpd", proxy, client, timeMachine, processor)) + .isNotInstanceOf(com.blazemeter.jmeter.videostreaming.hls.HlsSampler.class); + } +} \ No newline at end of file diff --git a/src/test/resources/log4j2-test.xml b/src/test/resources/log4j2-test.xml index a1cfa7a..6b57f32 100644 --- a/src/test/resources/log4j2-test.xml +++ b/src/test/resources/log4j2-test.xml @@ -10,5 +10,6 @@ + diff --git a/src/test/resources/master-slave/Dockerfile b/src/test/resources/master-slave/Dockerfile new file mode 100644 index 0000000..830f5a6 --- /dev/null +++ b/src/test/resources/master-slave/Dockerfile @@ -0,0 +1,32 @@ +FROM java:8 + +# Updating system & installing net-tools to have ifconfig command avaiable +# Due to issue non-zero code 100 (returned by apt-get update -y), this was the approach found to install net-tools. +RUN apt-get update -y || apt-get install -y net-tools + +ENV JMETER_VERSION 5.1.1 + +RUN mkdir /jmeter \ + && cd /jmeter/ \ + && wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz \ + && tar -xvzf apache-jmeter-$JMETER_VERSION.tgz \ + && rm apache-jmeter-$JMETER_VERSION.tgz \ + && chmod +x /jmeter/apache-jmeter-$JMETER_VERSION/bin/jmeter-server \ + && chmod +x /jmeter/apache-jmeter-$JMETER_VERSION/bin/jmeter + +ENV JMETER_HOME /jmeter/apache-jmeter-$JMETER_VERSION/bin + +# Disable keystore file on jmeter.properties +RUN sed -i '334s/.*/server.rmi.ssl.disable=true/' $JMETER_HOME/jmeter.properties + +COPY test.jmx /test.jmx +COPY master-slave-test.sh /master-slave-test.sh +COPY hlsparserj.jar jmeter/apache-jmeter-$JMETER_VERSION/lib/ +COPY jmeter-bzm-hls.jar jmeter/apache-jmeter-$JMETER_VERSION/lib/ext/ + +RUN chmod +x /master-slave-test.sh +# Setting master alias on jmeter.properties as remote host +RUN sed -i '259s/.*/remote_hosts=master/' $JMETER_HOME/jmeter.properties +CMD /master-slave-test.sh $JMETER_HOME + + diff --git a/src/test/resources/master-slave/HLSSamplerSlaveRemoteTest.jmx b/src/test/resources/master-slave/HLSSamplerSlaveRemoteTest.jmx new file mode 100644 index 0000000..85b873a --- /dev/null +++ b/src/test/resources/master-slave/HLSSamplerSlaveRemoteTest.jmx @@ -0,0 +1,49 @@ + + + + + + false + true + false + + + + + + + + stoptestnow + + false + 1 + + 1 + 1 + false + + + + + + + + + true + true + http://wiremock:8080/path/about_demo_hls_600k.m3u8 + true + 12 + + + minBandwidth + + minResolution + + false + + + + + + diff --git a/src/test/resources/master-slave/WiremockDockerfile b/src/test/resources/master-slave/WiremockDockerfile new file mode 100644 index 0000000..c55ac9d --- /dev/null +++ b/src/test/resources/master-slave/WiremockDockerfile @@ -0,0 +1,14 @@ +FROM java:8 + +ENV WIREMOCK_VERSION 2.26.3 + +RUN mkdir -p /wiremock/mappings/ \ + && cd /wiremock \ + && wget https://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/$WIREMOCK_VERSION/wiremock-standalone-$WIREMOCK_VERSION.jar \ + && cd /wiremock + +COPY mapping.json /wiremock/mappings/ +RUN nc -l 8080 & +# wiremock needs in same level as mappings +CMD cd /wiremock \ + && java -jar wiremock-standalone-$WIREMOCK_VERSION.jar --port=8080 \ No newline at end of file diff --git a/src/test/resources/master-slave/mapping.json b/src/test/resources/master-slave/mapping.json new file mode 100644 index 0000000..eff3900 --- /dev/null +++ b/src/test/resources/master-slave/mapping.json @@ -0,0 +1,65 @@ +{ + "mappings": [ + { + "request": { + "url": "/path/about_demo_hls_600k.m3u8", + "method": "GET" + }, + "response": { + "status": "200", + "headers": { + "X-GUploader-UploadID": "AAANsUkpELiAyEqX8NgkcOzv_u6YzHyhfz-wiy8O3bwBAUv8hNV0eKxBt38c2jNbaU_pBmDXjDXEJCFIyR-HZt6iR2E", + "ETag": "\"91c66e59d98212c729d67453d1e403ad\"", + "x-goog-generation": "1568384854647258", + "x-goog-metageneration": "1", + "x-goog-stored-content-encoding": "identity", + "x-goog-stored-content-length": "5302", + "Content-Type": "binary/octet-stream", + "x-goog-hash": [ + "crc32c=2XOV5g==", + "md5=kcZuWdmCEscp1nRT0eQDrQ==" + ], + "x-goog-storage-class": "STANDARD", + "Accept-Ranges": "bytes", + "Vary": "Origin", + "Server": "UploadServer", + "Cache-Control": "public, max-age=3600", + "Age": "174", + "Alt-Svc": "clear" + }, + "body": "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-ALLOW-CACHE:YES\n#EXT-X-TARGETDURATION:13\n#EXTINF:12.046444,\nabout_demo_hls_600k00000.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00001.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00002.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00003.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00004.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00005.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00006.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00007.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00008.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00009.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00010.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00011.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00012.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00013.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00014.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00015.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00016.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00017.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00018.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00019.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00020.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00021.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00022.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00023.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00024.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00025.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00026.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00027.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00028.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00029.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00030.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00031.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00032.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00033.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00034.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00035.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00036.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00037.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00038.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00039.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00040.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00041.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00042.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00043.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00044.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00045.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00046.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00047.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00048.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00049.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00050.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00051.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00052.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00053.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00054.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00055.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00056.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00057.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00058.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00059.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00060.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00061.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00062.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00063.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00064.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00065.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00066.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00067.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00068.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00069.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00070.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00071.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00072.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00073.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00074.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00075.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00076.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00077.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00078.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00079.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00080.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00081.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00082.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00083.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00084.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00085.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00086.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00087.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00088.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00089.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00090.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00091.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00092.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00093.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00094.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00095.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00096.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00097.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00098.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00099.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00100.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00101.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00102.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00103.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00104.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00105.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00106.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00107.ts\n#EXTINF:12.000000,\nabout_demo_hls_600k00108.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00109.ts\n#EXTINF:9.000000,\nabout_demo_hls_600k00110.ts\n#EXTINF:10.133333,\nabout_demo_hls_600k00111.ts\n#EXT-X-ENDLIST\n" + } + }, + { + "request": { + "url": "/path/about_demo_hls_600k00000.ts", + "method": "GET" + }, + "response": { + "status": "200", + "headers": { + "X-GUploader-UploadID": "AAANsUnT6Fniltl-sOPZh1dlN6YxXuQVEVhPNyd2qzEbOh8qldDBAllORRpOk5aOUPe78v58Az7yMiH7a4X4r7zPtZMki5v-UQ", + "ETag": "\"75f541c4609ea7a94aaa498bc4c17ab2\"", + "x-goog-generation": "1568384853113469", + "x-goog-metageneration": "1", + "x-goog-stored-content-encoding": "identity", + "x-goog-stored-content-length": "542192", + "Content-Type": "text/plain", + "x-goog-hash": [ + "crc32c=L3kjVQ==", + "md5=dfVBxGCep6lKqkmLxMF6sg==" + ], + "x-goog-storage-class": "STANDARD", + "Accept-Ranges": "bytes", + "Vary": "Origin", + "Server": "UploadServer", + "Cache-Control": "public, max-age=3600", + "Age": "173", + "Alt-Svc": "clear", + "X-MEDIA-SEGMENT-DURATION": "12.0" + }, + "body": "TestResponseData" + } + } + ] +} diff --git a/src/test/resources/master-slave/master-slave-test.sh b/src/test/resources/master-slave/master-slave-test.sh new file mode 100644 index 0000000..75752b6 --- /dev/null +++ b/src/test/resources/master-slave/master-slave-test.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +echo "Starting slave" +sh $1/jmeter-server > /slave_output & +touch /master_logs /result +tail -f /slave_output /master_logs /result diff --git a/src/test/resources/master-slave/rmi_keystore.jks b/src/test/resources/master-slave/rmi_keystore.jks new file mode 100644 index 0000000..ebb2c73 Binary files /dev/null and b/src/test/resources/master-slave/rmi_keystore.jks differ