/** * Licensed to the zk1931 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 com.github.zk1931.jzab; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestName; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import static org.junit.Assert.assertTrue; /** * A base class for all the test cases. */ public class TestBase { private static final Logger LOG = LoggerFactory.getLogger(TestBase.class); // Used to generate unique port number for unit tests. private static AtomicInteger portGenerator = new AtomicInteger(60000); @Rule public TestName testName = new TestName(); /** * Logs the beginning and the end of the each test method. */ @Rule public TestWatcher watchman= new TestWatcher() { @Override protected void starting(Description description) { int numThreads = Thread.getAllStackTraces().keySet().size(); LOG.info("STARTING: {}, # of threads: {}", description, numThreads); MDC.put("test", description.toString()); } @Override protected void failed(Throwable e, Description description) { int numThreads = Thread.getAllStackTraces().keySet().size(); LOG.error("FAILED: {}, # of threads: {}", description, numThreads, e); MDC.put("test", ""); } @Override protected void succeeded(Description description) { int numThreads = Thread.getAllStackTraces().keySet().size(); LOG.info("SUCCEEDED: {}, # of threads: {}", description, numThreads); MDC.put("test", ""); } }; @Before public void createDirectory() { Stack<File> stack = new Stack<>(); File directory = getDirectory(); if (directory.exists()) { stack.push(directory); } while (!stack.empty()) { File dir = stack.pop(); File[] files = dir.listFiles(); if (files == null) { continue; } for (File file : files) { if (file.isDirectory()) { stack.push(file); } else { assertTrue("Unable to delete file " + file, file.delete()); } } } } /** * Creates a data directory for the calling test method. * * The format of the directory name is "target/data/$classname/$methodname". * * @return the name of the directory */ protected File getDirectory() { String dirName = "target" + File.separator + "data" + File.separator + this.getClass().getCanonicalName() + File.separator + testName.getMethodName(); File dir = new File(dirName); LOG.debug("Creating a data directory: {}", dirName); dir.mkdirs(); return dir; } /** * Returns a unique integer to be used as a port number. */ protected int getUniquePort() { return portGenerator.getAndIncrement(); } /** * Returns host:port with a given port. */ protected String getHostPort(int port) { try { return InetAddress.getLocalHost().getCanonicalHostName().toString() + ":" + port; } catch (UnknownHostException ex) { return "localhost" + ":" + port; } } /** * Returns host:port string with a unique port. */ protected String getUniqueHostPort() { return getHostPort(getUniquePort()); } PersistentState makeInitialState(String serverId, int numTxnsInLog) throws IOException { PersistentState state = new PersistentState(new File(getDirectory(), serverId)); Log log = state.getLog(); for (int i = 0; i < numTxnsInLog; ++i) { String body = "Txn " + i; log.append(new Transaction(new Zxid(0, i), ByteBuffer.wrap(body.getBytes()))); } return state; } void appendTxns(Log log, Zxid startZxid, int count) throws IOException { for (int i = 0; i < count; ++i) { Zxid zxid = new Zxid(startZxid.getEpoch(), startZxid.getXid() + i); log.append(new Transaction(zxid, ByteBuffer.wrap("Txn".getBytes()))); } } }