content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the Lice`nse is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package androidx.test.rule;
  
  import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
  import static org.hamcrest.CoreMatchers.containsString;
  import static org.hamcrest.CoreMatchers.is;
  import static org.hamcrest.MatcherAssert.assertThat;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  import static org.junit.runner.JUnitCore.runClasses;
  
  import android.app.Service;
  import android.content.Intent;
  import android.os.Binder;
  import android.os.IBinder;
  import android.os.SystemClock;
  import androidx.annotation.NonNull;
  import androidx.test.ext.junit.runners.AndroidJUnit4;
  import androidx.test.filters.LargeTest;
  import java.util.concurrent.TimeUnit;
  import java.util.concurrent.TimeoutException;
  import org.junit.After;
  import org.junit.Before;
  import org.junit.Rule;
  import org.junit.Test;
  import org.junit.rules.ExpectedException;
  import org.junit.runner.Result;
  import org.junit.runner.RunWith;
  
  @RunWith(AndroidJUnit4.class)
  @LargeTest
  public class ServiceTestRuleTest {
  
    public static class TestService extends Service {
  
      private final IBinder binder = new Binder();
  
      @Override
      public IBinder onBind(Intent intent) {
        return binder;
      }
    }
  
    public static class ServiceThatCantBeBoundTo extends Service {
      @Override
      public IBinder onBind(Intent intent) {
        // returns null so clients can not bind to the service
        return null;
      }
    }
  
    public static class ServiceThatIsNotDefinedInManifest extends Service {
      // This service is not declared in the manifest on purpose
      @Override
      public IBinder onBind(Intent intent) {
        // returns null so clients can not bind to the service
        return null;
      }
    }
  
    public static class TimeoutService extends Service {
  
      @Override
      public IBinder onBind(Intent intent) {
        SystemClock.sleep(100);
        return null;
      }
  
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
        SystemClock.sleep(100);
        return super.onStartCommand(intent, flags, startId);
      }
    }
  
    public static class StartedServiceLifecycleTest {
      private static StringBuilder log = new StringBuilder();
  
      @Rule
      public final ServiceTestRule serviceRule =
          new ServiceTestRule() {
  
            @Override
            public void beforeService() {
              log.append("beforeService ");
            }
  
            @Override
            public void startService(@NonNull Intent intent) throws TimeoutException {
              log.append("startService ");
            }
  
            @Override
            public IBinder bindService(@NonNull Intent intent) throws TimeoutException {
              log.append("bindService ");
              return null;
            }
  
            @Override
            public void afterService() {
              log.append("afterService ");
            }
  
            @Override
            void shutdownService() throws TimeoutException {
              log.append("shutdownService ");
            }
          };
  
      @Before
      public void before() {
        log.append("before ");
      }
  
      @After
      public void after() {
        log.append("after ");
      }
  
      @Test
      public void dummyTestToLaunchService() throws TimeoutException {
        log.append("test ");
        serviceRule.startService(new Intent());
        fail("This is a dummy test to start a service");
      }
    }
  
    @Test
    public void checkLifecycleOfStartedService() {
      Result result = runClasses(StartedServiceLifecycleTest.class);
      assertEquals(1, result.getFailureCount());
      assertThat(
          result.getFailures().get(0).getMessage(), is("This is a dummy test to start a service"));
      assertThat(
          StartedServiceLifecycleTest.log.toString(),
          is("beforeService before test startService after shutdownService afterService "));
    }
  
    public static class BoundServiceLifecycleTest {
      private static StringBuilder log = new StringBuilder();
  
      @Rule
      public final ServiceTestRule serviceRule =
          new ServiceTestRule() {
  
            @Override
            public void beforeService() {
              log.append("beforeService ");
            }
  
            @Override
            public void startService(@NonNull Intent intent) throws TimeoutException {
              log.append("startService ");
            }
  
            @Override
            public IBinder bindService(@NonNull Intent intent) throws TimeoutException {
              log.append("bindService ");
              return null;
            }
  
            @Override
            public void afterService() {
              log.append("afterService ");
            }
  
            @Override
            public void shutdownService() {
              log.append("shutdownService ");
            }
          };
  
      @Before
      public void before() {
        log.append("before ");
      }
  
      @After
      public void after() {
        log.append("after ");
      }
  
      @Test
      public void dummyTestToLaunchService() throws TimeoutException {
        log.append("test ");
        serviceRule.bindService(new Intent());
        fail("This is a dummy test to bind to a service");
      }
    }
  
    @Test
    public void checkLifecycleOfBoundService() {
      Result result = runClasses(BoundServiceLifecycleTest.class);
      assertEquals(1, result.getFailureCount());
      assertThat(
          result.getFailures().get(0).getMessage(), is("This is a dummy test to bind to a service"));
      assertThat(
          BoundServiceLifecycleTest.log.toString(),
          is("beforeService before test bindService after shutdownService afterService "));
    }
  
    public static class TimedOutServiceTest {
  
      @Rule
      public final ServiceTestRule serviceRule =
          ServiceTestRule.withTimeout(50, TimeUnit.MILLISECONDS);
  
      @Rule public final ExpectedException thrown = ExpectedException.none();
  
      @Test
      public void verifyStartServiceTimeout() throws TimeoutException {
        thrown.expect(TimeoutException.class);
        thrown.expectMessage("Waited for 50 MILLISECONDS, but service was never connected");
        // TimeoutService takes >= 100 milliseconds to start.
        serviceRule.startService(new Intent(getApplicationContext(), TimeoutService.class));
      }
  
      @Test
      public void verifyBindServiceTimeout() throws TimeoutException {
        thrown.expect(TimeoutException.class);
        thrown.expectMessage("Waited for 50 MILLISECONDS, but service was never connected");
        // TimeoutService takes >= 100 milliseconds to bind.
        serviceRule.bindService(new Intent(getApplicationContext(), TimeoutService.class));
      }
    }
  
    @Test
    public void checkServiceTimeoutLogic() {
      Result result = runClasses(TimedOutServiceTest.class);
      // since we're catching exception inside the test, nothing should report failure
      assertEquals(0, result.getFailureCount());
    }
  
    @Rule public final ServiceTestRule serviceRule = new ServiceTestRule();
  
    @Test
    public void verifyFailureIfServiceNotStarted() throws Exception {
      final class NotDeclaredService extends Service {
        @Override
        public IBinder onBind(Intent intent) {
          return null;
        }
      }
      try {
        serviceRule.startService(new Intent(getApplicationContext(), NotDeclaredService.class));
        fail("Should have thrown");
      } catch (IllegalArgumentException e) {
        assertThat(e.getMessage(), containsString("did not start a service"));
      }
    }
  
    @Test
    public void verifySuccessfulServiceStart() throws TimeoutException {
      serviceRule.startService(new Intent(getApplicationContext(), TestService.class));
      assertTrue("The service was not started", serviceRule.serviceStarted);
      assertTrue("The service was not bound", serviceRule.serviceBound);
    }
  
    @Test
    public void verifySuccessfulServiceBind() throws TimeoutException {
      serviceRule.bindService(new Intent(getApplicationContext(), TestService.class));
      assertTrue("The service was not bound", serviceRule.serviceBound);
      assertFalse("The service started instead of bound", serviceRule.serviceStarted);
    }
  
    @Test
    public void serviceCanBeBoundTwice() throws TimeoutException {
      Intent intent = new Intent(getApplicationContext(), TestService.class);
  
      IBinder firstBinder = serviceRule.bindService(intent);
      assertNotNull("Service failed to bind 1/2", firstBinder);
  
      IBinder secondBinder = serviceRule.bindService(intent);
      assertNotNull("Service failed to bind 2/2", secondBinder);
    }
  
    @Test
    public void serviceCanBindAfterUnbind() throws TimeoutException {
      Intent intent = new Intent(getApplicationContext(), TestService.class);
  
      IBinder firstBinder = serviceRule.bindService(intent);
      assertNotNull("Service failed to bind 1/2", firstBinder);
      serviceRule.unbindService();
  
      IBinder secondBinder = serviceRule.bindService(intent);
      assertNotNull("Service failed to bind 2/2", secondBinder);
    }
  
    @Test
    public void serviceThatCantBeBoundTo() {
      Intent intent = new Intent(getApplicationContext(), ServiceThatCantBeBoundTo.class);
      try {
        serviceRule.startService(intent);
        fail("TimeoutException was not thrown");
      } catch (TimeoutException e) {
        // expected
      }
    }
  
    @Test
    public void serviceThatIsNotDefinedInManifest() throws TimeoutException {
      Intent intent = new Intent(getApplicationContext(), ServiceThatIsNotDefinedInManifest.class);
      assertFalse(serviceRule.bindServiceAndWait(intent, null, 123));
    }
  }
  