├── .gitignore
├── .travis.yml
├── LICENSE.md
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── uk
│ └── co
│ └── probablyfine
│ └── bytemonkey
│ ├── AddChanceOfFailure.java
│ ├── AgentArguments.java
│ ├── ByteMonkeyAgent.java
│ ├── ByteMonkeyClassTransformer.java
│ ├── ByteMonkeyException.java
│ ├── CreateAndThrowException.java
│ ├── DirectlyThrowException.java
│ ├── FilterByClassAndMethodName.java
│ ├── OperationMode.java
│ └── testfiles
│ ├── FaultTestObject.java
│ ├── MissingPropertyException.java
│ ├── NullabilityTestPojo.java
│ └── TryCatchTestObject.java
└── test
└── java
└── uk
└── co
└── probablyfine
└── bytemonkey
├── fault
├── DefaultExceptionTypeTest.java
├── Rate0Test.java
├── Rate100Test.java
├── Rate50Test.java
└── ThrowExceptionIfDeclaredTest.java
├── latency
└── Rate100Test.java
├── nullify
├── DoNothingWithEmptyParamMethodsTest.java
├── DoNothingWithNoObjectParamMethodsTest.java
├── NullifyArgumentsTest.java
└── OnlyNullifyNonPrimitiveArgumentsTest.java
└── shortcircuit
├── TryCatchObjectNormalTest.java
├── TryCatchObjectSCTest.java
└── TryCatchObjectSCWithParamTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | target/
4 | .classpath
5 | .project
6 | .settings/
7 | bin
8 | spooned/
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
4 | - oraclejdk9
5 | - oraclejdk11
6 | after_script:
7 | - mvn clean test jacoco:report coveralls:report -Dcoveralls.token=$coveralls_repo_token
8 |
9 | matrix:
10 | allow_failures:
11 | - jdk: oraclejdk9
12 | - jdk: oraclejdk11
13 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Alex Wilson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # byte-monkey
2 |
3 | [](https://travis-ci.org/mrwilson/byte-monkey)
4 | [](https://coveralls.io/github/mrwilson/byte-monkey?branch=master)
5 |
6 | Byte-Monkey is a small Java library for testing failure scenarios in JVM applications - it works by instrumenting application code on the fly to deliberately introduce faults like exceptions and latency. Original blogpost [here](http://blog.probablyfine.co.uk/2016/05/30/announcing-byte-monkey.html).
7 |
8 |
9 | ## Download
10 |
11 | Latest version: [1.0.0](https://github.com/mrwilson/byte-monkey/releases/download/1.0.0/byte-monkey.jar)
12 |
13 | ## How to use
14 |
15 | ```bash
16 | java -javaagent:byte-monkey.jar -jar your-java-app.jar
17 | ```
18 |
19 | ## Supported Modes
20 |
21 | * **Fault**: Throw exceptions from methods that declare those exceptions
22 | * **Latency**: Introduce latency on method-calls
23 | * **Nullify**: Replace the first non-primitive argument to the method with *null*
24 | * **Short-circuit**: Throw corresponding exceptions at the very beginning of try blocks
25 |
26 | ## Options
27 |
28 | * `mode`: What mode to run in - currently supports `fault`, `latency`, `nullify`, and `scircuit`. **Default is fault**
29 | * `rate`: Value between 0 and 1 - how often to activate the fault. **Default is 1, i.e. 100%**
30 | * `filter`: Only instrument packages or methods matching the (java-style) regex. **Default is .*, i.e. all methods**
31 |
32 | byte-monkey is configured with a comma-separated key-value pair string of the options as the agent argument.
33 |
34 | ```bash
35 | java -javaagent:byte-monkey.jar=mode:fault,rate:0.5,filter:uk/co/probablyfine/ -jar your-java-app.jar
36 | ```
37 |
38 | The example above would run in fault mode, activating on 50% of eligible method calls, for anything in the package tree below `uk.co.probablyfine`
39 |
40 | ## Modes
41 |
42 | ### Fault
43 |
44 | Running byte-monkey in `fault` mode will cause the first declared exception in a method signature to be thrown.
45 |
46 | **CAVEAT**: Byte-Monkey can only create Exceptions that expose a public default constructor as a result of how it instantiates them. If such a constructor doesn't exist, it falls back to a `ByteMonkeyException` instead.
47 |
48 | ### Latency
49 |
50 | Running byte-monkey in `latency` mode will cause the method to sleep before executing further instructions.
51 |
52 | There is a configuration option available only during this mode:
53 |
54 | * `latency`: Duration (in millis) to wait on method calls, only valid when running in **Latency** mode. **Default is 100ms**
55 |
56 | Example: `java -javaagent:byte-monkey.jar=mode:latency,rate:0.5,latency:150 -jar your-java-app.jar`
57 |
58 | ### Nullify
59 |
60 | Running byte-monkey in `nullify` mode will replace the first non-primitive argument to the method call with a null value.
61 |
62 | Methods with only primitive arguments or no arguments at all will not be affected by the agent in this mode.
63 |
64 | ### Short-circuit
65 |
66 | Running byte-monkey in `scircuit` mode will throw corresponding exceptions in the very beginning of try blocks.
67 |
68 | There is a configuration option available only during this mode:
69 |
70 | * `tcindex`: Index of which exception to throw when there are multiple catch blocks, e.g. `tcindex=0` indicates the first type of exception in the catch block. Only valid when running in **Short-circuit** mode. **Default is -1/first**
71 |
72 | Example:
73 | ```bash
74 | java -javaagent:byte-monkey.jar=mode:scircuit,filter:package/path/ClassName/MethodName,tcindex=0 -jar your-java-app.jar
75 | ```
76 |
77 | You can read [this paper](https://hal.inria.fr/hal-01062969/document) or [this blog](http://blog.gluckzhang.com/archives/107/) for more information about short-circuit testing.
78 |
79 | ## Implementation Details
80 |
81 | Byte-Monkey uses the JVM [Instrumentation API](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html). Implementing the API enables you to register transformers that can iterate over (and transform) class files as they are loaded by the JVM. Byte-Monkey uses [Objectweb ASM](http://asm.ow2.org/) which comes packaged with the JDK to chance the underlying bytecode of loaded classes
82 |
83 | ### Injecting Failure
84 |
85 | The bytecode of a simple "Hello, World!" method prior to having an exception injected looks like this:
86 |
87 | ```
88 | public void printSomething() throws java.io.IOException;
89 | Code:
90 | 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
91 | 3: ldc #3 // String Hello!
92 | 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
93 | 8: return
94 | ```
95 |
96 | After being transformed by the Byte-Monkey, it instead looks like this:
97 | ```
98 | public void printSomething() throws java.io.IOException;
99 | Code:
100 | 0: ldc2_w #18 // double 0.5d
101 | 3: invokestatic #25 // Method uk/co/probablyfine/bytemonkey/AddChanceOfFailure.shouldActivate:(D)Z
102 | 6: ifeq 15
103 | 9: ldc #26 // String java/io/IOException
104 | 11: invokestatic #32 // Method uk/co/probablyfine/bytemonkey/CreateAndThrowException.throwOrDefault:(Ljava/lang/String;)Ljava/lang/Throwable;
105 | 14: athrow
106 | 15: getstatic #38 // Field java/lang/System.out:Ljava/io/PrintStream;
107 | 18: ldc #40 // String Hello!
108 | 20: invokevirtual #46 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
109 | 23: return
110 | ```
111 |
112 | This is the core of how Byte-Monkey works:
113 |
114 | * **0:** Load the failure injection rate onto the stack
115 | * **3:** A call to `AddChanceOfFailure.shouldActivate` which returns true/false depending on the rate
116 | * **6:** If `shouldActivate` was false, we jump straight to instruction 15 - the beginning of the original code.
117 | * **9:** Load the name of the exception that would be thrown (here an IOException)
118 | * **11:** Create the exception if it has a default constructor, or create a wrapper exception
119 | * **14:** Throw the exception
120 |
121 | For modes other than `fault`, instructions 9 to 14 are replaced with mode-specific instructions.
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | uk.co.probablyfine
6 | byte-monkey
7 | 1.0.0
8 |
9 |
10 | 1.8
11 | 1.8
12 | UTF-8
13 | 1.3
14 | NOT-A-TOKEN
15 |
16 |
17 |
18 | ${project.artifactId}
19 |
20 |
21 |
22 | org.apache.maven.plugins
23 | maven-jar-plugin
24 | 3.0.0
25 |
26 |
27 | true
28 |
29 | uk.co.probablyfine.bytemonkey.ByteMonkeyAgent
30 |
31 |
32 |
33 |
34 |
35 | org.apache.maven.plugins
36 | maven-surefire-plugin
37 | 2.19.1
38 |
39 | false
40 |
41 |
42 |
43 | -noverify
44 |
45 |
46 |
47 | org.apache.maven.plugins
48 | maven-compiler-plugin
49 | 2.5.1
50 |
51 | -XDignore.symbol.file
52 | 1.8
53 | 1.8
54 |
55 |
56 |
57 | org.jacoco
58 | jacoco-maven-plugin
59 | 0.7.9
60 |
61 |
62 | prepare-agent
63 |
64 | prepare-agent
65 |
66 |
67 |
68 |
69 |
70 | org.eluder.coveralls
71 | coveralls-maven-plugin
72 | 4.3.0
73 |
74 | ${coveralls.token}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | junit
83 | junit
84 | 4.12
85 | test
86 |
87 |
88 | com.ea.agentloader
89 | ea-agent-loader
90 | 1.0.0
91 | test
92 |
93 |
94 | org.hamcrest
95 | hamcrest-core
96 | ${hamcrest.version}
97 | test
98 |
99 |
100 | org.hamcrest
101 | hamcrest-library
102 | ${hamcrest.version}
103 | test
104 |
105 |
106 | org.hamcrest
107 | hamcrest-all
108 | ${hamcrest.version}
109 | test
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/AddChanceOfFailure.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | import java.util.Random;
4 |
5 | import jdk.internal.org.objectweb.asm.Opcodes;
6 | import jdk.internal.org.objectweb.asm.tree.FrameNode;
7 | import jdk.internal.org.objectweb.asm.tree.InsnList;
8 | import jdk.internal.org.objectweb.asm.tree.JumpInsnNode;
9 | import jdk.internal.org.objectweb.asm.tree.LabelNode;
10 | import jdk.internal.org.objectweb.asm.tree.LdcInsnNode;
11 | import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
12 |
13 | public class AddChanceOfFailure {
14 |
15 | private static final Random random = new Random();
16 |
17 | public InsnList apply(InsnList newInstructions, double chanceOfFailure) {
18 | final InsnList list = new InsnList();
19 |
20 | final LabelNode originalCodeLabel = new LabelNode();
21 |
22 | list.add(new LdcInsnNode(chanceOfFailure));
23 | list.add(new MethodInsnNode(
24 | Opcodes.INVOKESTATIC,
25 | "uk/co/probablyfine/bytemonkey/AddChanceOfFailure",
26 | "shouldActivate",
27 | "(D)Z",
28 | false // this is not a method on an interface
29 | ));
30 |
31 | list.add(new JumpInsnNode(Opcodes.IFEQ, originalCodeLabel));
32 |
33 | list.add(newInstructions);
34 |
35 | list.add(new FrameNode(
36 | Opcodes.F_APPEND, // append to the last stack frame
37 | 0, new Object[] {}, // no local variables here
38 | 0, new Object[] {} // no stack either!
39 | ));
40 |
41 | list.add(originalCodeLabel);
42 |
43 | return list;
44 | }
45 |
46 | public static boolean shouldActivate(double chanceOfFailure) {
47 | return random.nextDouble() < chanceOfFailure;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/AgentArguments.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | public class AgentArguments {
4 | private final long latency;
5 | private final double chanceOfFailure;
6 | private final int tcIndex;
7 |
8 | public AgentArguments(long latency, double activationRatio, int tcIndex) {
9 | this.latency = latency;
10 | this.chanceOfFailure = activationRatio;
11 | this.tcIndex = tcIndex;
12 | }
13 |
14 | public long latency() {
15 | return latency;
16 | }
17 |
18 | public double chanceOfFailure() {
19 | return chanceOfFailure;
20 | }
21 |
22 | public int tcIndex() { return tcIndex; }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/ByteMonkeyAgent.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | import java.lang.instrument.Instrumentation;
4 | import java.lang.instrument.UnmodifiableClassException;
5 |
6 | public class ByteMonkeyAgent {
7 |
8 | public static void premain(String agentArguments, Instrumentation instrumentation) throws UnmodifiableClassException {
9 | ByteMonkeyClassTransformer transformer = new ByteMonkeyClassTransformer(agentArguments);
10 | instrumentation.addTransformer(transformer);
11 | }
12 |
13 | /* Duplicate of premain(), needed for ea-agent-loader in tests */
14 | public static void agentmain(String agentArguments, Instrumentation instrumentation) throws UnmodifiableClassException {
15 | premain(agentArguments, instrumentation);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/ByteMonkeyClassTransformer.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | import jdk.internal.org.objectweb.asm.ClassReader;
4 | import jdk.internal.org.objectweb.asm.ClassWriter;
5 | import jdk.internal.org.objectweb.asm.tree.*;
6 |
7 | import java.lang.instrument.ClassFileTransformer;
8 | import java.lang.instrument.IllegalClassFormatException;
9 | import java.security.ProtectionDomain;
10 | import java.util.Arrays;
11 | import java.util.Map;
12 | import java.util.Optional;
13 | import java.util.stream.Collectors;
14 |
15 | import static java.util.Optional.ofNullable;
16 |
17 | public class ByteMonkeyClassTransformer implements ClassFileTransformer {
18 |
19 | private final AddChanceOfFailure addChanceOfFailure = new AddChanceOfFailure();
20 |
21 | private final OperationMode failureMode;
22 | private final AgentArguments arguments;
23 | private final FilterByClassAndMethodName filter;
24 |
25 | public ByteMonkeyClassTransformer(String args) {
26 | Map configuration = argumentMap(args == null ? "" : args);
27 |
28 | long latency = Long.valueOf(configuration.getOrDefault("latency","100"));
29 | double activationRatio = Double.valueOf(configuration.getOrDefault("rate","1"));
30 | int tcIndex = Integer.valueOf(configuration.getOrDefault("tcindex", "-1"));
31 |
32 | this.arguments = new AgentArguments(latency, activationRatio, tcIndex);
33 | this.failureMode = OperationMode.fromLowerCase(configuration.getOrDefault("mode", OperationMode.FAULT.name()));
34 | this.filter = new FilterByClassAndMethodName(configuration.getOrDefault("filter", ".*"));
35 | }
36 |
37 | private Map argumentMap(String args) {
38 | return Arrays
39 | .stream(args.split(","))
40 | .map(line -> line.split(":"))
41 | .filter(line -> line.length == 2)
42 | .collect(Collectors.toMap(
43 | keyValue -> keyValue[0],
44 | keyValue -> keyValue[1])
45 | );
46 | }
47 |
48 | public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
49 | ProtectionDomain protectionDomain, byte[] classFileBuffer
50 | ) throws IllegalClassFormatException {
51 | return meddle(classFileBuffer);
52 | }
53 |
54 | private byte[] meddle(byte[] classFileBuffer) {
55 | ClassNode cn = new ClassNode();
56 | new ClassReader(classFileBuffer).accept(cn, 0);
57 |
58 | if (cn.name.startsWith("java/") || cn.name.startsWith("sun/") || cn.name.contains("$")) return classFileBuffer;
59 |
60 | switch (failureMode) {
61 | case SCIRCUIT:
62 | int tcIndex = arguments.tcIndex();
63 | if (tcIndex < 0) {
64 | cn.methods.stream()
65 | .filter(method -> !method.name.startsWith("<"))
66 | .filter(method -> filter.matches(cn.name, method.name))
67 | .filter(method -> method.tryCatchBlocks.size() > 0)
68 | .forEach(method -> {
69 | // inject an exception in each try-catch block
70 | // take the first exception type in catch block
71 | // for 1 try -> n catch, we should do different injections through params
72 | // TODO: these codes really need to be beautified
73 | LabelNode ln = method.tryCatchBlocks.get(0).start;
74 | int i = 0;
75 | for (TryCatchBlockNode tc : method.tryCatchBlocks) {
76 | if (ln == tc.start && i > 0) {
77 | // if two try-catch-block-nodes have the same "start", it indicates that it's one try block with multiple catch
78 | // so we should only inject one exception each time
79 | continue;
80 | }
81 | InsnList newInstructions = failureMode.generateByteCode(tc, tcIndex, arguments);
82 | method.maxStack += newInstructions.size();
83 | method.instructions.insert(tc.start, newInstructions);
84 | ln = tc.start;
85 | i++;
86 | }
87 | });
88 | } else {
89 | // should work together with filter
90 | cn.methods.stream()
91 | .filter(method -> !method.name.startsWith("<"))
92 | .filter(method -> filter.matches(cn.name, method.name))
93 | .filter(method -> method.tryCatchBlocks.size() > 0)
94 | .forEach(method -> {
95 | int index = 0;
96 | for (TryCatchBlockNode tc : method.tryCatchBlocks) {
97 | if (index == tcIndex) {
98 | InsnList newInstructions = failureMode.generateByteCode(tc, tcIndex, arguments);
99 | method.maxStack += newInstructions.size();
100 | method.instructions.insert(tc.start, newInstructions);
101 | break;
102 | } else {
103 | index ++;
104 | }
105 | }
106 | });
107 | }
108 | break;
109 | default:
110 | cn.methods.stream()
111 | .filter(method -> !method.name.startsWith("<"))
112 | .filter(method -> filter.matches(cn.name, method.name))
113 | .forEach(method -> {
114 | createNewInstructions(method).ifPresent(newInstructions -> {
115 | method.maxStack += newInstructions.size();
116 | method.instructions.insertBefore(
117 | method.instructions.getFirst(),
118 | newInstructions
119 | );
120 | });
121 | });
122 | break;
123 | }
124 |
125 | final ClassWriter cw = new ClassWriter(0);
126 | cn.accept(cw);
127 | return cw.toByteArray();
128 | }
129 |
130 | private Optional createNewInstructions(MethodNode method) {
131 | InsnList newInstructions = failureMode.generateByteCode(method, arguments);
132 |
133 | return ofNullable(
134 | addChanceOfFailure.apply(newInstructions, arguments.chanceOfFailure())
135 | );
136 | }
137 |
138 | }
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/ByteMonkeyException.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | public class ByteMonkeyException extends RuntimeException {
4 | public ByteMonkeyException(String exceptionName) {
5 | super("You've made a monkey out of me! Simulating throw of ["+exceptionName+"]");
6 | }
7 | }
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/CreateAndThrowException.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | public class CreateAndThrowException {
4 |
5 | public static void throwDirectly(String name) throws Throwable {
6 | String dotSeparatedClassName = name.replace("/", ".");
7 | Class> p = Class.forName(dotSeparatedClassName, false, ClassLoader.getSystemClassLoader());
8 | if (Throwable.class.isAssignableFrom(p)) {
9 | throw (Throwable) p.newInstance();
10 | } else {
11 | throw new ByteMonkeyException(name);
12 | }
13 | }
14 |
15 | public static Throwable throwOrDefault(String name) {
16 | String dotSeparatedClassName = name.replace("/", ".");
17 |
18 | try {
19 | Class> p = Class.forName(dotSeparatedClassName, false, ClassLoader.getSystemClassLoader());
20 |
21 | if (Throwable.class.isAssignableFrom(p)) {
22 | return (Throwable) p.newInstance();
23 | } else {
24 | return new ByteMonkeyException(name);
25 | }
26 | } catch (IllegalAccessException e) {
27 | return new ByteMonkeyException(name);
28 | } catch (Exception e) {
29 | return new RuntimeException(name);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/DirectlyThrowException.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | public class DirectlyThrowException {
4 | public static void throwDirectly(String name) throws Throwable {
5 | String dotSeparatedClassName = name.replace("/", ".");
6 | Class> p = Class.forName(dotSeparatedClassName, false, ClassLoader.getSystemClassLoader());
7 | if (Throwable.class.isAssignableFrom(p)) {
8 | throw (Throwable) p.newInstance();
9 | } else {
10 | throw new ByteMonkeyException(name);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/FilterByClassAndMethodName.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | public class FilterByClassAndMethodName {
6 |
7 | private final Pattern pattern;
8 |
9 | public FilterByClassAndMethodName(String regex) {
10 | this.pattern = Pattern.compile(regex);
11 | }
12 |
13 | public boolean matches(String className, String methodName) {
14 | String fullName = className + "/" + methodName;
15 |
16 | return this.pattern.matcher(fullName).find();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/OperationMode.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey;
2 |
3 | import jdk.internal.org.objectweb.asm.Opcodes;
4 | import jdk.internal.org.objectweb.asm.Type;
5 | import jdk.internal.org.objectweb.asm.tree.*;
6 |
7 | import java.util.List;
8 | import java.util.OptionalInt;
9 | import java.util.stream.IntStream;
10 |
11 | public enum OperationMode {
12 | SCIRCUIT {
13 | public InsnList generateByteCode(TryCatchBlockNode tryCatchBlock, int tcIndex, AgentArguments arguments) {
14 | InsnList list = new InsnList();
15 |
16 | list.add(new LdcInsnNode(tryCatchBlock.type));
17 | list.add(new MethodInsnNode(
18 | Opcodes.INVOKESTATIC,
19 | "uk/co/probablyfine/bytemonkey/DirectlyThrowException",
20 | "throwDirectly",
21 | "(Ljava/lang/String;)V",
22 | false // this is not a method on an interface
23 | ));
24 |
25 | return list;
26 | }
27 |
28 | @Override
29 | public InsnList generateByteCode(MethodNode method, AgentArguments arguments) {
30 | // won't use this method
31 | return null;
32 | }
33 | },
34 | LATENCY {
35 | @Override
36 | public InsnList generateByteCode(MethodNode method, AgentArguments arguments) {
37 | final InsnList list = new InsnList();
38 |
39 | list.add(new LdcInsnNode(arguments.latency()));
40 | list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Thread", "sleep", "(J)V", false));
41 |
42 | return list;
43 | }
44 | @Override
45 | public InsnList generateByteCode(TryCatchBlockNode tryCatchBlock, int tcIndex, AgentArguments arguments) {
46 | // won't use this method
47 | return null;
48 | }
49 | },
50 | FAULT {
51 | @Override
52 | public InsnList generateByteCode(MethodNode method, AgentArguments arguments) {
53 | final List exceptionsThrown = method.exceptions;
54 |
55 | InsnList list = new InsnList();
56 |
57 | if (exceptionsThrown.size() == 0) return list;
58 |
59 | list.add(new LdcInsnNode(exceptionsThrown.get(0)));
60 | list.add(new MethodInsnNode(
61 | Opcodes.INVOKESTATIC,
62 | "uk/co/probablyfine/bytemonkey/CreateAndThrowException",
63 | "throwOrDefault",
64 | "(Ljava/lang/String;)Ljava/lang/Throwable;",
65 | false // this is not a method on an interface
66 | ));
67 |
68 | list.add(new InsnNode(Opcodes.ATHROW));
69 |
70 | return list;
71 | }
72 | @Override
73 | public InsnList generateByteCode(TryCatchBlockNode tryCatchBlock, int tcIndex, AgentArguments arguments) {
74 | // won't use this method
75 | return null;
76 | }
77 | },
78 | NULLIFY {
79 | @Override
80 | public InsnList generateByteCode(MethodNode method, AgentArguments arguments) {
81 | final InsnList list = new InsnList();
82 |
83 | final Type[] argumentTypes = Type.getArgumentTypes(method.desc);
84 |
85 | final OptionalInt firstNonPrimitiveArgument = IntStream
86 | .range(0, argumentTypes.length)
87 | .filter(i -> argumentTypes[i].getSort() == Type.OBJECT)
88 | .findFirst();
89 |
90 | if (!firstNonPrimitiveArgument.isPresent()) return list;
91 |
92 | list.add(new InsnNode(Opcodes.ACONST_NULL));
93 | list.add(new VarInsnNode(Opcodes.ASTORE, firstNonPrimitiveArgument.getAsInt() + 1));
94 |
95 | return list;
96 | }
97 | @Override
98 | public InsnList generateByteCode(TryCatchBlockNode tryCatchBlock, int tcIndex, AgentArguments arguments) {
99 | // won't use this method
100 | return null;
101 | }
102 | };
103 |
104 | public static OperationMode fromLowerCase(String mode) {
105 | return OperationMode.valueOf(mode.toUpperCase());
106 | }
107 |
108 | public abstract InsnList generateByteCode(MethodNode method, AgentArguments arguments);
109 | public abstract InsnList generateByteCode(TryCatchBlockNode tryCatchBlock, int tcIndex, AgentArguments arguments);
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/testfiles/FaultTestObject.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.testfiles;
2 |
3 | import java.io.IOException;
4 |
5 | public class FaultTestObject {
6 | public void printSomething() throws IOException {
7 | System.out.println("Hello!");
8 | }
9 |
10 | public void printSomethingElse() throws IllegalStateException {
11 | System.out.println("Goodbye!");
12 | }
13 |
14 | public void printAndThrowNonPublicException() throws ExceptionWithNoPublicConstructor {
15 | System.out.println("Uh-oh!");
16 | }
17 |
18 | public void safePrint() {
19 | System.out.println("Hi!");
20 | }
21 |
22 | public static class ExceptionWithNoPublicConstructor extends RuntimeException {
23 | private ExceptionWithNoPublicConstructor() {} // No constructor for you!
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/testfiles/MissingPropertyException.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.testfiles;
2 |
3 | public class MissingPropertyException extends Exception {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/testfiles/NullabilityTestPojo.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.testfiles;
2 |
3 | public class NullabilityTestPojo {
4 |
5 | private String name;
6 |
7 | public NullabilityTestPojo(String name) {
8 | this.name = name;
9 | }
10 |
11 | public String getName() {
12 | return name;
13 | }
14 |
15 | public void setName1stArg(String name) {
16 | this.name = name;
17 | }
18 |
19 | public void setName2ndArg(int i, String name) {
20 | this.name = name;
21 | }
22 |
23 | public void setNameNoArgs() {
24 | this.name = "zap";
25 | }
26 |
27 | public void setNamePrimitiveArgs(int i, int i2) {
28 | this.name = "zoom";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/uk/co/probablyfine/bytemonkey/testfiles/TryCatchTestObject.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.testfiles;
2 |
3 | import java.io.IOException;
4 | import java.util.Random;
5 |
6 | public class TryCatchTestObject {
7 | public String multipleTryCatch() {
8 | StringBuilder result = new StringBuilder();
9 | try {
10 | // 1st try-catch block
11 | result.append("_1st line in 1st tc");
12 | String arg = getArgument();
13 | String key = format(arg);
14 | } catch (MissingPropertyException e) {
15 | // MissingPropertyException occured in sourceIndependentTryCatch
16 | result.append("_mpe in 1st tc");
17 | } catch (IOException e) {
18 | result.append("_ioe in 1st tc");
19 | }
20 |
21 | try {
22 | // 2nd try-catch block
23 | result.append("_1st line in 2nd tc");
24 | String arg = getArgument();
25 | String key = format(arg);
26 | } catch (MissingPropertyException e) {
27 | result.append("_mpe in 2nd tc");
28 | } catch (IOException e) {
29 | result.append("_ioe in 2nd tc");
30 | }
31 |
32 | return result.toString();
33 | }
34 |
35 | public String sourceIndependentTryCatch() {
36 | Boolean isCacheActivated = false;
37 | System.out.println("in try right away!!");
38 | try {
39 | System.out.println("first line in try!!");
40 | String arg = getArgument();
41 | String key = format(arg);
42 | return getProperty(key, isCacheActivated);
43 | } catch (MissingPropertyException e) {
44 | System.out.println("MissingPropertyException occured in sourceIndependentTryCatch");
45 | return "missing property";
46 | } catch (IOException e) {
47 | System.out.println("IOException occured in sourceIndependentTryCatch");
48 | return "get argument failed";
49 | }
50 | }
51 |
52 | public String sourceDependentTryCatch() {
53 | Boolean isCacheActivated = true;
54 | String arg = "str_arg";
55 | String key = "str_key";
56 | try {
57 | isCacheActivated = getCacheAvailability();
58 | return getProperty(key, isCacheActivated);
59 | } catch (MissingPropertyException e) {
60 | System.out.println("MissingPropertyException occured in sourceDependentTryCatch");
61 | if (isCacheActivated) {
62 | return "missing property";
63 | } else {
64 | throw new CacheDisableException();
65 | }
66 | }
67 | }
68 |
69 | public String purelyResilientTryCatch() {
70 | String key = "str_key";
71 | try {
72 | return getPropertyFromCache(key);
73 | } catch (MissingPropertyException e) {
74 | System.out.println("MissingPropertyException occured in purelyResilientTryCatch");
75 | return getPropertyFromFile(key);
76 | }
77 | }
78 |
79 | private String getProperty(String key, Boolean isCacheActivated) throws MissingPropertyException {
80 | return null;
81 | }
82 |
83 | private String format(String arg) throws MissingPropertyException {
84 | return null;
85 | }
86 |
87 | private String getArgument() throws IOException {
88 | return null;
89 | }
90 |
91 | private boolean getCacheAvailability() {
92 | Random random = new Random();
93 | return random.nextDouble() < 0.5;
94 | }
95 |
96 | private String getPropertyFromFile(String key) {
97 | return "property_from_file";
98 | }
99 |
100 | private String getPropertyFromCache(String key) throws MissingPropertyException {
101 | return "property_from_cache";
102 | }
103 |
104 | public static class CacheDisableException extends RuntimeException {
105 | }
106 |
107 | public static void main(String[] args) {
108 | TryCatchTestObject tcTest = new TryCatchTestObject();
109 |
110 | System.out.println(tcTest.multipleTryCatch());
111 | tcTest.sourceIndependentTryCatch();
112 | tcTest.sourceDependentTryCatch();
113 | tcTest.purelyResilientTryCatch();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/fault/DefaultExceptionTypeTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.fault;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.hamcrest.core.StringContains;
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 | import org.junit.rules.ExpectedException;
8 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
9 | import uk.co.probablyfine.bytemonkey.ByteMonkeyException;
10 | import uk.co.probablyfine.bytemonkey.testfiles.FaultTestObject;
11 |
12 | import java.io.IOException;
13 |
14 | import static org.hamcrest.core.StringContains.containsString;
15 |
16 | public class DefaultExceptionTypeTest {
17 |
18 | @Rule
19 | public ExpectedException expectedException = ExpectedException.none();
20 |
21 | @Test
22 | public void shouldThrowSomethingIDK() throws IOException {
23 | AgentLoader.loadAgentClass(
24 | ByteMonkeyAgent.class.getName(),
25 | "mode:fault,filter:uk/co/probablyfine/bytemonkey/testfiles/FaultTestObject/printAndThrowNonPublicException"
26 | );
27 |
28 | expectedException.expect(ByteMonkeyException.class);
29 | expectedException.expectMessage(containsString("FaultTestObject$ExceptionWithNoPublicConstructor"));
30 |
31 | new FaultTestObject().printAndThrowNonPublicException();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/fault/Rate0Test.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.fault;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.junit.rules.ExpectedException;
7 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
8 | import uk.co.probablyfine.bytemonkey.testfiles.FaultTestObject;
9 |
10 | import java.io.IOException;
11 |
12 | public class Rate0Test {
13 |
14 | @Rule
15 | public ExpectedException expectedException = ExpectedException.none();
16 |
17 | @Test
18 | public void shouldThrowNotExceptionWhenInstrumented_throwPercentageIs0() throws IOException {
19 | AgentLoader.loadAgentClass(ByteMonkeyAgent.class.getName(), "mode:fault,rate:0");
20 |
21 | new FaultTestObject().printSomething();
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/fault/Rate100Test.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.fault;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.junit.rules.ExpectedException;
7 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
8 | import uk.co.probablyfine.bytemonkey.testfiles.FaultTestObject;
9 |
10 | import java.io.IOException;
11 |
12 | public class Rate100Test {
13 |
14 | @Rule
15 | public ExpectedException expectedException = ExpectedException.none();
16 |
17 | @Test
18 | public void shouldThrowExceptionWhenInstrumented_throwPercentageIs100() throws IOException {
19 | AgentLoader.loadAgentClass(ByteMonkeyAgent.class.getName(), "mode:fault,rate:1");
20 |
21 | expectedException.expect(IOException.class);
22 |
23 | new FaultTestObject().printSomething();
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/fault/Rate50Test.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.fault;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.junit.rules.ExpectedException;
7 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
8 | import uk.co.probablyfine.bytemonkey.testfiles.FaultTestObject;
9 |
10 | import java.io.IOException;
11 | import java.util.concurrent.atomic.AtomicInteger;
12 | import java.util.stream.IntStream;
13 |
14 | import static org.hamcrest.MatcherAssert.assertThat;
15 | import static org.hamcrest.core.Is.is;
16 | import static org.hamcrest.number.IsCloseTo.closeTo;
17 |
18 | public class Rate50Test {
19 |
20 | @Rule
21 | public ExpectedException expectedException = ExpectedException.none();
22 |
23 | @Test
24 | public void shouldThrowExceptionWhenInstrumented_throwPercentageIsRoughlyHalf() throws IOException {
25 | AgentLoader.loadAgentClass(ByteMonkeyAgent.class.getName(), "mode:fault,rate:0.5");
26 |
27 | final AtomicInteger counter = new AtomicInteger(0);
28 |
29 | IntStream.range(0, 10_000).forEach(x -> {
30 | try {
31 | new FaultTestObject().printSomething();
32 | } catch (Exception e) {
33 | if (IOException.class.isAssignableFrom(e.getClass())) {
34 | counter.incrementAndGet();
35 | }
36 | }
37 | });
38 |
39 | double percentFailure = counter.get()/10_000f;
40 |
41 | assertThat(percentFailure, is(closeTo(0.5f, 0.1)));
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/fault/ThrowExceptionIfDeclaredTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.fault;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.junit.rules.ExpectedException;
7 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
8 | import uk.co.probablyfine.bytemonkey.testfiles.FaultTestObject;
9 |
10 | import java.io.IOException;
11 |
12 | public class ThrowExceptionIfDeclaredTest {
13 |
14 | @Rule
15 | public ExpectedException expectedException = ExpectedException.none();
16 |
17 | @Test
18 | public void shouldThrowCorrectException() throws IOException {
19 | AgentLoader.loadAgentClass(
20 | ByteMonkeyAgent.class.getName(),
21 | "mode:fault,filter:uk/co/probablyfine/bytemonkey/testfiles/FaultTestObject/printSomethingElse"
22 | );
23 |
24 | expectedException.expect(IllegalStateException.class);
25 |
26 | new FaultTestObject().printSomething();
27 | new FaultTestObject().printSomethingElse();
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/latency/Rate100Test.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.latency;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Test;
5 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
6 | import uk.co.probablyfine.bytemonkey.testfiles.FaultTestObject;
7 |
8 | import java.io.IOException;
9 |
10 | import static org.junit.Assert.assertTrue;
11 |
12 | public class Rate100Test {
13 |
14 | @Test
15 | public void shouldAddLatency() throws IOException {
16 | AgentLoader.loadAgentClass(
17 | ByteMonkeyAgent.class.getName(),
18 | "mode:latency,rate:1,latency:200,filter:uk/co/probablyfine/bytemonkey/testfiles"
19 | );
20 |
21 | long timeTaken = timed(new FaultTestObject()::safePrint);
22 |
23 | assertTrue("Actually took "+timeTaken+"ms", timeTaken >= 200 && timeTaken < 500);
24 | }
25 |
26 | public static long timed(Runnable runnable) {
27 | long startTime = System.currentTimeMillis();
28 |
29 | runnable.run();
30 |
31 | return System.currentTimeMillis() - startTime;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/nullify/DoNothingWithEmptyParamMethodsTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.nullify;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Test;
5 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
6 | import uk.co.probablyfine.bytemonkey.testfiles.NullabilityTestPojo;
7 |
8 | import java.io.IOException;
9 |
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class DoNothingWithEmptyParamMethodsTest {
14 |
15 | @Test
16 | public void shouldOnlyNullifyObjects() throws IOException {
17 | AgentLoader.loadAgentClass(
18 | ByteMonkeyAgent.class.getName(),
19 | "mode:nullify,rate:1,filter:uk/co/probablyfine/bytemonkey/testfiles/NullabilityTestPojo/setNameNoArgs"
20 | );
21 |
22 | NullabilityTestPojo pojo = new NullabilityTestPojo("foo");
23 |
24 | pojo.setNameNoArgs();
25 |
26 | assertThat(pojo.getName(), is("zap"));
27 | }
28 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/nullify/DoNothingWithNoObjectParamMethodsTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.nullify;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Test;
5 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
6 | import uk.co.probablyfine.bytemonkey.testfiles.NullabilityTestPojo;
7 |
8 | import java.io.IOException;
9 |
10 | import static org.hamcrest.CoreMatchers.is;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class DoNothingWithNoObjectParamMethodsTest {
14 |
15 | @Test
16 | public void shouldOnlyNullifyObjects() throws IOException {
17 | AgentLoader.loadAgentClass(
18 | ByteMonkeyAgent.class.getName(),
19 | "mode:nullify,rate:1,filter:uk/co/probablyfine/bytemonkey/testfiles/NullabilityTestPojo/setNamePrimitiveArgs"
20 | );
21 |
22 | NullabilityTestPojo pojo = new NullabilityTestPojo("foo");
23 |
24 | pojo.setNamePrimitiveArgs(1,2);
25 |
26 | assertThat(pojo.getName(), is("zoom"));
27 | }
28 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/nullify/NullifyArgumentsTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.nullify;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Test;
5 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
6 | import uk.co.probablyfine.bytemonkey.testfiles.NullabilityTestPojo;
7 |
8 | import java.io.IOException;
9 |
10 | import static org.junit.Assert.assertNull;
11 |
12 | public class NullifyArgumentsTest {
13 |
14 | @Test
15 | public void shouldNullifyArguments() throws IOException {
16 | AgentLoader.loadAgentClass(
17 | ByteMonkeyAgent.class.getName(),
18 | "mode:nullify,rate:1,filter:uk/co/probablyfine/bytemonkey/testfiles/NullabilityTestPojo/setName1stArg"
19 | );
20 |
21 | NullabilityTestPojo pojo = new NullabilityTestPojo("foo");
22 |
23 | pojo.setName1stArg("bar");
24 |
25 | assertNull(pojo.getName());
26 |
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/nullify/OnlyNullifyNonPrimitiveArgumentsTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.nullify;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Test;
5 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
6 | import uk.co.probablyfine.bytemonkey.testfiles.NullabilityTestPojo;
7 |
8 | import java.io.IOException;
9 |
10 | import static org.junit.Assert.assertNull;
11 |
12 | public class OnlyNullifyNonPrimitiveArgumentsTest {
13 |
14 | @Test
15 | public void shouldOnlyNullifyObjects() throws IOException {
16 | AgentLoader.loadAgentClass(
17 | ByteMonkeyAgent.class.getName(),
18 | "mode:nullify,rate:1,filter:uk/co/probablyfine/bytemonkey/testfiles/NullabilityTestPojo/setName2ndArg"
19 | );
20 |
21 | NullabilityTestPojo pojo = new NullabilityTestPojo("foo");
22 |
23 | pojo.setName2ndArg(1, "bar");
24 |
25 | assertNull(pojo.getName());
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/shortcircuit/TryCatchObjectNormalTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.shortcircuit;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 | import uk.co.probablyfine.bytemonkey.testfiles.TryCatchTestObject;
6 |
7 | public class TryCatchObjectNormalTest {
8 | @Test
9 | public void normalMultipleTryCatchTest() {
10 | // do not inject exceptions, multipleTryCatch() should execute smoothly
11 | TryCatchTestObject tcTest = new TryCatchTestObject();
12 | Assert.assertEquals(tcTest.multipleTryCatch(), "_1st line in 1st tc_1st line in 2nd tc");
13 | }
14 | }
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/shortcircuit/TryCatchObjectSCTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.shortcircuit;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Assert;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
8 | import uk.co.probablyfine.bytemonkey.testfiles.TryCatchTestObject;
9 |
10 | public class TryCatchObjectSCTest {
11 |
12 | @Before
13 | public void loadAgent() {
14 | AgentLoader.loadAgentClass(ByteMonkeyAgent.class.getName(), "mode:scircuit");
15 | }
16 |
17 | @Test
18 | public void scMultipleTryCatchTest() {
19 | // this time, we do short-circuit testing, exceptions will be injected into the beginning of every try block
20 | // hence "_1st line in xxx tc" should not appear in the return value
21 | TryCatchTestObject tcTest = new TryCatchTestObject();
22 | Assert.assertEquals(tcTest.multipleTryCatch(), "_mpe in 1st tc_mpe in 2nd tc");
23 | }
24 |
25 | public static void main(String[] args) {
26 | AgentLoader.loadAgentClass(ByteMonkeyAgent.class.getName(), "mode:scircuit");
27 |
28 | TryCatchTestObject tcTest = new TryCatchTestObject();
29 | System.out.println(tcTest.multipleTryCatch());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/uk/co/probablyfine/bytemonkey/shortcircuit/TryCatchObjectSCWithParamTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.probablyfine.bytemonkey.shortcircuit;
2 |
3 | import com.ea.agentloader.AgentLoader;
4 | import org.junit.Assert;
5 | import org.junit.Before;
6 | import org.junit.Test;
7 | import uk.co.probablyfine.bytemonkey.ByteMonkeyAgent;
8 | import uk.co.probablyfine.bytemonkey.testfiles.TryCatchTestObject;
9 |
10 | public class TryCatchObjectSCWithParamTest {
11 | @Before
12 | public void loadAgent() {
13 | AgentLoader.loadAgentClass(ByteMonkeyAgent.class.getName(), "mode:scircuit,tcindex:2,filter:uk/co/probablyfine/bytemonkey/testfiles/TryCatchTestObject/multipleTryCatch");
14 | }
15 |
16 | @Test
17 | public void scMultipleTryCatchWithParamTest() {
18 | TryCatchTestObject tcTest = new TryCatchTestObject();
19 | Assert.assertEquals("_1st line in 1st tc_mpe in 2nd tc", tcTest.multipleTryCatch());
20 | }
21 | }
--------------------------------------------------------------------------------