http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java new file mode 100644 index 0000000..e14f966 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java @@ -0,0 +1,1434 @@ +/* + * 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.junits; + +import junit.framework.*; +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.cluster.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.events.*; +import org.apache.ignite.internal.*; +import org.apache.ignite.internal.util.*; +import org.apache.ignite.lang.*; +import org.apache.ignite.marshaller.*; +import org.apache.ignite.marshaller.jdk.*; +import org.apache.ignite.marshaller.optimized.*; +import org.apache.log4j.*; +import org.apache.ignite.internal.processors.license.*; +import org.apache.ignite.internal.processors.resource.*; +import org.apache.ignite.spi.checkpoint.sharedfs.*; +import org.apache.ignite.spi.communication.tcp.*; +import org.apache.ignite.spi.discovery.tcp.*; +import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.testframework.*; +import org.apache.ignite.testframework.config.*; +import org.jetbrains.annotations.*; +import org.springframework.beans.*; +import org.springframework.context.*; +import org.springframework.context.support.*; + +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +import static org.apache.ignite.cache.GridCacheAtomicWriteOrderMode.*; +import static org.apache.ignite.cache.GridCacheAtomicityMode.*; +import static org.apache.ignite.cache.GridCacheDistributionMode.*; +import static org.apache.ignite.cache.GridCacheWriteSynchronizationMode.*; + +/** + * Common abstract test for GridGain tests. + */ +@SuppressWarnings({ + "TransientFieldInNonSerializableClass", + "ProhibitedExceptionDeclared", + "JUnitTestCaseWithNonTrivialConstructors" +}) +public abstract class GridAbstractTest extends TestCase { + /************************************************************** + * DO NOT REMOVE TRANSIENT - THIS OBJECT MIGHT BE TRANSFERRED * + * TO ANOTHER NODE. * + **************************************************************/ + /** Null name for execution map. */ + private static final String NULL_NAME = UUID.randomUUID().toString(); + + /** */ + private static final long DFLT_TEST_TIMEOUT = 5 * 60 * 1000; + + /** */ + private static final transient Map<Class<?>, TestCounters> tests = new ConcurrentHashMap<>(); + + /** */ + private transient boolean startGrid; + + /** */ + protected transient IgniteLogger log; + + /** */ + private transient ClassLoader clsLdr; + + /** */ + private transient boolean stopGridErr; + + /** Timestamp for tests. */ + private static long ts = System.currentTimeMillis(); + + static { + System.setProperty(IgniteSystemProperties.GG_ATOMIC_CACHE_DELETE_HISTORY_SIZE, "10000"); + + Thread timer = new Thread(new GridTestClockTimer(), "gridgain-clock-for-tests"); + + timer.setDaemon(true); + + timer.setPriority(10); + + timer.start(); + } + + /** */ + protected GridAbstractTest() { + this(false); + + log = getTestCounters().getTestResources().getLogger().getLogger(getClass()); + } + + /** + * @param startGrid Start grid flag. + */ + @SuppressWarnings({"OverriddenMethodCallDuringObjectConstruction"}) + protected GridAbstractTest(boolean startGrid) { + assert isJunitFrameworkClass() : "GridAbstractTest class cannot be extended directly " + + "(use GridCommonAbstractTest class instead)."; + + // Initialize properties. Logger initialized here. + GridTestProperties.init(); + + log = getTestCounters().getTestResources().getLogger().getLogger(getClass()); + + this.startGrid = startGrid; + } + + /** + * @param cls Class to create. + * @return Instance of class. + * @throws Exception If failed. + */ + protected <T> T allocateInstance(Class<T> cls) throws Exception { + return (T)GridUnsafe.unsafe().allocateInstance(cls); + } + + /** + * @param cls Class to create. + * @return Instance of class. + */ + @Nullable protected <T> T allocateInstance0(Class<T> cls) { + try { + return (T)GridUnsafe.unsafe().allocateInstance(cls); + } + catch (InstantiationException e) { + e.printStackTrace(); + + return null; + } + } + + /** + * @return Flag to check if class is Junit framework class. + */ + protected boolean isJunitFrameworkClass() { + return false; + } + + /** + * @return Test resources. + */ + protected GridTestResources getTestResources() { + return getTestCounters().getTestResources(); + } + + /** + * @param msg Message to print. + */ + protected void info(String msg) { + if (log().isInfoEnabled()) + log().info(msg); + } + + /** + * @param msg Message to print. + */ + protected void error(String msg) { + log().error(msg); + } + + /** + * @param msg Message to print. + * @param t Error to print. + */ + protected void error(String msg, Throwable t) { + log().error(msg, t); + } + + /** + * @return logger. + */ + protected IgniteLogger log() { + return log; + } + + /** + * Resets log4j programmatically. + * + * @param log4jLevel Level. + * @param logToFile If {@code true}, then log to file under "work/log" folder. + * @param cat Category. + * @param cats Additional categories. + */ + @SuppressWarnings({"deprecation"}) + protected void resetLog4j(Level log4jLevel, boolean logToFile, String cat, String... cats) { + for (String c : F.concat(false, cat, F.asList(cats))) + Logger.getLogger(c).setLevel(log4jLevel); + + if (logToFile) { + Logger log4j = Logger.getRootLogger(); + + log4j.removeAllAppenders(); + + // Console appender. + ConsoleAppender c = new ConsoleAppender(); + + c.setName("CONSOLE_ERR"); + c.setTarget("System.err"); + c.setThreshold(Priority.WARN); + c.setLayout(new PatternLayout("[%d{ABSOLUTE}][%-5p][%t][%c{1}] %m%n")); + + c.activateOptions(); + + log4j.addAppender(c); + + // File appender. + RollingFileAppender file = new RollingFileAppender(); + + file.setName("FILE"); + file.setThreshold(log4jLevel); + file.setFile(home() + "/work/log/gridgain.log"); + file.setAppend(false); + file.setMaxFileSize("10MB"); + file.setMaxBackupIndex(10); + file.setLayout(new PatternLayout("[%d{ABSOLUTE}][%-5p][%t][%c{1}] %m%n")); + + file.activateOptions(); + + log4j.addAppender(file); + } + } + + /** + * Executes runnable and prints out how long it took. + * + * @param name Name of execution. + * @param r Runnable to execute. + */ + protected void time(String name, Runnable r) { + System.gc(); + + long start = System.currentTimeMillis(); + + r.run(); + + long dur = System.currentTimeMillis() - start; + + info(name + " took " + dur + "ms."); + } + + /** + * Runs given code in multiple threads and synchronously waits for all threads to complete. + * If any thread failed, exception will be thrown out of this method. + * + * @param r Runnable. + * @param threadNum Thread number. + * @throws Exception If failed. + */ + protected void multithreaded(Runnable r, int threadNum) throws Exception { + multithreaded(r, threadNum, getTestGridName()); + } + + /** + * Runs given code in multiple threads and synchronously waits for all + * threads to complete. If any thread failed, exception will be thrown + * out of this method. + * + * @param r Runnable. + * @param threadNum Thread number. + * @param threadName Thread name. + * @throws Exception If failed. + */ + protected void multithreaded(Runnable r, int threadNum, String threadName) throws Exception { + GridTestUtils.runMultiThreaded(r, threadNum, threadName); + } + + /** + * Runs given code in multiple threads. Returns future that ends upon + * threads completion. If any thread failed, exception will be thrown + * out of this method. + * + * @param r Runnable. + * @param threadNum Thread number. + * @throws Exception If failed. + * @return Future. + */ + protected IgniteFuture<?> multithreadedAsync(Runnable r, int threadNum) throws Exception { + return multithreadedAsync(r, threadNum, getTestGridName()); + } + + /** + * Runs given code in multiple threads. Returns future that ends upon + * threads completion. If any thread failed, exception will be thrown + * out of this method. + * + * @param r Runnable. + * @param threadNum Thread number. + * @param threadName Thread name. + * @throws Exception If failed. + * @return Future. + */ + protected IgniteFuture<?> multithreadedAsync(Runnable r, int threadNum, String threadName) throws Exception { + return GridTestUtils.runMultiThreadedAsync(r, threadNum, threadName); + } + + /** + * Runs given code in multiple threads and synchronously waits for all threads to complete. + * If any thread failed, exception will be thrown out of this method. + * + * @param c Callable. + * @param threadNum Thread number. + * @throws Exception If failed. + */ + protected void multithreaded(Callable<?> c, int threadNum) throws Exception { + multithreaded(c, threadNum, getTestGridName()); + } + + /** + * Runs given code in multiple threads and synchronously waits for all threads to complete. + * If any thread failed, exception will be thrown out of this method. + * + * @param c Callable. + * @param threadNum Thread number. + * @param threadName Thread name. + * @throws Exception If failed. + */ + protected void multithreaded(Callable<?> c, int threadNum, String threadName) throws Exception { + GridTestUtils.runMultiThreaded(c, threadNum, threadName); + } + + /** + * Runs given code in multiple threads and asynchronously waits for all threads to complete. + * If any thread failed, exception will be thrown out of this method. + * + * @param c Callable. + * @param threadNum Thread number. + * @throws Exception If failed. + * @return Future. + */ + protected IgniteFuture<?> multithreadedAsync(Callable<?> c, int threadNum) throws Exception { + return multithreadedAsync(c, threadNum, getTestGridName()); + } + + /** + * Runs given code in multiple threads and asynchronously waits for all threads to complete. + * If any thread failed, exception will be thrown out of this method. + * + * @param c Callable. + * @param threadNum Thread number. + * @param threadName Thread name. + * @throws Exception If failed. + * @return Future. + */ + protected IgniteFuture<?> multithreadedAsync(Callable<?> c, int threadNum, String threadName) throws Exception { + return GridTestUtils.runMultiThreadedAsync(c, threadNum, threadName); + } + + /** + * @return Test kernal context. + */ + protected GridTestKernalContext newContext() { + GridTestKernalContext ctx = new GridTestKernalContext(); + + ctx.config().setGridLogger(log()); + + return ctx; + } + + /** + * Called before execution of every test method in class. + * + * @throws Exception If failed. {@link #afterTest()} will be called in this case. + */ + protected void beforeTest() throws Exception { + // No-op. + } + + /** + * Called after execution of every test method in class or + * if {@link #beforeTest()} failed without test method execution. + * + * @throws Exception If failed. + */ + protected void afterTest() throws Exception { + // No-op. + } + + /** + * Called before execution of all test methods in class. + * + * @throws Exception If failed. {@link #afterTestsStopped()} will be called in this case. + */ + protected void beforeTestsStarted() throws Exception { + // No-op. + } + + /** + * Called after execution of all test methods in class or + * if {@link #beforeTestsStarted()} failed without execution of any test methods. + * + * @throws Exception If failed. + */ + protected void afterTestsStopped() throws Exception { + // No-op. + } + + /** {@inheritDoc} */ + @Override protected void setUp() throws Exception { + stopGridErr = false; + + clsLdr = Thread.currentThread().getContextClassLoader(); + + // Change it to the class one. + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + + // Clear log throttle. + LT.clear(); + + // Clear license registry. + GridLicenseUseRegistry.clear(); + + TestCounters cntrs = getTestCounters(); + + if (isDebug()) + info("Test counters [numOfTests=" + cntrs.getNumberOfTests() + ", started=" + cntrs.getStarted() + + ", stopped=" + cntrs.getStopped() + ']'); + + if (cntrs.isReset()) { + info("Resetting test counters."); + + int started = cntrs.getStarted() % cntrs.getNumberOfTests(); + int stopped = cntrs.getStopped() % cntrs.getNumberOfTests(); + + cntrs.reset(); + + cntrs.setStarted(started); + cntrs.setStopped(stopped); + } + + if (isFirstTest()) { + info(">>> Starting test class: " + getClass().getSimpleName() + " <<<"); + + if (startGrid) { + IgniteConfiguration cfg = optimize(getConfiguration()); + + G.start(cfg); + } + + try { + beforeTestsStarted(); + } + catch (Exception | Error t) { + t.printStackTrace(); + + getTestCounters().setStopped(getTestCounters().getNumberOfTests() - 1); + + try { + tearDown(); + } + catch (Exception e) { + log.error("Failed to tear down test after exception was thrown in beforeTestsStarted (will " + + "ignore)", e); + } + + throw t; + } + } + + info(">>> Starting test: " + getName() + " <<<"); + + try { + beforeTest(); + } + catch (Exception | Error t) { + try { + tearDown(); + } + catch (Exception e) { + log.error("Failed to tear down test after exception was thrown in beforeTest (will ignore)", e); + } + + throw t; + } + + ts = System.currentTimeMillis(); + } + + /** + * @return Started grid. + * @throws Exception If anything failed. + */ + protected Ignite startGrid() throws Exception { + return startGrid(getTestGridName()); + } + + /** + * @param cnt Grid count. + * @return First started grid. + * @throws Exception If failed. + */ + protected final Ignite startGrids(int cnt) throws Exception { + assert cnt > 0; + + Ignite ignite = null; + + for (int i = 0; i < cnt; i++) + if (ignite == null) + ignite = startGrid(i); + else + startGrid(i); + + checkTopology(cnt); + + assert ignite != null; + + return ignite; + } + + /** + * @param cnt Grid count. + * @return First started grid. + * @throws Exception If failed. + */ + protected Ignite startGridsMultiThreaded(int cnt) throws Exception { + if (cnt == 1) + return startGrids(1); + + Ignite ignite = startGridsMultiThreaded(0, cnt); + + checkTopology(cnt); + + return ignite; + } + + /** + * @param init Start grid index. + * @param cnt Grid count. + * @return First started grid. + * @throws Exception If failed. + */ + protected final Ignite startGridsMultiThreaded(int init, int cnt) throws Exception { + assert init >= 0; + assert cnt > 0; + + info("Starting grids: " + cnt); + + final AtomicInteger gridIdx = new AtomicInteger(init); + + GridTestUtils.runMultiThreaded( + new Callable<Object>() { + @Nullable @Override public Object call() throws Exception { + startGrid(gridIdx.getAndIncrement()); + + return null; + } + }, + cnt, + "grid-starter-" + getName() + ); + + assert gridIdx.get() - init == cnt; + + return grid(init); + } + + /** + * @param cnt Grid count + * @throws Exception If an error occurs. + */ + @SuppressWarnings({"BusyWait"}) + protected void checkTopology(int cnt) throws Exception { + for (int j = 0; j < 10; j++) { + boolean topOk = true; + + for (int i = 0; i < cnt; i++) { + if (cnt != grid(i).nodes().size()) { + U.warn(log, "Grid size is incorrect (will re-run check in 1000 ms) " + + "[name=" + grid(i).name() + ", size=" + grid(i).nodes().size() + ']'); + + topOk = false; + + break; + } + } + + if (topOk) + return; + else + Thread.sleep(1000); + } + + throw new Exception("Failed to wait for proper topology."); + } + + /** */ + protected void stopGrid() { + stopGrid(getTestGridName()); + } + + /** + * Starts new grid with given index. + * + * @param idx Index of the grid to start. + * @return Started grid. + * @throws Exception If anything failed. + */ + protected Ignite startGrid(int idx) throws Exception { + return startGrid(getTestGridName(idx)); + } + + /** + * Starts new grid with given index and Spring application context. + * + * @param idx Index of the grid to start. + * @param ctx Spring context. + * @return Started grid. + * @throws Exception If anything failed. + */ + protected Ignite startGrid(int idx, GridSpringResourceContext ctx) throws Exception { + return startGrid(getTestGridName(idx), ctx); + } + + /** + * Starts new grid with given name. + * + * @param gridName Grid name. + * @return Started grid. + * @throws Exception If failed. + */ + protected Ignite startGrid(String gridName) throws Exception { + return startGrid(gridName, (GridSpringResourceContext)null); + } + + /** + * Starts new grid with given name. + * + * @param gridName Grid name. + * @param ctx Spring context. + * @return Started grid. + * @throws Exception If failed. + */ + protected Ignite startGrid(String gridName, GridSpringResourceContext ctx) throws Exception { + return GridGainEx.start(optimize(getConfiguration(gridName)), ctx); + } + + /** + * Optimizes configuration to achieve better test performance. + * + * @param cfg Configuration. + * @return Optimized configuration (by modifying passed in one). + */ + protected IgniteConfiguration optimize(IgniteConfiguration cfg) { + // TODO: GG-4048: propose another way to avoid network overhead in tests. + if (cfg.getLocalHost() == null) { + if (cfg.getDiscoverySpi() instanceof TcpDiscoverySpi) + cfg.setLocalHost("127.0.0.1"); + else + cfg.setLocalHost(getTestResources().getLocalHost()); + } + + // Do not add redundant data if it is not needed. + if (cfg.getIncludeProperties() == null) + cfg.setIncludeProperties(); + + return cfg; + } + + /** + * @param gridName Grid name. + */ + protected void stopGrid(@Nullable String gridName) { + stopGrid(gridName, true); + } + + /** + * @param gridName Grid name. + * @param cancel Cancel flag. + */ + @SuppressWarnings({"deprecation"}) + protected void stopGrid(@Nullable String gridName, boolean cancel) { + try { + Ignite ignite = G.ignite(gridName); + + assert ignite != null : "GridGain returned null grid for name: " + gridName; + + info(">>> Stopping grid [name=" + ignite.name() + ", id=" + ignite.cluster().localNode().id() + ']'); + + G.stop(gridName, cancel); + } + catch (IllegalStateException ignored) { + // Ignore error if grid already stopped. + } + catch (Throwable e) { + error("Failed to stop grid [gridName=" + gridName + ", cancel=" + cancel + ']', e); + + stopGridErr = true; + } + } + + /** + * + */ + protected void stopAllGrids() { + stopAllGrids(true); + } + + /** + * @param cancel Cancel flag. + */ + protected void stopAllGrids(boolean cancel) { + List<Ignite> ignites = G.allGrids(); + + for (Ignite g : ignites) + stopGrid(g.name(), cancel); + + assert G.allGrids().isEmpty(); + } + + /** + * @param cancel Cancel flag. + */ + protected void stopAllClients(boolean cancel) { + List<Ignite> ignites = G.allGrids(); + + for (Ignite g : ignites) { + if (g.cluster().localNode().isClient()) + stopGrid(g.name(), cancel); + } + } + + /** + * @param cancel Cancel flag. + */ + protected void stopAllServers(boolean cancel) { + List<Ignite> ignites = G.allGrids(); + + for (Ignite g : ignites) { + if (!g.cluster().localNode().isClient()) + stopGrid(g.name(), cancel); + } + } + + /** + * @param ignite Grid + * @param cnt Count + * @throws IgniteCheckedException If failed. + */ + @SuppressWarnings({"BusyWait"}) + protected void waitForRemoteNodes(Ignite ignite, int cnt) throws IgniteCheckedException { + while (true) { + Collection<ClusterNode> nodes = ignite.cluster().forRemotes().nodes(); + + if (nodes != null && nodes.size() >= cnt) + return; + + try { + Thread.sleep(100); + } + catch (InterruptedException ignored) { + throw new IgniteCheckedException("Interrupted while waiting for remote nodes [gridName=" + ignite.name() + + ", count=" + cnt + ']'); + } + } + } + + /** + * @param ignites Grids + * @throws IgniteCheckedException If failed. + */ + protected void waitForDiscovery(Ignite... ignites) throws IgniteCheckedException { + assert ignites != null; + assert ignites.length > 1; + + for (Ignite ignite : ignites) + waitForRemoteNodes(ignite, ignites.length - 1); + } + + /** + * Gets grid for given name. + * + * @param name Name. + * @return Grid instance. + */ + protected GridEx grid(String name) { + return (GridEx)G.ignite(name); + } + + /** + * Gets grid for given index. + * + * @param idx Index. + * @return Grid instance. + */ + protected GridEx grid(int idx) { + return (GridEx)G.ignite(getTestGridName(idx)); + } + + /** + * @param idx Index. + * @return Ignite instance. + */ + protected Ignite ignite(int idx) { + return G.ignite(getTestGridName(idx)); + } + + /** + * Gets grid for given test. + * + * @return Grid for given test. + */ + protected GridEx grid() { + return (GridEx)G.ignite(getTestGridName()); + } + + /** + * Starts grid using provided grid name and spring config location. + * <p> + * Note that grids started this way should be stopped with {@code G.stop(..)} methods. + * + * @param gridName Grid name. + * @param springCfgPath Path to config file. + * @return Grid Started grid. + * @throws Exception If failed. + */ + protected Ignite startGrid(String gridName, String springCfgPath) throws Exception { + return startGrid(gridName, loadConfiguration(springCfgPath)); + } + + /** + * Starts grid using provided grid name and config. + * <p> + * Note that grids started this way should be stopped with {@code G.stop(..)} methods. + * + * @param gridName Grid name. + * @param cfg Config. + * @return Grid Started grid. + * @throws Exception If failed. + */ + protected Ignite startGrid(String gridName, IgniteConfiguration cfg) throws Exception { + cfg.setGridName(gridName); + + return G.start(cfg); + } + + /** + * Loads configuration from the given Spring XML file. + * + * @param springCfgPath Path to file. + * @return Grid configuration. + * @throws IgniteCheckedException If load failed. + */ + @SuppressWarnings("deprecation") + protected IgniteConfiguration loadConfiguration(String springCfgPath) throws IgniteCheckedException { + URL cfgLocation = U.resolveGridGainUrl(springCfgPath); + + assert cfgLocation != null; + + ApplicationContext springCtx; + + try { + springCtx = new FileSystemXmlApplicationContext(cfgLocation.toString()); + } + catch (BeansException e) { + throw new IgniteCheckedException("Failed to instantiate Spring XML application context.", e); + } + + Map cfgMap; + + try { + // Note: Spring is not generics-friendly. + cfgMap = springCtx.getBeansOfType(IgniteConfiguration.class); + } + catch (BeansException e) { + throw new IgniteCheckedException("Failed to instantiate bean [type=" + IgniteConfiguration.class + ", err=" + + e.getMessage() + ']', e); + } + + if (cfgMap == null) + throw new IgniteCheckedException("Failed to find a single grid factory configuration in: " + springCfgPath); + + if (cfgMap.isEmpty()) + throw new IgniteCheckedException("Can't find grid factory configuration in: " + springCfgPath); + else if (cfgMap.size() > 1) + throw new IgniteCheckedException("More than one configuration provided for cache load test: " + cfgMap.values()); + + IgniteConfiguration cfg = (IgniteConfiguration)cfgMap.values().iterator().next(); + + cfg.setNodeId(UUID.randomUUID()); + + return cfg; + } + + /** + * @param idx Index of the grid to stop. + */ + protected void stopGrid(int idx) { + stopGrid(getTestGridName(idx), false); + } + + /** + * @param idx Grid index. + * @param cancel Cancel flag. + */ + @SuppressWarnings("deprecation") + protected void stopGrid(int idx, boolean cancel) { + String gridName = getTestGridName(idx); + + try { + Ignite ignite = G.ignite(gridName); + + assert ignite != null : "GridGain returned null grid for name: " + gridName; + + info(">>> Stopping grid [name=" + ignite.name() + ", id=" + ignite.cluster().localNode().id() + ']'); + + G.stop(gridName, cancel); + } + catch (IllegalStateException ignored) { + // Ignore error if grid already stopped. + } + catch (Throwable e) { + error("Failed to stop grid [gridName=" + gridName + ", cancel=" + cancel + ']', e); + + stopGridErr = true; + } + } + + /** + * @param idx Index of the grid to stop. + */ + protected void stopAndCancelGrid(int idx) { + stopGrid(getTestGridName(idx), true); + } + + /** + * @return Grid test configuration. + * @throws Exception If failed. + */ + protected IgniteConfiguration getConfiguration() throws Exception { + // Generate unique grid name. + return getConfiguration(getTestGridName()); + } + + /** + * This method should be overridden by subclasses to change configuration parameters. + * + * @param gridName Grid name. + * @return Grid configuration used for starting of grid. + * @throws Exception If failed. + */ + @SuppressWarnings("deprecation") + protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = getConfiguration(gridName, getTestResources()); + + cfg.setNodeId(null); + + return cfg; + } + + /** + * This method should be overridden by subclasses to change configuration parameters. + * + * @return Grid configuration used for starting of grid. + * @param rsrcs Resources. + * @throws Exception If failed. + */ + protected IgniteConfiguration getConfiguration(GridTestResources rsrcs) throws Exception { + return getConfiguration(getTestGridName(), rsrcs); + } + + /** + * @return Generated unique test grid name. + */ + public String getTestGridName() { + String[] parts = getClass().getName().split("\\."); + + return parts[parts.length - 2] + '.' + parts[parts.length - 1]; + } + + /** + * @param idx Index of the grid. + * @return Indexed grid name. + */ + public String getTestGridName(int idx) { + return getTestGridName() + idx; + } + + /** + * Parses test grid index from test grid name. + * + * @param testGridName Test grid name, returned by {@link #getTestGridName(int)}. + * @return Test grid index. + */ + public int getTestGridIndex(String testGridName) { + return Integer.parseInt(testGridName.substring(getTestGridName().length())); + } + + /** + * @return {@code True} if system property -DDEBUG is set. + */ + public boolean isDebug() { + return System.getProperty("DEBUG") != null; + } + + /** + * @param marshaller Marshaller to get checkpoint path for. + * @return Path for specific marshaller. + */ + @SuppressWarnings({"IfMayBeConditional", "deprecation"}) + protected String getDefaultCheckpointPath(IgniteMarshaller marshaller) { + if (marshaller instanceof IgniteJdkMarshaller) + return SharedFsCheckpointSpi.DFLT_DIR_PATH + "/jdk/"; + else + return SharedFsCheckpointSpi.DFLT_DIR_PATH + '/' + marshaller.getClass().getSimpleName() + '/'; + } + + /** + * @param name Name to mask. + * @return Masked name. + */ + private String maskNull(String name) { + return name == null ? NULL_NAME : name; + } + + /** + * @return GridGain home. + */ + protected String home() { + return getTestResources().getGridgainHome(); + } + + /** + * This method should be overridden by subclasses to change configuration parameters. + * + * @return Grid configuration used for starting of grid. + * @param gridName Grid name. + * @param rsrcs Resources. + * @throws Exception If failed. + */ + @SuppressWarnings("deprecation") + protected IgniteConfiguration getConfiguration(String gridName, GridTestResources rsrcs) throws Exception { + IgniteConfiguration cfg = new IgniteConfiguration(); + + cfg.setGridName(gridName); + cfg.setGridLogger(rsrcs.getLogger()); + cfg.setMarshaller(rsrcs.getMarshaller()); + cfg.setNodeId(rsrcs.getNodeId()); + cfg.setGridGainHome(rsrcs.getGridgainHome()); + cfg.setMBeanServer(rsrcs.getMBeanServer()); + cfg.setPeerClassLoadingEnabled(true); + cfg.setMetricsLogFrequency(0); + + TcpCommunicationSpi commSpi = new TcpCommunicationSpi(); + + commSpi.setLocalPort(GridTestUtils.getNextCommPort(getClass())); + commSpi.setTcpNoDelay(true); + + cfg.setCommunicationSpi(commSpi); + + TcpDiscoverySpi discoSpi = new TcpDiscoverySpi(); + + if (isDebug()) { + discoSpi.setMaxMissedHeartbeats(Integer.MAX_VALUE); + cfg.setNetworkTimeout(Long.MAX_VALUE); + } + else { + // Set network timeout to 10 sec to avoid unexpected p2p class loading errors. + cfg.setNetworkTimeout(10000); + + // Increase max missed heartbeats to avoid unexpected node fails. + discoSpi.setMaxMissedHeartbeats(30); + } + + // Set heartbeat interval to 1 second to speed up tests. + discoSpi.setHeartbeatFrequency(1000); + + String mcastAddr = GridTestUtils.getNextMulticastGroup(getClass()); + + if (!F.isEmpty(mcastAddr)) { + TcpDiscoveryMulticastIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder(); + + ipFinder.setMulticastGroup(mcastAddr); + ipFinder.setMulticastPort(GridTestUtils.getNextMulticastPort(getClass())); + + discoSpi.setIpFinder(ipFinder); + } + + cfg.setDiscoverySpi(discoSpi); + + SharedFsCheckpointSpi cpSpi = new SharedFsCheckpointSpi(); + + Collection<String> paths = new ArrayList<>(); + + paths.add(getDefaultCheckpointPath(cfg.getMarshaller())); + + cpSpi.setDirectoryPaths(paths); + + cfg.setCheckpointSpi(cpSpi); + + cfg.setRestEnabled(false); + + cfg.setIncludeEventTypes(IgniteEventType.EVTS_ALL); + + return cfg; + } + + /** + * @return New cache configuration with modified defaults. + */ + public static CacheConfiguration defaultCacheConfiguration() { + CacheConfiguration cfg = new CacheConfiguration(); + + cfg.setStartSize(1024); + cfg.setQueryIndexEnabled(true); + cfg.setAtomicWriteOrderMode(PRIMARY); + cfg.setAtomicityMode(TRANSACTIONAL); + cfg.setDistributionMode(NEAR_PARTITIONED); + cfg.setWriteSynchronizationMode(FULL_SYNC); + cfg.setEvictionPolicy(null); + + return cfg; + } + + /** + * Gets external class loader. + * + * @return External class loader. + */ + protected static ClassLoader getExternalClassLoader() { + String path = GridTestProperties.getProperty("p2p.uri.cls"); + + try { + return new URLClassLoader(new URL[] {new URL(path)}, U.gridClassLoader()); + } + catch (MalformedURLException e) { + throw new RuntimeException("Failed to create URL: " + path, e); + } + } + + /** {@inheritDoc} */ + @Override protected void tearDown() throws Exception { + long dur = System.currentTimeMillis() - ts; + + info(">>> Stopping test: " + getName() + " in " + dur + " ms <<<"); + + TestCounters cntrs = getTestCounters(); + + if (isDebug()) + info("Test counters [numOfTests=" + cntrs.getNumberOfTests() + ", started=" + cntrs.getStarted() + + ", stopped=" + cntrs.getStopped() + ']'); + + try { + afterTest(); + } + finally { + if (isLastTest()) { + info(">>> Stopping test class: " + getClass().getSimpleName() + " <<<"); + + TestCounters counters = getTestCounters(); + + // Stop all threads started by runMultithreaded() methods. + GridTestUtils.stopThreads(log); + + // Safety. + getTestResources().stopThreads(); + + // Set reset flags, so counters will be reset on the next setUp. + counters.setReset(true); + + afterTestsStopped(); + + if (startGrid) + G.stop(getTestGridName(), true); + + // Remove counters. + tests.remove(getClass()); + + // Remove resources cached in static, if any. + GridClassLoaderCache.clear(); + IgniteOptimizedMarshaller.clearCache(); + IgniteMarshallerExclusions.clearCache(); + GridEnumCache.clear(); + } + + Thread.currentThread().setContextClassLoader(clsLdr); + + clsLdr = null; + } + } + + /** + * @return First test flag. + */ + protected boolean isFirstTest() { + TestCounters cntrs = getTestCounters(); + + return cntrs.getStarted() == 1 && cntrs.getStopped() == 0; + } + + /** + * @return Last test flag. + */ + protected boolean isLastTest() { + TestCounters cntrs = getTestCounters(); + + return cntrs.getStopped() == cntrs.getNumberOfTests(); + } + + /** + * @return Test counters. + */ + protected synchronized TestCounters getTestCounters() { + TestCounters tc = tests.get(getClass()); + + if (tc == null) + tests.put(getClass(), tc = new TestCounters()); + + return tc; + } + + /** {@inheritDoc} */ + @SuppressWarnings({"ProhibitedExceptionDeclared"}) + @Override protected void runTest() throws Throwable { + final AtomicReference<Throwable> ex = new AtomicReference<>(); + + Thread runner = new Thread("test-runner") { + @Override public void run() { + try { + runTestInternal(); + } + catch (Throwable e) { + IgniteClosure<Throwable, Throwable> hnd = errorHandler(); + + ex.set(hnd != null ? hnd.apply(e) : e); + } + } + }; + + runner.start(); + + runner.join(isDebug() ? 0 : getTestTimeout()); + + if (runner.isAlive()) { + U.error(log, + "Test has been timed out and will be interrupted (threads dump will be taken before interruption) [" + + "test=" + getName() + ", timeout=" + getTestTimeout() + ']'); + + // We dump threads to stdout, because we can loose logs in case + // the build is cancelled on TeamCity. + U.dumpThreads(null); + + U.dumpThreads(log); + + U.interrupt(runner); + + U.join(runner, log); + + throw new TimeoutException("Test has been timed out [test=" + getName() + ", timeout=" + + getTestTimeout() + ']' ); + } + + Throwable t = ex.get(); + + if (t != null) { + U.error(log, "Test failed.", t); + + throw t; + } + + assert !stopGridErr : "Error occurred on grid stop (see log for more details)."; + } + + /** + * @return Error handler to process all uncaught exceptions of the test run + * ({@code null} by default). + */ + protected IgniteClosure<Throwable, Throwable> errorHandler() { + return null; + } + + /** + * @throws Throwable If failed. + */ + @SuppressWarnings({"ProhibitedExceptionDeclared"}) + private void runTestInternal() throws Throwable { + super.runTest(); + } + + /** + * @return Test case timeout. + */ + protected long getTestTimeout() { + return getDefaultTestTimeout(); + } + + /** + * @return Default test case timeout. + */ + private long getDefaultTestTimeout() { + String timeout = GridTestProperties.getProperty("test.timeout"); + + if (timeout != null) + return Long.parseLong(timeout); + + return DFLT_TEST_TIMEOUT; + } + + /** + * Test counters. + */ + protected class TestCounters { + /** */ + private int numOfTests = -1; + + /** */ + private int started; + + /** */ + private int stopped; + + /** */ + private boolean reset; + + /** */ + private GridTestResources rsrcs = new GridTestResources(); + + /** + * @return Reset flag. + */ + public boolean isReset() { + return reset; + } + + /** + * @return Test resources. + */ + public GridTestResources getTestResources() { + return rsrcs; + } + + /** + * @param reset Reset flag. + */ + public void setReset(boolean reset) { + this.reset = reset; + } + + /** */ + public void reset() { + numOfTests = -1; + started = 0; + stopped = 0; + reset = false; + } + + /** + * @return Started flag. + */ + public int getStarted() { + return started; + } + + /** + * @param started Started flag. + */ + public void setStarted(int started) { + this.started = started; + } + + /** + * @return Stopped flag. + */ + public int getStopped() { + return stopped; + } + + /** + * @param stopped Stopped flag. + */ + public void setStopped(int stopped) { + this.stopped = stopped; + } + + /** */ + public void incrementStarted() { + if (isDebug()) + info("Incrementing started tests counter."); + + started++; + } + + /** */ + public void incrementStopped() { + if (isDebug()) + info("Incrementing stopped tests counter."); + + stopped++; + } + + /** + * @return Number of tests + */ + public int getNumberOfTests() { + if (numOfTests == -1) { + int cnt = 0; + + for (Method m : GridAbstractTest.this.getClass().getMethods()) + if (m.getDeclaringClass().getName().startsWith("org.gridgain") || + m.getDeclaringClass().getName().startsWith("org.apache.ignite")) { + if (m.getName().startsWith("test") && Modifier.isPublic(m.getModifiers())) + cnt++; + } + + numOfTests = cnt; + } + + countTestCases(); + + return numOfTests; + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestIgnite.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestIgnite.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestIgnite.java new file mode 100644 index 0000000..77eb689 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestIgnite.java @@ -0,0 +1,224 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.testframework.junits; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.cluster.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.hadoop.*; +import org.apache.ignite.marshaller.IgniteMarshaller; +import org.apache.ignite.plugin.*; +import org.apache.ignite.plugin.security.*; +import org.apache.ignite.product.*; +import org.jetbrains.annotations.*; + +import javax.management.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * Ignite mock. + */ +public class GridTestIgnite implements Ignite { + /** Ignite name */ + private final String name; + + /** Local host. */ + private final String locHost; + + /** */ + private final UUID nodeId; + + /** */ + private IgniteMarshaller marshaller; + + /** */ + private final MBeanServer jmx; + + /** */ + private final String home; + + /** + * Mock values + * + * @param name Name. + * @param locHost Local host. + * @param nodeId Node ID. + * @param marshaller Marshaller. + * @param jmx Jmx Bean Server. + * @param home Ignite home. + */ + public GridTestIgnite( + String name, String locHost, UUID nodeId, IgniteMarshaller marshaller, MBeanServer jmx, String home) { + this.locHost = locHost; + this.nodeId = nodeId; + this.marshaller = marshaller; + this.jmx = jmx; + this.home = home; + this.name = name; + } + + /** {@inheritDoc} */ + @Override public String name() { + return name; + } + + /** {@inheritDoc} */ + @Override public IgniteLogger log() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteConfiguration configuration() { + IgniteConfiguration cfg = new IgniteConfiguration(); + + cfg.setMarshaller(marshaller); + cfg.setNodeId(nodeId); + cfg.setMBeanServer(jmx); + cfg.setGridGainHome(home); + cfg.setLocalHost(locHost); + + return cfg; + } + + /** {@inheritDoc} */ + @Override public IgniteCluster cluster() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteCompute compute() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteCompute compute(ClusterGroup prj) { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteMessaging message() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteMessaging message(ClusterGroup prj) { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteEvents events() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteEvents events(ClusterGroup prj) { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteManaged managed() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteManaged managed(ClusterGroup prj) { + return null; + } + + /** {@inheritDoc} */ + @Override public ExecutorService executorService() { + return null; + } + + /** {@inheritDoc} */ + @Override public ExecutorService executorService(ClusterGroup prj) { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteProduct product() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteScheduler scheduler() { + return null; + } + + /** {@inheritDoc} */ + @Override public GridSecurity security() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgnitePortables portables() { + return null; + } + + /** {@inheritDoc} */ + @Override public <K, V> GridCache<K, V> cache(@Nullable String name) { + return null; + } + + /** {@inheritDoc} */ + @Override public Collection<GridCache<?, ?>> caches() { + return null; + } + + /** {@inheritDoc} */ + @Override public <K, V> IgniteCache<K, V> jcache(@Nullable String name) { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteTransactions transactions() { + return null; + } + + /** {@inheritDoc} */ + @Override public <K, V> IgniteDataLoader<K, V> dataLoader(@Nullable String cacheName) { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteFs fileSystem(String name) { + return null; + } + + /** {@inheritDoc} */ + @Override public Collection<IgniteFs> fileSystems() { + return null; + } + + /** {@inheritDoc} */ + @Override public GridHadoop hadoop() { + return null; + } + + /** {@inheritDoc} */ + @Override public IgniteStreamer streamer(@Nullable String name) { + return null; + } + + /** {@inheritDoc} */ + @Override public Collection<IgniteStreamer> streamers() { + return null; + } + + /** {@inheritDoc} */ + @Override public <T extends IgnitePlugin> T plugin(String name) throws PluginNotFoundException { + return null; + } + + /** {@inheritDoc} */ + @Override public void close() throws IgniteCheckedException {} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java new file mode 100644 index 0000000..14d8817 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestKernalContext.java @@ -0,0 +1,85 @@ +/* + * 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.junits; + +import org.apache.ignite.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.internal.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.testframework.*; + +import java.util.*; + +/** + * Test context. + */ +public class GridTestKernalContext extends GridKernalContextImpl { + /** + * + */ + public GridTestKernalContext() { + super(null, + new GridKernal(null), + new IgniteConfiguration(), + new GridKernalGatewayImpl(null), + null, + false); + + GridTestUtils.setFieldValue(grid(), "cfg", config()); + } + + /** + * @param log Logger to use in context config. + */ + public GridTestKernalContext(IgniteLogger log) { + this(); + + config().setGridLogger(log); + } + + /** + * Starts everything added (in the added order). + * + * @throws IgniteCheckedException If failed + */ + public void start() throws IgniteCheckedException { + for (GridComponent comp : this) + comp.start(); + } + + /** + * Stops everything added. + * + * @param cancel Cancel parameter. + * @throws IgniteCheckedException If failed. + */ + public void stop(boolean cancel) throws IgniteCheckedException { + List<GridComponent> comps = components(); + + for (ListIterator<GridComponent> it = comps.listIterator(comps.size()); it.hasPrevious();) { + GridComponent comp = it.previous(); + + comp.stop(cancel); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(GridTestKernalContext.class, this, super.toString()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestResources.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestResources.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestResources.java new file mode 100644 index 0000000..6eee6b1 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridTestResources.java @@ -0,0 +1,255 @@ +/* + * 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.junits; + +import org.apache.ignite.*; +import org.apache.ignite.marshaller.*; +import org.apache.ignite.marshaller.optimized.*; +import org.apache.ignite.resources.*; +import org.apache.ignite.thread.*; +import org.apache.ignite.internal.processors.resource.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.testframework.config.*; +import org.apache.ignite.testframework.junits.logger.*; +import org.jetbrains.annotations.*; + +import javax.management.*; +import java.io.*; +import java.lang.management.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * Test resources for injection. + */ +public class GridTestResources { + /** */ + private static final IgniteLogger rootLog = new GridTestLog4jLogger(false); + + /** */ + private final IgniteLogger log; + + /** Local host. */ + private final String locHost; + + /** */ + private final UUID nodeId; + + /** */ + private IgniteMarshaller marshaller; + + /** */ + private final MBeanServer jmx; + + /** */ + private final String home; + + /** */ + private ThreadPoolExecutor execSvc; + + /** */ + private GridResourceProcessor rsrcProc; + + /** */ + public GridTestResources() { + log = rootLog.getLogger(getClass()); + nodeId = UUID.randomUUID(); + jmx = ManagementFactory.getPlatformMBeanServer(); + home = U.getGridGainHome(); + locHost = localHost(); + + GridTestKernalContext ctx = new GridTestKernalContext(); + + ctx.config().setGridLogger(log); + + rsrcProc = new GridResourceProcessor(ctx); + } + + /** + * @param jmx JMX server. + */ + public GridTestResources(MBeanServer jmx) { + assert jmx != null; + + this.jmx = jmx; + + log = rootLog.getLogger(getClass()); + + nodeId = UUID.randomUUID(); + home = U.getGridGainHome(); + locHost = localHost(); + + GridTestKernalContext ctx = new GridTestKernalContext(); + + ctx.config().setGridLogger(log); + + rsrcProc = new GridResourceProcessor(ctx); + } + + /** + * @param log Logger. + */ + public GridTestResources(IgniteLogger log) { + assert log != null; + + this.log = log.getLogger(getClass()); + + nodeId = UUID.randomUUID(); + jmx = ManagementFactory.getPlatformMBeanServer(); + home = U.getGridGainHome(); + locHost = localHost(); + + GridTestKernalContext ctx = new GridTestKernalContext(); + + ctx.config().setGridLogger(log); + + rsrcProc = new GridResourceProcessor(ctx); + } + + /** + * @return Resource processor. + */ + public GridResourceProcessor resources() { + return rsrcProc; + } + + /** + * @return Local host. + */ + @Nullable private String localHost() { + try { + return U.getLocalHost().getHostAddress(); + } + catch (IOException e) { + System.err.println("Failed to detect local host address."); + + e.printStackTrace(); + + return null; + } + } + + /** + * @param prestart Prestart flag. + */ + public void startThreads(boolean prestart) { + execSvc = new IgniteThreadPoolExecutor(nodeId.toString(), 40, 40, Long.MAX_VALUE, + new LinkedBlockingQueue<Runnable>()); + + // Improve concurrency for testing. + if (prestart) + execSvc.prestartAllCoreThreads(); + } + + /** */ + public void stopThreads() { + if (execSvc != null) { + U.shutdownNow(getClass(), execSvc, log); + + execSvc = null; + } + } + + /** + * @param target Target. + * @throws IgniteCheckedException If failed. + */ + public void inject(Object target) throws IgniteCheckedException { + assert target != null; + assert getLogger() != null; + + rsrcProc.injectBasicResource(target, IgniteLoggerResource.class, getLogger().getLogger(target.getClass())); + rsrcProc.injectBasicResource(target, IgniteInstanceResource.class, + new GridTestIgnite(null, locHost, nodeId, getMarshaller(), jmx, home)); + } + + /** + * @return Executor service. + */ + public ExecutorService getExecutorService() { + return execSvc; + } + + /** + * @return GridGain home. + */ + public String getGridgainHome() { + return home; + } + + /** + * @return MBean server. + */ + public MBeanServer getMBeanServer() { + return jmx; + } + + /** + * @return Logger for specified class. + */ + public static IgniteLogger getLogger(Class<?> cls) { + return rootLog.getLogger(cls); + } + + /** + * @return Logger. + */ + public IgniteLogger getLogger() { + return log; + } + + /** + * @return Node ID. + */ + public UUID getNodeId() { + return nodeId; + } + + /** + * @return Local host. + */ + public String getLocalHost() { + return locHost; + } + + /** + * @return Marshaller. + * @throws IgniteCheckedException If failed. + */ + @SuppressWarnings("unchecked") + public synchronized IgniteMarshaller getMarshaller() throws IgniteCheckedException { + if (marshaller == null) { + String marshallerName = GridTestProperties.getProperty("marshaller.class"); + + if (marshallerName == null) + marshaller = new IgniteOptimizedMarshaller(); + else { + try { + Class<? extends IgniteMarshaller> cls = (Class<? extends IgniteMarshaller>)Class.forName(marshallerName); + + marshaller = cls.newInstance(); + } + catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + throw new IgniteCheckedException("Failed to create test marshaller [marshaller=" + marshallerName + ']', e); + } + } + } + + return marshaller; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java new file mode 100644 index 0000000..59f9d8f --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/GridAbstractCacheStoreSelfTest.java @@ -0,0 +1,563 @@ +/* + * 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.junits.cache; + +import org.apache.ignite.*; +import org.apache.ignite.cache.store.*; +import org.apache.ignite.internal.processors.cache.*; +import org.apache.ignite.lang.*; +import org.apache.ignite.transactions.*; +import org.apache.ignite.internal.util.lang.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.apache.ignite.testframework.*; +import org.apache.ignite.testframework.junits.common.*; +import org.jetbrains.annotations.*; + +import javax.cache.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * Abstract cache store test. + */ +public abstract class GridAbstractCacheStoreSelfTest<T extends CacheStore<Object, Object>> + extends GridCommonAbstractTest { + /** */ + protected final T store; + + /** */ + protected TestThreadLocalCacheSession ses = new TestThreadLocalCacheSession(); + + /** + * @throws Exception If failed. + */ + @SuppressWarnings({"AbstractMethodCallInConstructor", "OverriddenMethodCallDuringObjectConstruction"}) + protected GridAbstractCacheStoreSelfTest() throws Exception { + super(false); + + store = store(); + + inject(store); + } + + /** + * @throws Exception If failed. + */ + public void testStore() throws Exception { + // Create dummy transaction + IgniteTx tx = new DummyTx(); + + ses.newSession(tx); + + store.write(new CacheEntryImpl<>("k1", "v1")); + store.write(new CacheEntryImpl<>("k2", "v2")); + + store.txEnd(true); + + ses.newSession(null); + + assertEquals("v1", store.load("k1")); + assertEquals("v2", store.load("k2")); + assertNull(store.load("k3")); + + ses.newSession(tx); + + store.delete("k1"); + + store.txEnd(true); + + ses.newSession(null); + + assertNull(store.load("k1")); + assertEquals("v2", store.load("k2")); + assertNull(store.load("k3")); + } + + /** + * @throws IgniteCheckedException if failed. + */ + public void testRollback() throws IgniteCheckedException { + IgniteTx tx = new DummyTx(); + + ses.newSession(tx); + + // Put. + store.write(new CacheEntryImpl<>("k1", "v1")); + + store.txEnd(false); // Rollback. + + tx = new DummyTx(); + + ses.newSession(tx); + + assertNull(store.load("k1")); + + // Put all. + assertNull(store.load("k2")); + + Collection<Cache.Entry<? extends Object, ? extends Object>> col = new ArrayList<>(); + + col.add(new CacheEntryImpl<>("k2", "v2")); + + store.writeAll(col); + + store.txEnd(false); // Rollback. + + tx = new DummyTx(); + + ses.newSession(tx); + + assertNull(store.load("k2")); + + col = new ArrayList<>(); + + col.add(new CacheEntryImpl<>("k3", "v3")); + + store.writeAll(col); + + store.txEnd(true); // Commit. + + tx = new DummyTx(); + + ses.newSession(tx); + + assertEquals("v3", store.load("k3")); + + store.write(new CacheEntryImpl<>("k4", "v4")); + + store.txEnd(false); // Rollback. + + tx = new DummyTx(); + + ses.newSession(tx); + + assertNull(store.load("k4")); + + assertEquals("v3", store.load("k3")); + + // Remove. + store.delete("k3"); + + store.txEnd(false); // Rollback. + + tx = new DummyTx(); + + ses.newSession(tx); + + assertEquals("v3", store.load("k3")); + + // Remove all. + store.deleteAll(Arrays.asList("k3")); + + store.txEnd(false); // Rollback. + + tx = new DummyTx(); + + ses.newSession(tx); + + assertEquals("v3", store.load("k3")); + } + + /** + * @throws IgniteCheckedException if failed. + */ + public void testAllOpsWithTXNoCommit() throws IgniteCheckedException { + doTestAllOps(new DummyTx(), false); + } + + /** + * @throws IgniteCheckedException if failed. + */ + public void testAllOpsWithTXCommit() throws IgniteCheckedException { + doTestAllOps(new DummyTx(), true); + } + + /** + * @throws IgniteCheckedException if failed. + */ + public void testAllOpsWithoutTX() throws IgniteCheckedException { + doTestAllOps(null, false); + } + + /** + * @param tx Transaction. + * @param commit Commit. + * @throws IgniteCheckedException If failed. + */ + private void doTestAllOps(@Nullable IgniteTx tx, boolean commit) throws IgniteCheckedException { + try { + ses.newSession(tx); + + store.write(new CacheEntryImpl<>("key1", "val1")); + + if (tx != null && commit) { + store.txEnd(true); + + tx = new DummyTx(); + + ses.newSession(tx); + } + + if (tx == null || commit) + assertEquals("val1", store.load("key1")); + + Collection<Cache.Entry<? extends Object, ? extends Object>> col = new ArrayList<>(); + + col.add(new CacheEntryImpl<>("key2", "val2")); + col.add(new CacheEntryImpl<>("key3", "val3")); + + store.writeAll(col); + + if (tx != null && commit) { + store.txEnd(true); + + tx = new DummyTx(); + } + + if (tx == null || commit) { + Map<Object, Object> loaded = store.loadAll(Arrays.asList("key1", "key2", "key3", "no_such_key")); + + for (Map.Entry<Object, Object> e : loaded.entrySet()) { + Object key = e.getKey(); + Object val = e.getValue(); + + if ("key1".equals(key)) + assertEquals("val1", val); + + if ("key2".equals(key)) + assertEquals("val2", val); + + if ("key3".equals(key)) + assertEquals("val3", val); + + if ("no_such_key".equals(key)) + fail(); + } + + assertEquals(3, loaded.size()); + } + + store.deleteAll(Arrays.asList("key2", "key3")); + + if (tx != null && commit) { + store.txEnd(true); + + tx = new DummyTx(); + + ses.newSession(tx); + } + + if (tx == null || commit) { + assertNull(store.load("key2")); + assertNull(store.load("key3")); + assertEquals("val1", store.load("key1")); + } + + store.delete("key1"); + + if (tx != null && commit) { + store.txEnd(true); + + tx = new DummyTx(); + + ses.newSession(tx); + } + + if (tx == null || commit) + assertNull(store.load("key1")); + } + finally { + if (tx != null) { + store.txEnd(false); + + ses.newSession(null); + } + } + } + + /** + * @throws Exception If failed. + */ + public void testSimpleMultithreading() throws Exception { + final Random rnd = new Random(); + + final LinkedBlockingQueue<UUID> queue = new LinkedBlockingQueue<>(); + + multithreaded(new Callable<Object>() { + @Override public Object call() throws Exception { + for (int i = 0; i < 1000; i++) { + IgniteTx tx = rnd.nextBoolean() ? new DummyTx() : null; + + ses.newSession(tx); + + int op = rnd.nextInt(10); + + boolean queueEmpty = false; + + if (op < 4) { // Load. + UUID key = queue.poll(); + + if (key == null) + queueEmpty = true; + else { + if (rnd.nextBoolean()) + assertNotNull(store.load(key)); + else { + Map<Object, Object> loaded = store.loadAll(Collections.singleton(key)); + + assertEquals(1, loaded.size()); + + Map.Entry<Object, Object> e = loaded.entrySet().iterator().next(); + + UUID k = (UUID)e.getKey(); + UUID v = (UUID)e.getValue(); + + assertTrue(k.equals(v) || (k.getMostSignificantBits() == v.getLeastSignificantBits() + && k.getLeastSignificantBits() == v.getMostSignificantBits())); + } + + if (tx != null) + store.txEnd(true); + + queue.add(key); + } + } + else if (op < 6) { // Remove. + UUID key = queue.poll(); + + if (key == null) + queueEmpty = true; + else { + if (rnd.nextBoolean()) + store.delete(key); + else + store.deleteAll(Collections.singleton(key)); + + if (tx != null) + store.txEnd(true); + } + } + else { // Update. + UUID key = queue.poll(); + + if (key == null) + queueEmpty = true; + else { + UUID val = new UUID(key.getLeastSignificantBits(), key.getMostSignificantBits()); + + if (rnd.nextBoolean()) + store.write(new CacheEntryImpl<>(key, val)); + else { + Collection<Cache.Entry<? extends Object, ? extends Object>> col = new ArrayList<>(); + + col.add(new CacheEntryImpl<>(key, val)); + + store.writeAll(col); + } + + if (tx != null) + store.txEnd(true); + + queue.add(key); + } + } + + if (queueEmpty) { // Add. + UUID key = UUID.randomUUID(); + + if (rnd.nextBoolean()) + store.write(new CacheEntryImpl<>(key, key)); + else { + Collection<Cache.Entry<? extends Object, ? extends Object>> col = new ArrayList<>(); + + col.add(new CacheEntryImpl<>(key, key)); + + store.writeAll(col); + } + + if (tx != null) + store.txEnd(true); + + queue.add(key); + } + } + + return null; + } + }, 37); + } + + /** + * @param store Store. + * @throws Exception If failed. + */ + protected void inject(T store) throws Exception { + getTestResources().inject(store); + + GridTestUtils.setFieldValue(store, CacheStore.class, "ses", ses); + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + U.startLifecycleAware(F.asList(store)); + + final Collection<Object> keys = new ArrayList<>(); + + CI2<Object, Object> c = new CI2<Object, Object>() { + @Override + public void apply(Object k, Object v) { + keys.add(k); + } + }; + + store.loadCache(c); + + if (keys.isEmpty()) + return; + + ses.newSession(null); + + store.deleteAll(keys); + + keys.clear(); + + store.loadCache(c); + + assertTrue(keys.isEmpty()); + } + + /** + * @throws Exception If failed. + */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + U.stopLifecycleAware(log, F.asList(store)); + } + + /** + * @return Store. + */ + protected abstract T store(); + + /** + * Dummy transaction for test purposes. + */ + public static class DummyTx extends GridMetadataAwareAdapter implements IgniteTx { + /** */ + private final IgniteUuid xid = IgniteUuid.randomUuid(); + + /** {@inheritDoc} */ + @Nullable @Override public IgniteUuid xid() { + return xid; + } + + /** {@inheritDoc} */ + @Nullable @Override public UUID nodeId() { + return null; + } + + /** {@inheritDoc} */ + @Override public long threadId() { + return 0; + } + + /** {@inheritDoc} */ + @Override public long startTime() { + return 0; + } + + /** {@inheritDoc} */ + @Nullable @Override public IgniteTxIsolation isolation() { + return null; + } + + /** {@inheritDoc} */ + @Nullable @Override public IgniteTxConcurrency concurrency() { + return null; + } + + /** {@inheritDoc} */ + @Override public boolean implicit() { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean isInvalidate() { + return false; + } + + /** {@inheritDoc} */ + @Nullable @Override public IgniteTxState state() { + return null; + } + + /** {@inheritDoc} */ + @Override public long timeout() { + return 0; + } + + /** {@inheritDoc} */ + @Override public long timeout(long timeout) { + return 0; + } + + /** {@inheritDoc} */ + @Override public boolean setRollbackOnly() { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean isRollbackOnly() { + return false; + } + + /** {@inheritDoc} */ + @Override public void commit() throws IgniteCheckedException { + // No-op. + } + + /** {@inheritDoc} */ + @Override public void close() throws IgniteCheckedException { + // No-op. + } + + /** {@inheritDoc} */ + @Override public IgniteAsyncSupport enableAsync() { + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public boolean isAsync() { + return false; + } + + /** {@inheritDoc} */ + @Override public <R> IgniteFuture<R> future() { + return null; + } + + /** {@inheritDoc} */ + @Override public void rollback() throws IgniteCheckedException { + // No-op. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestCacheSession.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestCacheSession.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestCacheSession.java new file mode 100644 index 0000000..4500618 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestCacheSession.java @@ -0,0 +1,65 @@ +/* + * 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.junits.cache; + +import org.apache.ignite.cache.store.*; +import org.apache.ignite.transactions.*; +import org.apache.ignite.internal.util.typedef.internal.*; +import org.jetbrains.annotations.*; + +import java.util.*; + +/** + * + */ +public class TestCacheSession implements CacheStoreSession { + /** */ + private IgniteTx tx; + + /** */ + private Map<Object, Object> props; + + /** + * + * @param tx Transaction. + */ + public void newSession(@Nullable IgniteTx tx) { + this.tx = tx; + + props = null; + } + + /** {@inheritDoc} */ + @Nullable @Override public IgniteTx transaction() { + return tx; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public <K, V> Map<K, V> properties() { + if (props == null) + props = U.newHashMap(1); + + return (Map<K, V>)props; + } + + /** {@inheritDoc} */ + @Nullable @Override public String cacheName() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestThreadLocalCacheSession.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestThreadLocalCacheSession.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestThreadLocalCacheSession.java new file mode 100644 index 0000000..91ccf71 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/cache/TestThreadLocalCacheSession.java @@ -0,0 +1,65 @@ +/* + * 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.junits.cache; + +import org.apache.ignite.cache.store.*; +import org.apache.ignite.transactions.*; +import org.jetbrains.annotations.*; + +import java.util.*; + +/** + * + */ +public class TestThreadLocalCacheSession implements CacheStoreSession { + /** */ + private final ThreadLocal<TestCacheSession> sesHolder = new ThreadLocal<>(); + + /** + * @param tx Transaction. + */ + public void newSession(@Nullable IgniteTx tx) { + TestCacheSession ses = new TestCacheSession(); + + ses.newSession(tx); + + sesHolder.set(ses); + } + + /** {@inheritDoc} */ + @Nullable @Override public IgniteTx transaction() { + TestCacheSession ses = sesHolder.get(); + + return ses != null ? ses.transaction() : null; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public <K, V> Map<K, V> properties() { + TestCacheSession ses = sesHolder.get(); + + return ses != null ? (Map<K, V>)ses.properties() : null; + } + + /** {@inheritDoc} */ + @Nullable @Override public String cacheName() { + TestCacheSession ses = sesHolder.get(); + + return ses != null ? ses.cacheName() : null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/bd28003b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridAbstractExamplesTest.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridAbstractExamplesTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridAbstractExamplesTest.java new file mode 100644 index 0000000..9df954a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridAbstractExamplesTest.java @@ -0,0 +1,79 @@ +/* + * 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.junits.common; + +import org.apache.ignite.internal.util.typedef.internal.*; + +import java.io.*; +import java.util.*; + +/** + * Base class for examples test. + */ +public abstract class GridAbstractExamplesTest extends GridCommonAbstractTest { + /** */ + protected static final String[] EMPTY_ARGS = new String[0]; + + /** */ + protected static final int RMT_NODES_CNT = 3; + + /** */ + protected static final String RMT_NODE_CFGS = "modules/core/src/test/config/examples.properties"; + + /** */ + protected static final String DFLT_CFG = "examples/config/example-compute.xml"; + + /** */ + private static final Properties rmtCfgs = new Properties(); + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + } + + /** + * Starts remote nodes. + * + * @throws Exception If failed. + */ + protected final void startRemoteNodes() throws Exception { + String name = getName().replaceFirst("test", ""); + + if (rmtCfgs.isEmpty()) { + info("Loading remote configs properties from file: " + RMT_NODE_CFGS); + + try (FileReader reader = new FileReader(U.resolveGridGainPath(RMT_NODE_CFGS))) { + rmtCfgs.load(reader); + } + } + + String cfg = rmtCfgs.getProperty(name, defaultConfig()); + + info("Config for remote nodes [name=" + name + ", cfg=" + cfg + ", dflt=" + defaultConfig() + "]"); + + for (int i = 0; i < RMT_NODES_CNT; i++) + startGrid(getTestGridName(i), cfg); + } + + /** + * @return Default config for this test. + */ + protected String defaultConfig() { + return DFLT_CFG; + } +}