1#!/usr/bin/python
2# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import dbus, logging
7from autotest_lib.client.common_lib import error
8
9sample = {
10    'pdu' :
11      '07914140540510F0040B916171056429F500001190804181106904D4F29C0E',
12    'parsed' :
13      {'text' : 'Test',
14       'number' : '+16175046925',
15       'timestamp' : '110908141801-04',
16       'smsc' : '+14044550010'
17       }
18    }
19
20sample_multipart = {
21    'pdu' :
22      ['07912160130320F8440B916171056429F5000011909161037469A0050003920201A9'
23       'E5391DF43683E6EF7619C47EBBCF207A194F0789EB74D03D4D47BFEB7450D89D0791'
24       'D366737A5C67D3416374581E1ED3CBF23928ED1EB3EBE43219947683E8E832A85D9E'
25       'CFC3E7B20B4445A7E72077B94C9E83E86F90B80C7ADBCB72101D5D06B1CBEE331D0D'
26       'A2A3E5E539FACD2683CC6F39888E2E83D8EF71980D9ABFCDF47B585E06D1DF',
27       '07912160130320F5440B916171056429F50000119091610384691505000392020241'
28       'E437888E2E83E670769AEE02'],
29    'parsed' :
30      {'text' : 'Test of some long text but without any difficult characters'
31       ' included in the message. This needs to be over the length threshold'
32       ' for the local software to do the split.',
33       'number' : '+16175046925',
34       'timestamp' : '110919163047-04',
35       'smsc' : '+12063130028'
36       }
37    }
38
39
40class SmsStore(object):
41    '''SMS content management - this maintains an internal model of the
42    index->PDU mapping that the fakemodem program should be returning so
43    that tests can add and remove individual PDUs and handles generating
44    the correct set of responses, including the complete SMS list.
45    '''
46
47    def __init__(self, fakemodem):
48        self.fakemodem = fakemodem
49        self.smsdict = {}
50        self.fakemodem.SetResponse('\+CMGR=', '', '+CMS ERROR: 321')
51        self.fakemodem.SetResponse('\+CMGD=', '', '+CMS ERROR: 321')
52        self._sms_regen_list()
53
54    def sms_insert(self, index, pdu):
55        '''Add a SMS to the fake modem's list.'''
56        smsc_len = int(pdu[0:1], 16)
57        mlen = len(pdu)/2 - smsc_len - 1
58
59        self.fakemodem.RemoveResponse('\+CMGD=')
60        self.fakemodem.RemoveResponse('\+CMGR=')
61        self.fakemodem.SetResponse('\+CMGD=%d' % (index), '', '')
62        self.fakemodem.SetResponse('\+CMGR=%d' % (index),
63                                   '+CMGR: 1,,%d\r\n%s' % (mlen, pdu), '')
64        self.fakemodem.SetResponse('\+CMGR=', '', '+CMS ERROR: 321')
65        self.fakemodem.SetResponse('\+CMGD=', '', '+CMS ERROR: 321')
66
67        self.smsdict[index] = pdu
68        self._sms_regen_list()
69
70    def sms_receive(self, index, pdu):
71        '''Add a SMS to the fake modem's list, like sms_insert(), and generate
72        an unsolicited new-sms message.'''
73        self.sms_insert(index, pdu)
74        self.fakemodem.SendUnsolicited('+CMTI: "ME",%d'%(index))
75
76    def sms_remove(self, index):
77        '''Remove a SMS from the fake modem's list'''
78        self.fakemodem.RemoveResponse('\+CMGR=%d' % (index))
79        self.fakemodem.RemoveResponse('\+CMGD=%d' % (index))
80        del self.smsdict[index]
81        self._sms_regen_list()
82
83    def _sms_regen_list(self):
84        response = ''
85        keys = self.smsdict.keys()
86        keys.sort()
87        for i in keys:
88            pdu = self.smsdict[i]
89            smsc_len = int(pdu[0:1],16)
90            mlen = len(pdu)/2 - smsc_len - 1
91            response = response + '+CMGL: %d,1,,%d\r\n%s\r\n' % (i, mlen, pdu)
92        self.fakemodem.SetResponse('\+CMGL=4', response, '')
93
94
95class SmsTest(object):
96    def __init__(self, gsmsms):
97        self.gsmsms = gsmsms
98
99    def compare(self, expected, got):
100        '''Compare two SMS dictionaries, discounting the index number if
101        not specified in the first.'''
102        if expected == got:
103            return True
104        if 'index' in expected:
105            return False
106        if 'index' not in got:
107            return False
108        got = dict(got)
109        del got['index']
110        return expected == got
111
112    def compare_list(self, expected_list, got_list):
113        if len(expected_list) != len(got_list):
114            return False
115        # There must be a more Pythonic way to do this
116        for (expected,got) in zip(expected_list, got_list):
117            if self.compare(expected, got) == False:
118                return False
119        return True
120
121    def test_get(self, index, expected):
122        try:
123            sms = self.gsmsms.Get(index)
124        except dbus.DBusException, db:
125            if expected is not None:
126                raise
127            return
128
129        if expected is None:
130            logging.info('Got %s' % sms)
131            raise error.TestFail('SMS.Get(%d) succeeded unexpectedly' %
132                                 index)
133        if self.compare(expected, sms) == False:
134            logging.info('Got %s, expected %s' % (sms, expected))
135            raise error.TestFail('SMS.Get(%d) did not match expected values' %
136                                 index)
137
138    def test_delete(self, index, expected_success):
139        try:
140            self.gsmsms.Delete(index)
141            if expected_success == False:
142                raise error.TestFail('SMS.Delete(%d) succeeded unexpectedly' %
143                                     index)
144        except dbus.DBusException, db:
145            if expected_success:
146                raise
147
148    def test_list(self, expected_list):
149        sms_list = self.gsmsms.List()
150        if self.compare_list(expected_list, sms_list) == False:
151            logging.info('Got %s, expected %s' % (sms_list, expected_list))
152            raise error.TestFail('SMS.List() did not match expected values')
153
154    def test_has_none(self):
155        '''Test that the SMS interface has no messages.'''
156        self.test_list([])
157        self.test_get(1, None)
158        self.test_delete(1, False)
159        self.test_delete(2, False)
160
161    def test_has_one(self, parsed_sms):
162        '''Test that the SMS interface has exactly one message at index 1
163        As a side effect, deletes the message.'''
164        self.test_list([parsed_sms])
165        self.test_get(1, parsed_sms)
166        self.test_get(2, None)
167        self.test_delete(2, False)
168        self.test_delete(1, True)
169