from invoke import Collection, Context, Call
from invoke.parser import ParseResult, ParserContext, Argument
from fabric import Executor, Task, Connection
from fabric.executor import ConnectionCall
from fabric.exceptions import NothingToDo

from mock import Mock
from pytest import skip, raises  # noqa


def _get_executor(val=None, post=None, remainder=""):
    post_tasks = []
    if post is not None:
        post_tasks.append(post)
    hosts = Argument(name="hosts")
    hosts.value = val
    core_args = ParseResult([ParserContext(args=[hosts])])
    core_args.remainder = remainder
    task = Mock(pre=[], post=[])
    coll = Collection(mytask=Task(task, post=post_tasks))
    return task, Executor(coll, core=core_args)


def _execute(val=None, post=None, remainder="", invocation=None):
    if invocation is None:
        invocation = ["mytask"]
    task, executor = _get_executor(val, post, remainder)
    executor.execute(*invocation)
    return task


class Executor_:
    class expand_calls:
        class hosts_flag_empty:
            def no_parameterization_is_done(self):
                task = _execute()
                assert task.call_count == 1
                assert isinstance(task.call_args[0][0], Context)

        class hosts_flag_set:
            def parameterization_per_host(self):
                task = _execute(val="host1,host2,host3")
                assert task.call_count == 3
                assert isinstance(task.call_args[0][0], Connection)

            def post_tasks_happen_once_only(self):
                post = Mock()
                task = _execute(val="host1,host2,host3", post=Task(post))
                assert task.call_count == 3
                assert post.call_count == 1

        class remainder:
            def raises_NothingToDo_when_no_hosts(self):
                with raises(NothingToDo):
                    _execute(remainder="whatever")

            def creates_anonymous_call_per_host(self):
                # TODO: annoying to do w/o mucking around w/ our Executor class
                # more, and that stuff wants to change semi soon anyways when
                # we grow past --hosts; punting.
                skip()

        class dedupe:
            def deduplication_not_performed(self):
                task = _execute(invocation=["mytask", "mytask"])
                assert task.call_count == 2  # not 1

        class parameterize:
            def always_generates_ConnectionCall_with_host_attr(self):
                task, executor = _get_executor(val="host1,host2,host3")
                calls = executor.expand_calls(calls=[Call(task)])
                assert len(calls) == 3
                assert all(isinstance(x, ConnectionCall) for x in calls)
                assert [x.host for x in calls] == ["host1", "host2", "host3"]


# TODO: add new tests for desired interpretation of @hosts-driven .hosts attrs
# on Task objs (similarly unit-style)

@@ -315,19 +315,44 @@
 xecutor(
-val
+hosts_flag=None, hosts_kwarg
 =None, p
@@ -506,19 +506,26 @@
 value = 
-val
+hosts_flag
 %0A    cor
@@ -700,16 +700,35 @@
 st_tasks
+, hosts=hosts_kwarg
 ))%0A    r
@@ -789,68 +789,23 @@
 ute(
-val=None, post=None, remainder=%22%22, invocation=None
+**kwargs
 ):%0A    
-if 
 invo
@@ -815,25 +815,22 @@
 ion 
-is None:%0A        
+= kwargs.pop(%22
 invo
@@ -835,18 +835,18 @@
 vocation
- =
+%22,
  %5B%22mytas
@@ -848,16 +848,17 @@
 mytask%22%5D
+)
 %0A    tas
@@ -889,28 +889,16 @@
 tor(
-val, post, remainder
+**kwargs
 )%0A  
@@ -1318,35 +1318,42 @@
 task = _execute(
-val
+hosts_flag
 =%22host1,host2,ho
@@ -1584,19 +1584,47 @@
 execute(
-val
+%0A                    hosts_flag
 =%22host1,
@@ -1652,16 +1652,33 @@
 sk(post)
+%0A                
 )%0A      
@@ -2558,19 +2558,26 @@
 xecutor(
-val
+hosts_flag
 =%22host1,
@@ -2850,123 +2850,4 @@
 3%22%5D%0A
-%0A%0A# TODO: add new tests for desired interpretation of @hosts-driven .hosts attrs%0A# on Task objs (similarly unit-style)%0A

Test refactor + set up for Task(hosts=xxx)