Package org.gradle.foundation

Source Code of org.gradle.foundation.TestUtility

/*
* Copyright 2010 the original author or authors.
*
* 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 org.gradle.foundation;

import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.foundation.ipc.gradle.ExecuteGradleCommandServerProtocol;
import org.gradle.gradleplugin.foundation.DOM4JSerializer;
import org.gradle.gradleplugin.foundation.GradlePluginLord;
import org.gradle.gradleplugin.foundation.request.ExecutionRequest;
import org.gradle.gradleplugin.foundation.request.RefreshTaskListRequest;
import org.gradle.gradleplugin.foundation.request.Request;
import org.gradle.util.UncheckedException;
import org.jmock.Expectations;
import org.jmock.integration.junit4.JUnit4Mockery;

import javax.swing.filechooser.FileFilter;
import java.io.File;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Utility class for initializing various test objects related.
*
* @author mhunsicker
*/
public class TestUtility {
    private static long uniqueNameCounter = 1; //used to make unique names for JMock objects.

    /**
     * Creates a mock project with the specified properties.
     *
     * Note: depth is 0 for a root project. 1 for a root project's subproject, etc.
    */
    public static Project createMockProject(JUnit4Mockery context, final String name, final String buildFilePath, final int depth, Project[] subProjectArray, Task[] tasks, String[] defaultTasks, Project... dependsOnProjects) {
        final Project project = context.mock(Project.class, "[project]_" + name + '_' + uniqueNameCounter++);

        context.checking(new Expectations() {{
            allowing(project).getName();
            will(returnValue(name));
            allowing(project).getDescription();
            will(returnValue(null));
            allowing(project).getBuildFile();
            will(returnValue(new File(buildFilePath)));
            allowing(project).getDepth();
            will(returnValue(depth));
        }});

        attachSubProjects(context, project, subProjectArray);
        attachTasks(context, project, tasks);
        assignDefaultTasks(context, project, defaultTasks);
        assignDependsOnProjects(context, project, dependsOnProjects);

        return project;
    }

    /**
     * This makes the sub projects children of the parent project. If you call this repeatedly on the same
     * parentProject, any previous sub projects will be replaced with the new ones.
     *
     * @param context the mock context
     * @param parentProject where to attach the sub projects. This must be a mock object.
     * @param subProjectArray the sub projects to attach to the parent. These must be mock objects. Pass in null or an
     * empty array to set no sub projects.
    */
    public static void attachSubProjects(JUnit4Mockery context, final Project parentProject, Project... subProjectArray) {
        final Map<String, Project> childProjects = new LinkedHashMap<String, Project>();
        if (subProjectArray != null) {
            for (final Project subProject : subProjectArray) {
                childProjects.put(String.valueOf(childProjects.size()), subProject);
                context.checking(new Expectations() {{
                    allowing(subProject).getParent();
                    will(returnValue(parentProject));
                }});
            }
        }

        //populate the subprojects (this may be an empty set)
        context.checking(new Expectations() {{
            allowing(parentProject).getChildProjects();
            will(returnValue(childProjects));
        }});
    }

    /**
     * Creates a mock task with the specified properites.
    */
    public static Task createTask(JUnit4Mockery context, final String name, final String description) {
        final Task task = context.mock(Task.class, "[task]_" + name + '_' + uniqueNameCounter++);

        context.checking(new Expectations() {{
            allowing(task).getName();
            will(returnValue(name));
            allowing(task).getDescription();
            will(returnValue(description));
        }});

        return task;
    }

    /**
     * This makes the tasks children of the parent project. If you call this repeatedly on the same parentProject, any
     * previous tasks will be replaced with the new ones.
     *
     * @param context the mock context
     * @param parentProject where to attach the sub projects. This must be a mock object.
     * @param taskArray the tasks to attach to the parent. these must be mock objects. Pass in null or an empty array to
     * set no tasks.
    */
    public static void attachTasks(JUnit4Mockery context, final Project parentProject, Task... taskArray) {
        //first, make our project return our task container
        final TaskContainer taskContainer = context.mock(TaskContainer.class, "[taskcontainer]_" + parentProject.getName() + '_' + uniqueNameCounter++);

        context.checking(new Expectations() {{
            allowing(parentProject).getTasks();
            will(returnValue(taskContainer));
        }});

        final Set<Task> set = new LinkedHashSet<Task>();   //using a LinkedHashSet rather than TreeSet (which is what gradle uses) so I don't have to deal with compareTo() being called on mock objects.

        if (taskArray != null && taskArray.length != 0) {
            set.addAll(Arrays.asList(taskArray));

            //set the parent project of the tasks
            for (int index = 0; index < taskArray.length; index++) {
                final Task task = taskArray[index];

                context.checking(new Expectations() {{
                    allowing(task).getProject();
                    will(returnValue(parentProject));
                }});
            }
        }

        //populate the task container (this may be an empty set)
        context.checking(new Expectations() {{
            allowing(taskContainer).iterator();
            will(returnIterator(set));
        }});
    }

