public final class

AnimatorTestRule

extends java.lang.Object

 java.lang.Object

↳androidx.core.animation.AnimatorTestRule

Gradle dependencies

compile group: 'androidx.core', name: 'core-animation-testing', version: '1.0.0-alpha02'

  • groupId: androidx.core
  • artifactId: core-animation-testing
  • version: 1.0.0-alpha02

Artifact androidx.core:core-animation-testing:1.0.0-alpha02 it located at Google repository (https://maven.google.com/)

Overview

JUnit that can be used to run Animators without actually waiting for the duration of the animation. This also helps the test to be written in a deterministic manner. Create an instance of AnimatorTestRule and specify it as a of the test class. Use AnimatorTestRule.advanceTimeBy(long) to advance animators that have been started. Note that AnimatorTestRule.advanceTimeBy(long) should be called from the same thread you have used to start the animator.

  @SmallTest
  @RunWith(AndroidJUnit4.class)
 public class SampleAnimatorTest {

      @ClassRule
     public static AnimatorTestRule sAnimatorTestRule = new AnimatorTestRule();

      @UiThreadTest
      @Test
     public void sample() {
         final ValueAnimator animator = ValueAnimator.ofInt(0, 1000);
         animator.setDuration(1000L);
         assertThat(animator.getAnimatedValue(), is(0));
         animator.start();
         sAnimatorTestRule.advanceTimeBy(500L);
         assertThat(animator.getAnimatedValue(), is(500));
     }
 }
 

Summary

Constructors
publicAnimatorTestRule()

Methods
public voidadvanceTimeBy(long timeDelta)

Advances the animation clock by the given amount of delta in milliseconds.

public Statementapply(Statement base, Description description)

public longgetCurrentTime()

Returns the current time in milliseconds tracked by AnimationHandler.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public AnimatorTestRule()

Methods

public Statement apply(Statement base, Description description)

public void advanceTimeBy(long timeDelta)

Advances the animation clock by the given amount of delta in milliseconds. This call will produce an animation frame to all the ongoing animations. This method needs to be called on the same thread as Animator.start().

Parameters:

timeDelta: the amount of milliseconds to advance

public long getCurrentTime()

Returns the current time in milliseconds tracked by AnimationHandler. Note that this is a different time than the time tracked by @{link SystemClock} This method needs to be called on the same thread as Animator.start().

Source

/*
 * Copyright (C) 2018 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 License 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.core.animation;

import android.os.Looper;
import android.os.SystemClock;
import android.util.AndroidRuntimeException;

import androidx.annotation.NonNull;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

/**
 * JUnit {@link TestRule} that can be used to run {@link Animator}s without actually waiting for the
 * duration of the animation. This also helps the test to be written in a deterministic manner.
 *
 * Create an instance of {@code AnimatorTestRule} and specify it as a {@link org.junit.ClassRule}
 * of the test class. Use {@link #advanceTimeBy(long)} to advance animators that have been started.
 * Note that {@link #advanceTimeBy(long)} should be called from the same thread you have used to
 * start the animator.
 *
 * <pre>
 * {@literal @}SmallTest
 * {@literal @}RunWith(AndroidJUnit4.class)
 * public class SampleAnimatorTest {
 *
 *     {@literal @}ClassRule
 *     public static AnimatorTestRule sAnimatorTestRule = new AnimatorTestRule();
 *
 *     {@literal @}UiThreadTest
 *     {@literal @}Test
 *     public void sample() {
 *         final ValueAnimator animator = ValueAnimator.ofInt(0, 1000);
 *         animator.setDuration(1000L);
 *         assertThat(animator.getAnimatedValue(), is(0));
 *         animator.start();
 *         sAnimatorTestRule.advanceTimeBy(500L);
 *         assertThat(animator.getAnimatedValue(), is(500));
 *     }
 * }
 * </pre>
 */
public final class AnimatorTestRule implements TestRule {

    final AnimationHandler mTestHandler;
    final long mStartTime;
    private long mTotalTimeDelta = 0;
    private final Object mLock = new Object();

    public AnimatorTestRule() {
        mStartTime = SystemClock.uptimeMillis();
        mTestHandler = new AnimationHandler(new TestProvider());
    }

    @NonNull
    @Override
    public Statement apply(@NonNull final Statement base, @NonNull Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                AnimationHandler.setTestHandler(mTestHandler);
                try {
                    base.evaluate();
                } finally {
                    AnimationHandler.setTestHandler(null);
                }
            }
        };
    }

    /**
     * Advances the animation clock by the given amount of delta in milliseconds. This call will
     * produce an animation frame to all the ongoing animations. This method needs to be
     * called on the same thread as {@link Animator#start()}.
     *
     * @param timeDelta the amount of milliseconds to advance
     */
    public void advanceTimeBy(long timeDelta) {
        if (Looper.myLooper() == null) {
            // Throw an exception
            throw new AndroidRuntimeException("AnimationTestRule#advanceTimeBy(long) may only be"
                    + "called on Looper threads");
        }
        synchronized (mLock) {
            // Advance time & pulse a frame
            mTotalTimeDelta += timeDelta < 0 ? 0 : timeDelta;
        }
        // produce a frame
        mTestHandler.onAnimationFrame(getCurrentTime());
    }


    /**
     * Returns the current time in milliseconds tracked by AnimationHandler. Note that this is a
     * different time than the time tracked by @{link SystemClock} This method needs to be called on
     * the same thread as {@link Animator#start()}.
     */
    public long getCurrentTime() {
        if (Looper.myLooper() == null) {
            // Throw an exception
            throw new AndroidRuntimeException("AnimationTestRule#getCurrentTime() may only be"
                    + "called on Looper threads");
        }
        synchronized (mLock) {
            return mStartTime + mTotalTimeDelta;
        }
    }


    private class TestProvider implements AnimationHandler.AnimationFrameCallbackProvider {
        TestProvider() {}

        @Override
        public void onNewCallbackAdded(AnimationHandler.AnimationFrameCallback callback) {
            callback.doAnimationFrame(getCurrentTime());
        }

        @Override
        public void postFrameCallback() {
        }

        @Override
        public void setFrameDelay(long delay) {
        }

        @Override
        public long getFrameDelay() {
            return 0;
        }
    }
}