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; 025import java.util.function.Consumer; 026 027import ch.powerunit.exception.InternalError; 028 029/** 030 * Definition of a statement (piece of code that can thrown Throwable). 031 * <p> 032 * A statement can be used inside test (to isolate a code that must thrown an 033 * exception) and are used internally by the framework to compose and execute 034 * test sequence element. 035 * 036 * @author borettim 037 * @param <P> 038 * The type of the parameter 039 * @param <T> 040 * the exception type 041 */ 042@FunctionalInterface 043public interface Statement<P, T extends Throwable> { 044 045 /** 046 * Executable code. 047 * 048 * @param parameter 049 * A parameter for the statement 050 * @throws Throwable 051 * in case of error. 052 */ 053 void run(P parameter) throws Throwable;// should be T, but T seem to produce 054 // a bug in the compiler 055 056 /** 057 * Used to provide a name (for internal use purpose). 058 * 059 * @return the string, by default null. 060 * @since 0.1.0 061 */ 062 default String getName() { 063 return null; 064 } 065 066 /** 067 * Aggregate this statement and then the following. The second statement is 068 * done, even in case of exception in the first one. 069 * 070 * @param after 071 * the next statement 072 * @return the new statement 073 */ 074 default Statement<P, T> andThenAlways(Statement<P, T> after) { 075 Objects.requireNonNull(after); 076 return (p) -> { 077 try { 078 run(p); 079 } finally { 080 after.run(p); 081 } 082 }; 083 } 084 085 /** 086 * Aggregate this statement and then the following. The second statement is 087 * done except in case of exception in the first one. 088 * 089 * @param after 090 * the next statement 091 * @return the new statement 092 */ 093 default Statement<P, T> andThenOnlySuccess(Statement<P, T> after) { 094 Objects.requireNonNull(after); 095 return (p) -> { 096 run(p); 097 after.run(p); 098 }; 099 } 100 101 /** 102 * Build a around statement (do something, then something others, and after 103 * one a third statement, event in case of exception. 104 * 105 * @param internal 106 * the internal part 107 * @param before 108 * the first statement 109 * @param after 110 * the last statement, done event in case of exception. 111 * @return the new statement. 112 * @param <P> 113 * The type of the parameter 114 * @param <T> 115 * the exception type 116 */ 117 static <P, T extends Throwable> Statement<P, T> around( 118 Statement<P, T> internal, Statement<P, T> before, 119 Statement<P, T> after) { 120 Objects.requireNonNull(internal); 121 Objects.requireNonNull(before); 122 Objects.requireNonNull(after); 123 return before.andThenOnlySuccess(internal).andThenAlways(after); 124 } 125 126 /** 127 * Build a statement based on a method- 128 * 129 * @param target 130 * the target object 131 * @param method 132 * the method 133 * @return the new statement. 134 * @param <P> 135 * The type of the parameter 136 * @param <T> 137 * the exception type 138 */ 139 static <P, T extends Throwable> Statement<P, T> reflectionMethod( 140 Object target, Method method) { 141 Objects.requireNonNull(target); 142 Objects.requireNonNull(method); 143 return new Statement<P, T>() { 144 145 @Override 146 public void run(P parameter) throws Throwable { 147 try { 148 method.invoke(target); 149 } catch (InvocationTargetException e) { 150 throw e.getCause(); 151 } catch (IllegalAccessException | IllegalArgumentException e) { 152 throw new InternalError("Unexpected error " 153 + e.getMessage(), e); 154 } 155 } 156 157 @Override 158 public String getName() { 159 return method.getName(); 160 } 161 }; 162 } 163 164 /** 165 * Build a statement based on a method- 166 * 167 * @param method 168 * the method 169 * @param param 170 * the param 171 * @return the new statement. 172 * @param <P> 173 * The type of the parameter 174 * @param <T> 175 * the exception type 176 * @since 0.2.0 177 */ 178 static <P, T extends Throwable> Statement<P, T> reflectionMethod( 179 Consumer<Object> method, Object param) { 180 Objects.requireNonNull(method); 181 return new Statement<P, T>() { 182 183 @Override 184 public void run(P parameter) throws Throwable { 185 method.accept(param); 186 } 187 188 @Override 189 public String getName() { 190 return "N/A"; 191 } 192 }; 193 } 194 195}