1import collections
2import dbus
3import dbus.service
4import dbus.mainloop.glib
5import gobject
6import logging
7import threading
8
9""" MockFlimflam provides a select few methods from the flimflam
10    DBus API so that we can track "dbus-send" invocations sent
11    by the shill init scripts.  It could be used as a kernel for
12    a test of other facilities that use the shill/flimflam DBus
13    API and at that point it should be moved out of this specific
14    test. """
15
16MethodCall = collections.namedtuple("MethodCall", ["method", "argument"])
17
18class FlimflamManager(dbus.service.Object):
19    """ The flimflam DBus Manager object instance.  Methods in this
20        object are called whenever a DBus RPC method is invoked. """
21    def __init__(self, bus, object_path):
22        dbus.service.Object.__init__(self, bus, object_path)
23        self.method_calls = []
24
25
26    @dbus.service.method('org.chromium.flimflam.Manager',
27                         in_signature='s', out_signature='o')
28    def CreateProfile(self, profile):
29        """ Creates a profile.
30
31        @param profile string name of profile to create.
32
33        """
34        self.add_method_call('CreateProfile', profile)
35        return '/'
36
37
38    @dbus.service.method('org.chromium.flimflam.Manager',
39                         in_signature='s', out_signature='')
40    def RemoveProfile(self, profile):
41        """ Removes a profile.
42
43        @param profile string name of profile to remove.
44
45        """
46        self.add_method_call('RemoveProfile', profile)
47
48
49    @dbus.service.method('org.chromium.flimflam.Manager',
50                         in_signature='s', out_signature='o')
51    def PushProfile(self, profile):
52        """ Pushes a profile.
53
54        @param profile string name of profile to push.
55
56        """
57        self.add_method_call('PushProfile', profile)
58        return '/'
59
60
61    @dbus.service.method('org.chromium.flimflam.Manager',
62                         in_signature='ss', out_signature='o')
63    def InsertUserProfile(self, profile, user_hash):
64        """ Inserts a profile.
65
66        @param profile string name of profile to insert.
67        @param user_hash string user hash associated with this profile.
68
69        """
70        self.add_method_call('InsertUserProfile', (profile, user_hash))
71        return '/'
72
73
74    @dbus.service.method('org.chromium.flimflam.Manager',
75                         in_signature='s', out_signature='')
76    def PopProfile(self, profile):
77        """ Pops a profile.
78
79        @param profile string name of profile to pop.
80
81        """
82        self.add_method_call('PopProfile', profile)
83
84
85    @dbus.service.method('org.chromium.flimflam.Manager',
86                         in_signature='', out_signature='')
87    def PopAllUserProfiles(self):
88        """ Pops all user profiles from the profile stack.. """
89        self.add_method_call('PopAllUserProfiles', '')
90
91
92    def add_method_call(self, method, arg):
93        """ Note that a method call was made.
94
95        @param method string the method that was called.
96        @param arg tuple list of arguments that were called on |method|.
97
98        """
99        print "Called method %s" % method
100        logging.info("Mock Flimflam method %s called with argument %s",
101                     method, arg)
102        self.method_calls.append(MethodCall(method, arg))
103
104
105    def get_method_calls(self):
106        """ Provide the method call list, clears this list internally.
107
108        @return list of MethodCall objects
109
110        """
111        method_calls = self.method_calls
112        self.method_calls = []
113        return method_calls
114
115
116class MockFlimflam(threading.Thread):
117    """ This thread object instantiates a mock flimflam manager and
118        runs a mainloop that receives DBus API messages. """
119    FLIMFLAM = "org.chromium.flimflam"
120    def __init__(self):
121        threading.Thread.__init__(self)
122        gobject.threads_init()
123
124
125    def run(self):
126        """ Runs the main loop. """
127        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
128        self.bus = dbus.SystemBus()
129        name = dbus.service.BusName(self.FLIMFLAM, self.bus)
130        self.manager = FlimflamManager(self.bus, '/')
131        self.mainloop = gobject.MainLoop()
132        self.mainloop.run()
133
134
135    def quit(self):
136        """ Quits the main loop. """
137        self.mainloop.quit()
138
139
140    def get_method_calls(self):
141        """ Returns the method calls that were called on the mock object.
142
143        @return list of MethodCall objects representing the methods called.
144
145         """
146        return self.manager.get_method_calls()
147
148
149if __name__ == '__main__':
150    MockFlimflam().run()
151