    private static void assignDefaultTasks(JUnit4Mockery context, final Project project, final String... defaultTasksArray) {
        final List<String> defaultTaskList = new ArrayList<String>();

        if (defaultTasksArray != null && defaultTasksArray.length != 0) {
            defaultTaskList.addAll(Arrays.asList(defaultTasksArray));
        }

        context.checking(new Expectations() {{
            allowing(project).getDefaultTasks();
            will(returnValue(defaultTaskList));
        }});
    }

    private static void assignDependsOnProjects(JUnit4Mockery context, final Project project, final Project... dependsOnProjects) {
        final Set<Project> set = new LinkedHashSet<Project>();   //using a LinkedHashSet rather than TreeSet (which is what gradle uses) so I don't have to deal with compareTo() being called on mock objects.

        if (dependsOnProjects != null && dependsOnProjects.length != 0) {
            set.addAll(Arrays.asList(dependsOnProjects));
        }

        //populate the subprojects (this may be an empty set)
        context.checking(new Expectations() {{
            allowing(project).getDependsOnProjects();
            will(returnValue(set));
        }});
    }

    public static <T> void assertListContents(List<T> actualObjects, T... expectedObjectsArray) {
        assertListContents(actualObjects, Arrays.asList(expectedObjectsArray));
    }

    public static <T> void assertListContents(List<T> actualObjects, List<T> expectedObjects) {
        assertUnorderedListContents(actualObjects, expectedObjects);
    }

    /**
     * This asserts the contents of the list are as expected. The important aspect of this function is that we don't
     * care about ordering. We just want to make sure the contents are the same.
     *
     * @param actualObjecs the list to check
     * @param expectedObjects what we expect in the list
    */
    public static <T> void assertUnorderedListContents(List<T> actualObjecs, List<T> expectedObjects) {
        List<T> expectedObjecsList = new ArrayList<T>(expectedObjects);   //make a copy of it, so we can modify it.

        while (!expectedObjecsList.isEmpty()) {
            T expectedObject = expectedObjecsList.remove(0);

            if (!actualObjecs.contains(expectedObject)) {
                throw new AssertionFailedError(
                        "Failed to locate object. Sought object:\n" + expectedObject + "\n\nExpected:\n" + dumpList(
                                expectedObjects) + "\nActual:\n" + dumpList(actualObjecs));
        }
        }

        if (actualObjecs.size() != expectedObjects.size()) {
            throw new AssertionFailedError(
                    "Expected " + expectedObjects.size() + " items but found " + actualObjecs.size() + "\nExpected:\n"
                            + dumpList(expectedObjects) + "\nActual:\n" + dumpList(actualObjecs));
    }
    }

    //function for getting a prettier dump of a list.

    public static String dumpList(List list) {
        if (list == null) {
            return "[null]";
        }
        if (list.isEmpty()) {
            return "[empty]";
        }

        StringBuilder builder = new StringBuilder();
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object object = iterator.next();
            if (object == null) {
                builder.append("**** [null object in list] ****\n");
            } else {
                builder.append(object.toString()).append('\n');
        }
        }

