Skip to content

Commit

Permalink
[TEST] Scope.SUITE is not reproducible due to late cluster initiali…
Browse files Browse the repository at this point in the history
…zation

The cluster for `Scope.SUITE` tests must be initialize in a static manner
before the first test runs otherwise the random context used to initialize
the cluster is taken from tests randomness rather than the suites randomness.
This means test clusters will have different setups if only a single test is
executed or even the test might have a entirely different random sequence.
  • Loading branch information
s1monw committed Oct 26, 2014
1 parent dde52c8 commit d6aeed6
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,11 @@ protected final void beforeInternal() throws IOException {
switch (currentClusterScope) {
case GLOBAL:
clearClusters();
assert GLOBAL_CLUSTER != null : "Scope.GLOBAL cluster must be initialied in a static context";
currentCluster = GLOBAL_CLUSTER;
break;
case SUITE:
assert clusters.containsKey(this.getClass()) : "Scope.SUITE cluster must be initialized in a static context";
currentCluster = buildAndPutCluster(currentClusterScope, false);
break;
case TEST:
Expand Down Expand Up @@ -513,6 +515,8 @@ public TestCluster buildAndPutCluster(Scope currentClusterScope, boolean createI
if (createIfExists || testCluster == null) {
testCluster = buildTestCluster(currentClusterScope);
} else {
// if we carry that cluster over ie. it was already there we remove it and then call clearClusters
// and put it back afterwards such that all pending leftover clusters are closed and disposed...
clusters.remove(this.getClass());
}
clearClusters();
Expand Down Expand Up @@ -1385,7 +1389,7 @@ public void clearScroll(String... scrollIds) {
assertThat(clearResponse.isSucceeded(), equalTo(true));
}

private ClusterScope getAnnotation(Class<?> clazz) {
private static ClusterScope getAnnotation(Class<?> clazz) {
if (clazz == Object.class || clazz == ElasticsearchIntegrationTest.class) {
return null;
}
Expand All @@ -1397,7 +1401,11 @@ private ClusterScope getAnnotation(Class<?> clazz) {
}

private Scope getCurrentClusterScope() {
ClusterScope annotation = getAnnotation(this.getClass());
return getCurrentClusterScope(this.getClass());
}

private static Scope getCurrentClusterScope(Class<?> clazz) {
ClusterScope annotation = getAnnotation(clazz);
// if we are not annotated assume global!
return annotation == null ? Scope.GLOBAL : annotation.scope();
}
Expand Down Expand Up @@ -1575,8 +1583,20 @@ public static void afterClass() throws IOException {

private static void initializeSuiteScope() throws Exception {
Class<?> targetClass = getContext().getTargetClass();
/**
* Note we create these test class instance via reflection
* since JUnit creates a new instance per test and that is also
* the reason why INSTANCE is static since this entire method
* must be executed in a static context.
*/
assert INSTANCE == null;
if (isSuiteScope(targetClass)) {
if (getCurrentClusterScope(targetClass) == Scope.SUITE) {
final ElasticsearchIntegrationTest instance = (ElasticsearchIntegrationTest) targetClass.newInstance();
// if we have suite scoped cluster here we need to initialize the
// cluster before the first test starts for reproducibility
instance.buildAndPutCluster(Scope.SUITE, false);
}
if (isSuiteScopedTest(targetClass)) {
// note we need to do this this way to make sure this is reproducible
INSTANCE = (ElasticsearchIntegrationTest) targetClass.newInstance();
boolean success = false;
Expand All @@ -1603,15 +1623,26 @@ private static void initializeSuiteScope() throws Exception {
protected void setupSuiteScopeCluster() throws Exception {
}

private static boolean isSuiteScope(Class<?> clazz) {
private static boolean isSuiteScopedTest(Class<?> clazz) {
if (clazz == Object.class || clazz == ElasticsearchIntegrationTest.class) {
return false;
}
SuiteScopeTest annotation = clazz.getAnnotation(SuiteScopeTest.class);
if (annotation != null) {
return true;
}
return isSuiteScopedTest(clazz.getSuperclass());
}

private static boolean isSuiteScopeCluster(Class<?> clazz) {
if (clazz == Object.class || clazz == ElasticsearchIntegrationTest.class) {
return false;
}
SuiteScopeTest annotation = clazz.getAnnotation(SuiteScopeTest.class);
if (annotation != null) {
return true;
}
return isSuiteScope(clazz.getSuperclass());
return isSuiteScopedTest(clazz.getSuperclass());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.test.test;

import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.TestCluster;
import org.junit.Test;

import java.io.IOException;

import static org.hamcrest.Matchers.equalTo;

/**
* This test ensures that the cluster initializion for GLOBAL scope is not influencing
* the tests random sequence due to initializtion using the same random instance.
*/
public class GlobalScopeClusterTests extends ElasticsearchIntegrationTest {
private static int ITER = 0;
private static long[] SEQUENCE = new long[100];

@Test
@Repeat(iterations = 10, useConstantSeed = true)
public void testReproducible() {
if (ITER++ == 0) {
for (int i = 0; i < SEQUENCE.length; i++) {
SEQUENCE[i] = randomLong();
}
} else {
for (int i = 0; i < SEQUENCE.length; i++) {
assertThat(SEQUENCE[i], equalTo(randomLong()));
}
}
}

protected TestCluster buildTestCluster(Scope scope) throws IOException {
// produce some randomness
int iters = between(1, 100);
for (int i = 0; i < iters; i++) {
randomLong();
}
return super.buildTestCluster(scope);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.test.test;

import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.TestCluster;
import org.junit.Test;

import java.io.IOException;

import static org.hamcrest.Matchers.equalTo;

/**
* This test ensures that the cluster initializion for suite scope is not influencing
* the tests random sequence due to initializtion using the same random instance.
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE)
public class SuiteScopeClusterTests extends ElasticsearchIntegrationTest {
private static int ITER = 0;
private static long[] SEQUENCE = new long[100];

@Test
@Repeat(iterations = 10, useConstantSeed = true)
public void testReproducible() {
if (ITER++ == 0) {
for (int i = 0; i < SEQUENCE.length; i++) {
SEQUENCE[i] = randomLong();
}
} else {
for (int i = 0; i < SEQUENCE.length; i++) {
assertThat(SEQUENCE[i], equalTo(randomLong()));
}
}
}

protected TestCluster buildTestCluster(Scope scope) throws IOException {
// produce some randomness
int iters = between(1, 100);
for (int i = 0; i < iters; i++) {
randomLong();
}
return super.buildTestCluster(scope);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.test.test;

import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.TestCluster;
import org.junit.Test;

import java.io.IOException;

import static org.hamcrest.Matchers.equalTo;

/**
* This test ensures that the cluster initializion for TEST scope is not influencing
* the tests random sequence due to initializtion using the same random instance.
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST)
public class TestScopeClusterTests extends ElasticsearchIntegrationTest {
private static int ITER = 0;
private static long[] SEQUENCE = new long[100];

@Test
@Repeat(iterations = 10, useConstantSeed = true)
public void testReproducible() {
if (ITER++ == 0) {
for (int i = 0; i < SEQUENCE.length; i++) {
SEQUENCE[i] = randomLong();
}
} else {
for (int i = 0; i < SEQUENCE.length; i++) {
assertThat(SEQUENCE[i], equalTo(randomLong()));
}
}
}

protected TestCluster buildTestCluster(Scope scope) throws IOException {
// produce some randomness
int iters = between(1, 100);
for (int i = 0; i < iters; i++) {
randomLong();
}
return super.buildTestCluster(scope);
}
}

0 comments on commit d6aeed6

Please sign in to comment.