1# Copyright (C) 2009 Nokia Corporation
2# Copyright (C) 2009 Collabora Ltd.
3#
4# This library is free software; you can redistribute it and/or
5# modify it under the terms of the GNU Lesser General Public
6# License as published by the Free Software Foundation; either
7# version 2.1 of the License, or (at your option) any later version.
8#
9# This library is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12# Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public
15# License along with this library; if not, write to the Free Software
16# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17# 02110-1301 USA
18
19import dbus
20"""Feature test for accounts becoming valid.
21"""
22
23import os
24
25import dbus
26import dbus.service
27
28from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \
29        call_async, sync_dbus
30from mctest import exec_test, SimulatedConnection, create_fakecm_account, MC
31import constants as cs
32
33cm_name_ref = dbus.service.BusName(
34        cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus())
35
36account1_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint'
37account2_id = 'fakecm/fakeprotocol/jc_2edenton_40example_2ecom'
38
39def preseed(q, bus, fake_accounts_service):
40
41    accounts_dir = os.environ['MC_ACCOUNT_DIR']
42
43    try:
44        os.mkdir(accounts_dir, 0o700)
45    except OSError:
46        pass
47
48    fake_accounts_service.update_attributes(account1_id, changed={
49        'manager': 'fakecm',
50        'protocol': 'fakeprotocol',
51        'DisplayName': 'Work account',
52        'NormalizedName': 'jc.denton@unatco.int',
53        'Enabled': True,
54        'ConnectAutomatically': True,
55        'AutomaticPresenceType': dbus.UInt32(2),
56        'AutomaticPresenceStatus': 'available',
57        'AutomaticPresenceMessage': 'My vision is augmented',
58        'Nickname': 'JC',
59        'AvatarMime': 'image/jpeg',
60        })
61    fake_accounts_service.update_attributes(account2_id, changed={
62        'manager': 'fakecm',
63        'protocol': 'fakeprotocol',
64        'DisplayName': 'Personal account',
65        'NormalizedName': 'jc.denton@example.com',
66        'Enabled': True,
67        'ConnectAutomatically': False,
68        'AutomaticPresenceType': dbus.UInt32(2),
69        'AutomaticPresenceStatus': 'available',
70        'AutomaticPresenceMessage': 'My vision is augmented',
71        'Nickname': 'JC',
72        'AvatarMime': 'image/jpeg',
73        })
74
75    # The passwords are missing, so the accounts can't connect yet.
76    fake_accounts_service.update_parameters(account1_id, changed={
77        'account': 'jc.denton@unatco.int',
78        })
79    fake_accounts_service.update_parameters(account2_id, changed={
80        'account': 'jc.denton@example.com',
81        })
82
83    os.makedirs(accounts_dir + '/' + account1_id)
84    avatar_bin = open(accounts_dir + '/' + account1_id + '/avatar.bin', 'w')
85    avatar_bin.write('Deus Ex')
86    avatar_bin.close()
87
88    os.makedirs(accounts_dir + '/' + account2_id)
89    avatar_bin = open(accounts_dir + '/' + account2_id + '/avatar.bin', 'w')
90    avatar_bin.write('Invisible War')
91    avatar_bin.close()
92
93    account_connections_file = open(accounts_dir + '/.mc_connections', 'w')
94    account_connections_file.write("")
95    account_connections_file.close()
96
97def test(q, bus, unused, **kwargs):
98
99    # make sure RequestConnection doesn't get called yet
100    events = [EventPattern('dbus-method-call', method='RequestConnection')]
101    q.forbid_events(events)
102
103    fake_accounts_service = kwargs['fake_accounts_service']
104    preseed(q, bus, fake_accounts_service)
105
106    # Wait for MC to load
107    mc = MC(q, bus)
108
109    # Trying to make a channel on account 1 doesn't work, because it's
110    # not valid
111
112    account_path = (cs.tp_path_prefix + '/Account/' + account1_id)
113
114    cd = bus.get_object(cs.CD, cs.CD_PATH)
115
116    user_action_time = dbus.Int64(1238582606)
117    request = dbus.Dictionary({
118            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
119            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
120            cs.CHANNEL + '.TargetID': 'juliet',
121            }, signature='sv')
122    call_async(q, cd, 'CreateChannel',
123            account_path, request, user_action_time, "",
124            dbus_interface=cs.CD)
125    ret = q.expect('dbus-return', method='CreateChannel')
126    request_path = ret.value[0]
127
128    cr = bus.get_object(cs.CD, request_path)
129    request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE)
130    assert request_props['Account'] == account_path
131    assert request_props['Requests'] == [request]
132    assert request_props['UserActionTime'] == user_action_time
133    assert request_props['PreferredHandler'] == ""
134    assert request_props['Interfaces'] == []
135
136    sync_dbus(bus, q, mc)
137
138    cr.Proceed(dbus_interface=cs.CR)
139
140    # FIXME: error isn't specified (NotAvailable perhaps?)
141    q.expect('dbus-signal', path=cr.object_path,
142            interface=cs.CR, signal='Failed')
143
144    # Make account 1 valid: it should connect automatically
145
146    account_path = (cs.tp_path_prefix + '/Account/' + account1_id)
147    account = bus.get_object(cs.MC, account_path)
148
149    sync_dbus(bus, q, mc)
150    q.unforbid_events(events)
151
152    call_async(q, account, 'UpdateParameters', {'password': 'nanotech'}, [],
153            dbus_interface=cs.ACCOUNT)
154
155    expected_params = {'password': 'nanotech',
156            'account': 'jc.denton@unatco.int'}
157
158    e = q.expect('dbus-method-call', method='RequestConnection',
159            args=['fakeprotocol', expected_params],
160            destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
161            path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
162            interface=cs.tp_name_prefix + '.ConnectionManager',
163            handled=False)
164
165    conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 'account1',
166            'myself', has_presence=True)
167
168    q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so')
169
170    q.expect('dbus-method-call', method='Connect',
171            path=conn.object_path, handled=True, interface=cs.CONN)
172    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE)
173
174    set_presence, e = q.expect_many(
175            EventPattern('dbus-method-call', path=conn.object_path,
176                interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence',
177                handled=True),
178            EventPattern('dbus-signal', signal='AccountPropertyChanged',
179                path=account_path, interface=cs.ACCOUNT,
180                predicate=lambda e: 'CurrentPresence' in e.args[0]
181                    and e.args[0]['CurrentPresence'][2] != ''),
182            )
183
184    assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_AVAILABLE,
185            'available', 'My vision is augmented')
186
187    # Request an online presence on account 2, then make it valid
188
189    q.forbid_events(events)
190
191    account_path = (cs.tp_path_prefix + '/Account/' + account2_id)
192    account = bus.get_object(cs.MC, account_path)
193
194    requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY),
195        'busy', 'Talking to Illuminati'))
196    account.Set(cs.ACCOUNT, 'RequestedPresence',
197            dbus.Struct(requested_presence, variant_level=1),
198            dbus_interface=cs.PROPERTIES_IFACE)
199
200    sync_dbus(bus, q, mc)
201    q.unforbid_events(events)
202
203    # Make the account valid
204    call_async(q, account, 'UpdateParameters', {'password': 'nanotech'}, [],
205            dbus_interface=cs.ACCOUNT)
206
207    expected_params = {'password': 'nanotech',
208            'account': 'jc.denton@example.com'}
209
210    e = q.expect('dbus-method-call', method='RequestConnection',
211            args=['fakeprotocol', expected_params],
212            destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
213            path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
214            interface=cs.tp_name_prefix + '.ConnectionManager',
215            handled=False)
216
217    conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 'account2',
218            'myself', has_presence=True)
219
220    q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so')
221
222    q.expect('dbus-method-call', method='Connect',
223            path=conn.object_path, handled=True, interface=cs.CONN)
224    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE)
225
226    set_presence = q.expect('dbus-method-call', path=conn.object_path,
227            interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence',
228            handled=True)
229
230    e = q.expect('dbus-signal', signal='AccountPropertyChanged',
231            path=account_path, interface=cs.ACCOUNT,
232            predicate=lambda e: 'CurrentPresence' in e.args[0]
233                and e.args[0]['CurrentPresence'][1] == 'busy')
234
235    assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_BUSY,
236            'busy', 'Talking to Illuminati')
237
238if __name__ == '__main__':
239    exec_test(test, {}, preload_mc=False, use_fake_accounts_service=True,
240            pass_kwargs=True)
241