/*
* Copyright 2010 Henry Coles
*
* Licensed 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.pitest.mutationtest.execute;
import java.io.IOException;
import java.lang.management.MemoryNotificationInfo;
import java.net.Socket;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.openmbean.CompositeData;
import org.pitest.boot.HotSwapAgent;
import org.pitest.classinfo.ClassByteArraySource;
import org.pitest.classinfo.ClassName;
import org.pitest.classpath.ClassloaderByteArraySource;
import org.pitest.functional.F3;
import org.pitest.functional.FCollection;
import org.pitest.functional.prelude.Prelude;
import org.pitest.mutationtest.mocksupport.BendJavassistToMyWillTransformer;
import org.pitest.mutationtest.mocksupport.JavassistInputStreamInterceptorAdapater;
import org.pitest.mutationtest.mocksupport.JavassistInterceptor;
import org.pitest.testapi.Configuration;
import org.pitest.testapi.TestUnit;
import org.pitest.testapi.execute.FindTestUnits;
import org.pitest.util.ExitCode;
import org.pitest.util.Glob;
import org.pitest.util.IsolationUtils;
import org.pitest.util.Log;
import org.pitest.util.SafeDataInputStream;
public class MutationTestMinion {
private static final Logger LOG = Log.getLogger();
private final SafeDataInputStream dis;
private final Reporter reporter;
public MutationTestMinion(final SafeDataInputStream dis,
final Reporter reporter) {
this.dis = dis;
this.reporter = reporter;
}
public void run() {
try {
final MinionArguments paramsFromParent = this.dis
.read(MinionArguments.class);
Log.setVerbose(paramsFromParent.isVerbose());
final ClassLoader loader = IsolationUtils.getContextClassLoader();
final ClassByteArraySource byteSource = new ClassloaderByteArraySource(
loader);
final F3<ClassName, ClassLoader, byte[], Boolean> hotswap = new HotSwap(
byteSource);
final MutationTestWorker worker = new MutationTestWorker(hotswap,
paramsFromParent.engine.createMutator(byteSource), loader);
final List<TestUnit> tests = findTestsForTestClasses(loader,
paramsFromParent.testClasses, paramsFromParent.pitConfig);
worker.run(paramsFromParent.mutations, this.reporter,
new TimeOutDecoratedTestSource(paramsFromParent.timeoutStrategy,
tests, this.reporter));
this.reporter.done(ExitCode.OK);
} catch (final Throwable ex) {
ex.printStackTrace(System.out);
LOG.log(Level.WARNING, "Error during mutation test", ex);
this.reporter.done(ExitCode.UNKNOWN_ERROR);
}
}
public static void main(final String[] args) {
LOG.log(Level.FINE, "minion started");
enablePowerMockSupport();
final int port = Integer.valueOf(args[0]);
Socket s = null;
try {
s = new Socket("localhost", port);
final SafeDataInputStream dis = new SafeDataInputStream(
s.getInputStream());
final Reporter reporter = new DefaultReporter(s.getOutputStream());
addMemoryWatchDog(reporter);
final MutationTestMinion instance = new MutationTestMinion(dis, reporter);
instance.run();
} catch (final Throwable ex) {
ex.printStackTrace(System.out);
LOG.log(Level.WARNING, "Error during mutation test", ex);
} finally {
if (s != null) {
safelyCloseSocket(s);
}
}
}
private static List<TestUnit> findTestsForTestClasses(
final ClassLoader loader, final Collection<ClassName> testClasses,
final Configuration pitConfig) {
final Collection<Class<?>> tcs = FCollection.flatMap(testClasses,
ClassName.nameToClass(loader));
FindTestUnits finder = new FindTestUnits(pitConfig);
return finder.findTestUnitsForAllSuppliedClasses(tcs);
}
@SuppressWarnings("unchecked")
private static void enablePowerMockSupport() {
// Bwahahahahahahaha
HotSwapAgent.addTransformer(new BendJavassistToMyWillTransformer(Prelude
.or(new Glob("javassist/*")), JavassistInputStreamInterceptorAdapater.inputStreamAdapterSupplier(JavassistInterceptor.class)));
}
private static void safelyCloseSocket(final Socket s) {
if (s != null) {
try {
s.close();
} catch (final IOException e) {
LOG.log(Level.WARNING, "Couldn't close socket", e);
}
}
}
private static void addMemoryWatchDog(final Reporter r) {
final NotificationListener listener = new NotificationListener() {
@Override
public void handleNotification(final Notification notification,
final Object handback) {
final String type = notification.getType();
if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
final CompositeData cd = (CompositeData) notification.getUserData();
final MemoryNotificationInfo memInfo = MemoryNotificationInfo
.from(cd);
CommandLineMessage.report(memInfo.getPoolName()
+ " has exceeded the shutdown threshold : " + memInfo.getCount()
+ " times.\n" + memInfo.getUsage());
r.done(ExitCode.OUT_OF_MEMORY);
} else {
LOG.warning("Unknown notification: " + notification);
}
}
};
MemoryWatchdog.addWatchDogToAllPools(90, listener);
}
}