        return builder.toString();
    }

    /**
     * This is an ExportInteraction implemention meant to be used by tests. You pass it a file to use and we'll return
     * that in promptForFile. This also checks to ensure something doesn't happen where we get into an endless loop if
     * promptForFile is called repeatedly. This can happen if promptForFile is called and its return value fails some
     * form of validation which makes promptForFile get called again or if you deny overwriting the file. You'll get
     * prompted again.
    */
    public static class TestExportInteraction implements DOM4JSerializer.ExportInteraction {
        private File file;
        private boolean confirmOverwrite;
        private int promptCount;

        public TestExportInteraction(File file, boolean confirmOverwrite) {
            this.file = file;
            this.confirmOverwrite = confirmOverwrite;
        }

        public File promptForFile(FileFilter fileFilters) {
            if (promptCount == 100) {
                throw new AssertionFailedError("Possible endless loop. PromptForFile has been called 100 times.");
            }

            promptCount++;
            return file;
        }

        /**
         * The file already exists. Confirm whether or not you want to overwrite it.
         *
         * @param file the file in question
         * @return true to overwrite it, false not to.
        */
        public boolean confirmOverwritingExisingFile(File file) {
            return confirmOverwrite;
        }

        public void reportError(String error) {
            throw new AssertionFailedError("Unexpected error: " + error);
        }
    }

    /**
     * This is an ImportInteraction implementation meant to be used by tests. See TestExportInteraction for more
     * information.
    */
    public static class TestImportInteraction implements DOM4JSerializer.ImportInteraction {
        private File file;
        private int promptCount;

        public TestImportInteraction(File file) {
            this.file = file;
        }

        public File promptForFile(FileFilter fileFilters) {
            if (promptCount == 100) {
                throw new AssertionFailedError("Possible endless loop. PromptForFile has been called 100 times.");
            }

            promptCount++;
            return file;
        }

        public void reportError(String error) {
            throw new AssertionFailedError("Unexpected error: " + error);
        }
    }

    //wrapper around File.createTempFile just so we don't have to deal with the exception for tests.

    /**
     * This refreshes the projects but blocks until it is complete (its being executed in a separate process).
     *
     * @param gradlePluginLord the plugin lord (will be used to execute the command and store the results).
    */
    public static void refreshProjectsBlocking(GradlePluginLord gradlePluginLord, int maximumWaitValue, TimeUnit maximumWaitUnits) {
        refreshProjectsBlocking(gradlePluginLord, new ExecuteGradleCommandServerProtocol.ExecutionInteraction() {
            public void reportExecutionStarted() {
            }

           public void reportNumberOfTasksToExecute( int size ) {
           }

           public void reportExecutionFinished(boolean wasSuccessful, String message, Throwable throwable) {
            }

            public void reportTaskStarted(String message, float percentComplete) {
            }

            public void reportTaskComplete(String message, float percentComplete) {
            }

            public void reportLiveOutput(String message) {
            }
        }, maximumWaitValue, maximumWaitUnits);
    }

    private static void refreshProjectsBlocking(GradlePluginLord gradlePluginLord, final ExecuteGradleCommandServerProtocol.ExecutionInteraction executionInteraction, int maximumWaitValue, TimeUnit maximumWaitUnits) {
        gradlePluginLord.startExecutionQueue();   //make sure its started

        final CountDownLatch complete = new CountDownLatch(1);

        GradlePluginLord.RequestObserver observer = new GradlePluginLord.RequestObserver() {
           public void executionRequestAdded( ExecutionRequest request ) {}
           public void refreshRequestAdded( RefreshTaskListRequest request )
           {
              request.setExecutionInteraction( executionInteraction );
           }
           public void aboutToExecuteRequest( Request request ) { }

           public void requestExecutionComplete( Request request, int result, String output ) {
               complete.countDown();
           }
        };

        gradlePluginLord.addRequestObserver( observer, false );   //add the observer before we add the request due to timing issues. It's possible for it to completely execute before we return from addRefreshRequestToQueue.
        Request request = gradlePluginLord.addRefreshRequestToQueue();

        //make sure we've got a request
        Assert.assertNotNull(request);

        //now wait until we're complete, but bail if we wait too long
        boolean completed;
        try {
            completed = complete.await(maximumWaitValue, maximumWaitUnits);
        } catch (InterruptedException e) {
            throw UncheckedException.asUncheckedException(e);
        }

        gradlePluginLord.removeRequestObserver( observer );

        if (!completed) //its still running. Something is wrong.
        {
            request.cancel(); //just to clean up after ourselves a little, cancel the request.
            throw new AssertionFailedError("Failed to complete refresh in alotted time: " + maximumWaitValue + " " + maximumWaitUnits + ". Considering this failed.");
        }
    }

   /**
    This executes a command and waits until it is finished.

    @param gradlePluginLord the plugin lord
    @param fullCommandLine the command to execute
    @param displayName the display name of the command. It doesn't usuall matter.
    @param executionInteraction this gets the results of the execution
    @param maximumWaitSeconds the maximum time to wait before considering it failed.
    */
    public static void executeBlocking(GradlePluginLord gradlePluginLord, String fullCommandLine, String displayName, final ExecuteGradleCommandServerProtocol.ExecutionInteraction executionInteraction, int maximumWaitSeconds) {
        gradlePluginLord.startExecutionQueue();   //make sure its started

        final AtomicBoolean isComplete = new AtomicBoolean();

        GradlePluginLord.RequestObserver observer = new GradlePluginLord.RequestObserver() {
           public void executionRequestAdded( ExecutionRequest request )
           {
              request.setExecutionInteraction( executionInteraction );
           }
           public void refreshRequestAdded( RefreshTaskListRequest request ) { }
           public void aboutToExecuteRequest( Request request ) { }

           public void requestExecutionComplete( Request request, int result, String output ) {
               isComplete.set(true);
           }
        };

        gradlePluginLord.addRequestObserver( observer, false );   //add the observer before we add the request due to timing issues. It's possible for it to completely execute before we return from addExecutionRequestToQueue.
        Request request = gradlePluginLord.addExecutionRequestToQueue( fullCommandLine, displayName );

        //make sure we've got a request
        Assert.assertNotNull(request);

        //now sleep until we're complete, but bail if we wait too long
        int totalWaitTime = 0;
        while (!isComplete.get() && totalWaitTime <= maximumWaitSeconds) {
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }

            totalWaitTime += 1;
        }

        gradlePluginLord.removeRequestObserver( observer );

        if (!isComplete.get()) //its still running. Something is wrong.
        {
            request.cancel(); //just to clean up after ourselves a little, cancel the request.
            throw new AssertionFailedError("Failed to comlete execution in alotted time: " + maximumWaitSeconds + " seconds. Considering this failed.");
        }
    }
}
TOP

Related Classes of org.gradle.foundation.TestUtility

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.