ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/CompletableFutureTest.java
(Generate patch)

Comparing jsr166/src/test/tck/CompletableFutureTest.java (file contents):
Revision 1.198 by dl, Mon Sep 17 11:50:55 2018 UTC vs.
Revision 1.207 by jsr166, Sun Sep 23 02:06:28 2018 UTC

# Line 551 | Line 551 | public class CompletableFutureTest exten
551          public CompletableFuture<Integer> apply(Integer x) {
552              invoked();
553              value = x;
554 <            CompletableFuture<Integer> f = new CompletableFuture<>();
555 <            assertTrue(f.complete(inc(x)));
556 <            return f;
554 >            return CompletableFuture.completedFuture(inc(x));
555          }
556      }
557  
# Line 574 | Line 572 | public class CompletableFutureTest exten
572          ExceptionalCompletableFutureFunction(ExecutionMode m) { super(m); }
573          public CompletionStage<Integer> apply(Throwable x) {
574              invoked();
575 <            CompletableFuture<Integer> d = new CompletableFuture<Integer>();
578 <            d.complete(value);
579 <            return d;
575 >            return CompletableFuture.completedFuture(value);
576          }
577      }
578  
# Line 930 | Line 926 | public class CompletableFutureTest exten
926          for (boolean createIncomplete : new boolean[] { true, false })
927          for (Integer v1 : new Integer[] { 1, null })
928      {
933        final AtomicInteger a = new AtomicInteger(0);
929          final CompletableFuture<Integer> f = new CompletableFuture<>();
930          if (!createIncomplete) assertTrue(f.complete(v1));
931          final CompletableFuture<Integer> g = m.exceptionally
932              (f, (Throwable t) -> {
938                a.getAndIncrement();
933                  threadFail("should not be called");
934                  return null;            // unreached
935              });
# Line 943 | Line 937 | public class CompletableFutureTest exten
937  
938          checkCompletedNormally(g, v1);
939          checkCompletedNormally(f, v1);
946        assertEquals(0, a.get());
940      }}
941  
942      /**
# Line 4633 | Line 4626 | public class CompletableFutureTest exten
4626   //         return stage.toCompletableFuture().copy().isDone();
4627   //     }
4628  
4629 +    // For testing default implementations
4630 +    // Only non-default interface methods defined.
4631 +    static final class DelegatedCompletionStage<T> implements CompletionStage<T> {
4632 +        final CompletableFuture<T> cf;
4633 +        DelegatedCompletionStage(CompletableFuture<T> cf) { this.cf = cf; }
4634 +        public CompletableFuture<T> toCompletableFuture() {
4635 +            return cf; }
4636 +        public CompletionStage<Void> thenRun
4637 +            (Runnable action) {
4638 +            return cf.thenRun(action); }
4639 +        public CompletionStage<Void> thenRunAsync
4640 +            (Runnable action) {
4641 +            return cf.thenRunAsync(action); }
4642 +        public CompletionStage<Void> thenRunAsync
4643 +            (Runnable action,
4644 +             Executor executor) {
4645 +            return cf.thenRunAsync(action, executor); }
4646 +        public CompletionStage<Void> thenAccept
4647 +            (Consumer<? super T> action) {
4648 +            return cf.thenAccept(action); }
4649 +        public CompletionStage<Void> thenAcceptAsync
4650 +            (Consumer<? super T> action) {
4651 +            return cf.thenAcceptAsync(action); }
4652 +        public CompletionStage<Void> thenAcceptAsync
4653 +            (Consumer<? super T> action,
4654 +             Executor executor) {
4655 +            return cf.thenAcceptAsync(action, executor); }
4656 +        public <U> CompletionStage<U> thenApply
4657 +            (Function<? super T,? extends U> a) {
4658 +            return cf.thenApply(a); }
4659 +        public <U> CompletionStage<U> thenApplyAsync
4660 +            (Function<? super T,? extends U> fn) {
4661 +            return cf.thenApplyAsync(fn); }
4662 +        public <U> CompletionStage<U> thenApplyAsync
4663 +            (Function<? super T,? extends U> fn,
4664 +             Executor executor) {
4665 +            return cf.thenApplyAsync(fn, executor); }
4666 +        public <U,V> CompletionStage<V> thenCombine
4667 +            (CompletionStage<? extends U> other,
4668 +             BiFunction<? super T,? super U,? extends V> fn) {
4669 +            return cf.thenCombine(other, fn); }
4670 +        public <U,V> CompletionStage<V> thenCombineAsync
4671 +            (CompletionStage<? extends U> other,
4672 +             BiFunction<? super T,? super U,? extends V> fn) {
4673 +            return cf.thenCombineAsync(other, fn); }
4674 +        public <U,V> CompletionStage<V> thenCombineAsync
4675 +            (CompletionStage<? extends U> other,
4676 +             BiFunction<? super T,? super U,? extends V> fn,
4677 +             Executor executor) {
4678 +            return cf.thenCombineAsync(other, fn, executor); }
4679 +        public <U> CompletionStage<Void> thenAcceptBoth
4680 +            (CompletionStage<? extends U> other,
4681 +             BiConsumer<? super T, ? super U> action) {
4682 +            return cf.thenAcceptBoth(other, action); }
4683 +        public <U> CompletionStage<Void> thenAcceptBothAsync
4684 +            (CompletionStage<? extends U> other,
4685 +             BiConsumer<? super T, ? super U> action) {
4686 +            return cf.thenAcceptBothAsync(other, action); }
4687 +        public <U> CompletionStage<Void> thenAcceptBothAsync
4688 +            (CompletionStage<? extends U> other,
4689 +             BiConsumer<? super T, ? super U> action,
4690 +             Executor executor) {
4691 +            return cf.thenAcceptBothAsync(other, action, executor); }
4692 +        public CompletionStage<Void> runAfterBoth
4693 +            (CompletionStage<?> other,
4694 +             Runnable action) {
4695 +            return cf.runAfterBoth(other, action); }
4696 +        public CompletionStage<Void> runAfterBothAsync
4697 +            (CompletionStage<?> other,
4698 +             Runnable action) {
4699 +            return cf.runAfterBothAsync(other, action); }
4700 +        public CompletionStage<Void> runAfterBothAsync
4701 +            (CompletionStage<?> other,
4702 +             Runnable action,
4703 +             Executor executor) {
4704 +            return cf.runAfterBothAsync(other, action, executor); }
4705 +        public <U> CompletionStage<U> applyToEither
4706 +            (CompletionStage<? extends T> other,
4707 +             Function<? super T, U> fn) {
4708 +            return cf.applyToEither(other, fn); }
4709 +        public <U> CompletionStage<U> applyToEitherAsync
4710 +            (CompletionStage<? extends T> other,
4711 +             Function<? super T, U> fn) {
4712 +            return cf.applyToEitherAsync(other, fn); }
4713 +        public <U> CompletionStage<U> applyToEitherAsync
4714 +            (CompletionStage<? extends T> other,
4715 +             Function<? super T, U> fn,
4716 +             Executor executor) {
4717 +            return cf.applyToEitherAsync(other, fn, executor); }
4718 +        public CompletionStage<Void> acceptEither
4719 +            (CompletionStage<? extends T> other,
4720 +             Consumer<? super T> action) {
4721 +            return cf.acceptEither(other, action); }
4722 +        public CompletionStage<Void> acceptEitherAsync
4723 +            (CompletionStage<? extends T> other,
4724 +             Consumer<? super T> action) {
4725 +            return cf.acceptEitherAsync(other, action); }
4726 +        public CompletionStage<Void> acceptEitherAsync
4727 +            (CompletionStage<? extends T> other,
4728 +             Consumer<? super T> action,
4729 +             Executor executor) {
4730 +            return cf.acceptEitherAsync(other, action, executor); }
4731 +        public CompletionStage<Void> runAfterEither
4732 +            (CompletionStage<?> other,
4733 +             Runnable action) {
4734 +            return cf.runAfterEither(other, action); }
4735 +        public CompletionStage<Void> runAfterEitherAsync
4736 +            (CompletionStage<?> other,
4737 +             Runnable action) {
4738 +            return cf.runAfterEitherAsync(other, action); }
4739 +        public CompletionStage<Void> runAfterEitherAsync
4740 +            (CompletionStage<?> other,
4741 +             Runnable action,
4742 +             Executor executor) {
4743 +            return cf.runAfterEitherAsync(other, action, executor); }
4744 +        public <U> CompletionStage<U> thenCompose
4745 +            (Function<? super T, ? extends CompletionStage<U>> fn) {
4746 +            return cf.thenCompose(fn); }
4747 +        public <U> CompletionStage<U> thenComposeAsync
4748 +            (Function<? super T, ? extends CompletionStage<U>> fn) {
4749 +            return cf.thenComposeAsync(fn); }
4750 +        public <U> CompletionStage<U> thenComposeAsync
4751 +            (Function<? super T, ? extends CompletionStage<U>> fn,
4752 +             Executor executor) {
4753 +            return cf.thenComposeAsync(fn, executor); }
4754 +        public <U> CompletionStage<U> handle
4755 +            (BiFunction<? super T, Throwable, ? extends U> fn) {
4756 +            return cf.handle(fn); }
4757 +        public <U> CompletionStage<U> handleAsync
4758 +            (BiFunction<? super T, Throwable, ? extends U> fn) {
4759 +            return cf.handleAsync(fn); }
4760 +        public <U> CompletionStage<U> handleAsync
4761 +            (BiFunction<? super T, Throwable, ? extends U> fn,
4762 +             Executor executor) {
4763 +            return cf.handleAsync(fn, executor); }
4764 +        public CompletionStage<T> whenComplete
4765 +            (BiConsumer<? super T, ? super Throwable> action) {
4766 +            return cf.whenComplete(action); }
4767 +        public CompletionStage<T> whenCompleteAsync
4768 +            (BiConsumer<? super T, ? super Throwable> action) {
4769 +            return cf.whenCompleteAsync(action); }
4770 +        public CompletionStage<T> whenCompleteAsync
4771 +            (BiConsumer<? super T, ? super Throwable> action,
4772 +             Executor executor) {
4773 +            return cf.whenCompleteAsync(action, executor); }
4774 +        public CompletionStage<T> exceptionally
4775 +            (Function<Throwable, ? extends T> fn) {
4776 +            return cf.exceptionally(fn); }
4777 +    }
4778 +
4779 +    /**
4780 +     * default-implemented exceptionallyAsync action is not invoked when
4781 +     * source completes normally, and source result is propagated
4782 +     */
4783 +    public void testDefaultExceptionallyAsync_normalCompletion() {
4784 +        for (boolean createIncomplete : new boolean[] { true, false })
4785 +        for (Integer v1 : new Integer[] { 1, null })
4786 +    {
4787 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4788 +        final DelegatedCompletionStage<Integer> d =
4789 +            new DelegatedCompletionStage<Integer>(f);
4790 +        if (!createIncomplete) assertTrue(f.complete(v1));
4791 +        final CompletionStage<Integer> g = d.exceptionallyAsync
4792 +            ((Throwable t) -> {
4793 +                threadFail("should not be called");
4794 +                return null;            // unreached
4795 +            });
4796 +        if (createIncomplete) assertTrue(f.complete(v1));
4797 +
4798 +        checkCompletedNormally(g.toCompletableFuture(), v1);
4799 +    }}
4800 +
4801 +    /**
4802 +     * default-implemented exceptionallyAsync action completes with
4803 +     * function value on source exception
4804 +     */
4805 +    public void testDefaultExceptionallyAsync_exceptionalCompletion() {
4806 +        for (boolean createIncomplete : new boolean[] { true, false })
4807 +        for (Integer v1 : new Integer[] { 1, null })
4808 +    {
4809 +        final AtomicInteger a = new AtomicInteger(0);
4810 +        final CFException ex = new CFException();
4811 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4812 +        final DelegatedCompletionStage<Integer> d =
4813 +            new DelegatedCompletionStage<Integer>(f);
4814 +        if (!createIncomplete) f.completeExceptionally(ex);
4815 +        final CompletionStage<Integer> g = d.exceptionallyAsync
4816 +            ((Throwable t) -> {
4817 +                threadAssertSame(t, ex);
4818 +                a.getAndIncrement();
4819 +                return v1;
4820 +            });
4821 +        if (createIncomplete) f.completeExceptionally(ex);
4822 +
4823 +        checkCompletedNormally(g.toCompletableFuture(), v1);
4824 +        assertEquals(1, a.get());
4825 +    }}
4826 +
4827 +    /**
4828 +     * Under default implementation, if an "exceptionally action"
4829 +     * throws an exception, it completes exceptionally with that
4830 +     * exception
4831 +     */
4832 +    public void testDefaultExceptionallyAsync_exceptionalCompletionActionFailed() {
4833 +        for (boolean createIncomplete : new boolean[] { true, false })
4834 +    {
4835 +        final AtomicInteger a = new AtomicInteger(0);
4836 +        final CFException ex1 = new CFException();
4837 +        final CFException ex2 = new CFException();
4838 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4839 +        final DelegatedCompletionStage<Integer> d =
4840 +            new DelegatedCompletionStage<Integer>(f);
4841 +        if (!createIncomplete) f.completeExceptionally(ex1);
4842 +        final CompletionStage<Integer> g = d.exceptionallyAsync
4843 +            ((Throwable t) -> {
4844 +                threadAssertSame(t, ex1);
4845 +                a.getAndIncrement();
4846 +                throw ex2;
4847 +            });
4848 +        if (createIncomplete) f.completeExceptionally(ex1);
4849 +
4850 +        checkCompletedWithWrappedException(g.toCompletableFuture(), ex2);
4851 +        checkCompletedExceptionally(f, ex1);
4852 +        checkCompletedExceptionally(d.toCompletableFuture(), ex1);
4853 +        assertEquals(1, a.get());
4854 +    }}
4855 +
4856 +    /**
4857 +     * default exceptionallyCompose result completes normally after normal
4858 +     * completion of source
4859 +     */
4860 +    public void testDefaultExceptionallyCompose_normalCompletion() {
4861 +        for (boolean createIncomplete : new boolean[] { true, false })
4862 +        for (Integer v1 : new Integer[] { 1, null })
4863 +    {
4864 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4865 +        final ExceptionalCompletableFutureFunction r =
4866 +            new ExceptionalCompletableFutureFunction(ExecutionMode.SYNC);
4867 +        final DelegatedCompletionStage<Integer> d =
4868 +            new DelegatedCompletionStage<Integer>(f);
4869 +        if (!createIncomplete) assertTrue(f.complete(v1));
4870 +        final CompletionStage<Integer> g = d.exceptionallyCompose(r);
4871 +        if (createIncomplete) assertTrue(f.complete(v1));
4872 +
4873 +        checkCompletedNormally(f, v1);
4874 +        checkCompletedNormally(g.toCompletableFuture(), v1);
4875 +        r.assertNotInvoked();
4876 +    }}
4877 +
4878 +    /**
4879 +     * default-implemented exceptionallyCompose result completes
4880 +     * normally after exceptional completion of source
4881 +     */
4882 +    public void testDefaultExceptionallyCompose_exceptionalCompletion() {
4883 +        for (boolean createIncomplete : new boolean[] { true, false })
4884 +    {
4885 +        final CFException ex = new CFException();
4886 +        final ExceptionalCompletableFutureFunction r =
4887 +            new ExceptionalCompletableFutureFunction(ExecutionMode.SYNC);
4888 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4889 +        final DelegatedCompletionStage<Integer> d =
4890 +            new DelegatedCompletionStage<Integer>(f);
4891 +        if (!createIncomplete) f.completeExceptionally(ex);
4892 +        final CompletionStage<Integer> g = d.exceptionallyCompose(r);
4893 +        if (createIncomplete) f.completeExceptionally(ex);
4894 +
4895 +        checkCompletedExceptionally(f, ex);
4896 +        checkCompletedNormally(g.toCompletableFuture(), r.value);
4897 +        r.assertInvoked();
4898 +    }}
4899 +
4900 +    /**
4901 +     * default-implemented exceptionallyCompose completes
4902 +     * exceptionally on exception if action does
4903 +     */
4904 +    public void testDefaultExceptionallyCompose_actionFailed() {
4905 +        for (boolean createIncomplete : new boolean[] { true, false })
4906 +        for (Integer v1 : new Integer[] { 1, null })
4907 +    {
4908 +        final CFException ex = new CFException();
4909 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4910 +        final FailingExceptionalCompletableFutureFunction r
4911 +            = new FailingExceptionalCompletableFutureFunction(ExecutionMode.SYNC);
4912 +        final DelegatedCompletionStage<Integer> d =
4913 +            new DelegatedCompletionStage<Integer>(f);
4914 +        if (!createIncomplete) f.completeExceptionally(ex);
4915 +        final CompletionStage<Integer> g = d.exceptionallyCompose(r);
4916 +        if (createIncomplete) f.completeExceptionally(ex);
4917 +
4918 +        checkCompletedExceptionally(f, ex);
4919 +        checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex);
4920 +        r.assertInvoked();
4921 +    }}
4922 +
4923 +    /**
4924 +     * default exceptionallyComposeAsync result completes normally after normal
4925 +     * completion of source
4926 +     */
4927 +    public void testDefaultExceptionallyComposeAsync_normalCompletion() {
4928 +        for (boolean createIncomplete : new boolean[] { true, false })
4929 +        for (Integer v1 : new Integer[] { 1, null })
4930 +    {
4931 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4932 +        final ExceptionalCompletableFutureFunction r =
4933 +            new ExceptionalCompletableFutureFunction(ExecutionMode.ASYNC);
4934 +        final DelegatedCompletionStage<Integer> d =
4935 +            new DelegatedCompletionStage<Integer>(f);
4936 +        if (!createIncomplete) assertTrue(f.complete(v1));
4937 +        final CompletionStage<Integer> g = d.exceptionallyComposeAsync(r);
4938 +        if (createIncomplete) assertTrue(f.complete(v1));
4939 +
4940 +        checkCompletedNormally(f, v1);
4941 +        checkCompletedNormally(g.toCompletableFuture(), v1);
4942 +        r.assertNotInvoked();
4943 +    }}
4944 +
4945 +    /**
4946 +     * default-implemented exceptionallyComposeAsync result completes
4947 +     * normally after exceptional completion of source
4948 +     */
4949 +    public void testDefaultExceptionallyComposeAsync_exceptionalCompletion() {
4950 +        for (boolean createIncomplete : new boolean[] { true, false })
4951 +    {
4952 +        final CFException ex = new CFException();
4953 +        final ExceptionalCompletableFutureFunction r =
4954 +            new ExceptionalCompletableFutureFunction(ExecutionMode.ASYNC);
4955 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4956 +        final DelegatedCompletionStage<Integer> d =
4957 +            new DelegatedCompletionStage<Integer>(f);
4958 +        if (!createIncomplete) f.completeExceptionally(ex);
4959 +        final CompletionStage<Integer> g = d.exceptionallyComposeAsync(r);
4960 +        if (createIncomplete) f.completeExceptionally(ex);
4961 +
4962 +        checkCompletedExceptionally(f, ex);
4963 +        checkCompletedNormally(g.toCompletableFuture(), r.value);
4964 +        r.assertInvoked();
4965 +    }}
4966 +
4967 +    /**
4968 +     * default-implemented exceptionallyComposeAsync completes
4969 +     * exceptionally on exception if action does
4970 +     */
4971 +    public void testDefaultExceptionallyComposeAsync_actionFailed() {
4972 +        for (boolean createIncomplete : new boolean[] { true, false })
4973 +        for (Integer v1 : new Integer[] { 1, null })
4974 +    {
4975 +        final CFException ex = new CFException();
4976 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
4977 +        final FailingExceptionalCompletableFutureFunction r
4978 +            = new FailingExceptionalCompletableFutureFunction(ExecutionMode.ASYNC);
4979 +        final DelegatedCompletionStage<Integer> d =
4980 +            new DelegatedCompletionStage<Integer>(f);
4981 +        if (!createIncomplete) f.completeExceptionally(ex);
4982 +        final CompletionStage<Integer> g = d.exceptionallyComposeAsync(r);
4983 +        if (createIncomplete) f.completeExceptionally(ex);
4984 +
4985 +        checkCompletedExceptionally(f, ex);
4986 +        checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex);
4987 +        r.assertInvoked();
4988 +    }}
4989 +
4990 +
4991 +    /**
4992 +     * default exceptionallyComposeAsync result completes normally after normal
4993 +     * completion of source
4994 +     */
4995 +    public void testDefaultExceptionallyComposeAsyncExecutor_normalCompletion() {
4996 +        for (boolean createIncomplete : new boolean[] { true, false })
4997 +        for (Integer v1 : new Integer[] { 1, null })
4998 +    {
4999 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
5000 +        final ExceptionalCompletableFutureFunction r =
5001 +            new ExceptionalCompletableFutureFunction(ExecutionMode.EXECUTOR);
5002 +        final DelegatedCompletionStage<Integer> d =
5003 +            new DelegatedCompletionStage<Integer>(f);
5004 +        if (!createIncomplete) assertTrue(f.complete(v1));
5005 +        final CompletionStage<Integer> g = d.exceptionallyComposeAsync(r, new ThreadExecutor());
5006 +        if (createIncomplete) assertTrue(f.complete(v1));
5007 +
5008 +        checkCompletedNormally(f, v1);
5009 +        checkCompletedNormally(g.toCompletableFuture(), v1);
5010 +        r.assertNotInvoked();
5011 +    }}
5012 +
5013 +    /**
5014 +     * default-implemented exceptionallyComposeAsync result completes
5015 +     * normally after exceptional completion of source
5016 +     */
5017 +    public void testDefaultExceptionallyComposeAsyncExecutor_exceptionalCompletion() {
5018 +        for (boolean createIncomplete : new boolean[] { true, false })
5019 +    {
5020 +        final CFException ex = new CFException();
5021 +        final ExceptionalCompletableFutureFunction r =
5022 +            new ExceptionalCompletableFutureFunction(ExecutionMode.EXECUTOR);
5023 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
5024 +        final DelegatedCompletionStage<Integer> d =
5025 +            new DelegatedCompletionStage<Integer>(f);
5026 +        if (!createIncomplete) f.completeExceptionally(ex);
5027 +        final CompletionStage<Integer> g = d.exceptionallyComposeAsync(r, new ThreadExecutor());
5028 +        if (createIncomplete) f.completeExceptionally(ex);
5029 +
5030 +        checkCompletedExceptionally(f, ex);
5031 +        checkCompletedNormally(g.toCompletableFuture(), r.value);
5032 +        r.assertInvoked();
5033 +    }}
5034 +
5035 +    /**
5036 +     * default-implemented exceptionallyComposeAsync completes
5037 +     * exceptionally on exception if action does
5038 +     */
5039 +    public void testDefaultExceptionallyComposeAsyncExecutor_actionFailed() {
5040 +        for (boolean createIncomplete : new boolean[] { true, false })
5041 +        for (Integer v1 : new Integer[] { 1, null })
5042 +    {
5043 +        final CFException ex = new CFException();
5044 +        final CompletableFuture<Integer> f = new CompletableFuture<>();
5045 +        final FailingExceptionalCompletableFutureFunction r
5046 +            = new FailingExceptionalCompletableFutureFunction(ExecutionMode.EXECUTOR);
5047 +        final DelegatedCompletionStage<Integer> d =
5048 +            new DelegatedCompletionStage<Integer>(f);
5049 +        if (!createIncomplete) f.completeExceptionally(ex);
5050 +        final CompletionStage<Integer> g = d.exceptionallyComposeAsync(r, new ThreadExecutor());
5051 +        if (createIncomplete) f.completeExceptionally(ex);
5052 +
5053 +        checkCompletedExceptionally(f, ex);
5054 +        checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex);
5055 +        r.assertInvoked();
5056 +    }}
5057 +
5058   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines