### Question:
Retry { public void perform(List<Exception> list, T obj) { do { try { op.operation(list); return; } catch (Exception e) { this.errors.add(e); if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(e)) { this.handleError.handleIssue(obj, e); return; } try { long testDelay = (long) Math.pow(2, this.attempts.intValue()) * 1000 + RANDOM.nextInt(1000); long delay = Math.min(testDelay, this.maxDelay); Thread.sleep(delay); } catch (InterruptedException f) { } } } while (true); } Retry(Operation op, HandleErrorIssue<T> handleError, int maxAttempts,
        long maxDelay, Predicate<Exception>... ignoreTests); void perform(List<Exception> list, T obj);  }### Answer:
@Test void performTest() { Retry.Operation op = (l) -> { if (!l.isEmpty()) { throw l.remove(0); } }; Retry.HandleErrorIssue<Order> handleError = (o, e) -> { }; var r1 = new Retry<>(op, handleError, 3, 30000, e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass())); var r2 = new Retry<>(op, handleError, 3, 30000, e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass())); var user = new User("Jim", "ABCD"); var order = new Order(user, "book", 10f); var arr1 = new ArrayList<>(List.of(new ItemUnavailableException(), new DatabaseUnavailableException())); try { r1.perform(arr1, order); } catch (Exception e1) { e1.printStackTrace(); } var arr2 = new ArrayList<>(List.of(new DatabaseUnavailableException(), new ItemUnavailableException())); try { r2.perform(arr2, order); } catch (Exception e1) { e1.printStackTrace(); } assertTrue(arr1.size() == 1 && arr2.size() == 0); }