1 // Copyright 2014 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net.urlconnection; 6 7 import static com.google.common.truth.Truth.assertThat; 8 9 import static org.junit.Assert.assertThrows; 10 11 import androidx.test.ext.junit.runners.AndroidJUnit4; 12 import androidx.test.filters.SmallTest; 13 14 import org.junit.Test; 15 import org.junit.runner.RunWith; 16 17 import org.chromium.base.test.util.Batch; 18 19 import java.io.InterruptedIOException; 20 import java.net.SocketTimeoutException; 21 import java.util.concurrent.ExecutorService; 22 import java.util.concurrent.Executors; 23 import java.util.concurrent.Future; 24 import java.util.concurrent.ThreadFactory; 25 26 /** Tests the MessageLoop implementation. */ 27 @Batch(Batch.UNIT_TESTS) 28 @RunWith(AndroidJUnit4.class) 29 public class MessageLoopTest { 30 private Thread mTestThread; 31 private final ExecutorService mExecutorService = 32 Executors.newSingleThreadExecutor(new ExecutorThreadFactory()); 33 34 private class ExecutorThreadFactory implements ThreadFactory { 35 @Override newThread(Runnable r)36 public Thread newThread(Runnable r) { 37 mTestThread = new Thread(r); 38 return mTestThread; 39 } 40 } 41 42 private boolean mFailed; 43 44 @Test 45 @SmallTest testInterrupt()46 public void testInterrupt() throws Exception { 47 final MessageLoop loop = new MessageLoop(); 48 assertThat(loop.isRunning()).isFalse(); 49 Future future = 50 mExecutorService.submit( 51 new Runnable() { 52 @Override 53 public void run() { 54 assertThrows(InterruptedIOException.class, loop::loop); 55 } 56 }); 57 Thread.sleep(1000); 58 assertThat(loop.isRunning()).isTrue(); 59 assertThat(loop.hasLoopFailed()).isFalse(); 60 mTestThread.interrupt(); 61 future.get(); 62 assertThat(loop.isRunning()).isFalse(); 63 assertThat(loop.hasLoopFailed()).isTrue(); 64 // Re-spinning the message loop is not allowed after interrupt. 65 mExecutorService 66 .submit( 67 new Runnable() { 68 @Override 69 public void run() { 70 assertThrows(InterruptedIOException.class, loop::loop); 71 } 72 }) 73 .get(); 74 } 75 76 @Test 77 @SmallTest testTaskFailed()78 public void testTaskFailed() throws Exception { 79 final MessageLoop loop = new MessageLoop(); 80 assertThat(loop.isRunning()).isFalse(); 81 Future future = 82 mExecutorService.submit( 83 new Runnable() { 84 @Override 85 public void run() { 86 assertThrows(NullPointerException.class, loop::loop); 87 } 88 }); 89 Runnable failedTask = 90 new Runnable() { 91 @Override 92 public void run() { 93 throw new NullPointerException(); 94 } 95 }; 96 Thread.sleep(1000); 97 assertThat(loop.isRunning()).isTrue(); 98 assertThat(loop.hasLoopFailed()).isFalse(); 99 loop.execute(failedTask); 100 future.get(); 101 assertThat(loop.isRunning()).isFalse(); 102 assertThat(loop.hasLoopFailed()).isTrue(); 103 // Re-spinning the message loop is not allowed after exception. 104 mExecutorService 105 .submit( 106 new Runnable() { 107 @Override 108 public void run() { 109 assertThrows(NullPointerException.class, loop::loop); 110 } 111 }) 112 .get(); 113 } 114 115 @Test 116 @SmallTest testLoopWithTimeout()117 public void testLoopWithTimeout() throws Exception { 118 final MessageLoop loop = new MessageLoop(); 119 assertThat(loop.isRunning()).isFalse(); 120 // The MessageLoop queue is empty. Use a timeout of 100ms to check that 121 // it doesn't block forever. 122 assertThrows(SocketTimeoutException.class, () -> loop.loop(100)); 123 assertThat(loop.isRunning()).isFalse(); 124 } 125 } 126