// ================================================================================================= // Copyright 2011 Twitter, Inc. // ------------------------------------------------------------------------------------------------- // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this work except in compliance with the License. // You may obtain a copy of the License in the LICENSE file, or 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.common.net.http.handlers; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.junit.Before; import org.junit.Test; import com.twitter.common.collections.Iterables2; import com.twitter.common.net.http.handlers.TimeSeriesDataSource.ResponseStruct; import com.twitter.common.stats.TimeSeries; import com.twitter.common.stats.TimeSeriesRepository; import com.twitter.common.testing.easymock.EasyMockTest; import static org.easymock.EasyMock.expect; import static org.junit.Assert.assertEquals; public class TimeSeriesDataSourceTest extends EasyMockTest { private static final String TIME_COLUMN = TimeSeriesDataSource.TIME_METRIC; private static final String TIME_SERIES_1 = "time_series_1"; private static final String TIME_SERIES_2 = "time_series_2"; private static final List<Number> TIMESTAMPS = Arrays.<Number>asList(1d, 2d, 3d, 4d); private static final Map<String, TimeSeries> TS_DATA = ImmutableMap.of( TIME_SERIES_1, makeTimeSeries(TIME_SERIES_1, 1, 2, 3, 4), TIME_SERIES_2, makeTimeSeries(TIME_SERIES_2, 0, 0, 0, 0) ); private final Gson gson = new Gson(); private TimeSeriesDataSource dataSource; private TimeSeriesRepository timeSeriesRepo; @Before public void setUp() { timeSeriesRepo = createMock(TimeSeriesRepository.class); dataSource = new TimeSeriesDataSource(timeSeriesRepo); } @Test public void testGetColumns() throws Exception { expect(timeSeriesRepo.getAvailableSeries()).andReturn(TS_DATA.keySet()); control.replay(); List<String> columns = gson.fromJson( dataSource.getResponse(null, null), new TypeToken<List<String>>() { }.getType()); assertEquals(ImmutableList.copyOf(TS_DATA.keySet()), columns); } @Test @SuppressWarnings("unchecked") // Needed because type information lost in vargs. public void testGetAllData() throws Exception { expect(timeSeriesRepo.getTimestamps()).andReturn(TIMESTAMPS); expect(timeSeriesRepo.get(TIME_SERIES_1)).andReturn(TS_DATA.get(TIME_SERIES_1)); expect(timeSeriesRepo.get(TIME_SERIES_2)).andReturn(TS_DATA.get(TIME_SERIES_2)); control.replay(); String colString = Joiner.on(',').join( Arrays.asList(TIME_SERIES_1, TIME_SERIES_2, TIME_COLUMN)); ResponseStruct response = gson.fromJson( dataSource.getResponse(colString, null), ResponseStruct.class); assertEquals(ImmutableList.of(TIME_COLUMN, TIME_SERIES_1, TIME_SERIES_2), response.names); Iterable<List<Number>> expectedData = Iterables2.zip(0, TIMESTAMPS, getSamples(TIME_SERIES_1), getSamples(TIME_SERIES_2)); checkRows(expectedData, response.data); } @Test @SuppressWarnings("unchecked") // Needed because type information lost in vargs. public void testFilterByTime() throws Exception { expect(timeSeriesRepo.getTimestamps()).andReturn(TIMESTAMPS); expect(timeSeriesRepo.get(TIME_SERIES_1)).andReturn(TS_DATA.get(TIME_SERIES_1)); expect(timeSeriesRepo.get(TIME_SERIES_2)).andReturn(TS_DATA.get(TIME_SERIES_2)); control.replay(); String colString = Joiner.on(',').join( Arrays.asList(TIME_SERIES_1, TIME_SERIES_2, TIME_COLUMN)); ResponseStruct response = gson.fromJson( dataSource.getResponse(colString, "2"), ResponseStruct.class); Iterable<List<Number>> expectedData = Iterables2.zip(0, TIMESTAMPS, getSamples(TIME_SERIES_1), getSamples(TIME_SERIES_2)); expectedData = Iterables.filter(expectedData, new Predicate<List<Number>>() { @Override public boolean apply(List<Number> row) { return row.get(0).intValue() >= 3; } }); checkRows(expectedData, response.data); } private void checkRows(Iterable<List<Number>> expected, List<List<Number>> actual) { assertEquals(Iterables.size(expected), actual.size()); Iterator<List<Number>> actualIterator = actual.iterator(); for (List<Number> expectedRow : expected) { Iterator<Number> actualValueIterator = actualIterator.next().iterator(); for (Number expectedValue : expectedRow) { assertEquals("Expected row data " + expected + ", found " + actual, expectedValue.doubleValue(), actualValueIterator.next().doubleValue(), 1e-9); } } } private static Iterable<Number> getSamples(String tsName) { return TS_DATA.get(tsName).getSamples(); } private static TimeSeries makeTimeSeries(final String name, final Number... values) { final List<Number> samples = Lists.newArrayList(); for (Number value : values) samples.add(value.doubleValue()); return new TimeSeries() { @Override public String getName() { return name; } @Override public Iterable<Number> getSamples() { return samples; } }; } }