// Copyright 2016 Twitter. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.twitter.heron.metricsmgr.sink;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import com.twitter.heron.spi.metricsmgr.metrics.ExceptionInfo;
import com.twitter.heron.spi.metricsmgr.metrics.MetricsInfo;
import com.twitter.heron.spi.metricsmgr.metrics.MetricsRecord;
import com.twitter.heron.spi.metricsmgr.sink.SinkContext;
/**
* WebSink Tester.
*/
public class WebSinkTest {
private class WebTestSink extends WebSink {
public String servicePath;
public int servicePort;
@Override
protected void startHttpServer(String path, int port) {
this.servicePath = path;
this.servicePort = port;
}
public Map<String, Object> getMetrics() {
return super.metricsCache.asMap();
}
public void syncCache() {
super.metricsCache.cleanUp();
}
}
private Map<String, Object> defaultConf;
private SinkContext context;
private List<MetricsRecord> records;
@Before
public void before() throws IOException {
defaultConf = new HashMap<>();
defaultConf.put("port", "9999");
defaultConf.put("path", "test");
defaultConf.put("flat-metrics", "true");
defaultConf.put("include-topology-name", "false");
context = Mockito.mock(SinkContext.class);
Mockito.when(context.getTopologyName()).thenReturn("testTopology");
Mockito.when(context.getSinkId()).thenReturn("testId");
Iterable<MetricsInfo> infos = Arrays.asList(new MetricsInfo("metric_1", "1.0"),
new MetricsInfo("metric_2", "2.0"));
records = Arrays.asList(
new MetricsRecord("machine/stuff/record_1", infos, Collections.<ExceptionInfo>emptyList()),
new MetricsRecord("record_2", infos, Collections.<ExceptionInfo>emptyList()));
}
/**
* Testing exception when illegal port is specified
*/
@Test(expected = IllegalArgumentException.class)
public void testIllegalPort() {
Map<String, Object> conf = new HashMap<>(defaultConf);
conf.put("port", "fdfsaf");
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
}
/**
* Testing exception when no port is specified
*/
@Test(expected = IllegalArgumentException.class)
public void testNoPort() {
Map<String, Object> conf = new HashMap<>(defaultConf);
conf.remove("port");
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
}
/**
* Testing port and path setting
*/
@Test
public void testPortAndPath() {
Map<String, Object> conf = new HashMap<>(defaultConf);
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
Assert.assertEquals(sink.servicePort, 9999);
Assert.assertEquals(sink.servicePath, "test");
}
/**
* Testing flat map with metrics
*/
@Test
public void testFlatMetrics() {
Map<String, Object> conf = new HashMap<>(defaultConf);
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
for (MetricsRecord r : records) {
sink.processRecord(r);
}
Map<String, Object> results = sink.getMetrics();
Assert.assertEquals(4, results.size());
Assert.assertEquals(results.get("/stuff/record_1/metric_1"), 1.0d);
Assert.assertEquals(results.get("/stuff/record_1/metric_2"), 2.0d);
Assert.assertEquals(results.get("/record_2/metric_1"), 1.0d);
Assert.assertEquals(results.get("/record_2/metric_2"), 2.0d);
}
/**
* Testing flat map with metrics, prefixed with topology name
*/
@Test
public void testIncludeTopologyName() {
Map<String, Object> conf = new HashMap<>(defaultConf);
conf.put("include-topology-name", "true");
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
for (MetricsRecord r : records) {
sink.processRecord(r);
}
Map<String, Object> results = sink.getMetrics();
Assert.assertEquals(4, results.size());
Assert.assertEquals(results.get("testTopology/stuff/record_1/metric_1"), 1.0d);
Assert.assertEquals(results.get("testTopology/stuff/record_1/metric_2"), 2.0d);
Assert.assertEquals(results.get("testTopology/record_2/metric_1"), 1.0d);
Assert.assertEquals(results.get("testTopology/record_2/metric_2"), 2.0d);
}
/**
* Testing grouped map with metrics
*/
@Test
public void testGroupedMetrics() {
Map<String, Object> conf = new HashMap<>(defaultConf);
conf.put("flat-metrics", "false");
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
for (MetricsRecord r : records) {
sink.processRecord(r);
}
//Update and override MetricsRecord 1
Iterable<MetricsInfo> infos2 = Arrays.asList(new MetricsInfo("metric_1", "3.0"),
new MetricsInfo("metric_3", "1.0"));
sink.processRecord(new MetricsRecord(records.get(0).getSource(),
infos2,
Collections.<ExceptionInfo>emptyList()));
Map<String, Object> results = sink.getMetrics();
Assert.assertEquals(2, results.size());
@SuppressWarnings("unchecked")
Map<String, Object> record1 = (Map<String, Object>) results.get("/stuff/record_1");
@SuppressWarnings("unchecked")
Map<String, Object> record2 = (Map<String, Object>) results.get("/record_2");
Assert.assertEquals(record1.get("metric_1"), 3.0d);
Assert.assertEquals(record1.get("metric_2"), 2.0d);
Assert.assertEquals(record1.get("metric_3"), 1.0d);
Assert.assertEquals(record2.get("metric_1"), 1.0d);
Assert.assertEquals(record2.get("metric_2"), 2.0d);
}
/**
* Testinging max metics size, and oldest keys get expired
*/
@Test
public void testMaxMetrics() {
Map<String, Object> conf = new HashMap<>(defaultConf);
conf.put("metrics-cache-max-size", "2");
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
for (MetricsRecord r : records) {
sink.processRecord(r);
}
Map<String, Object> results = sink.getMetrics();
Assert.assertEquals(2, results.size());
Assert.assertEquals(results.get("/record_2/metric_1"), 1.0d);
Assert.assertEquals(results.get("/record_2/metric_2"), 2.0d);
}
/**
* Testinging TTL
*/
@Test
public void testTTLMetrics() throws InterruptedException {
Map<String, Object> conf = new HashMap<>(defaultConf);
conf.put("metrics-cache-ttl-sec", "1");
WebTestSink sink = new WebTestSink();
sink.init(conf, context);
for (MetricsRecord r : records) {
sink.processRecord(r);
}
Thread.sleep(1100);
sink.syncCache();
Map<String, Object> results = sink.getMetrics();
Assert.assertEquals(0, results.size());
}
}