001/** 002 * Powerunit - A JDK1.8 test framework 003 * Copyright (C) 2014 Mathieu Boretti. 004 * 005 * This file is part of Powerunit 006 * 007 * Powerunit is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * Powerunit is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>. 019 */ 020package ch.powerunit; 021 022import java.lang.reflect.InvocationTargetException; 023import java.lang.reflect.Method; 024import java.util.Objects; 025 026import ch.powerunit.exception.InternalError; 027 028/** 029 * Definition of a statement (piece of code that can thrown Throwable). 030 * <p> 031 * A statement can be used inside test (to isolate a code that must thrown an 032 * exception) and are used internally by the framework to compose and execute 033 * test sequence element. 034 * 035 * @author borettim 036 * @param <P> 037 * The type of the parameter 038 * @param <T> 039 * the exception type 040 */ 041@FunctionalInterface 042public interface Statement<P, T extends Throwable> { 043 044 /** 045 * Executable code. 046 * 047 * @param parameter 048 * A parameter for the statement 049 * @throws Throwable 050 * in case of error. 051 */ 052 void run(P parameter) throws Throwable;// should be T, but T seem to produce 053 // a bug in the compiler 054 055 /** 056 * Used to provide a name (for internal use purpose). 057 * 058 * @return the string, by default null. 059 * @since 0.1.0 060 */ 061 default String getName() { 062 return null; 063 } 064 065 /** 066 * Aggregate this statement and then the following. The second statement is 067 * done, even in case of exception in the first one. 068 * 069 * @param after 070 * the next statement 071 * @return the new statement 072 */ 073 default Statement<P, T> andThenAlways(Statement<P, T> after) { 074 Objects.requireNonNull(after); 075 return (p) -> { 076 try { 077 run(p); 078 } finally { 079 after.run(p); 080 } 081 }; 082 } 083 084 /** 085 * Aggregate this statement and then the following. The second statement is 086 * done except in case of exception in the first one. 087 * 088 * @param after 089 * the next statement 090 * @return the new statement 091 */ 092 default Statement<P, T> andThenOnlySuccess(Statement<P, T> after) { 093 Objects.requireNonNull(after); 094 return (p) -> { 095 run(p); 096 after.run(p); 097 }; 098 } 099 100 /** 101 * Build a around statement (do something, then something others, and after 102 * one a third statement, event in case of exception. 103 * 104 * @param internal 105 * the internal part 106 * @param before 107 * the first statement 108 * @param after 109 * the last statement, done event in case of exception. 110 * @return the new statement. 111 * @param <P> 112 * The type of the parameter 113 * @param <T> 114 * the exception type 115 */ 116 static <P, T extends Throwable> Statement<P, T> around( 117 Statement<P, T> internal, Statement<P, T> before, 118 Statement<P, T> after) { 119 Objects.requireNonNull(internal); 120 Objects.requireNonNull(before); 121 Objects.requireNonNull(after); 122 return before.andThenOnlySuccess(internal).andThenAlways(after); 123 } 124 125 /** 126 * Build a statement based on a method- 127 * 128 * @param target 129 * the target object 130 * @param method 131 * the method 132 * @return the new statement. 133 * @param <P> 134 * The type of the parameter 135 * @param <T> 136 * the exception type 137 */ 138 static <P, T extends Throwable> Statement<P, T> reflectionMethod( 139 Object target, Method method) { 140 Objects.requireNonNull(target); 141 Objects.requireNonNull(method); 142 return new Statement<P, T>() { 143 144 @Override 145 public void run(P parameter) throws Throwable { 146 try { 147 method.invoke(target); 148 } catch (InvocationTargetException e) { 149 throw e.getCause(); 150 } catch (IllegalAccessException | IllegalArgumentException e) { 151 throw new InternalError("Unexpected error " 152 + e.getMessage(), e); 153 } 154 } 155 156 @Override 157 public String getName() { 158 return method.getName(); 159 } 160 }; 161 } 162 163}