001package org.eclipse.aether.named;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.junit.Assert;
023import org.junit.Rule;
024import org.junit.Test;
025import org.junit.rules.TestName;
026
027import java.util.concurrent.CountDownLatch;
028import java.util.concurrent.TimeUnit;
029
030import static org.hamcrest.MatcherAssert.assertThat;
031import static org.hamcrest.Matchers.*;
032
033/**
034 * UT support for {@link NamedLockFactory}.
035 */
036public abstract class NamedLockFactoryTestSupport {
037
038    protected static NamedLockFactory namedLockFactory;
039
040    @Rule
041    public TestName testName = new TestName();
042
043    protected String lockName()
044    {
045        return testName.getMethodName();
046    }
047
048    @Test
049    public void refCounting() {
050        final String name = lockName();
051        try (NamedLock one = namedLockFactory.getLock(name);
052             NamedLock two = namedLockFactory.getLock(name)) {
053            assertThat(one, sameInstance(two));
054            one.close();
055            two.close();
056
057            try (NamedLock three = namedLockFactory.getLock(name)) {
058                assertThat(three, not(sameInstance(two)));
059            }
060        }
061    }
062
063    @Test(expected = IllegalStateException.class)
064    public void unlockWoLock() {
065        final String name = lockName();
066        try (NamedLock one = namedLockFactory.getLock(name)) {
067            one.unlock();
068        }
069    }
070
071    @Test
072    public void wwBoxing() throws InterruptedException {
073        final String name = lockName();
074        try (NamedLock one = namedLockFactory.getLock(name)) {
075            assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(true));
076            assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(true));
077            one.unlock();
078            one.unlock();
079        }
080    }
081
082    @Test
083    public void rrBoxing() throws InterruptedException {
084        final String name = lockName();
085        try (NamedLock one = namedLockFactory.getLock(name)) {
086            assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
087            assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
088            one.unlock();
089            one.unlock();
090        }
091    }
092
093    @Test
094    public void wrBoxing() throws InterruptedException {
095        final String name = lockName();
096        try (NamedLock one = namedLockFactory.getLock(name)) {
097            assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(true));
098            assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
099            one.unlock();
100            one.unlock();
101        }
102    }
103
104    @Test
105    public void rwBoxing() throws InterruptedException {
106        final String name = lockName();
107        try (NamedLock one = namedLockFactory.getLock(name)) {
108            assertThat(one.lockShared(1L, TimeUnit.MILLISECONDS), is(true));
109            assertThat(one.lockExclusively(1L, TimeUnit.MILLISECONDS), is(false));
110            one.unlock();
111        }
112    }
113
114    @Test(timeout = 5000)
115    public void sharedAccess() throws InterruptedException {
116        final String name = lockName();
117        CountDownLatch winners = new CountDownLatch(2); // we expect 2 winners
118        CountDownLatch losers = new CountDownLatch(0); // we expect 0 losers
119        Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
120        Thread t2 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
121        t1.start();
122        t2.start();
123        t1.join();
124        t2.join();
125        winners.await();
126        losers.await();
127    }
128
129    @Test(timeout = 5000)
130    public void exclusiveAccess() throws InterruptedException {
131        final String name = lockName();
132        CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
133        CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser
134        Thread t1 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
135        Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
136        t1.start();
137        t2.start();
138        t1.join();
139        t2.join();
140        winners.await();
141        losers.await();
142    }
143
144    @Test(timeout = 5000)
145    public void mixedAccess() throws InterruptedException {
146        final String name = lockName();
147        CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
148        CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser
149        Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
150        Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
151        t1.start();
152        t2.start();
153        t1.join();
154        t2.join();
155        winners.await();
156        losers.await();
157    }
158
159    @Test(timeout = 5000)
160    public void fullyConsumeLockTime() throws InterruptedException {
161        long start = System.nanoTime();
162        final String name = lockName();
163        CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
164        CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser
165        Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
166        Thread t2 = new Thread(new Access(namedLockFactory, name, false, winners, losers));
167        t1.start();
168        t2.start();
169        t1.join();
170        t2.join();
171        winners.await();
172        losers.await();
173        long end = System.nanoTime();
174        long duration = end - start;
175        long expectedDuration = TimeUnit.MILLISECONDS.toNanos( ACCESS_WAIT_MILLIS );
176        assertThat(duration, greaterThanOrEqualTo(expectedDuration)); // equal in ideal case
177    }
178
179    @Test(timeout = 5000)
180    public void releasedExclusiveAllowAccess() throws InterruptedException {
181        final String name = lockName();
182        CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner
183        CountDownLatch losers = new CountDownLatch(0); // we expect 0 loser
184        Thread t1 = new Thread(new Access(namedLockFactory, name, true, winners, losers));
185        try (NamedLock namedLock = namedLockFactory.getLock(name))
186        {
187            assertThat( namedLock.lockExclusively( 50L, TimeUnit.MILLISECONDS ), is( true ));
188            try {
189                t1.start();
190                Thread.sleep(50L );
191            }
192            finally
193            {
194                namedLock.unlock();
195            }
196        }
197        t1.join();
198        winners.await();
199        losers.await();
200    }
201
202    private static final long ACCESS_WAIT_MILLIS = 1000L;
203
204    private static class Access implements Runnable {
205        final NamedLockFactory namedLockFactory;
206        final String name;
207        final boolean shared;
208        final CountDownLatch winner;
209        final CountDownLatch loser;
210
211        public Access(NamedLockFactory namedLockFactory,
212                      String name,
213                      boolean shared,
214                      CountDownLatch winner,
215                      CountDownLatch loser) {
216            this.namedLockFactory = namedLockFactory;
217            this.name = name;
218            this.shared = shared;
219            this.winner = winner;
220            this.loser = loser;
221        }
222
223        @Override
224        public void run() {
225            try (NamedLock lock = namedLockFactory.getLock(name)) {
226                if (shared ? lock.lockShared(ACCESS_WAIT_MILLIS, TimeUnit.MILLISECONDS) : lock.lockExclusively(ACCESS_WAIT_MILLIS, TimeUnit.MILLISECONDS)) {
227                    try {
228                        winner.countDown();
229                        loser.await();
230                    } finally {
231                        lock.unlock();
232                    }
233                } else {
234                    loser.countDown();
235                    winner.await();
236                }
237            } catch (InterruptedException e) {
238                Assert.fail(e.getMessage());
239            }
240        }
241    }
242}