TiagoMSSantos/MobileRT

View on GitHub
app/src/androidTest/java/puscas/mobilertapp/utils/UtilsContextT.java

Summary

Maintainability
B
4 hrs
Test Coverage
package puscas.mobilertapp.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.widget.Button;

import androidx.annotation.NonNull;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.matcher.RootMatchers;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.platform.app.InstrumentationRegistry;

import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;

import puscas.mobilertapp.DrawView;
import puscas.mobilertapp.MainActivity;
import puscas.mobilertapp.MainRenderer;
import puscas.mobilertapp.R;
import puscas.mobilertapp.ViewActionWait;
import puscas.mobilertapp.constants.Accelerator;
import puscas.mobilertapp.constants.Constants;
import puscas.mobilertapp.constants.ConstantsUI;
import puscas.mobilertapp.constants.Shader;
import puscas.mobilertapp.constants.State;

/**
 * Helper class which contains helper methods that need the {@link Context} for the tests.
 */
public final class UtilsContextT {

    /**
     * Logger for this class.
     */
    private static final Logger logger = Logger.getLogger(UtilsContextT.class.getSimpleName());

    /**
     * Private constructor to avoid creating instances.
     */
    private UtilsContextT() {
        throw new UnsupportedOperationException("Not implemented.");
    }

    /**
     * Helper method that waits until the Ray Tracing engine reaches the expected {@link State}.
     *
     * @param testName           The name of the current test.
     * @param activity           The {@link MainActivity} of MobileRT.
     * @param expectedButtonText The expected {@link Button} text.
     * @param expectedStates     The expected {@link State}s.
     * @throws TimeoutException If the Ray Tracing engine didn't reach the expected {@link State} before a timeout occurs.
     */
    public static void waitUntil(final String testName,
                                 @NonNull final MainActivity activity,
                                 final String expectedButtonText,
                                 final State... expectedStates) throws TimeoutException {
        logger.info("waitUntil start, expected button: " + expectedButtonText + ", expected state(s): " + Arrays.toString(expectedStates));
        final AtomicBoolean done = new AtomicBoolean(false);
        final AtomicBoolean updatedBitmap = new AtomicBoolean(false);
        // The test 'RayTracingTest#testRenderSceneFromSDCardOB' starts to fail when using 15ms.
        final int waitInMillis = 10;

        final DrawView drawView = UtilsT.getPrivateField(activity, "drawView");
        final MainRenderer renderer = drawView.getRenderer();

        final int timeToWaitInMillis = Objects.equals(expectedButtonText, Constants.STOP) ? 20 * 1000 : 60 * 1000;
        for (int currentTimeMs = 0; currentTimeMs < timeToWaitInMillis && !done.get(); currentTimeMs += waitInMillis) {
            // RayTracingTest#testRenderSceneFromSDCardOBJ starts to fail when using 5ms.
            ViewActionWait.waitForBitmapUpdate(2);
            Espresso.onView(ViewMatchers.withId(R.id.renderButton))
                .inRoot(RootMatchers.isTouchable())
                .perform(new ViewActionWait<>(waitInMillis, R.id.renderButton))
                .check((view, exception) -> {
                    UtilsT.rethrowException(exception);
                    final Button renderButton = view.findViewById(R.id.renderButton);
                    final String renderButtonText = renderButton.getText().toString();
                    final State rendererState = renderer.getState();
                    if (Objects.equals(renderButtonText, expectedButtonText) && Arrays.asList(expectedStates).contains(rendererState)) {
                        done.set(true);
                        logger.info("waitUntil for button update success");
                        if (Objects.equals(expectedButtonText, Constants.STOP)) {
                            final Bitmap bitmap = UtilsT.getPrivateField(renderer, "bitmap");
                            final boolean bitmapSingleColor = UtilsT.isBitmapSingleColor(bitmap);
                            if (!bitmapSingleColor) {
                                updatedBitmap.set(true);
                                logger.info("waitUntil for bitmap update success");
                            }
                        }
                    }
            });
        }

        if (!done.get()) {
            final State rendererState = renderer.getState();
            final String errorMessage = "Test: " + testName + ", State: '" + rendererState.name() + "' (expecting " + Arrays.toString(expectedStates) + "), Expected button: '" + expectedButtonText + "'.";
            throw new TimeoutException("The Ray Tracing engine didn't reach the expected state in " + (float) (timeToWaitInMillis) / 1000 + " secs. " + errorMessage);
        }

        if (Objects.equals(expectedButtonText, Constants.STOP)) {
            final int timeToWaitForUpdatedImageInMillis = 60 * 1000;
            // The test 'PreviewTest#testPreviewSceneOrthographicCamera' starts to fail when using 1ms and 4ms.
            // The test 'UiTest' starts to fail when using 0ms.
            final int waitInMillisForBitmapUpdate = 2;
            logger.info("Waiting '" + timeToWaitForUpdatedImageInMillis + "'ms for Bitmap to contain some rendered pixels.");
            final long endTimeInstantMs = System.currentTimeMillis() + timeToWaitForUpdatedImageInMillis;
            long currentInstantMs;
            do {
                final Bitmap bitmap = UtilsT.getPrivateField(renderer, "bitmap");
                final boolean bitmapSingleColor = UtilsT.isBitmapSingleColor(bitmap);
                if (!bitmapSingleColor) {
                    updatedBitmap.set(true);
                    logger.info("waitUntil for bitmap update success");
                } else {
                    ViewActionWait.waitForBitmapUpdate(waitInMillisForBitmapUpdate);
                }
                currentInstantMs = System.currentTimeMillis();
            } while (currentInstantMs < endTimeInstantMs && !updatedBitmap.get());

            if (!updatedBitmap.get()) {
                final Bitmap bitmap = UtilsT.getPrivateField(renderer, "bitmap");
                final boolean bitmapSingleColor = UtilsT.isBitmapSingleColor(bitmap);
                final String errorMessage = "Test: " + testName + ", Bitmap had no pixel rendered: " + bitmapSingleColor + ".";
                throw new TimeoutException("The Ray Tracing engine didn't reach the expected state in " + (float) (timeToWaitForUpdatedImageInMillis) / 1000 + " secs. " + errorMessage);
            }
        }

        logger.info("waitUntil finished");
    }

    /**
     * Helper method that resets the {@link android.widget.NumberPicker}s values
     * in the UI to some predefined values.
     *
     * @param scene       The id of the scene to set.
     * @param accelerator The {@link Accelerator} to use.
     * @param spp         The number of samples per pixel. Acceptable range is: [1-99].
     * @param spl         The number of samples per light. Acceptable range is: [1-100].
     */
    public static void resetPickerValues(final int scene, final Accelerator accelerator, final int spp, final int spl) {
        logger.info("resetPickerValues");

        final int numCores = UtilsContext.getNumOfCores(InstrumentationRegistry.getInstrumentation().getTargetContext());

        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_SCENE, R.id.pickerScene, scene);
        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_THREADS, R.id.pickerThreads, numCores);
        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_SIZE, R.id.pickerSize, 8);
        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_SAMPLES_PIXEL, R.id.pickerSamplesPixel, spp);
        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_SAMPLES_LIGHT, R.id.pickerSamplesLight, spl);
        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_ACCELERATOR, R.id.pickerAccelerator, accelerator.ordinal());
        UtilsPickerT.changePickerValue(ConstantsUI.PICKER_SHADER, R.id.pickerShader, Shader.PATH_TRACING.ordinal());
    }

}