|
15 | 15 | */ |
16 | 16 | package com.mongodb.internal.async; |
17 | 17 |
|
18 | | -import com.mongodb.client.TestListener; |
19 | 18 | import org.junit.jupiter.api.Test; |
20 | | -import org.opentest4j.AssertionFailedError; |
21 | 19 |
|
22 | | -import java.util.ArrayList; |
23 | | -import java.util.List; |
24 | | -import java.util.concurrent.atomic.AtomicBoolean; |
25 | | -import java.util.concurrent.atomic.AtomicReference; |
26 | 20 | import java.util.function.BiConsumer; |
27 | 21 | import java.util.function.Consumer; |
28 | 22 | import java.util.function.Supplier; |
29 | 23 |
|
30 | 24 | import static com.mongodb.assertions.Assertions.assertNotNull; |
31 | 25 | import static com.mongodb.internal.async.AsyncRunnable.beginAsync; |
32 | | -import static org.junit.jupiter.api.Assertions.assertEquals; |
33 | | -import static org.junit.jupiter.api.Assertions.assertFalse; |
34 | 26 | import static org.junit.jupiter.api.Assertions.assertThrows; |
35 | | -import static org.junit.jupiter.api.Assertions.assertTrue; |
36 | | -import static org.junit.jupiter.api.Assertions.fail; |
37 | 27 |
|
38 | | -final class AsyncFunctionsTest { |
39 | | - private final TestListener listener = new TestListener(); |
40 | | - private final InvocationTracker invocationTracker = new InvocationTracker(); |
41 | | - private boolean isTestingAbruptCompletion = false; |
| 28 | +final class AsyncFunctionsTest extends AsyncFunctionsTestAbstract { |
42 | 29 |
|
43 | 30 | @Test |
44 | 31 | void test1Method() { |
@@ -877,275 +864,4 @@ void testDerivation() { |
877 | 864 | }); |
878 | 865 | } |
879 | 866 |
|
880 | | - // invoked methods: |
881 | | - |
882 | | - private void plain(final int i) { |
883 | | - int cur = invocationTracker.getNextOption(2); |
884 | | - if (cur == 0) { |
885 | | - listener.add("plain-exception-" + i); |
886 | | - throw new RuntimeException("affected method exception-" + i); |
887 | | - } else { |
888 | | - listener.add("plain-success-" + i); |
889 | | - } |
890 | | - } |
891 | | - |
892 | | - private int plainReturns(final int i) { |
893 | | - int cur = invocationTracker.getNextOption(2); |
894 | | - if (cur == 0) { |
895 | | - listener.add("plain-exception-" + i); |
896 | | - throw new RuntimeException("affected method exception-" + i); |
897 | | - } else { |
898 | | - listener.add("plain-success-" + i); |
899 | | - return i; |
900 | | - } |
901 | | - } |
902 | | - |
903 | | - private boolean plainTest(final int i) { |
904 | | - int cur = invocationTracker.getNextOption(3); |
905 | | - if (cur == 0) { |
906 | | - listener.add("plain-exception-" + i); |
907 | | - throw new RuntimeException("affected method exception-" + i); |
908 | | - } else if (cur == 1) { |
909 | | - listener.add("plain-false-" + i); |
910 | | - return false; |
911 | | - } else { |
912 | | - listener.add("plain-true-" + i); |
913 | | - return true; |
914 | | - } |
915 | | - } |
916 | | - |
917 | | - private void sync(final int i) { |
918 | | - assertFalse(invocationTracker.isAsyncStep); |
919 | | - affected(i); |
920 | | - } |
921 | | - |
922 | | - |
923 | | - private Integer syncReturns(final int i) { |
924 | | - assertFalse(invocationTracker.isAsyncStep); |
925 | | - return affectedReturns(i); |
926 | | - } |
927 | | - |
928 | | - private void async(final int i, final SingleResultCallback<Void> callback) { |
929 | | - assertTrue(invocationTracker.isAsyncStep); |
930 | | - if (isTestingAbruptCompletion) { |
931 | | - affected(i); |
932 | | - callback.complete(callback); |
933 | | - |
934 | | - } else { |
935 | | - try { |
936 | | - affected(i); |
937 | | - callback.complete(callback); |
938 | | - } catch (Throwable t) { |
939 | | - callback.onResult(null, t); |
940 | | - } |
941 | | - } |
942 | | - } |
943 | | - |
944 | | - private void asyncReturns(final int i, final SingleResultCallback<Integer> callback) { |
945 | | - assertTrue(invocationTracker.isAsyncStep); |
946 | | - if (isTestingAbruptCompletion) { |
947 | | - callback.complete(affectedReturns(i)); |
948 | | - } else { |
949 | | - try { |
950 | | - callback.complete(affectedReturns(i)); |
951 | | - } catch (Throwable t) { |
952 | | - callback.onResult(null, t); |
953 | | - } |
954 | | - } |
955 | | - } |
956 | | - |
957 | | - private void affected(final int i) { |
958 | | - int cur = invocationTracker.getNextOption(2); |
959 | | - if (cur == 0) { |
960 | | - listener.add("affected-exception-" + i); |
961 | | - throw new RuntimeException("exception-" + i); |
962 | | - } else { |
963 | | - listener.add("affected-success-" + i); |
964 | | - } |
965 | | - } |
966 | | - |
967 | | - private int affectedReturns(final int i) { |
968 | | - int cur = invocationTracker.getNextOption(2); |
969 | | - if (cur == 0) { |
970 | | - listener.add("affected-exception-" + i); |
971 | | - throw new RuntimeException("exception-" + i); |
972 | | - } else { |
973 | | - listener.add("affected-success-" + i); |
974 | | - return i; |
975 | | - } |
976 | | - } |
977 | | - |
978 | | - // assert methods: |
979 | | - |
980 | | - private void assertBehavesSameVariations(final int expectedVariations, final Runnable sync, |
981 | | - final Consumer<SingleResultCallback<Void>> async) { |
982 | | - assertBehavesSameVariations(expectedVariations, |
983 | | - () -> { |
984 | | - sync.run(); |
985 | | - return null; |
986 | | - }, |
987 | | - (c) -> { |
988 | | - async.accept((v, e) -> c.onResult(v, e)); |
989 | | - }); |
990 | | - } |
991 | | - |
992 | | - private <T> void assertBehavesSameVariations(final int expectedVariations, final Supplier<T> sync, |
993 | | - final Consumer<SingleResultCallback<T>> async) { |
994 | | - // run the variation-trying code twice, with direct/indirect exceptions |
995 | | - for (int i = 0; i < 2; i++) { |
996 | | - isTestingAbruptCompletion = i != 0; |
997 | | - |
998 | | - // the variation-trying code: |
999 | | - invocationTracker.reset(); |
1000 | | - do { |
1001 | | - invocationTracker.startInitialStep(); |
1002 | | - assertBehavesSame( |
1003 | | - sync, |
1004 | | - () -> invocationTracker.startMatchStep(), |
1005 | | - async); |
1006 | | - } while (invocationTracker.countDown()); |
1007 | | - assertEquals(expectedVariations, invocationTracker.getVariationCount(), |
1008 | | - "number of variations did not match"); |
1009 | | - } |
1010 | | - |
1011 | | - } |
1012 | | - |
1013 | | - private <T> void assertBehavesSame(final Supplier<T> sync, final Runnable between, |
1014 | | - final Consumer<SingleResultCallback<T>> async) { |
1015 | | - |
1016 | | - T expectedValue = null; |
1017 | | - Throwable expectedException = null; |
1018 | | - try { |
1019 | | - expectedValue = sync.get(); |
1020 | | - } catch (Throwable e) { |
1021 | | - expectedException = e; |
1022 | | - } |
1023 | | - List<String> expectedEvents = listener.getEventStrings(); |
1024 | | - |
1025 | | - listener.clear(); |
1026 | | - between.run(); |
1027 | | - |
1028 | | - AtomicReference<T> actualValue = new AtomicReference<>(); |
1029 | | - AtomicReference<Throwable> actualException = new AtomicReference<>(); |
1030 | | - AtomicBoolean wasCalled = new AtomicBoolean(false); |
1031 | | - try { |
1032 | | - async.accept((v, e) -> { |
1033 | | - actualValue.set(v); |
1034 | | - actualException.set(e); |
1035 | | - if (wasCalled.get()) { |
1036 | | - fail(); |
1037 | | - } |
1038 | | - wasCalled.set(true); |
1039 | | - }); |
1040 | | - } catch (Throwable e) { |
1041 | | - fail("async threw instead of using callback"); |
1042 | | - } |
1043 | | - |
1044 | | - // The following code can be used to debug variations: |
1045 | | -// System.out.println("===VARIATION START"); |
1046 | | -// System.out.println("sync: " + expectedEvents); |
1047 | | -// System.out.println("callback called?: " + wasCalled.get()); |
1048 | | -// System.out.println("value -- sync: " + expectedValue + " -- async: " + actualValue.get()); |
1049 | | -// System.out.println("excep -- sync: " + expectedException + " -- async: " + actualException.get()); |
1050 | | -// System.out.println("exception mode: " + (isTestingAbruptCompletion |
1051 | | -// ? "exceptions thrown directly (abrupt completion)" : "exceptions into callbacks")); |
1052 | | -// System.out.println("===VARIATION END"); |
1053 | | - |
1054 | | - // show assertion failures arising in async tests |
1055 | | - if (actualException.get() != null && actualException.get() instanceof AssertionFailedError) { |
1056 | | - throw (AssertionFailedError) actualException.get(); |
1057 | | - } |
1058 | | - |
1059 | | - assertTrue(wasCalled.get(), "callback should have been called"); |
1060 | | - assertEquals(expectedEvents, listener.getEventStrings(), "steps should have matched"); |
1061 | | - assertEquals(expectedValue, actualValue.get()); |
1062 | | - assertEquals(expectedException == null, actualException.get() == null, |
1063 | | - "both or neither should have produced an exception"); |
1064 | | - if (expectedException != null) { |
1065 | | - assertEquals(expectedException.getMessage(), actualException.get().getMessage()); |
1066 | | - assertEquals(expectedException.getClass(), actualException.get().getClass()); |
1067 | | - } |
1068 | | - |
1069 | | - listener.clear(); |
1070 | | - } |
1071 | | - |
1072 | | - /** |
1073 | | - * Tracks invocations: allows testing of all variations of a method calls |
1074 | | - */ |
1075 | | - private static class InvocationTracker { |
1076 | | - public static final int DEPTH_LIMIT = 50; |
1077 | | - private final List<Integer> invocationOptionSequence = new ArrayList<>(); |
1078 | | - private boolean isAsyncStep; // async = matching, vs initial step = populating |
1079 | | - private int currentInvocationIndex; |
1080 | | - private int variationCount; |
1081 | | - |
1082 | | - public void reset() { |
1083 | | - variationCount = 0; |
1084 | | - } |
1085 | | - |
1086 | | - public void startInitialStep() { |
1087 | | - variationCount++; |
1088 | | - isAsyncStep = false; |
1089 | | - currentInvocationIndex = -1; |
1090 | | - } |
1091 | | - |
1092 | | - public int getNextOption(final int myOptionsSize) { |
1093 | | - /* |
1094 | | - This method creates (or gets) the next invocation's option. Each |
1095 | | - invoker of this method has the "option" to behave in various ways, |
1096 | | - usually just success (option 1) and exceptional failure (option 0), |
1097 | | - though some callers might have more options. A sequence of method |
1098 | | - outcomes (options) is one "variation". Tests automatically test |
1099 | | - all possible variations (up to a limit, to prevent infinite loops). |
1100 | | -
|
1101 | | - Methods generally have labels, to ensure that corresponding |
1102 | | - sync/async methods are called in the right order, but these labels |
1103 | | - are unrelated to the "variation" logic here. There are two "modes" |
1104 | | - (whether completion is abrupt, or not), which are also unrelated. |
1105 | | - */ |
1106 | | - |
1107 | | - currentInvocationIndex++; // which invocation result we are dealing with |
1108 | | - |
1109 | | - if (currentInvocationIndex >= invocationOptionSequence.size()) { |
1110 | | - if (isAsyncStep) { |
1111 | | - fail("result should have been pre-initialized: steps may not match"); |
1112 | | - } |
1113 | | - if (isWithinDepthLimit()) { |
1114 | | - invocationOptionSequence.add(myOptionsSize - 1); |
1115 | | - } else { |
1116 | | - invocationOptionSequence.add(0); // choose "0" option, should always be an exception |
1117 | | - } |
1118 | | - } |
1119 | | - return invocationOptionSequence.get(currentInvocationIndex); |
1120 | | - } |
1121 | | - |
1122 | | - public void startMatchStep() { |
1123 | | - isAsyncStep = true; |
1124 | | - currentInvocationIndex = -1; |
1125 | | - } |
1126 | | - |
1127 | | - private boolean countDown() { |
1128 | | - while (!invocationOptionSequence.isEmpty()) { |
1129 | | - int lastItemIndex = invocationOptionSequence.size() - 1; |
1130 | | - int lastItem = invocationOptionSequence.get(lastItemIndex); |
1131 | | - if (lastItem > 0) { |
1132 | | - // count current digit down by 1, until 0 |
1133 | | - invocationOptionSequence.set(lastItemIndex, lastItem - 1); |
1134 | | - return true; |
1135 | | - } else { |
1136 | | - // current digit completed, remove (move left) |
1137 | | - invocationOptionSequence.remove(lastItemIndex); |
1138 | | - } |
1139 | | - } |
1140 | | - return false; |
1141 | | - } |
1142 | | - |
1143 | | - public int getVariationCount() { |
1144 | | - return variationCount; |
1145 | | - } |
1146 | | - |
1147 | | - public boolean isWithinDepthLimit() { |
1148 | | - return invocationOptionSequence.size() < DEPTH_LIMIT; |
1149 | | - } |
1150 | | - } |
1151 | 867 | } |
0 commit comments