blob: 3f9e1258536da5955ed6a846fbe25ce074547625 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.lens.cube.metadata.timeline;
import static org.testng.Assert.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import org.apache.lens.cube.metadata.TestTimePartition;
import org.apache.lens.cube.metadata.TimePartition;
import org.apache.lens.cube.metadata.UpdatePeriod;
import org.apache.lens.cube.metadata.UpdatePeriodTest;
import org.apache.lens.server.api.error.LensException;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TestPartitionTimelines {
private static final String TABLE_NAME = "storage_fact";
private static final String PART_COL = "pt";
private static final List<Class<? extends PartitionTimeline>> TIMELINE_IMPLEMENTATIONS = Arrays.asList(
StoreAllPartitionTimeline.class,
EndsAndHolesPartitionTimeline.class,
RangesPartitionTimeline.class
);
@DataProvider(name = "update-periods")
public Object[][] provideUpdatePeriods() {
return UpdatePeriodTest.provideUpdatePeriods();
}
@DataProvider(name = "update-periods-and-timeline-classes")
public Object[][] provideUpdatePeriodsAndTimelineClasses() {
UpdatePeriod[] values = UpdatePeriod.values();
Object[][] ret = new Object[values.length * TIMELINE_IMPLEMENTATIONS.size()][2];
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < TIMELINE_IMPLEMENTATIONS.size(); j++) {
ret[TIMELINE_IMPLEMENTATIONS.size() * i + j] = new Object[]{
values[i],
TIMELINE_IMPLEMENTATIONS.get(j),
};
}
}
return ret;
}
@Test(dataProvider = "update-periods")
public void testEquivalence(UpdatePeriod period) throws LensException, InvocationTargetException,
NoSuchMethodException, InstantiationException, IllegalAccessException {
final Random randomGenerator = new Random();
for (int j = 0; j < 10; j++) {
List<PartitionTimeline> timelines = Lists.newArrayList();
for (Class<? extends PartitionTimeline> clazz : TIMELINE_IMPLEMENTATIONS) {
timelines.add(getInstance(clazz, period));
}
final List<TimePartition> addedPartitions = Lists.newArrayList();
for (int i = 0; i < 20; i++) {
int randomInt = randomGenerator.nextInt(10) - 5;
TimePartition part = TimePartition.of(period, TestTimePartition.timeAtDiff(TestTimePartition.NOW, period,
randomInt));
addedPartitions.add(part);
for (PartitionTimeline timeline : timelines) {
timeline.add(part);
}
assertSameTimelines(timelines);
}
assertSameTimelines(timelines);
Collections.shuffle(addedPartitions);
Iterator<TimePartition> iter = addedPartitions.iterator();
while (iter.hasNext()) {
TimePartition part = iter.next();
iter.remove();
if (!addedPartitions.contains(part)) {
for (PartitionTimeline timeline : timelines) {
timeline.drop(part);
assertTrue(timeline.isConsistent());
}
}
}
for (PartitionTimeline timeline : timelines) {
assertTrue(timeline.isEmpty());
}
}
}
public static void assertSameTimelines(List<PartitionTimeline> timelines) {
List<Iterator<TimePartition>> iterators = Lists.newArrayList();
for (PartitionTimeline timeline : timelines) {
iterators.add(timeline.iterator());
}
while (iterators.get(0).hasNext()) {
Map<Class, TimePartition> parts = Maps.newHashMap();
for (Iterator<TimePartition> iterator : iterators) {
assertTrue(iterator.hasNext());
parts.put(iterator.getClass(), iterator.next());
}
assertEquals(new HashSet<>(parts.values()).size(), 1, "More than one values for next: " + parts.values());
}
for (Iterator<TimePartition> iterator : iterators) {
assertFalse(iterator.hasNext());
}
}
private <T extends PartitionTimeline> T getInstance(Class<T> clz, UpdatePeriod period) throws
NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return clz.getConstructor(String.class, UpdatePeriod.class, String.class)
.newInstance(TABLE_NAME, period, PART_COL);
}
@Test(dataProvider = "update-periods-and-timeline-classes")
public <T extends PartitionTimeline> void testPropertiesContract(UpdatePeriod period, Class<T> clz) throws
LensException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
// Make two instances, one to modify, other to validate against
T inst1 = getInstance(clz, period);
T inst2 = getInstance(clz, period);
// whenever we'll init from props, timeline should become empty.
Map<String, String> props = inst1.toProperties();
assertTrue(inst2.initFromProperties(props));
// init from props of an empty timeline: should succeed and make the timeline empty
assertEquals(inst1, inst2);
assertTrue(inst1.isEmpty());
assertTrue(inst2.isEmpty());
// Add single partition and test for non-equivalence
assertTrue(inst1.add(TimePartition.of(period, TestTimePartition.NOW)));
assertFalse(inst1.equals(inst2));
// add same parittion in other timeline, test for equality
assertTrue(inst2.add(TimePartition.of(period, TestTimePartition.NOW)));
assertTrue(inst1.isConsistent());
assertTrue(inst2.isConsistent());
assertEquals(inst1, inst2);
// init with blank properties. Should become empty
assertTrue(inst2.initFromProperties(props));
assertFalse(inst1.equals(inst2));
// init from properties of timeline with single partition.
assertTrue(inst2.initFromProperties(inst1.toProperties()));
assertEquals(inst1, inst2);
// clear timelines
inst1.initFromProperties(props);
inst2.initFromProperties(props);
// Make sparse partition range in one, init other from its properties. Test equality.
for (int i = 0; i < 500; i++) {
assertTrue(inst1.add(TimePartition.of(period, TestTimePartition.timeAtDiff(TestTimePartition.NOW, period,
i * 2))));
}
assertTrue(inst1.isConsistent());
inst2.initFromProperties(inst1.toProperties());
assertTrue(inst2.isConsistent());
assertEquals(inst1, inst2);
}
}