Skip to content
This repository has been archived by the owner on Feb 11, 2022. It is now read-only.

PT-429: input scripting #121

Merged
merged 9 commits into from
Jan 25, 2018
Merged

PT-429: input scripting #121

merged 9 commits into from
Jan 25, 2018

Conversation

tasomaniac
Copy link
Contributor

@tasomaniac tasomaniac commented Jan 31, 2017

Problem

Currently, user has to create a new task extending from Input to have input scripting. It doesn't really look good and we don't have much control over that task since we don't create it ourselves.

Solution

This is a proposal of a possible solution. I would like to get opinions and structure possible follow-ups for other tasks.

A NamedDomainObjectContainer is introduced in the extension. This is what productFlavors use in Android. This will allow users to have scripts like below

scripts {
    autoLogin {
        execute {
            2.times {
                text 'bob'
                enter()
            }
            enter()
        }
    }
}

This will then create an InputSpec object which will holds a Closure to be lazily executed in runtime of the task.

For backward compatibility, Closure script in Input task is not removed. But instead it now uses InputSpec as an inner field. script is converted to write-only property to keep backward compatibility. When called, prints a warning with a link to the appropriate section in README.

Deprecation is even visible to users in their build.gradle files

Said Tahsin Dane added 3 commits January 31, 2017 17:29
Have an named extension in the command extension to support DSL like scripting for inputs.
Put them into a group named `add script`
Also move a method in the spec to make more sense.
private configureInputScripts(AndroidCommandPluginExtension extension, Project project) {
extension.scripts.all { input ->
project.tasks.create(input.name, Input) {
group = 'adb script'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to have a brand new group called adb script. Suggestions are welcome.

@@ -83,6 +86,14 @@ public class AndroidCommandPluginExtension {
monkey
}

void script(Action<NamedDomainObjectContainer<InputSpec>> action) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this method: why do you need it? I'd expect you would expose the container itself (look at BPP for instance). This way the user will be able to create all the scripts he needs and then you can (lazily) parse all the InputSpec in there to create the Input tasks you need.

Copy link
Contributor Author

@tasomaniac tasomaniac Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That line in BPP adds an extension to the project, right? I want to add the same to the command extension we already have.
Should I do something like project.android.command.extensions.add()?

And I didn't understand how it will help being lazy. All the code in the extension closure runs immediately with any gradle task in the configuration phase, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to take a look at this tomorrow together, but beware the code executed in each entry of the container will be affecting only the InputSpec that is perfectly fine, because it's just about collecting the commands to be used later in the Input task

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep thats perfectly fine. I think it is 2 different ways of achieving the same thing. Let's discuss tomorrow and decide on a solution.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and about adding the container you don't need to do anything special, just make the scripts field public in the extension (in BPP we want to add it to the project we have no extension there)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really happy with evaluating the scripts early. That limits their use quite a bit since we lose all kinds of runtime evaluations. Or am I missing something?

def command = ["shell", "input"]
command.addAll(values)
runCommand(command)
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method has been just added and already deprecated: what's the point of it? 😕
If you want to introduce this change without breaking any public API you should keep all the public methods deleted, mark them as @Deprecated and delegate to the InputSpec instead. Other choice will be to annotate the script field as @Delegate and let Groovy do its magic, but then the users won't have any notion of deprecation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to deprecate the field script because now it is a proper input that I use. It is now used when the task is created in the plugin.

Before we had Closure script. With gradle magic, when the user creates a new task with type Input, they could use script like below (which looks like a method call)

This method is now introduced to support that the old behavior.

task autoLogin(type: com.novoda.gradle.command.Input) {
     script {
    }
}

I didn't want to keep all the methods because that would cause a lot of duplication. I know that it is not ideal but with this we are 100% backward compatible.

I will look into @Delegate. I guess with that I can just remove this method. I would have to remove deprecation but it is not really super visible anyways.

Copy link
Contributor

@mr-archano mr-archano Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're talking from the perspective of the sample we provide, not the real public API: if you remove public methods you're introducing breaking changes, right? Duplication in this case is not correct, because you will not duplicate the implementation, will just be tedious to make those method to use delegation instead. I feel we can bare with that as long as we have a proper strategy to move forward :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's very true :)
Then Delegate seems to be a logical way to do it.

@@ -83,6 +86,14 @@ public class AndroidCommandPluginExtension {
monkey
}

void script(Action<NamedDomainObjectContainer<InputSpec>> action) {
action.execute(scripts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't because of this that you can't have lazy initialisation of commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so. But I kinda need to execute the action to retrieve the name to create the task.

In the following example, I need to get the name autoLogin in advance to create the task with that name but still need to run the rest lazily. How would I do that?

By doing action.execute, the script container gets an InputSpec object with a name.

autoLogin {
        2.times {
            text 'bob'
            enter()
        }
        enter()
    }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can postpone the creation of the task waiting for the end of the configuration phase, after which the scripts container will be filled with all the entries. we do something similar in the other plugin :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. 👍 Should totally do that.

Keep the input scripting evaluation lazy by introducing another closure in the middle.
* https://github.com/novoda/gradle-android-command-plugin#input-scripting
*/
@Deprecated
void setScript(Closure script) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a new method deprecated but it really is the existing property being converted to write-only property and deprecated. Here is what I want to achieve.

Existing implementations will mark it as deprecated.

screen shot 2018-01-08 at 11 25 27 am

README.md Outdated
```groovy
inputScripts {
autoLogin {
inputScript {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is how the new NamedDomainObjectContainer is used. Unfortunately I had to create another inputScript property in the middle to make the execution of the Closure lazy.

When I remove it and have the closure in one level up (like below), configure method of NamedDomainObjectContainer just executes the whole closure.

        inputScripts {
            autoLogin {
                2.times {
                    text 'bob'
                    enter()
                }
                enter()
            }
        }

@tasomaniac
Copy link
Contributor Author

Since there are no behavior changes with this change, I will go ahead and merge this.

@tasomaniac tasomaniac merged commit 38f4d80 into develop Jan 25, 2018
@tasomaniac tasomaniac deleted the PT-429/input-scripting branch January 25, 2018 21:24
@tasomaniac tasomaniac added this to the v2.0 milestone Jan 30, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants