1/*
2 * Copyright (C) 2010 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.util.concurrent;
18
19import static org.truth0.Truth.ASSERT;
20
21import com.google.common.testing.NullPointerTester;
22
23import junit.framework.TestCase;
24
25import java.lang.Thread.UncaughtExceptionHandler;
26import java.util.concurrent.Executors;
27import java.util.concurrent.ThreadFactory;
28
29/**
30 * Tests for ThreadFactoryBuilder.
31 *
32 * @author Kurt Alfred Kluever
33 * @author Martin Buchholz
34 */
35public class ThreadFactoryBuilderTest extends TestCase {
36  private final Runnable monitoredRunnable = new Runnable() {
37    @Override public void run() {
38      completed = true;
39    }
40  };
41
42  private static final UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER =
43      new UncaughtExceptionHandler() {
44        @Override public void uncaughtException(Thread t, Throwable e) {
45          // No-op
46        }
47      };
48
49  private ThreadFactoryBuilder builder;
50  private volatile boolean completed = false;
51
52  @Override public void setUp() {
53    builder = new ThreadFactoryBuilder();
54  }
55
56  public void testThreadFactoryBuilder_defaults() throws InterruptedException {
57    ThreadFactory threadFactory = builder.build();
58    Thread thread = threadFactory.newThread(monitoredRunnable);
59    checkThreadPoolName(thread, 1);
60
61    Thread defaultThread =
62        Executors.defaultThreadFactory().newThread(monitoredRunnable);
63    assertEquals(defaultThread.isDaemon(), thread.isDaemon());
64    assertEquals(defaultThread.getPriority(), thread.getPriority());
65    assertSame(defaultThread.getThreadGroup(), thread.getThreadGroup());
66    assertSame(defaultThread.getUncaughtExceptionHandler(),
67        thread.getUncaughtExceptionHandler());
68
69    assertFalse(completed);
70    thread.start();
71    thread.join();
72    assertTrue(completed);
73
74    // Creating a new thread from the same ThreadFactory will have the same
75    // pool ID but a thread ID of 2.
76    Thread thread2 = threadFactory.newThread(monitoredRunnable);
77    checkThreadPoolName(thread2, 2);
78    assertEquals(
79        thread.getName().substring(0, thread.getName().lastIndexOf('-')),
80        thread2.getName().substring(0, thread.getName().lastIndexOf('-')));
81
82    // Building again should give us a different pool ID.
83    ThreadFactory threadFactory2 = builder.build();
84    Thread thread3 = threadFactory2.newThread(monitoredRunnable);
85    checkThreadPoolName(thread3, 1);
86    ASSERT.that(
87        thread2.getName().substring(0, thread.getName().lastIndexOf('-')))
88        .isNotEqualTo(
89            thread3.getName().substring(0, thread.getName().lastIndexOf('-')));
90  }
91
92  private static void checkThreadPoolName(Thread thread, int threadId) {
93    assertTrue(thread.getName().matches("^pool-\\d+-thread-" + threadId + "$"));
94  }
95
96  public void testNameFormatWithPercentS_custom() {
97    String format = "super-duper-thread-%s";
98    ThreadFactory factory = builder.setNameFormat(format).build();
99    for (int i = 0; i < 11; i++) {
100      assertEquals(String.format(format, i),
101          factory.newThread(monitoredRunnable).getName());
102    }
103  }
104
105  public void testNameFormatWithPercentD_custom() {
106    String format = "super-duper-thread-%d";
107    ThreadFactory factory = builder.setNameFormat(format).build();
108    for (int i = 0; i < 11; i++) {
109      assertEquals(String.format(format, i),
110          factory.newThread(monitoredRunnable).getName());
111    }
112  }
113
114  public void testDaemon_false() {
115    ThreadFactory factory = builder.setDaemon(false).build();
116    Thread thread = factory.newThread(monitoredRunnable);
117    assertFalse(thread.isDaemon());
118  }
119
120  public void testDaemon_true() {
121    ThreadFactory factory = builder.setDaemon(true).build();
122    Thread thread = factory.newThread(monitoredRunnable);
123    assertTrue(thread.isDaemon());
124  }
125
126  public void testPriority_custom() {
127    for (int i = Thread.MIN_PRIORITY; i <= Thread.MAX_PRIORITY; i++) {
128      ThreadFactory factory = builder.setPriority(i).build();
129      Thread thread = factory.newThread(monitoredRunnable);
130      assertEquals(i, thread.getPriority());
131    }
132  }
133
134  public void testPriority_tooLow() {
135    try {
136      builder.setPriority(Thread.MIN_PRIORITY - 1);
137      fail();
138    } catch (IllegalArgumentException expected) {
139    }
140  }
141
142  public void testPriority_tooHigh() {
143    try {
144      builder.setPriority(Thread.MAX_PRIORITY + 1);
145      fail();
146    } catch (IllegalArgumentException expected) {
147    }
148  }
149
150  public void testUncaughtExceptionHandler_custom() {
151    assertEquals(UNCAUGHT_EXCEPTION_HANDLER,
152        builder.setUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER).build()
153        .newThread(monitoredRunnable).getUncaughtExceptionHandler());
154  }
155
156  public void testBuildMutateBuild() {
157    ThreadFactory factory1 = builder.setPriority(1).build();
158    assertEquals(1, factory1.newThread(monitoredRunnable).getPriority());
159
160    ThreadFactory factory2 = builder.setPriority(2).build();
161    assertEquals(1, factory1.newThread(monitoredRunnable).getPriority());
162    assertEquals(2, factory2.newThread(monitoredRunnable).getPriority());
163  }
164
165  public void testBuildTwice() {
166    builder.build();  // this is allowed
167    builder.build();  // this is *also* allowed
168  }
169
170  public void testBuildMutate() {
171    ThreadFactory factory1 = builder.setPriority(1).build();
172    assertEquals(1, factory1.newThread(monitoredRunnable).getPriority());
173
174    builder.setPriority(2);  // change the state of the builder
175    assertEquals(1, factory1.newThread(monitoredRunnable).getPriority());
176  }
177
178  public void testThreadFactory() throws InterruptedException {
179    final String THREAD_NAME = "ludicrous speed";
180    final int THREAD_PRIORITY = 1;
181    final boolean THREAD_DAEMON = false;
182    ThreadFactory backingThreadFactory = new ThreadFactory() {
183      @Override public Thread newThread(Runnable r) {
184        Thread thread = new Thread(r);
185        thread.setName(THREAD_NAME);
186        thread.setPriority(THREAD_PRIORITY);
187        thread.setDaemon(THREAD_DAEMON);
188        thread.setUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
189        return thread;
190      }
191    };
192
193    Thread thread = builder.setThreadFactory(backingThreadFactory).build()
194        .newThread(monitoredRunnable);
195
196    assertEquals(THREAD_NAME, thread.getName());
197    assertEquals(THREAD_PRIORITY, thread.getPriority());
198    assertEquals(THREAD_DAEMON, thread.isDaemon());
199    assertSame(UNCAUGHT_EXCEPTION_HANDLER,
200        thread.getUncaughtExceptionHandler());
201    assertSame(Thread.State.NEW, thread.getState());
202
203    assertFalse(completed);
204    thread.start();
205    thread.join();
206    assertTrue(completed);
207  }
208
209  public void testNulls() {
210    NullPointerTester npTester = new NullPointerTester();
211    npTester.testAllPublicConstructors(ThreadFactoryBuilder.class);
212    npTester.testAllPublicStaticMethods(ThreadFactoryBuilder.class);
213    npTester.testAllPublicInstanceMethods(builder);
214  }
215}
216