/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.service.dxl;
import com.foundationdb.server.service.session.Session;
import com.foundationdb.server.service.session.TestSessionFactory;
import com.foundationdb.util.MultipleCauseException;
import com.foundationdb.util.Strings;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
public final class CompositeHookTest {
private List<String> output;
private Session session;
@Before
public void setUp() {
output = new ArrayList<>();
session = TestSessionFactory.get().createSession();
}
@Test
public void noExceptions() {
DXLFunctionsHook hook = compose(output, "alpha", "beta", "gamma");
hook.hookFunctionIn(session, DXLFunctionsHook.DXLFunction.GET_AIS);
hook.hookFunctionFinally(session, DXLFunctionsHook.DXLFunction.GET_AIS, null);
check(
"alpha into GET_AIS",
"beta into GET_AIS",
"gamma into GET_AIS",
"gamma out of GET_AIS",
"beta out of GET_AIS",
"alpha out of GET_AIS"
);
}
@Test
public void wrappedThrowsException() {
DXLFunctionsHook hook = compose(output, "alpha", "beta", "gamma");
MySampleException e = new MySampleException();
hook.hookFunctionIn(session, DXLFunctionsHook.DXLFunction.CREATE_TABLE);
hook.hookFunctionCatch(session, DXLFunctionsHook.DXLFunction.CREATE_TABLE, e);
hook.hookFunctionFinally(session, DXLFunctionsHook.DXLFunction.CREATE_TABLE, e);
check(
"alpha into CREATE_TABLE",
"beta into CREATE_TABLE",
"gamma into CREATE_TABLE",
"gamma caught MySampleException in CREATE_TABLE",
"beta caught MySampleException in CREATE_TABLE",
"alpha caught MySampleException in CREATE_TABLE",
"gamma out of CREATE_TABLE",
"beta out of CREATE_TABLE",
"alpha out of CREATE_TABLE"
);
}
@Test
public void crashOnIn() {
DXLFunctionsHook hook = compose(output, "alpha", "beta: CRASH_IN", "gamma");
try {
hook.hookFunctionIn(session, DXLFunctionsHook.DXLFunction.GET_AIS);
fail();
} catch (MySampleCash e) {
// good
}
MySampleException e = new MySampleException();
hook.hookFunctionCatch(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
hook.hookFunctionFinally(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
check(
"alpha into GET_AIS",
"beta: CRASH_IN into GET_AIS",
"beta: CRASH_IN caught MySampleException in GET_AIS",
"alpha caught MySampleException in GET_AIS",
"beta: CRASH_IN out of GET_AIS",
"alpha out of GET_AIS"
);
}
@Test
public void crashOnCatch() {
DXLFunctionsHook hook = compose(output, "alpha", "beta: CRASH_CATCH", "gamma");
hook.hookFunctionIn(session, DXLFunctionsHook.DXLFunction.GET_AIS);
MySampleException e = new MySampleException();
try {
hook.hookFunctionCatch(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
fail();
} catch (MySampleCash e1) {
// good
}
hook.hookFunctionFinally(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
check(
"alpha into GET_AIS",
"beta: CRASH_CATCH into GET_AIS",
"gamma into GET_AIS",
"gamma caught MySampleException in GET_AIS",
"beta: CRASH_CATCH caught MySampleException in GET_AIS",
"alpha caught MySampleException in GET_AIS",
"gamma out of GET_AIS",
"beta: CRASH_CATCH out of GET_AIS",
"alpha out of GET_AIS"
);
}
@Test
public void crashOnFinally() {
DXLFunctionsHook hook = compose(output, "alpha", "beta: CRASH_FINALLY", "gamma");
MySampleException e = new MySampleException();
hook.hookFunctionIn(session, DXLFunctionsHook.DXLFunction.GET_AIS);
hook.hookFunctionCatch(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
try {
hook.hookFunctionFinally(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
fail();
} catch (MySampleCash e1) {
// good
}
check(
"alpha into GET_AIS",
"beta: CRASH_FINALLY into GET_AIS",
"gamma into GET_AIS",
"gamma caught MySampleException in GET_AIS",
"beta: CRASH_FINALLY caught MySampleException in GET_AIS",
"alpha caught MySampleException in GET_AIS",
"gamma out of GET_AIS",
"beta: CRASH_FINALLY out of GET_AIS",
"alpha out of GET_AIS"
);
}
@Test
public void multipleCrashes() {
DXLFunctionsHook hook = compose(output,
"alpha",
"beta: CRASH_CATCH CRASH_FINALLY",
"gamma: CRASH_CATCH CRASH_FINALLY",
"delta"
);
hook.hookFunctionIn(session, DXLFunctionsHook.DXLFunction.GET_AIS);
MySampleException e = new MySampleException();
try {
hook.hookFunctionCatch(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
fail();
} catch (MultipleCauseException e1) {
assertEquals("causes", 2, e1.getCauses().size());
// good
}
try {
hook.hookFunctionFinally(session, DXLFunctionsHook.DXLFunction.GET_AIS, e);
fail();
} catch (MultipleCauseException e1) {
assertEquals("causes", 2, e1.getCauses().size());
// good
}
check(
"alpha into GET_AIS",
"beta: CRASH_CATCH CRASH_FINALLY into GET_AIS",
"gamma: CRASH_CATCH CRASH_FINALLY into GET_AIS",
"delta into GET_AIS",
"delta caught MySampleException in GET_AIS",
"gamma: CRASH_CATCH CRASH_FINALLY caught MySampleException in GET_AIS",
"beta: CRASH_CATCH CRASH_FINALLY caught MySampleException in GET_AIS",
"alpha caught MySampleException in GET_AIS",
"delta out of GET_AIS",
"gamma: CRASH_CATCH CRASH_FINALLY out of GET_AIS",
"beta: CRASH_CATCH CRASH_FINALLY out of GET_AIS",
"alpha out of GET_AIS"
);
}
private void check(String... expected) {
// if this fails, joining the lists makes it easier to diff
assertEquals("messages", Strings.join(Arrays.asList(expected)), Strings.join(output));
// sanity check that our lists are really really equal, not just equivalent toString
assertEquals("messages", Arrays.asList(expected), output);
}
DXLFunctionsHook compose(List<String> output, String... messages) {
List<DXLFunctionsHook> hooks = new ArrayList<>();
for (String message : messages) {
hooks.add( new ToListHook(message, output) );
}
return new CompositeHook(hooks);
}
private static class ToListHook implements DXLFunctionsHook {
private final String message;
private final List<String> output;
private ToListHook(String message, List<String> output) {
this.output = output;
this.message = message;
}
@Override
public void hookFunctionIn(Session session, DXLFunction function) {
output.add(String.format("%s into %s", message, function.name()));
if (message.contains("CRASH_IN")) {
throw new MySampleCash();
}
}
@Override
public void hookFunctionCatch(Session session, DXLFunction function, Throwable throwable) {
output.add(
String.format(
"%s caught %s in %s",
message,
throwable.getClass().getSimpleName(),
function.name()
)
);
if (message.contains("CRASH_CATCH")) {
throw new MySampleCash();
}
}
@Override
public void hookFunctionFinally(Session session, DXLFunction function, Throwable thrown) {
output.add(String.format("%s out of %s", message, function.name()));
if (message.contains("CRASH_FINALLY")) {
throw new MySampleCash();
}
}
}
private static class MySampleException extends Exception {
}
private static class MySampleCash extends RuntimeException {
}
}