001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.assertions; 018 019import static java.util.Arrays.*; 020import static org.apache.juneau.commons.utils.AssertionUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import java.io.*; 024import java.lang.reflect.*; 025import java.util.*; 026import java.util.function.*; 027 028import org.apache.juneau.*; 029import org.apache.juneau.cp.*; 030import org.apache.juneau.serializer.*; 031 032/** 033 * Used for fluent assertion calls against primitive array objects (e.g. <c><jk>int</jk>[]</c>). 034 * 035 * <h5 class='section'>Test Methods:</h5> 036 * <p> 037 * <ul class='javatree'> 038 * <li class='jc'>{@link FluentPrimitiveArrayAssertion} 039 * <ul class='javatreec'> 040 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isHas(Object...) isHas(Object...)} 041 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#is(Predicate) is(Predicate)} 042 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isAny(Predicate) isAny(Predicate)} 043 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isAll(Predicate) isAll(Predicate)} 044 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isEmpty() isEmpty()} 045 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isNotEmpty() isNotEmpty()} 046 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isSize(int) isSize(int)} 047 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isContains(Object) isContains(Object)} 048 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#isNotContains(Object) isNotContains(Object)} 049 * </ul> 050 * <li class='jc'>{@link FluentObjectAssertion} 051 * <ul class='javatreec'> 052 * <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()} 053 * <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)} 054 * <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)} 055 * <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)} 056 * <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)} 057 * <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)} 058 * <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()} 059 * <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()} 060 * <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)} 061 * <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)} 062 * <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)} 063 * <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)} 064 * <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)} 065 * <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)} 066 * <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)} 067 * <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)} 068 * </ul> 069 * </ul> 070 * 071 * <h5 class='section'>Transform Methods:</h5> 072 * <p> 073 * <ul class='javatree'> 074 * <li class='jc'>{@link FluentPrimitiveArrayAssertion} 075 * <ul class='javatreec'> 076 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#asItem(int) asItem(int)} 077 * <li class='jm'>{@link FluentPrimitiveArrayAssertion#asLength() asLength()} 078 * </ul> 079 * <li class='jc'>{@link FluentObjectAssertion} 080 * <ul class='javatreec'> 081 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 082 * <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)} 083 * <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)} 084 * <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()} 085 * <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()} 086 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)} 087 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 088 * </ul> 089 * </ul> 090 * 091 * <h5 class='section'>Configuration Methods:</h5> 092 * <p> 093 * <ul class='javatree'> 094 * <li class='jc'>{@link Assertion} 095 * <ul class='javatreec'> 096 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 097 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 098 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 099 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 100 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 101 * </ul> 102 * </ul> 103 * 104 * <h5 class='section'>See Also:</h5><ul> 105 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem Overview</a> 106 * </ul> 107 * 108 * @param <E> The array element type. 109 * @param <T> The array type. 110 * @param <R> The return type. 111 */ 112public class FluentPrimitiveArrayAssertion<E,T,R> extends FluentObjectAssertion<T,R> { 113 114 private static final Map<Class<?>,Function<Object,String>> STRINGIFIERS = new HashMap<>(); 115 static { 116 STRINGIFIERS.put(boolean.class, x -> Arrays.toString((boolean[])x)); 117 STRINGIFIERS.put(byte.class, x -> Arrays.toString((byte[])x)); 118 STRINGIFIERS.put(char.class, x -> Arrays.toString((char[])x)); 119 STRINGIFIERS.put(double.class, x -> Arrays.toString((double[])x)); 120 STRINGIFIERS.put(float.class, x -> Arrays.toString((float[])x)); 121 STRINGIFIERS.put(int.class, x -> Arrays.toString((int[])x)); 122 STRINGIFIERS.put(long.class, x -> Arrays.toString((long[])x)); 123 STRINGIFIERS.put(short.class, x -> Arrays.toString((short[])x)); 124 } 125 126 // @formatter:off 127 private static final Messages MESSAGES = Messages.of(FluentPrimitiveArrayAssertion.class, "Messages"); 128 static final String 129 MSG_objectWasNotAnArray = MESSAGES.getString("objectWasNotAnArray"), 130 MSG_arrayWasNotEmpty = MESSAGES.getString("arrayWasNotEmpty"), 131 MSG_arrayWasEmpty = MESSAGES.getString("arrayWasEmpty"), 132 MSG_arrayDidNotHaveExpectedSize = MESSAGES.getString("arrayDidNotHaveExpectedSize"), 133 MSG_arrayDidNotContainExpectedValue = MESSAGES.getString("arrayDidNotContainExpectedValue"), 134 MSG_arrayDidNotContainExpectedValueAt = MESSAGES.getString("arrayDidNotContainExpectedValueAt"), 135 MSG_arrayContainedUnexpectedValue = MESSAGES.getString("arrayContainedUnexpectedValue"), 136 MSG_arrayDidntContainAnyMatchingValue = MESSAGES.getString("arrayDidntContainAnyMatchingValue"), 137 MSG_arrayContainedNonMatchingValueAt = MESSAGES.getString("arrayContainedNonMatchingValueAt"); 138 // @formatter:on 139 140 /** 141 * Chained constructor. 142 * 143 * <p> 144 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 145 * 146 * @param creator 147 * The assertion that created this assertion. 148 * <br>Should be <jk>null</jk> if this is the top-level assertion. 149 * @param value 150 * The object being tested. 151 * <br>Can be <jk>null</jk>. 152 * @param returns 153 * The object to return after a test method is called. 154 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 155 * used on the same assertion. 156 */ 157 public FluentPrimitiveArrayAssertion(Assertion creator, T value, R returns) { 158 super(creator, value, returns); 159 if (nn(value)) { 160 var c = value.getClass(); 161 if (! (c.isArray() && c.getComponentType().isPrimitive())) 162 throw new BasicAssertionError(MSG_objectWasNotAnArray, cn(value.getClass())); 163 } 164 } 165 166 /** 167 * Constructor. 168 * 169 * @param value 170 * The object being tested. 171 * <br>Can be <jk>null</jk>. 172 * @param returns 173 * The object to return after a test method is called. 174 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 175 * used on the same assertion. 176 */ 177 public FluentPrimitiveArrayAssertion(T value, R returns) { 178 this(null, value, returns); 179 } 180 181 /** 182 * Returns an object assertion on the item specified at the specified index. 183 * 184 * <p> 185 * If the array is <jk>null</jk> or the index is out-of-bounds, the returned assertion is a null assertion 186 * (meaning {@link FluentAnyAssertion#isExists()} returns <jk>false</jk>). 187 * 188 * @param index The index of the item to retrieve from the array. 189 * @return A new assertion. 190 */ 191 public FluentAnyAssertion<E,R> asItem(int index) { 192 return new FluentAnyAssertion<>(this, at(index), returns()); 193 } 194 195 /** 196 * Returns an integer assertion on the length of this array. 197 * 198 * <p> 199 * If the array is <jk>null</jk> or the index is out-of-bounds, the returned assertion is a null assertion 200 * (meaning {@link FluentIntegerAssertion#isExists()} returns <jk>false</jk>). 201 * 202 * @return A new assertion. 203 */ 204 public FluentIntegerAssertion<R> asLength() { 205 return new FluentIntegerAssertion<>(this, valueIsNull() ? null : Array.getLength(value()), returns()); 206 } 207 208 @Override /* Overridden from FluentBaseAssertion */ 209 public FluentStringAssertion<R> asString() { 210 return new FluentStringAssertion<>(this, valueIsNull() ? null : r(value()), returns()); 211 } 212 213 @Override /* Overridden from FluentObjectAssertion */ 214 public FluentPrimitiveArrayAssertion<E,T,R> asTransformed(Function<T,T> function) { // NOSONAR - Intentional. 215 return new FluentPrimitiveArrayAssertion<>(this, function.apply(orElse(null)), returns()); 216 } 217 218 /** 219 * Asserts that the contents of this list pass the specified tests. 220 * 221 * @param tests The tests to run. <jk>null</jk> entries are ignored. 222 * @return This object. 223 * @throws AssertionError If assertion failed. 224 */ 225 @SafeVarargs 226 public final R is(Predicate<E>...tests) throws AssertionError { 227 isSize(tests.length); 228 for (int i = 0, j = length2(); i < j; i++) { 229 var t = tests[i]; 230 if (nn(t) && ! t.test(at(i))) 231 throw error(MSG_arrayDidNotContainExpectedValueAt, i, getFailureMessage(t, at(i))); 232 } 233 return returns(); 234 } 235 236 /** 237 * Asserts that all values in the array pass the specified test. 238 * 239 * @param test The predicate test. 240 * @return The fluent return object. 241 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 242 */ 243 public R isAll(Predicate<E> test) throws AssertionError { 244 assertArgNotNull("test", test); 245 for (int i = 0, j = length2(); i < j; i++) 246 if (! test.test(at(i))) 247 throw error(MSG_arrayContainedNonMatchingValueAt, i, getFailureMessage(test, at(i))); 248 return returns(); 249 } 250 251 /** 252 * Asserts that at least one value in the array passes the specified test. 253 * 254 * @param test The predicate test. 255 * @return The fluent return object. 256 * @throws AssertionError If assertion failed or value was <jk>null</jk>. 257 */ 258 public R isAny(Predicate<E> test) throws AssertionError { 259 assertArgNotNull("test", test); 260 for (int i = 0, j = length2(); i < j; i++) 261 if (test.test(at(i))) 262 return returns(); 263 throw error(MSG_arrayDidntContainAnyMatchingValue, r(value())); 264 } 265 266 /** 267 * Asserts that the array contains the expected entry. 268 * 269 * @param entry The value to check for. 270 * @return The fluent return object. 271 * @throws AssertionError If assertion failed. 272 */ 273 public R isContains(E entry) throws AssertionError { 274 for (int i = 0, j = length2(); i < j; i++) 275 if (eq(at(i), entry)) 276 return returns(); 277 throw error(MSG_arrayDidNotContainExpectedValue, r(entry), r(value())); 278 } 279 280 /** 281 * Asserts that the collection exists and is empty. 282 * 283 * @return The fluent return object. 284 * @throws AssertionError If assertion failed. 285 */ 286 public R isEmpty() throws AssertionError { 287 if (length2() != 0) 288 throw error(MSG_arrayWasNotEmpty); 289 return returns(); 290 } 291 292 /** 293 * Asserts that the contents of this list contain the specified values. 294 * 295 * @param entries The expected entries in this list. 296 * @return This object. 297 * @throws AssertionError If assertion failed. 298 */ 299 @SuppressWarnings("unchecked") 300 public R isHas(E...entries) throws AssertionError { 301 assertArgNotNull("entries", entries); 302 Predicate<E>[] p = stream(entries).map(AssertionPredicates::eq).toArray(Predicate[]::new); 303 return is(p); 304 } 305 306 /** 307 * Asserts that the array does not contain the expected value. 308 * 309 * @param entry The value to check for. 310 * @return The fluent return object. 311 * @throws AssertionError If assertion failed. 312 */ 313 public R isNotContains(E entry) throws AssertionError { 314 for (var i = 0; i < length2(); i++) 315 if (eq(at(i), entry)) 316 throw error(MSG_arrayContainedUnexpectedValue, r(entry), r(value())); 317 return returns(); 318 } 319 320 /** 321 * Asserts that the collection exists and is not empty. 322 * 323 * @return The fluent return object. 324 * @throws AssertionError If assertion failed. 325 */ 326 public R isNotEmpty() throws AssertionError { 327 if (length2() == 0) 328 throw error(MSG_arrayWasEmpty); 329 return returns(); 330 } 331 332 /** 333 * Asserts that the collection exists and is the specified size. 334 * 335 * @param size The expected size. 336 * @return The fluent return object. 337 * @throws AssertionError If assertion failed. 338 */ 339 public R isSize(int size) throws AssertionError { 340 if (length2() != size) 341 throw error(MSG_arrayDidNotHaveExpectedSize, size, asLength()); 342 return returns(); 343 } 344 345 @Override /* Overridden from Assertion */ 346 public FluentPrimitiveArrayAssertion<E,T,R> setMsg(String msg, Object...args) { 347 super.setMsg(msg, args); 348 return this; 349 } 350 351 @Override /* Overridden from Assertion */ 352 public FluentPrimitiveArrayAssertion<E,T,R> setOut(PrintStream value) { 353 super.setOut(value); 354 return this; 355 } 356 357 @Override /* Overridden from Assertion */ 358 public FluentPrimitiveArrayAssertion<E,T,R> setSilent() { 359 super.setSilent(); 360 return this; 361 } 362 363 @Override /* Overridden from Assertion */ 364 public FluentPrimitiveArrayAssertion<E,T,R> setStdOut() { 365 super.setStdOut(); 366 return this; 367 } 368 369 @Override /* Overridden from Assertion */ 370 public FluentPrimitiveArrayAssertion<E,T,R> setThrowable(Class<? extends java.lang.RuntimeException> value) { 371 super.setThrowable(value); 372 return this; 373 } 374 375 @Override 376 public String toString() { 377 if (valueIsNull()) 378 return null; // NOSONAR - Intentional. 379 return STRINGIFIERS.get(value().getClass().getComponentType()).apply(value()); 380 } 381 382 @SuppressWarnings("unchecked") 383 private E at(int index) { 384 return valueIsNull() || index < 0 || index >= length2() ? null : (E)Array.get(value(), index); 385 } 386 387 private int length2() { 388 return Array.getLength(value()); 389 } 390}