|
26 | 26 |
|
27 | 27 | import static com.mongodb.assertions.Assertions.assertNotNull; |
28 | 28 | import static com.mongodb.internal.async.AsyncRunnable.beginAsync; |
| 29 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
29 | 30 |
|
30 | 31 | abstract class AsyncFunctionsAbstractTest extends AsyncFunctionsTestBase { |
31 | 32 | private static final TimeoutContext TIMEOUT_CONTEXT = new TimeoutContext(new TimeoutSettings(0, 0, 0, 0L, 0)); |
@@ -723,6 +724,120 @@ void testTryCatchTestAndRethrow() { |
723 | 724 | }); |
724 | 725 | } |
725 | 726 |
|
| 727 | + @Test |
| 728 | + void testWhile() { |
| 729 | + // last iteration: 3 < 3 = 1 |
| 730 | + // 1(plainTest exception) + 1(plainTest false) + 1(sync exception) + 1(sync success) * 1(transition to next iteration) = 4 |
| 731 | + // 1(plainTest exception) + 1(plainTest false) + 1(sync exception) + 1(sync success) * 4(transition to next iteration) = 7 |
| 732 | + // 1(plainTest exception) + 1(plainTest false) + 1(sync exception) + 1(sync success) * 7(transition to next iteration) = 10 |
| 733 | + assertBehavesSameVariations(10, |
| 734 | + () -> { |
| 735 | + int counter = 0; |
| 736 | + while (counter < 3 && plainTest(counter)) { |
| 737 | + counter++; |
| 738 | + sync(counter); |
| 739 | + } |
| 740 | + }, |
| 741 | + (callback) -> { |
| 742 | + MutableValue<Integer> counter = new MutableValue<>(0); |
| 743 | + beginAsync().thenRunWhileLoop(() -> counter.get() < 3 && plainTest(counter.get()), c2 -> { |
| 744 | + counter.set(counter.get() + 1); |
| 745 | + async(counter.get(), c2); |
| 746 | + }).finish(callback); |
| 747 | + }); |
| 748 | + } |
| 749 | + |
| 750 | + @Test |
| 751 | + void testWhileWithThenRun() { |
| 752 | + // while: last iteration: 3 < 3 = 1 |
| 753 | + // 1(plainTest exception) + 1(plainTest false) + 1(sync exception) + 1(sync success) * 1(transition to next iteration) = 4 |
| 754 | + // 1(plainTest exception) + 1(plainTest false) + 1(sync exception) + 1(sync success) * 4(transition to next iteration) = 7 |
| 755 | + // 1(plainTest exception) + 1(plainTest false) + 1(sync exception) + 1(sync success) * 7(transition to next iteration) = 10 |
| 756 | + // trailing sync: 1(exception) + 1(success) = 2 |
| 757 | + // 6(while exception) + 4(while success) * 2(trailing sync) = 14 |
| 758 | + assertBehavesSameVariations(14, |
| 759 | + () -> { |
| 760 | + int counter = 0; |
| 761 | + while (counter < 3 && plainTest(counter)) { |
| 762 | + counter++; |
| 763 | + sync(counter); |
| 764 | + } |
| 765 | + sync(counter + 1); |
| 766 | + }, |
| 767 | + (callback) -> { |
| 768 | + MutableValue<Integer> counter = new MutableValue<>(0); |
| 769 | + beginAsync().thenRun(c -> { |
| 770 | + beginAsync().thenRunWhileLoop(() -> counter.get() < 3 && plainTest(counter.get()), c2 -> { |
| 771 | + counter.set(counter.get() + 1); |
| 772 | + async(counter.get(), c2); |
| 773 | + }).finish(c); |
| 774 | + }).thenRun(c -> { |
| 775 | + async(counter.get() + 1, c); |
| 776 | + }).finish(callback); |
| 777 | + }); |
| 778 | + } |
| 779 | + |
| 780 | + @Test |
| 781 | + void testNestedWhileLoops() { |
| 782 | + // inner while: 4 success + 6 exception = 10 |
| 783 | + // last inner iteration: 3 < 3 = 1 |
| 784 | + // 1(outer plainTest exception) + 1(outer plainTest false) + (inner while) * 1(transition to next iteration) = 12 |
| 785 | + // 1(outer plainTest exception) + 1(outer plainTest false) + (inner while) * 12(transition to next iteration) = 56 |
| 786 | + // 1(outer plainTest exception) + 1(outer plainTest false) + (inner while) * 56(transition to next iteration) = 232 |
| 787 | + assertBehavesSameVariations(232, |
| 788 | + () -> { |
| 789 | + int outer = 0; |
| 790 | + while (outer < 3 && plainTest(outer)) { |
| 791 | + int inner = 0; |
| 792 | + while (inner < 3 && plainTest(inner)) { |
| 793 | + sync(outer + inner); |
| 794 | + inner++; |
| 795 | + } |
| 796 | + outer++; |
| 797 | + } |
| 798 | + }, |
| 799 | + (callback) -> { |
| 800 | + MutableValue<Integer> outer = new MutableValue<>(0); |
| 801 | + beginAsync().thenRunWhileLoop(() -> outer.get() < 3 && plainTest(outer.get()), c -> { |
| 802 | + MutableValue<Integer> inner = new MutableValue<>(0); |
| 803 | + beginAsync().thenRunWhileLoop( |
| 804 | + () -> inner.get() < 3 && plainTest(inner.get()), |
| 805 | + c2 -> { |
| 806 | + beginAsync().thenRun(c3 -> { |
| 807 | + async(outer.get() + inner.get(), c3); |
| 808 | + }).thenRun(c3 -> { |
| 809 | + inner.set(inner.get() + 1); |
| 810 | + c3.complete(c3); |
| 811 | + }).finish(c2); |
| 812 | + } |
| 813 | + ).thenRun(c2 -> { |
| 814 | + outer.set(outer.get() + 1); |
| 815 | + c2.complete(c2); |
| 816 | + }).finish(c); |
| 817 | + }).finish(callback); |
| 818 | + }); |
| 819 | + } |
| 820 | + |
| 821 | + @Test |
| 822 | + void testWhileLoopStackConstant() { |
| 823 | + int depthWith100 = maxStackDepthForIterations(100); |
| 824 | + int depthWith10000 = maxStackDepthForIterations(10_000); |
| 825 | + assertEquals(depthWith100, depthWith10000, "Stack depth should be constant regardless of iteration count (trampoline)"); |
| 826 | + } |
| 827 | + |
| 828 | + private int maxStackDepthForIterations(final int iterations) { |
| 829 | + MutableValue<Integer> counter = new MutableValue<>(0); |
| 830 | + MutableValue<Integer> maxDepth = new MutableValue<>(0); |
| 831 | + beginAsync().thenRunWhileLoop(() -> counter.get() < iterations, c -> { |
| 832 | + maxDepth.set(Math.max(maxDepth.get(), Thread.currentThread().getStackTrace().length)); |
| 833 | + counter.set(counter.get() + 1); |
| 834 | + c.complete(c); |
| 835 | + }).finish((v, t) -> {}); |
| 836 | + |
| 837 | + assertEquals(iterations, counter.get()); |
| 838 | + return maxDepth.get(); |
| 839 | + } |
| 840 | + |
726 | 841 | @Test |
727 | 842 | void testRetryLoop() { |
728 | 843 | assertBehavesSameVariations(InvocationTracker.DEPTH_LIMIT * 2 + 1, |
@@ -768,6 +883,65 @@ void testDoWhileLoop() { |
768 | 883 | }); |
769 | 884 | } |
770 | 885 |
|
| 886 | + @Test |
| 887 | + void testNestedDoWhileLoops() { |
| 888 | + // inner do-while: 3 success + 5 exception = 8 |
| 889 | + // last outer iteration: 3 < 3 = 1 |
| 890 | + // 5(inner exception) + 3(inner success) * 1(transition to next iteration) = 8 |
| 891 | + // 5(inner exception) + 3(inner success) * 1(outer plainTest exception) + 1(outer plainTest false) + 8(transition to next iteration) = 35 |
| 892 | + // 5(inner exception) + 3(inner success) * 1(outer plainTest exception) + 1(outer plainTest false) + 35(transition to next iteration) = 116 |
| 893 | + assertBehavesSameVariations(116, |
| 894 | + () -> { |
| 895 | + int outer = 0; |
| 896 | + do { |
| 897 | + int inner = 0; |
| 898 | + do { |
| 899 | + sync(outer + inner); |
| 900 | + inner++; |
| 901 | + } while (inner < 3 && plainTest(inner)); |
| 902 | + outer++; |
| 903 | + } while (outer < 3 && plainTest(outer)); |
| 904 | + }, |
| 905 | + (callback) -> { |
| 906 | + MutableValue<Integer> outer = new MutableValue<>(0); |
| 907 | + beginAsync().thenRunDoWhileLoop(c -> { |
| 908 | + MutableValue<Integer> inner = new MutableValue<>(0); |
| 909 | + beginAsync().thenRunDoWhileLoop(c2 -> { |
| 910 | + beginAsync().thenRun(c3 -> { |
| 911 | + async(outer.get() + inner.get(), c3); |
| 912 | + }).thenRun(c3 -> { |
| 913 | + inner.set(inner.get() + 1); |
| 914 | + c3.complete(c3); |
| 915 | + }).finish(c2); |
| 916 | + }, () -> inner.get() < 3 && plainTest(inner.get()) |
| 917 | + ).thenRun(c2 -> { |
| 918 | + outer.set(outer.get() + 1); |
| 919 | + c2.complete(c2); |
| 920 | + }).finish(c); |
| 921 | + }, () -> outer.get() < 3 && plainTest(outer.get())).finish(callback); |
| 922 | + }); |
| 923 | + } |
| 924 | + |
| 925 | + @Test |
| 926 | + void testDoWhileLoopStackConstant() { |
| 927 | + int depthWith100 = maxDoWhileStackDepthForIterations(100); |
| 928 | + int depthWith10000 = maxDoWhileStackDepthForIterations(10_000); |
| 929 | + assertEquals(depthWith100, depthWith10000, |
| 930 | + "Stack depth should be constant regardless of iteration count"); |
| 931 | + } |
| 932 | + |
| 933 | + private int maxDoWhileStackDepthForIterations(final int iterations) { |
| 934 | + MutableValue<Integer> counter = new MutableValue<>(0); |
| 935 | + MutableValue<Integer> maxDepth = new MutableValue<>(0); |
| 936 | + beginAsync().thenRunDoWhileLoop(c -> { |
| 937 | + maxDepth.set(Math.max(maxDepth.get(), Thread.currentThread().getStackTrace().length)); |
| 938 | + counter.set(counter.get() + 1); |
| 939 | + c.complete(c); |
| 940 | + }, () -> counter.get() < iterations).finish((v, t) -> {}); |
| 941 | + assertEquals(iterations, counter.get()); |
| 942 | + return maxDepth.get(); |
| 943 | + } |
| 944 | + |
771 | 945 | @Test |
772 | 946 | void testFinallyWithPlainInsideTry() { |
773 | 947 | // (in try: normal flow + exception + exception) * (in finally: normal + exception) = 6 |
|
0 commit comments