blob: 475a4a2743d5c0277a4c8f649a2e46a0ed54d0a1 [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.ignite.testframework.configvariations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtNewConstructor;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.testframework.junits.IgniteCacheConfigVariationsAbstractTest;
import org.apache.ignite.testframework.junits.IgniteConfigVariationsAbstractTest;
import org.jetbrains.annotations.Nullable;
/**
* Configuration variations test suite builder.
*/
public class ConfigVariationsTestSuiteBuilder {
/** */
private static final AtomicInteger cntr = new AtomicInteger(0);
/** */
private static final Map<String, VariationsTestsConfig> cfgs = new ConcurrentHashMap<>();
/** */
private ConfigParameter<IgniteConfiguration>[][] igniteParams =
ConfigVariations.igniteBasicSet();
/** */
private ConfigParameter<CacheConfiguration>[][] cacheParams;
/** */
private CacheStartMode cacheStartMode = CacheStartMode.DYNAMIC;
/** */
private boolean withClients;
/** */
private int gridsCnt = 3;
/** */
private int testedNodeCnt = 1;
/** */
private Class<? extends IgniteConfigVariationsAbstractTest> cls;
/** */
private int[] specificIgniteParam;
/** */
private int[] specificCacheParam;
/** */
private int backups = -1;
/** */
private IgnitePredicate<IgniteConfiguration>[] igniteCfgFilters;
/** */
private IgnitePredicate<CacheConfiguration>[] cacheCfgFilters;
/** */
private boolean skipWaitPartMapExchange;
/**
* @param cls Test class.
*/
public ConfigVariationsTestSuiteBuilder(Class<? extends IgniteConfigVariationsAbstractTest> cls) {
this.cls = cls;
}
/** Invoked by reflection in {@link #makeTestClass(String, VariationsTestsConfig)}. */
@SuppressWarnings("unused")
public static VariationsTestsConfig getCfg(String key) {
return cfgs.get(key);
}
/**
* Returns lists of test classes to execute with config variations.
*
* @return List of classes.
*/
public List<Class<?>> classes() {
assert testedNodeCnt > 0;
assert gridsCnt > 0;
VariationsIterator igniteCfgIter = specificIgniteParam == null ? new VariationsIterator(igniteParams)
: new OneElementVariationsIterator(specificIgniteParam, igniteParams);
final List<VariationsTestsConfig> cfgsToTest = new ArrayList<>();
for (; igniteCfgIter.hasNext(); ) {
final int[] igniteCfgVariation = igniteCfgIter.next();
if (!passIgniteConfigFilter(igniteCfgVariation))
continue;
if (cacheParams == null) {
cfgsToTest.addAll(build(igniteCfgVariation, null, true));
continue;
}
VariationsIterator cacheCfgIter = specificCacheParam == null ? new VariationsIterator(cacheParams)
: new OneElementVariationsIterator(specificCacheParam, cacheParams);
for (; cacheCfgIter.hasNext(); ) {
int[] cacheCfgVariation = cacheCfgIter.next();
if (!passCacheConfigFilter(cacheCfgVariation))
continue;
// Stop all grids before starting new ignite configuration.
boolean stopNodes = !cacheCfgIter.hasNext();
cfgsToTest.addAll(build(igniteCfgVariation, cacheCfgVariation, stopNodes));
}
}
String pkg = "org.apache.ignite.testframework.configvariations.generated";
return cfgsToTest.stream().map(cfg -> makeTestClass(pkg, cfg)).collect(Collectors.toList());
}
/**
* @param variation Variation.
* @return {@code True} if variation pass filters.
*/
private boolean passIgniteConfigFilter(int[] variation) {
ConfigVariationsFactory factory = new ConfigVariationsFactory(igniteParams, variation, null, null);
IgniteConfiguration cfg = factory.getConfiguration(null, null);
if (igniteCfgFilters != null) {
for (IgnitePredicate<IgniteConfiguration> filter : igniteCfgFilters) {
if (!filter.apply(cfg))
return false;
}
}
return true;
}
/**
* @param variation Variation.
* @return {@code True} if variation pass filters.
*/
private boolean passCacheConfigFilter(int[] variation) {
ConfigVariationsFactory factory = new ConfigVariationsFactory(null, null, cacheParams, variation);
CacheConfiguration cfg = factory.cacheConfiguration(null);
if (cacheCfgFilters != null) {
for (IgnitePredicate<CacheConfiguration> filter : cacheCfgFilters) {
if (!filter.apply(cfg))
return false;
}
}
return true;
}
/**
* @param igniteCfgVariation Ignite Variation.
* @param cacheCfgVariation Cache Variation.
* @param stopNodes Stop nodes.
* @return List of config variations.
*/
private List<VariationsTestsConfig> build(int[] igniteCfgVariation, @Nullable int[] cacheCfgVariation,
boolean stopNodes) {
ConfigVariationsFactory factory = new ConfigVariationsFactory(igniteParams,
igniteCfgVariation, cacheParams, cacheCfgVariation);
factory.backups(backups);
String clsNameSuffix = "[igniteCfgVariation=" + Arrays.toString(igniteCfgVariation)
+ ", cacheCfgVariation=" + Arrays.toString(cacheCfgVariation)
+ ", igniteCfg=" + factory.getIgniteConfigurationDescription()
+ ", cacheCfg=" + factory.getCacheConfigurationDescription() + "]";
VariationsTestsConfig testCfg = new VariationsTestsConfig(factory, clsNameSuffix, stopNodes, cacheStartMode,
gridsCnt, !skipWaitPartMapExchange);
return testedNodeCnt > 1 ? createMultiNodeTestSuite(
testCfg, testedNodeCnt, withClients, skipWaitPartMapExchange) : Collections.singletonList(testCfg);
}
/**
* @param cfg Configuration.
* @param testedNodeCnt Count of tested nodes.
*/
private static List<VariationsTestsConfig> createMultiNodeTestSuite(
VariationsTestsConfig cfg, int testedNodeCnt, boolean withClients, boolean skipWaitParMapExchange) {
List<VariationsTestsConfig> suite = new ArrayList<>();
if (cfg.gridCount() < testedNodeCnt)
throw new IllegalArgumentException("Failed to initialize test suite [nodeCnt=" + testedNodeCnt
+ ", cfgGridCnt=" + cfg.gridCount() + "]");
for (int i = 0; i < testedNodeCnt; i++) {
boolean stopNodes = cfg.isStopNodes() && i + 1 == testedNodeCnt;
boolean startCache = i == 0;
boolean stopCache = i + 1 == testedNodeCnt;
VariationsTestsConfig cfg0 = new VariationsTestsConfig(cfg.configurationFactory(), cfg.description(),
stopNodes, startCache, stopCache, cfg.cacheStartMode(), cfg.gridCount(), i, withClients,
!skipWaitParMapExchange);
suite.add(cfg0);
}
return suite;
}
/**
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder withClients() {
if (testedNodeCnt < 2)
throw new IllegalStateException("Tested node count should be more than 1: " + testedNodeCnt);
withClients = true;
return this;
}
/**
* All tests will be run for first {@code testedNodeCnt} grids. For {@code grid(0)}, {@code grid(1)}, ... ,
* {@code grid(testedNodeCnt - 1)}.
* <p>
* Usually it needs if you want to execute tests for both client and data nodes (see {@link #withClients()}).
* <ul>
* <li> If test-class extends {@link IgniteConfigVariationsAbstractTest} then use {@code testedNodesCount(2)}. </li>
* <li> if test-class extends {@link IgniteCacheConfigVariationsAbstractTest} then use {@code testedNodesCount(3)}. </li>
*</ul>
*
* @param testedNodeCnt Tested node count.
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder testedNodesCount(int testedNodeCnt) {
this.testedNodeCnt = testedNodeCnt;
return this;
}
/**
* @param cnt Count.
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder gridsCount(int cnt) {
assert cnt > 0;
gridsCnt = cnt;
return this;
}
/**
* @param igniteParams New ignite params.
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder igniteParams(
ConfigParameter<IgniteConfiguration>[][] igniteParams) {
this.igniteParams = igniteParams.clone();
return this;
}
/**
* @param cacheParams New cache params.
* @return {@code this} for chaining.
*/
@SuppressWarnings("unused")
public ConfigVariationsTestSuiteBuilder cacheParams(ConfigParameter<CacheConfiguration>[][] cacheParams) {
this.cacheParams = cacheParams.clone();
return this;
}
/**
* Sets basic cache params and basic count of backups.
*
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder withBasicCacheParams() {
cacheParams = ConfigVariations.cacheBasicSet();
backups = 1;
return this;
}
/**
* @param backups Backups.
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder backups(int backups) {
assert backups > 0 : backups;
this.backups = backups;
return this;
}
/**
* @param singleIgniteParam Param.
* @return {@code this} for chaining.
*/
@SuppressWarnings("unused")
public ConfigVariationsTestSuiteBuilder specifyIgniteParam(int... singleIgniteParam) {
specificIgniteParam = singleIgniteParam.clone();
return this;
}
/**
* @param singleParam Param.
* @return {@code this} for chaining.
*/
@SuppressWarnings("unused")
public ConfigVariationsTestSuiteBuilder specifyCacheParam(int... singleParam) {
specificCacheParam = singleParam.clone();
return this;
}
/**
* @param filters Ignite configuration filters.
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder withIgniteConfigFilters(IgnitePredicate<IgniteConfiguration>... filters) {
igniteCfgFilters = filters.clone();
return this;
}
/** */
public ConfigVariationsTestSuiteBuilder skipWaitPartitionMapExchange() {
skipWaitPartMapExchange = true;
return this;
}
/**
* @param filters Ignite configuration filters.
* @return {@code this} for chaining.
*/
public ConfigVariationsTestSuiteBuilder withCacheConfigFilters(IgnitePredicate<CacheConfiguration>... filters) {
cacheCfgFilters = filters.clone();
return this;
}
/** */
private static class OneElementVariationsIterator extends VariationsIterator {
/** */
private int[] elem;
/** */
private boolean hasNext = true;
/**
* @param elem Element.
*/
OneElementVariationsIterator(int[] elem, Object[][] params) {
super(params);
this.elem = elem;
}
/** {@inheritDoc} */
@Override public boolean hasNext() {
return hasNext;
}
/** {@inheritDoc} */
@SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException")
@Override public int[] next() {
hasNext = false;
return elem.clone();
}
}
/**
* Creates test class for given config variation.
*
* @param pkg Package to put class in.
* @param cfg Config variation.
* @return Test class.
*/
private Class<?> makeTestClass(String pkg, VariationsTestsConfig cfg) {
int idx = cntr.getAndIncrement();
String clsName = cls.getSimpleName() + "_" + idx;
cfgs.put(clsName, cfg);
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(new ClassClassPath(ConfigVariationsTestSuiteBuilder.class));
CtClass cl = cp.makeClass(pkg + "." + clsName);
try {
cl.setSuperclass(cp.get(cls.getName()));
cl.addConstructor(CtNewConstructor.make("public " + clsName + "() { "
+ "this.testsCfg = "
+ ConfigVariationsTestSuiteBuilder.class.getName()
+ ".getCfg(\"" + clsName + "\"); "
+ "}", cl));
return cl.toClass();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}