Browse Source

0.1.1 pilna versija

Ivars 7 years ago
parent
commit
3b2deaac9b
81 changed files with 10434 additions and 0 deletions
  1. 184
    0
      Contents/Code/DumbTools.py
  2. 257
    0
      Contents/Code/__init__.py
  3. 24
    0
      Contents/DefaultPrefs.json
  4. 10
    0
      Contents/Info.plist
  5. BIN
      Contents/Resources/art-default.jpg
  6. BIN
      Contents/Resources/back.png
  7. BIN
      Contents/Resources/cinemalive.png
  8. BIN
      Contents/Resources/euronews.png
  9. BIN
      Contents/Resources/filmas.png
  10. BIN
      Contents/Resources/filmix.png
  11. BIN
      Contents/Resources/filmon.png
  12. BIN
      Contents/Resources/folder.png
  13. BIN
      Contents/Resources/folder2.png
  14. BIN
      Contents/Resources/folder_search.png
  15. BIN
      Contents/Resources/icon-default.png
  16. BIN
      Contents/Resources/iplayer.png
  17. BIN
      Contents/Resources/latvia1.png
  18. BIN
      Contents/Resources/latvia7.png
  19. BIN
      Contents/Resources/lmt.jpg
  20. BIN
      Contents/Resources/lmt.png
  21. BIN
      Contents/Resources/lr_1_lv.png
  22. BIN
      Contents/Resources/lr_2_lv.png
  23. BIN
      Contents/Resources/lr_3_lv.png
  24. BIN
      Contents/Resources/movieplace.png
  25. BIN
      Contents/Resources/my_archive.png
  26. BIN
      Contents/Resources/my_kids.png
  27. BIN
      Contents/Resources/my_radio.png
  28. BIN
      Contents/Resources/my_tv.png
  29. BIN
      Contents/Resources/my_video.png
  30. BIN
      Contents/Resources/prefs.png
  31. BIN
      Contents/Resources/replay.png
  32. BIN
      Contents/Resources/riga24.png
  33. BIN
      Contents/Resources/shortcut.png
  34. BIN
      Contents/Resources/skaties.png
  35. BIN
      Contents/Resources/tvdom.png
  36. BIN
      Contents/Resources/tvplay.png
  37. BIN
      Contents/Resources/ustvnow.png
  38. BIN
      Contents/Resources/viaplay.jpg
  39. BIN
      Contents/Resources/viaplay.png
  40. BIN
      Contents/Resources/video.png
  41. 1
    0
      Contents/VERSION
  42. 1
    0
      plex_test_case/.gitignore
  43. 3
    0
      plex_test_case/.gitmodules
  44. 22
    0
      plex_test_case/LICENSE.md
  45. 154
    0
      plex_test_case/README.md
  46. 171
    0
      plex_test_case/__init__.py
  47. 108
    0
      plex_test_case/bootstrap.py
  48. 219
    0
      plex_test_case/bootstrap0.py
  49. 113
    0
      plex_test_case/config.py
  50. 6
    0
      plex_test_case/mock.py
  51. 20
    0
      plex_test_case/mock/.gitignore
  52. 4
    0
      plex_test_case/mock/.testr.conf
  53. 25
    0
      plex_test_case/mock/.travis.yml
  54. 26
    0
      plex_test_case/mock/LICENSE.txt
  55. 79
    0
      plex_test_case/mock/NEWS
  56. 29
    0
      plex_test_case/mock/README.rst
  57. 1
    0
      plex_test_case/mock/docs/changelog.txt
  58. 210
    0
      plex_test_case/mock/docs/conf.py
  59. 189
    0
      plex_test_case/mock/docs/index.txt
  60. 1
    0
      plex_test_case/mock/extendmock.py
  61. 26
    0
      plex_test_case/mock/mock.wpr
  62. 4
    0
      plex_test_case/mock/mock/__init__.py
  63. 2556
    0
      plex_test_case/mock/mock/mock.py
  64. 3
    0
      plex_test_case/mock/mock/tests/__init__.py
  65. 18
    0
      plex_test_case/mock/mock/tests/__main__.py
  66. 36
    0
      plex_test_case/mock/mock/tests/support.py
  67. 158
    0
      plex_test_case/mock/mock/tests/testcallable.py
  68. 975
    0
      plex_test_case/mock/mock/tests/testhelpers.py
  69. 533
    0
      plex_test_case/mock/mock/tests/testmagicmethods.py
  70. 1593
    0
      plex_test_case/mock/mock/tests/testmock.py
  71. 1883
    0
      plex_test_case/mock/mock/tests/testpatch.py
  72. 33
    0
      plex_test_case/mock/mock/tests/testsentinel.py
  73. 306
    0
      plex_test_case/mock/mock/tests/testwith.py
  74. 6
    0
      plex_test_case/mock/requirements.txt
  75. 45
    0
      plex_test_case/mock/setup.cfg
  76. 6
    0
      plex_test_case/mock/setup.py
  77. 38
    0
      plex_test_case/mock/tools/applypatch-transform
  78. 37
    0
      plex_test_case/mock/tools/pre-applypatch
  79. 40
    0
      plex_test_case/mock/tox.ini
  80. 95
    0
      plex_test_case/mock/unittest.cfg
  81. 186
    0
      plex_test_case/plex.py

+ 184
- 0
Contents/Code/DumbTools.py View File

@@ -0,0 +1,184 @@
1
+# DumbTools for Plex v1.1 by Cory <babylonstudio@gmail.com>
2
+import urllib2
3
+
4
+
5
+class DumbKeyboard:
6
+    clients = ['Plex for iOS', 'Plex Media Player', 'Plex Web']
7
+    KEYS = list('abcdefghijklmnopqrstuvwxyz1234567890-=;[]\\\',./')
8
+    SHIFT_KEYS = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+:{}|\"<>?')
9
+
10
+    def __init__(self, prefix, oc, callback, dktitle=None, dkthumb=None,
11
+                 dkplaceholder=None, dksecure=False, **kwargs):
12
+        cb_hash = hash(str(callback)+str(kwargs))
13
+        Route.Connect(prefix+'/dumbkeyboard/%s'%cb_hash, self.Keyboard)
14
+        Route.Connect(prefix+'/dumbkeyboard/%s/submit'%cb_hash, self.Submit)
15
+        Route.Connect(prefix+'/dumbkeyboard/%s/history'%cb_hash, self.History)
16
+        Route.Connect(prefix+'/dumbkeyboard/%s/history/clear'%cb_hash, self.ClearHistory)
17
+        Route.Connect(prefix+'/dumbkeyboard/%s/history/add/{query}'%cb_hash, self.AddHistory)
18
+        # Add our directory item
19
+        oc.add(DirectoryObject(key=Callback(self.Keyboard, query=dkplaceholder),
20
+                               title=str(dktitle) if dktitle else \
21
+                                     u'%s'%L('DumbKeyboard Search'),
22
+                               thumb=dkthumb))
23
+        # establish our dict entry
24
+        if 'DumbKeyboard-History' not in Dict:
25
+            Dict['DumbKeyboard-History'] = []
26
+            Dict.Save()
27
+        self.Callback = callback
28
+        self.callback_args = kwargs
29
+        self.secure = dksecure
30
+
31
+    def Keyboard(self, query=None, shift=False):
32
+        if self.secure and query is not None:
33
+            string = ''.join(['*' for i in range(len(query[:-1]))]) + query[-1]
34
+        else:
35
+            string = query if query else ""
36
+
37
+        oc = ObjectContainer()
38
+        # Submit
39
+        oc.add(DirectoryObject(key=Callback(self.Submit, query=query),
40
+                               title=u'%s: %s'%(L('Submit'), string.replace(' ', '_'))))
41
+        # Search History
42
+        if Dict['DumbKeyboard-History']:
43
+            oc.add(DirectoryObject(key=Callback(self.History),
44
+                                   title=u'%s'%L('Search History')))
45
+        # Space
46
+        oc.add(DirectoryObject(key=Callback(self.Keyboard,
47
+                                            query=query+" " if query else " "),
48
+                               title='Space'))
49
+        # Backspace (not really needed since you can just hit back)
50
+        if query is not None:
51
+            oc.add(DirectoryObject(key=Callback(self.Keyboard, query=query[:-1]),
52
+                                   title='Backspace'))
53
+        # Shift
54
+        oc.add(DirectoryObject(key=Callback(self.Keyboard, query=query, shift=True),
55
+                               title='Shift'))
56
+        # Keys
57
+        for key in self.KEYS if not shift else self.SHIFT_KEYS:
58
+            oc.add(DirectoryObject(key=Callback(self.Keyboard,
59
+                                                query=query+key if query else key),
60
+                                   title=u'%s'%key))
61
+        return oc
62
+
63
+    def History(self):
64
+        oc = ObjectContainer()
65
+        if Dict['DumbKeyboard-History']:
66
+            oc.add(DirectoryObject(key=Callback(self.ClearHistory),
67
+                                   title=u'%s'%L('Clear History')))
68
+        for item in Dict['DumbKeyboard-History']:
69
+            oc.add(DirectoryObject(key=Callback(self.Submit, query=item),
70
+                                   title=u'%s'%item))
71
+        return oc
72
+
73
+    def ClearHistory(self):
74
+        Dict['DumbKeyboard-History'] = []
75
+        Dict.Save()
76
+        return self.History()
77
+
78
+    def AddHistory(self, query):
79
+        if query not in Dict['DumbKeyboard-History']:
80
+            Dict['DumbKeyboard-History'].append(query)
81
+            Dict.Save()
82
+
83
+    def Submit(self, query):
84
+        self.AddHistory(query)
85
+        kwargs = {'query': query}
86
+        kwargs.update(self.callback_args)
87
+        return self.Callback(**kwargs)
88
+
89
+
90
+class DumbPrefs:
91
+    clients = ['Plex for iOS', 'Plex Media Player', 'Plex Home Theater',
92
+               'OpenPHT', 'Plex for Roku']
93
+
94
+    def __init__(self, prefix, oc, title=None, thumb=None):
95
+        self.host = 'http://127.0.0.1:32400'
96
+        try:
97
+            self.CheckAuth()
98
+        except Exception as e:
99
+            Log.Error('DumbPrefs: this user cant access prefs: %s' % str(e))
100
+            return
101
+
102
+        Route.Connect(prefix+'/dumbprefs/list', self.ListPrefs)
103
+        Route.Connect(prefix+'/dumbprefs/listenum', self.ListEnum)
104
+        Route.Connect(prefix+'/dumbprefs/set', self.Set)
105
+        Route.Connect(prefix+'/dumbprefs/settext',  self.SetText)
106
+        oc.add(DirectoryObject(key=Callback(self.ListPrefs),
107
+                               title=title if title else L('Preferences'),
108
+                               thumb=thumb))
109
+        self.prefix = prefix
110
+        self.GetPrefs()
111
+
112
+    def GetHeaders(self):
113
+        headers = Request.Headers
114
+        headers['Connection'] = 'close'
115
+        return headers
116
+
117
+    def CheckAuth(self):
118
+        """ Only the main users token is accepted at /myplex/account """
119
+        headers = {'X-Plex-Token': Request.Headers.get('X-Plex-Token', '')}
120
+        req = urllib2.Request("%s/myplex/account" % self.host, headers=headers)
121
+        res = urllib2.urlopen(req)
122
+
123
+    def GetPrefs(self):
124
+        data = HTTP.Request("%s/:/plugins/%s/prefs" % (self.host, Plugin.Identifier),
125
+                            headers=self.GetHeaders())
126
+        prefs = XML.ElementFromString(data).xpath('/MediaContainer/Setting')
127
+
128
+        self.prefs = [{'id': pref.xpath("@id")[0],
129
+                       'type': pref.xpath("@type")[0],
130
+                       'label': pref.xpath("@label")[0],
131
+                       'default': pref.xpath("@default")[0],
132
+                       'secure': True if pref.xpath("@secure")[0] == "true" else False,
133
+                       'values': pref.xpath("@values")[0].split("|") \
134
+                                 if pref.xpath("@values") else None
135
+                      } for pref in prefs]
136
+
137
+    def Set(self, key, value):
138
+        HTTP.Request("%s/:/plugins/%s/prefs/set?%s=%s" % (self.host,
139
+                                                          Plugin.Identifier,
140
+                                                          key, value),
141
+                     headers=self.GetHeaders(),
142
+                     immediate=True)
143
+        return ObjectContainer()
144
+
145
+    def ListPrefs(self):
146
+        oc = ObjectContainer(no_cache=True)
147
+        for pref in self.prefs:
148
+            do = DirectoryObject()
149
+            value = Prefs[pref['id']] if not pref['secure'] else \
150
+                    ''.join(['*' for i in range(len(Prefs[pref['id']]))])
151
+            title = u'%s: %s = %s' % (L(pref['label']), pref['type'], L(value))
152
+            if pref['type'] == 'enum':
153
+                do.key = Callback(self.ListEnum, id=pref['id'])
154
+            elif pref['type'] == 'bool':
155
+                do.key = Callback(self.Set, key=pref['id'],
156
+                                  value=str(not Prefs[pref['id']]).lower())
157
+            elif pref['type'] == 'text':
158
+                if Client.Product in DumbKeyboard.clients:
159
+                    DumbKeyboard(self.prefix, oc, self.SetText,
160
+                                 id=pref['id'],
161
+                                 dktitle=title,
162
+                                 dkplaceholder=Prefs[pref['id']],
163
+                                 dksecure=pref['secure'])
164
+                else:
165
+                    oc.add(InputDirectoryObject(key=Callback(self.SetText, id=pref['id']),
166
+                                                title=title))
167
+                continue
168
+            else:
169
+                do.key = Callback(self.ListPrefs)
170
+            do.title = title
171
+            oc.add(do)
172
+        return oc
173
+
174
+    def ListEnum(self, id):
175
+        oc = ObjectContainer()
176
+        for pref in self.prefs:
177
+            if pref['id'] == id:
178
+                for i, option in enumerate(pref['values']):
179
+                    oc.add(DirectoryObject(key=Callback(self.Set, key=id, value=i),
180
+                                           title=u'%s'%option))
181
+        return oc
182
+
183
+    def SetText(self, query, id):
184
+        return self.Set(key=id, value=query)

+ 257
- 0
Contents/Code/__init__.py View File

@@ -0,0 +1,257 @@
1
+VERSION = "0.1.1"
2
+CHANNEL_NAME = "PlayStream"
3
+PREFIX   = '/video/playstream'
4
+DEV = True if Platform.MachineIdentifier == 'Unit testing' else False
5
+
6
+ART = "art-default.jpg"
7
+ICON = "icon-default.png"
8
+BACK = "back.png"
9
+FOLDER = "folder.png"
10
+VIDEO = "video.png"
11
+SEARCH = "folder_search.png"
12
+PREFS = "prefs.png"
13
+
14
+import sys, os, re
15
+import traceback, glob, inspect, urllib
16
+import content
17
+from content.ContentSources import ContentSources
18
+from content import util
19
+
20
+import Framework
21
+#from Framework.api.objectkit import *
22
+from DumbTools import DumbKeyboard, DumbPrefs
23
+
24
+sources_directory = os.path.join(os.path.dirname(inspect.getsourcefile(content)), "sources")
25
+sources = ContentSources(sources_directory)
26
+data0 = None
27
+title0 = None
28
+history = []
29
+
30
+view_modes = {
31
+    "List": 65586, "InfoList": 65592, "MediaPreview": 458803, "Showcase": 458810, "Coverflow": 65591,
32
+    "PanelStream": 131124, "WallStream": 131125, "Songs": 65593, "Seasons": 65593, "Albums": 131123,
33
+    "Episodes": 65590,"ImageStream":458809,"Pictures":131123
34
+}
35
+'''
36
+for source in sources.plugins:
37
+  if not ("options" in dir(sources.plugins[source]) and sources.plugins[source].options): continue
38
+  options = sources.plugins[source].options
39
+  if not options: continue
40
+  for option in options:
41
+    key="%s_%s"%(source,option)
42
+    if key in ("viaplay_device"): continue # exception list,
43
+    value = plugin.get_setting(key)
44
+    options[option] = value
45
+  sources.plugins[source].options_write(options)
46
+prefix = ""
47
+'''
48
+
49
+###################################################################################################
50
+def Start():
51
+
52
+    ObjectContainer.title1 = CHANNEL_NAME
53
+    ObjectContainer.art = R(ART)
54
+    Plugin.AddViewGroup("InfoList", viewMode="InfoList", mediaType="items")
55
+    Plugin.AddViewGroup("List", viewMode="List", mediaType="items")
56
+
57
+    #DirectoryObject.thumb = R(ICON)
58
+    #DirectoryObject.art = R(ART)
59
+    #EpisodeObject.thumb = R(ICON)
60
+    #EpisodeObject.art = R(ART)
61
+    #VideoClipObject.thumb = R(ICON)
62
+    #VideoClipObject.art = R(ART)
63
+
64
+    #HTTP.CacheTime = CACHE_1DAY
65
+    #HTTP.Headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
66
+
67
+def ValidatePrefs():
68
+    #preferences =  Prefs._sandbox.preferences.get()
69
+    for source in sources.plugins:
70
+        if not ("options" in dir(sources.plugins[source]) and sources.plugins[source].options): continue
71
+        options = sources.plugins[source].options
72
+        if not options: continue
73
+        for option in options:
74
+            key="%s_%s"%(source,option)
75
+            if key in ("viaplay_device"): continue # exception list,
76
+            value = Prefs[key]
77
+            options[option] = value
78
+        if not DEV:
79
+            sources.plugins[source].options_write(options)
80
+
81
+####################################################################################################
82
+@handler(PREFIX, CHANNEL_NAME, art=ART, thumb=ICON)
83
+def Main():
84
+
85
+    data = "config::home"
86
+    oc = Menu(data, "Home")
87
+    oc.view_group = "InfoList"
88
+    ValidatePrefs()
89
+    return oc
90
+
91
+####################################################################################################
92
+@route(PREFIX+'/{data}/')
93
+def Menu2(data, title):
94
+    return Menu(data, title)
95
+
96
+@route(PREFIX+'/search')
97
+def Search(query, data, title):
98
+    #data = data.replace("%2F", "/")
99
+    Log("Search data="+data)
100
+    Log("Search query="+query)
101
+    data2 = data.format(query)
102
+    return Menu(data2, title)
103
+
104
+@route(PREFIX+'/{data}')
105
+def Menu(data, title, **kwargs):
106
+         #includeBandwidths=1, checkFiles=0, includeConcerts=0, includeExtras=0, includeOnDeck=0, includePopularLeaves=1&includeChapters=1&checkFiles=1):
107
+
108
+    global data0, title0, history
109
+    #sources.plugins["config"].read_streams()
110
+    Log("[playstream] ** Menu call: %s - %s" % ( data, title))
111
+    if not data:
112
+        data = u"config::home"
113
+
114
+    # Process history
115
+    if data == "back":
116
+        data = history[-1][0].replace("/", "%2F") if history and history[-1][0] else "config::home"
117
+        title = history[-1][1] if history and history[-1][0] else "Home"
118
+    if data == data0 or (len(history) > 2 and data == history[-2]):
119
+        # fake call, skipped
120
+        Log("history skipped")
121
+    else:
122
+        if history and history[-1][0] and data == history[-1][0]:
123
+            history.pop()
124
+            Log("history poped")
125
+        else:
126
+            history.append((data0, title0))
127
+            Log("history added %s" % data0)
128
+    data0 = data
129
+    title0 = title
130
+    data = data.replace("%2F", "/")
131
+    hst = ""
132
+    for h in history: hst += "\n%s" % h[0]
133
+    #Log("[playstream] history="+hst)
134
+
135
+    ### Processig call ###
136
+    Log("[playstream] processing data=%s"%data)
137
+    try:
138
+        is_video = sources.is_video(data)
139
+    except:
140
+        Log(traceback.format_exc())
141
+        return ObjectContainer(header="Error", message=unicode(e))
142
+
143
+    ### Video handling ###
144
+    if is_video:
145
+        try:
146
+            streams = sources.get_streams(data)
147
+        except Exception,e:
148
+            Log(traceback.format_exc())
149
+            return ObjectContainer(header="Error", message=unicode(e))
150
+        if streams:
151
+            data2 = data.replace("/", "%2F")
152
+            title = streams[0]["name"] if isinstance(streams[0]["name"] , unicode) else streams[0]["name"].decode("utf8")
153
+            if not title: title = "Title"
154
+            desc = streams[0]["desc"] if isinstance(streams[0]["desc"] , unicode) else streams[0]["desc"] .decode("utf8")
155
+            img = streams[0]["img"] if isinstance(streams[0]["img"] , unicode) else streams[0]["img"] .decode("utf8")
156
+            vco =  VideoClipObject(
157
+                key=Callback(Menu, data=data2, title=title ),
158
+                rating_key=data2,
159
+                title=title,
160
+                summary=desc,
161
+                thumb=thumb_data(img, video=True)
162
+            )
163
+            for stream in streams:
164
+                vco.add(MediaObject(
165
+                    #bitraate=0,
166
+                    #container=Container.MPEGTS
167
+                    optimized_for_streaming = True,
168
+                    parts=[PartObject(key=HTTPLiveStreamURL(stream["url"]))]
169
+                ))
170
+            include_container = True
171
+            return ObjectContainer(objects = [vco]) if include_container else vco
172
+        else:
173
+            return ObjectContainer(header="Error", message="No streams found!")
174
+
175
+
176
+    ### List handling ###
177
+    oc = ObjectContainer(title2=title)
178
+    oc.view_group = "InfoList"
179
+    try:
180
+        content = sources.get_content(data)
181
+    except Exception,e:
182
+        Log(traceback.format_exc())
183
+        return ObjectContainer(header="Error", message=unicode(e))
184
+    Log( "[playstream] %s items returned"%len(content))
185
+
186
+    for item in content:
187
+        data2 = item[1]
188
+        #data2 = urllib.quote(data2, safe="")
189
+        data2 = data2.replace("/", "%2F")
190
+        title = item[0] if isinstance(item[0], unicode) else item[0].decode("utf8")
191
+        if not title: title = "Title"
192
+        img = item[2]  #if isinstance(item[2], unicode) else item[2].decode("utf8")
193
+        desc = item[3] if isinstance(item[3], unicode) else item[3].decode("utf8")
194
+
195
+        # Search item #
196
+        if "{0}" in data2:
197
+            #q = "aaa"  # TODO InputDirectoryObject
198
+            ##data = data.format(q)
199
+            if Client.Product in DumbKeyboard.clients:
200
+                DumbKeyboard(PREFIX, oc, Search,
201
+                            dktitle = title,
202
+                            dkthumb = thumb_data(SEARCH),
203
+                            title=title,
204
+                            data=data2
205
+                            )
206
+            else:
207
+                oc.add(InputDirectoryObject(
208
+                    key=Callback(Search, data=data2, title=title),
209
+                    title = title,
210
+                    thumb = thumb_data(img), #Resource.ContentsOfURLWithFallback(img, fallback=R(ICON)),
211
+                    prompt=desc,
212
+                    summary =desc
213
+                ))
214
+
215
+        # Video item #
216
+        elif sources.is_video(item[1]):
217
+            oc.add(VideoClipObject(
218
+                key=Callback(Menu, data=data2, title=title),
219
+                title = title,
220
+                thumb = thumb_data(img, video=True),  #Resource.ContentsOfURLWithFallback(img, fallback=R(ICON)),
221
+                summary = desc,
222
+                rating_key=data2
223
+            ))
224
+
225
+        # List object #
226
+        else:
227
+            oc.add(DirectoryObject(
228
+                key=Callback(Menu, data=data2, title=title),
229
+                title = title,
230
+                thumb = thumb_data(img) if not data2=="back" else R(BACK),
231
+                summary =desc
232
+            ))
233
+
234
+    if data == "config::home":
235
+        if Client.Product in DumbPrefs.clients:
236
+            DumbPrefs(PREFIX, oc,
237
+                      title = "Plugin options",
238
+                      thumb = R(PREFS))
239
+        else:
240
+            oc.add(PrefsObject(
241
+                title="Plugin options",
242
+                summary="Update plugin options",
243
+                thumb=R(PREFS),
244
+                art=R(ART)
245
+            ))
246
+        ValidatePrefs()
247
+    return oc
248
+
249
+def thumb_data(img, video=False):
250
+    default =  R(VIDEO) if video else R(FOLDER)
251
+    if img.startswith('http'):
252
+        img2 = Resource.ContentsOfURLWithFallback(img, default)
253
+    elif img in ("default", ""):
254
+        img2 = default
255
+    else:
256
+        img2 = R(img)
257
+    return img2

+ 24
- 0
Contents/DefaultPrefs.json View File

@@ -0,0 +1,24 @@
1
+[
2
+    { "id": "general_proxy", "label": "General: Launch stream proxy", "type": "bool", "default": "false" },
3
+    { "id": "general_port", "label": "General: Proxy port", "type": "number", "default": "8880" },
4
+    { "id": "general_proxy_url", "label": "General: Proxy URL", "type": "text", "default": "http://localhost:8880/" },
5
+
6
+    { "id": "ltc_user", "label": "Lattelecom.tv: User", "type": "text", "default": "change user" },
7
+    { "id": "ltc_password", "label": "Lattelecom.tv: Password", "type": "text", "default": "change password" },
8
+
9
+    { "id": "viaplay_user", "label": "Viaplay: User", "type": "text", "default": "change user" },
10
+    { "id": "viaplay_password", "label": "Viaplay: Password", "type": "text", "default": "change password" },
11
+
12
+    { "id": "tvdom_user", "label": "TVDom: User", "type": "text", "default": "change user" },
13
+    { "id": "tvdom_password", "label": "TVDom: User", "type": "text", "default": "change password" },
14
+    { "id": "tvdom_region", "label": "TVDom: Region", "type": "text", "default": "lv" },
15
+    { "id": "tvdom_lang", "label": "TVDom: Language", "type": "text", "default": "lv" },
16
+
17
+    { "id": "iplayer_user", "label": "iPLayer: User", "type": "text", "default": "change user" },
18
+    { "id": "iplayer_password", "label": "iPlayer: Password", "type": "text", "default": "change password" },
19
+
20
+    { "id": "euronews_language", "label": "Euronews: Language", "type": "text", "default": "en" },
21
+
22
+    { "id": "ustvnow_user", "label": "USTVNow: User", "type": "text", "default": "change user" },
23
+    { "id": "ustvnow_password", "label": "USTVNow: Password", "type": "text", "default": "change password" }
24
+]

+ 10
- 0
Contents/Info.plist View File

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleIdentifier</key> 		<string>com.plexapp.plugins.playstream</string>
6
+	<key>PlexClientPlatforms</key> 		<string>*</string>
7
+	<key>PlexFrameworkVersion</key>		<string>2</string>
8
+	<key>PlexPluginCodePolicy</key>		<string>Elevated</string>
9
+</dict>
10
+</plist>

BIN
Contents/Resources/art-default.jpg View File


BIN
Contents/Resources/back.png View File


BIN
Contents/Resources/cinemalive.png View File


BIN
Contents/Resources/euronews.png View File


BIN
Contents/Resources/filmas.png View File


BIN
Contents/Resources/filmix.png View File


BIN
Contents/Resources/filmon.png View File


BIN
Contents/Resources/folder.png View File


BIN
Contents/Resources/folder2.png View File


BIN
Contents/Resources/folder_search.png View File


BIN
Contents/Resources/icon-default.png View File


BIN
Contents/Resources/iplayer.png View File


BIN
Contents/Resources/latvia1.png View File


BIN
Contents/Resources/latvia7.png View File


BIN
Contents/Resources/lmt.jpg View File


BIN
Contents/Resources/lmt.png View File


BIN
Contents/Resources/lr_1_lv.png View File


BIN
Contents/Resources/lr_2_lv.png View File


BIN
Contents/Resources/lr_3_lv.png View File


BIN
Contents/Resources/movieplace.png View File


BIN
Contents/Resources/my_archive.png View File


BIN
Contents/Resources/my_kids.png View File


BIN
Contents/Resources/my_radio.png View File


BIN
Contents/Resources/my_tv.png View File


BIN
Contents/Resources/my_video.png View File


BIN
Contents/Resources/prefs.png View File


BIN
Contents/Resources/replay.png View File


BIN
Contents/Resources/riga24.png View File


BIN
Contents/Resources/shortcut.png View File


BIN
Contents/Resources/skaties.png View File


BIN
Contents/Resources/tvdom.png View File


BIN
Contents/Resources/tvplay.png View File


BIN
Contents/Resources/ustvnow.png View File


BIN
Contents/Resources/viaplay.jpg View File


BIN
Contents/Resources/viaplay.png View File


BIN
Contents/Resources/video.png View File


+ 1
- 0
Contents/VERSION View File

@@ -0,0 +1 @@
1
+0.1.1

+ 1
- 0
plex_test_case/.gitignore View File

@@ -0,0 +1 @@
1
+*.pyc

+ 3
- 0
plex_test_case/.gitmodules View File

@@ -0,0 +1,3 @@
1
+[submodule "mock"]
2
+	path = mock
3
+	url = https://github.com/testing-cabal/mock.git

+ 22
- 0
plex_test_case/LICENSE.md View File

@@ -0,0 +1,22 @@
1
+The MIT License
2
+===============
3
+
4
+Copyright (c) 2016 by Korney Czukowski <carbofos@seznam.cz>
5
+
6
+Permission is hereby granted, free of charge, to any person obtaining a copy
7
+of this software and associated documentation files (the "Software"), to deal
8
+in the Software without restriction, including without limitation the rights
9
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+copies of the Software, and to permit persons to whom the Software is
11
+furnished to do so, subject to the following conditions:
12
+
13
+The above copyright notice and this permission notice shall be included in
14
+all copies or substantial portions of the Software.
15
+
16
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+THE SOFTWARE.

+ 154
- 0
plex_test_case/README.md View File

@@ -0,0 +1,154 @@
1
+Unit Tests for Plex Framework
2
+=============================
3
+
4
+This package is intended for [Plex][plex] channel developers and aims for recreating the Plex Framework environment
5
+for [unit testing][unittest] the channel code. The framework runs channel code in a restricted sandbox and subjects
6
+it to various restrictions making it hard to test the code independently. This package provides means to test the
7
+channel code within the original sandbox as if it was run by the Plex Media Server.
8
+
9
+Add to your channel
10
+-------------------
11
+
12
+The suggested place for the tests is a `Tests` subdirectory of the `Contents` directory, but it's not required.
13
+The most simple way to add this module to your channel is to install it as a git submodule:
14
+
15
+ 1. Create and navigate to the `Contents/Tests` directory of your channel.
16
+ 2. Add a submodule to your repo:
17
+    
18
+    `git submodule add https://bitbucket.org/czukowski/plex-test-case.git plex_test_case`
19
+    
20
+ 3. Initialize the submodule to have it download necessary code:
21
+    
22
+    `git submodule update --init --recursive`
23
+
24
+Running tests
25
+-------------
26
+
27
+We'll use Python bundled with Plex Media Server to run the tests. To have it set up correctly, quite a lot of paths
28
+need to be set up in `PYTHONPATH`. It helps to use an IDE that can help configure it more easily and also provide a
29
+test runner. The following setup is described for the [PyCharm IDE][PyCharm] (though know that PyCharm introduces a
30
+couple of its own obstacles to running tests).
31
+
32
+ 1. On Windows, the Python interpreter is a file named `PlexScriptHost.exe` located in the Plex folder in Program Files.
33
+    For some reason PyCharm doesn't want to take a file with such name as a Python interpreter, so making its copy named
34
+    `python.exe` may be necessary. It [might be fixed][PY-11992] somewhere in the future.
35
+    
36
+    For Plex on other operating systems things may be different.
37
+    
38
+    The interpreter is set in [Settings / Project Interpreter][PyCharm Interpreter] dialog.
39
+    
40
+ 2. Unit test runner that comes with PyCharm is not very well compatible with the Python version Plex Media Server
41
+    comes with. In PyCharm version 2017, a test runner tries to use a `discover` module which can not be found in
42
+    Plex distribution (see [PY-24057]). The workaround for now is, sadly, editing a test runner helper file named
43
+    [`_jb_unittest_runner.py`][_jb_unittest_runner.py], where you can add `import discover` module and skip the
44
+    highlighted lines on `ImportError`. In PyCharm versions before 2017, the test runner, [`utrunner.py`][utrunner.py]
45
+    [does not load modules from a ZIP file][PY-12072]. To fix it, [`pycharm_run_utils.py`][pycharm_run_utils.py]
46
+    needs to be patched as described in [PY-12072]. This is not good as the IDE updates will overwrite those files
47
+    and you'll need to do it again.
48
+    
49
+ 3. Python libraries and extensions must be included in `PYTHONPATH`. The easy way to do it is to add them as content
50
+    roots in [Settings / Project Structure][PyCharm Content Roots] dialog. The following files and directories are
51
+    needed (example again from Windows, other OS users should figure that on their own, sorry):
52
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\DLLs`
53
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\Exts`
54
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\python27.zip`
55
+    
56
+ 4. Plex Framework specific code also need to be included in `PYTHONPATH`. This is done the same way as above, but first
57
+    the actual base path has to be determined. It is located in a subfolder named `Plug-ins-???????` under the path
58
+    `%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\` (question marks stand for a string that resembles a git
59
+    commit id) and may be different depending on the Plex Media Server version. Also it may change on PMS update and
60
+    the old folder deleted. A good idea may be to create a symbolic link named just `Plug-ins` and re-link it with each
61
+    server update instead of re-configuring a number of paths in all projects. The following directories need to be added
62
+    as content roots:
63
+    
64
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Platforms\Shared\Libraries`
65
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Versions\2\Python`
66
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Versions\1\Python`
67
+     * `%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Versions\0\Python`
68
+    
69
+    Note the different framework versions in the list above, their load order is actually important, but PyCharm doesn't
70
+    seem to offer a way to add the Content Roots in a specific order. A workaround for that is implemented in `bootstrap.py`
71
+    to change the load order if needed.
72
+    
73
+ 5. A [run configuration][PyCharm Run Tests] for unit tests needs to be added for the project. Make sure 'Add content roots
74
+    to PYTHONPATH' is checked. There is also an option to set environment variables that could be used to add the folders
75
+    above to `PYTHONPATH`, but adding them as content roots has a benefit of offering some insight into the actual
76
+    Framework code during debugging as well as a (very) limited code completion.
77
+
78
+It should be possible to run the tests without any IDE and its test runners. The only thing necessary would be to configure
79
+`PYTHONPATH` to include all the paths mentioned above, perhaps by creating a batch file.
80
+
81
+Creating test cases
82
+-------------------
83
+
84
+Test case classes must extend the base test class, `PlexTestCase`. It provides the functionality to initialize the framework
85
+and import the tested module in its sandbox. See `__init__.py` file for the implementation details. Many of it may be still
86
+work in progress though.
87
+
88
+To test the module, create a test case module with the same name and a `_test` suffix, then the tested module may be
89
+accessed using `self.module` property. If you choose another naming convention, you'll need to set `module_name` attribute
90
+in the test case class beforehand. Note that the tested module must be imported from `Code/__init__.py` either directly
91
+or indirectly in order for it to be added to the sandboxed environment.
92
+
93
+It is possible to test request handling by the channel controllers, using `self.request()` method.
94
+
95
+HTTP requests made during testing will not send for real. The intention is to be able to mock these requests and return
96
+predefined responses. The desired response body and headers may be preset using `self.networking.http_response_body` and
97
+`self.networking.http_response_headers` in the test case class respectively.
98
+
99
+There is a shortcut method to load file contents, accessible via `self.get_file_contents()`. It takes a file name that
100
+is placed under the current module's subdirectory named after the current test case class.
101
+
102
+Example test case module:
103
+
104
+```python
105
+from plex_test_case import PlexTestCase
106
+from Framework.api.objectkit import ObjectContainer
107
+
108
+class MyChannelTest(PlexTestCase):
109
+
110
+    def test_main_menu(self):
111
+        # Load './MyChannelTest/MainMenuObjectContainer.xml' as expected XML generated by channel.
112
+        expected = self.get_file_contents('MainMenuObjectContainer.xml')
113
+        # Fix HTTP response from the './MyChannelTest/Index.html' file contents.
114
+        self.networking.http_response_body = self.get_file_contents('Index.html')
115
+        # Make a request to channel code (which makes a HTTP request mocked above).
116
+        status, headers, body = self.request('/video/mychannel')
117
+        # Check the return values against expected.
118
+        self.assertEquals(200, status)
119
+        self.assertEquals('application/xml', headers['Content-Type'])
120
+        self.assertEquals(expected, body)
121
+
122
+class ParserTest(PlexTestCase):
123
+
124
+    module_name = 'parser'
125
+
126
+    def test_parse(self):
127
+        # Load './ParserTest/Homepage.html' as input for parser.
128
+        contents = self.get_file_contents('Homepage.html')
129
+        # This is what the channel code would have called as 'HTML.ElementFromString(contents)'.
130
+        # Shared Code modules (*.pys) run in a separate sandbox and could be accessed as `self.shared_code_environment`.
131
+        nav = self.environment['HTML'].ElementFromString(contents)
132
+        # Invoke `parse_categories()` function in 'parser.py' module.
133
+        actual = self.module.parse_categories(nav)
134
+        # Check actual return value against expected data structure, etc...
135
+        ...
136
+```
137
+
138
+License
139
+-------
140
+
141
+This code is distributed under the MIT License.
142
+
143
+ [plex]: https://plex.tv/
144
+ [PY-11992]: https://youtrack.jetbrains.com/issue/PY-11992#comment=27-1259532
145
+ [PY-12072]: https://youtrack.jetbrains.com/issue/PY-12072#comment=27-1255005
146
+ [PY-24057]: https://youtrack.jetbrains.com/issue/PY-24057
147
+ [PyCharm]: https://www.jetbrains.com/pycharm/
148
+ [PyCharm Interpreter]: https://www.jetbrains.com/help/pycharm/configuring-python-interpreter-for-a-project.html
149
+ [PyCharm Content Roots]: https://www.jetbrains.com/pycharm/help/configuring-content-roots.html
150
+ [PyCharm Run Tests]: https://www.jetbrains.com/help/pycharm/creating-run-debug-configuration-for-tests.html
151
+ [pycharm_run_utils.py]: https://github.com/JetBrains/intellij-community/blob/a7ac25ffa1298dd8d53f807889662763e4791a4c/python/helpers/pycharm/pycharm_run_utils.py
152
+ [unittest]: https://docs.python.org/2/library/unittest.html
153
+ [utrunner.py]: https://github.com/JetBrains/intellij-community/blob/a7ac25ffa1298dd8d53f807889662763e4791a4c/python/helpers/pycharm/utrunner.py
154
+ [_jb_unittest_runner.py]: https://github.com/JetBrains/intellij-community/blob/b7d10af1bb03866f733d0712dcd9b31eee719f04/python/helpers/pycharm/_jb_unittest_runner.py#L20-L28

+ 171
- 0
plex_test_case/__init__.py View File

@@ -0,0 +1,171 @@
1
+import inspect
2
+import os
3
+
4
+from unittest import TestCase
5
+from abc import ABCMeta
6
+
7
+import bootstrap
8
+from mock import mock
9
+from plex import BasicRequest, FrameworkCore, PreferenceManager, Runtime
10
+
11
+
12
+class PlexTestCase(TestCase):
13
+
14
+    __metaclass__ = ABCMeta
15
+
16
+    module_name = None
17
+
18
+    mock = mock
19
+
20
+    def __init__(self, methodName='runTest'):
21
+        TestCase.__init__(self, methodName)
22
+        # Get bundle and channel info for framework initialization.
23
+        self.bundle_directory = bootstrap.bundle_directory
24
+        self.config = bootstrap.config
25
+        self.framework_directory = bootstrap.framework_directory
26
+        # Autodetect module name if not defined explicitly.
27
+        if self.module_name is None:
28
+            self.module_name = self.__module__
29
+            if self.module_name[-5:] == '_test':
30
+                self.module_name = self.module_name[:-5]
31
+
32
+    def tearDown(self):
33
+        # Call teardown methods automatically if not called from the child classes.
34
+        if hasattr(self, '_core'):
35
+            self.teardown_framework()
36
+
37
+    def setup_framework(self):
38
+        # Setup framework core object, either call this from `setUp` method or it'll be called when first accessing
39
+        # the object's `core` property.
40
+        self._initialize_framework(self.bundle_directory, self.framework_directory, self.config)
41
+
42
+    def _initialize_framework(self, bundle_path, framework_dir, config):
43
+        # Check if the framework has already been initialized.
44
+        if hasattr(self, '_core'):
45
+            raise RuntimeError('Plex Framework already initialized.')
46
+        # Create Framework core object instance.
47
+        self._core = FrameworkCore(bundle_path, framework_dir, config)
48
+        # Force wait for shared code sandbox loaded event.
49
+        self._core.services.shared_code_sandbox
50
+        # Initialize framework code and start plugin (same as in framework's bootstrap method `run`).
51
+        if not self._core.load_code():
52
+            raise RuntimeError('Error loading bundle code.')
53
+        self._core.start()
54
+        default_prefs = {k: v.default_value for k, v in self._core.sandbox.preferences.get()._prefs.items()}
55
+        self._core.sandbox.preferences = PreferenceManager(default_prefs)
56
+
57
+    def teardown_framework(self):
58
+        # Tear down framework core object, call this from `tearDown` method.
59
+        if hasattr(self, '_core'):
60
+            self._core.runtime.taskpool.shutdown()
61
+            self._core.runtime.check_threads()
62
+            del self._core
63
+
64
+    @property
65
+    def core(self):
66
+        # Initialize framework automatically if not yet done.
67
+        if not hasattr(self, '_core'):
68
+            self.setup_framework()
69
+        return self._core
70
+
71
+    @property
72
+    def environment(self):
73
+        return self.core.sandbox.environment
74
+
75
+    @property
76
+    def module(self):
77
+        return self.environment[self.module_name]
78
+
79
+    @property
80
+    def networking(self):
81
+        return self.core.networking
82
+
83
+    @property
84
+    def preferences(self):
85
+        return self.core.sandbox.preferences.get()
86
+
87
+    @property
88
+    def shared_code_environment(self):
89
+        return self.core.services.shared_code_sandbox.environment
90
+
91
+    def get_file_contents(self, filename):
92
+        # Read file placed in a subdirectory named after the testcase class name.
93
+        test_path = os.path.dirname(inspect.getfile(self.__class__))
94
+        with open('%s/%s/%s' % (test_path, self.__class__.__name__, filename), 'r') as content_file:
95
+            content = content_file.read()
96
+        return content
97
+
98
+    def request(self, path, headers={}, method='GET', body=''):
99
+        # Make a request to the channel code.
100
+        request = BasicRequest(path, headers, method, body)
101
+        return self.core.runtime.handle_request(request)
102
+
103
+
104
+class PlexCall():
105
+
106
+    def __init__(self):
107
+        # Get bundle and channel info for framework initialization.
108
+        self.bundle_directory = bootstrap.bundle_directory
109
+        self.config = bootstrap.config
110
+        self.framework_directory = bootstrap.framework_directory
111
+
112
+    def tearDown(self):
113
+        # Call teardown methods automatically if not called from the child classes.
114
+        if hasattr(self, '_core'):
115
+            self.teardown_framework()
116
+
117
+    def setup_framework(self):
118
+        # Setup framework core object, either call this from `setUp` method or it'll be called when first accessing
119
+        # the object's `core` property.
120
+        self._initialize_framework(self.bundle_directory, self.framework_directory, self.config)
121
+
122
+    def _initialize_framework(self, bundle_path, framework_dir, config):
123
+        # Check if the framework has already been initialized.
124
+        if hasattr(self, '_core'):
125
+            raise RuntimeError('Plex Framework already initialized.')
126
+        # Create Framework core object instance.
127
+        self._core = FrameworkCore(bundle_path, framework_dir, config)
128
+        # Force wait for shared code sandbox loaded event.
129
+        self._core.services.shared_code_sandbox
130
+        # Initialize framework code and start plugin (same as in framework's bootstrap method `run`).
131
+        if not self._core.load_code():
132
+            raise RuntimeError('Error loading bundle code.')
133
+        self._core.start()
134
+        # TODO ielasa no xml faila prefences
135
+        default_prefs = {k: v.default_value for k, v in self._core.sandbox.preferences.get()._prefs.items()}
136
+        self._core.sandbox.preferences = PreferenceManager(default_prefs)
137
+
138
+    def teardown_framework(self):
139
+        # Tear down framework core object, call this from `tearDown` method.
140
+        if hasattr(self, '_core'):
141
+            self._core.runtime.taskpool.shutdown()
142
+            self._core.runtime.check_threads()
143
+            del self._core
144
+
145
+    @property
146
+    def core(self):
147
+        # Initialize framework automatically if not yet done.
148
+        if not hasattr(self, '_core'):
149
+            self.setup_framework()
150
+        return self._core
151
+
152
+    @property
153
+    def environment(self):
154
+        return self.core.sandbox.environment
155
+
156
+    @property
157
+    def networking(self):
158
+        return self.core.networking
159
+
160
+    @property
161
+    def preferences(self):
162
+        return self.core.sandbox.preferences.get()
163
+
164
+    @property
165
+    def shared_code_environment(self):
166
+        return self.core.services.shared_code_sandbox.environment
167
+
168
+    def request(self, path, headers={}, method='GET', body=''):
169
+        # Make a request to the channel code.
170
+        request = BasicRequest(path, headers, method, body)
171
+        return self.core.runtime.handle_request(request)

+ 108
- 0
plex_test_case/bootstrap.py View File

@@ -0,0 +1,108 @@
1
+import errno
2
+import os
3
+
4
+import config
5
+
6
+BUNDLED_PLUGINS_PATH_KEY = 'PLEXBUNDLEDPLUGINSPATH'
7
+LOCAL_APP_DATA_KEY = 'PLEXLOCALAPPDATA'
8
+
9
+framework_dir_template = os.path.join('Framework.bundle', 'Contents')
10
+plugins_path_template = os.path.join(framework_dir_template, 'Resources', 'Versions', '%i', 'Python')
11
+shared_libs_path_template = os.path.join(framework_dir_template, 'Resources', 'Platforms', 'Shared', 'Libraries')
12
+framework_versions = [2, 1, 0]
13
+plugin_bundle_path_template = ['Plex Media Server', 'Plug-ins', '.bundle', 'Contents']
14
+plugin_bundle_key = 2
15
+
16
+
17
+# Raise error if in the environment variable key is not set or its value is not a directory.
18
+def validate_directory(key):
19
+    if not os.environ[key]:
20
+        raise ValueError('Environment variable %s is empty' % key)
21
+    elif not os.path.isdir(os.environ[key]):
22
+        raise OSError(errno.ENOTDIR, os.strerror(errno.ENOENT), os.environ[key])
23
+
24
+
25
+# Determine whether the path in the argument is a framework version path and returns it, or None.
26
+def pick_framework_path(path_arg):
27
+    if not path_arg.endswith('Python'):
28
+        return None
29
+    for version in framework_versions:
30
+        path = plugins_path_template % version
31
+        if path_arg.endswith(path):
32
+            return path
33
+    return None
34
+
35
+
36
+# Find the current plugin bundle directory based on assumption that the current file is in some of its subdirectories.
37
+def find_bundle_directory(path_arg):
38
+    if not __file__.startswith(path_arg):
39
+        raise ValueError('File path (%s) expected to start with %s' % (__file__, path_arg))
40
+    bundle_parts = [path_arg.rstrip(os.sep)]
41
+    parts = __file__[len(path_arg):].split(os.sep)
42
+    for index, actual_name in enumerate(parts):
43
+        if index >= len(plugin_bundle_path_template):
44
+            break
45
+        expected_name = plugin_bundle_path_template[index]
46
+        if index != plugin_bundle_key and actual_name != expected_name:
47
+            raise ValueError('Unexpected directory name in %s, expected %s' % (__file__, actual_name))
48
+        elif index == plugin_bundle_key and not actual_name.endswith(expected_name):
49
+            raise ValueError('Unexpected directory name in %s, expected to end with %s' % (__file__, actual_name))
50
+        if index <= plugin_bundle_key:
51
+            bundle_parts.append(actual_name)
52
+    return os.path.join(*bundle_parts)
53
+
54
+
55
+# Try autodetect `PLEXLOCALAPPDATA` environment variable if not already set.
56
+if LOCAL_APP_DATA_KEY not in os.environ:
57
+    def find_plex_local_app_data_path():
58
+        if 'LOCALAPPDATA' not in os.environ:
59
+            raise KeyError('Environment variable %s is not set' % 'LOCALAPPDATA')
60
+        return os.environ['LOCALAPPDATA'] + os.sep
61
+    os.environ[LOCAL_APP_DATA_KEY] = find_plex_local_app_data_path()
62
+validate_directory(LOCAL_APP_DATA_KEY)
63
+
64
+
65
+# Try autodetect `PLEXBUNDLEDPLUGINSPATH` environment variable if not already set.
66
+if BUNDLED_PLUGINS_PATH_KEY not in os.environ:
67
+    def find_plex_bundled_plugins_path():
68
+        for sys_path in os.sys.path:
69
+            path = pick_framework_path(sys_path)
70
+            if path is not None:
71
+                bundle_path = sys_path[:-len(path)]
72
+                return bundle_path.rstrip(os.sep)
73
+    os.environ[BUNDLED_PLUGINS_PATH_KEY] = find_plex_bundled_plugins_path()
74
+validate_directory(BUNDLED_PLUGINS_PATH_KEY)
75
+
76
+
77
+# Add shared libraries path if not already added.
78
+def setup_shared_libs_path(bundled_plugins_path):
79
+    path = os.path.join(bundled_plugins_path, shared_libs_path_template)
80
+    if path not in os.sys.path:
81
+        os.sys.path.append(path)
82
+setup_shared_libs_path(os.environ[BUNDLED_PLUGINS_PATH_KEY])
83
+
84
+
85
+# Remove and re-add framework paths in `sys.path` as they may have been added in the wrong order.
86
+def setup_framework_paths(bundled_plugins_path):
87
+    def make_framework_path(version):
88
+        return os.path.join(bundled_plugins_path, plugins_path_template) % version
89
+    version_index = 0
90
+    for path_index, sys_path in enumerate(os.sys.path):
91
+        if version_index not in framework_versions:
92
+            break
93
+        path = pick_framework_path(sys_path)
94
+        if path is not None:
95
+            os.sys.path[path_index] = make_framework_path(framework_versions[version_index])
96
+            version_index += 1
97
+    for version_index in range(version_index, len(framework_versions)):
98
+        os.sys.path.append(make_framework_path(framework_versions[version_index]))
99
+setup_framework_paths(os.environ[BUNDLED_PLUGINS_PATH_KEY])
100
+
101
+# Set module variables useful for framework initialization inside test case.
102
+#bbundle_directory = find_bundle_directory(os.environ[LOCAL_APP_DATA_KEY])
103
+bundle_directory = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
104
+framework_directory = os.path.join(os.environ[BUNDLED_PLUGINS_PATH_KEY], plugins_path_template, '..')
105
+framework_directory = os.path.abspath(framework_directory) % 2
106
+
107
+# Import subsystem to install some built-ins required by framework.
108
+import subsystem

+ 219
- 0
plex_test_case/bootstrap0.py View File

@@ -0,0 +1,219 @@
1
+#!/usr/bin/python
2
+#
3
+#  Plex Extension Framework
4
+#  Copyright (C) 2008-2012 Plex, Inc. (James Clarke, Elan Feingold). All Rights Reserved.
5
+#
6
+
7
+## CONFIGURE THE PYTHON ENVIRONMENT ##
8
+
9
+import sys
10
+reload(sys)
11
+sys.setdefaultencoding('utf-8')
12
+
13
+import subsystem
14
+
15
+import os
16
+import config
17
+
18
+if sys.platform == "win32":
19
+  # This is needed to ensure binary data transfer over stdio between PMS and plugins
20
+  import msvcrt
21
+  msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
22
+  msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
23
+
24
+  # This is needed to prevent explosions when there's a system Python installed whose libraries conflict with our own
25
+  if 'PLEXLOCALAPPDATA' in os.environ:
26
+    key = 'PLEXLOCALAPPDATA'
27
+  else:
28
+    key = 'LOCALAPPDATA'
29
+  ourlocalappdata = os.path.join(os.environ[key], 'Plex Media Server')
30
+  for x in [x for x in sys.path if sys.prefix.lower() not in x.lower() and ourlocalappdata.lower() not in x.lower()]:
31
+    sys.path.remove(x)
32
+else:
33
+  import traceback
34
+  import sys
35
+  def dumpstacks(signal, frame):
36
+    code = []
37
+    for threadId, stack in sys._current_frames().items():
38
+      code.append("\n# Thread: %d" % (threadId))
39
+      for filename, lineno, name, line in traceback.extract_stack(stack):
40
+        code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
41
+        if line:
42
+          code.append("  %s" % (line.strip()))
43
+    print "\n".join(code)
44
+
45
+  import signal
46
+  signal.signal(signal.SIGUSR1, dumpstacks)
47
+
48
+PYTHON_DIR = os.path.dirname(os.path.abspath(sys.argv[0]))
49
+FRAMEWORK_DIR = os.path.abspath(os.path.join(PYTHON_DIR, '..'))
50
+
51
+# Redirect stdout to stderr
52
+sys.stdout = sys.stderr
53
+
54
+if sys.platform == "win32":
55
+  os_name = "Windows"
56
+  cpu_name = "i386"
57
+  #TODO - support Windows x64 (Win64)
58
+else:
59
+  uname = os.uname()
60
+  os_name = uname[0]
61
+  cpu_name = uname[4]
62
+
63
+mapped_cpu = config.cpu_map.get(cpu_name, cpu_name)
64
+
65
+# Special case for Linux/x64 (really should be special case for OS X...)
66
+if os_name == 'Linux' and cpu_name == 'x86_64':
67
+  mapped_cpu = 'x86_64'
68
+
69
+PLATFORM_DIR = os.path.abspath(os.path.join(FRAMEWORK_DIR, '..', '..', "Platforms", config.os_map[os_name], mapped_cpu))
70
+SHARED_DIR = os.path.abspath(os.path.join(FRAMEWORK_DIR, '..', '..', "Platforms", "Shared"))
71
+#TODO: Check for errors
72
+
73
+# If the environment variable PLEXBUNDLEDEXTS is set, this indicates a newer
74
+# server which comes bundled with its own binaries so we'll skip this step
75
+# completely.
76
+#
77
+if 'PLEXBUNDLEDEXTS' not in os.environ:
78
+  lib_path = os.path.join(PLATFORM_DIR, "Libraries")
79
+  # The binary lib path goes at the end on non-Mac platforms, because binary
80
+  # extensions should be picked up from the PMS Exts directory first.
81
+  #
82
+  if sys.platform != "darwin":
83
+    sys.path.append(lib_path)
84
+  else:
85
+    sys.path.insert(0, lib_path)
86
+
87
+# Insert the shared (Python-only) libraries.
88
+sys.path.insert(0, os.path.join(SHARED_DIR, "Libraries"))
89
+
90
+## LOAD AND CONFIGURE THE FRAMEWORK ##
91
+
92
+import Framework
93
+import Framework.constants as const
94
+from optparse import OptionParser
95
+
96
+parser = OptionParser()
97
+parser.add_option("-i", "--interface", dest="interface", default=config.default_interface)
98
+parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=True)
99
+parser.add_option("-p", "--socket-interface-port", dest="socket_interface_port", default=config.socket_interface_port)
100
+parser.add_option("-s", "--server-version", dest="server_version")
101
+parser.add_option("-d", "--daemon", dest="daemon_command")
102
+parser.add_option("-P", "--pid-file", dest="pid_file")
103
+parser.add_option("-l", "--log-file", dest="log_file")
104
+parser.add_option("-c", "--config-file", dest="config_file")
105
+(options, args) = parser.parse_args()
106
+
107
+bundle_path = args[0]
108
+
109
+del parser
110
+del OptionParser
111
+
112
+# Select the interface class to use
113
+if options.interface == const.interface.pipe:
114
+  interface_class = Framework.interfaces.PipeInterface
115
+elif options.interface == const.interface.socket:
116
+  interface_class = Framework.interfaces.SocketInterface
117
+  if int(options.socket_interface_port) != config.socket_interface_port:
118
+    config.socket_interface_port = int(options.socket_interface_port)
119
+else:
120
+  #TODO: Error info - no matching interface found
121
+  sys.stderr.write('No matching interface found.\n')
122
+  sys.exit(1)
123
+
124
+if options.server_version != None:
125
+  config.server_version = options.server_version
126
+
127
+# Configure the log_dir, if one was given
128
+if options.log_file:
129
+  config.log_file = os.path.abspath(options.log_file)
130
+
131
+# Configure the pid file, if one was given
132
+if options.pid_file:
133
+  config.pid_file = os.path.abspath(options.pid_file)
134
+
135
+# Load the config file if one was provided
136
+if options.config_file:
137
+  import simplejson
138
+  f = open(options.config_file, 'r')
139
+  json_config = simplejson.load(f)
140
+  f.close()
141
+  for key in json_config:
142
+    setattr(config, key, json_config[key])
143
+
144
+def run(daemonized=False):
145
+  # Copy the damonized attribute into config
146
+  setattr(config, 'daemonized', daemonized)
147
+
148
+  # Create a core object for the plug-in bundle
149
+  core = Framework.core.FrameworkCore(bundle_path, FRAMEWORK_DIR, config)
150
+
151
+  # Try to load the plug-in code
152
+  if not core.load_code():
153
+    sys.stderr.write('Error loading bundle code.\n')
154
+    sys.exit(2)
155
+
156
+  # Create an instance of the selected interface
157
+  interface = interface_class(core)
158
+
159
+  # Try to start the core
160
+  if not core.start():
161
+    sys.stderr.write('Error starting framework core for %s.\n' % bundle_path)
162
+    sys.exit(3)
163
+
164
+  # Start listening on the interface
165
+  interface.listen(daemonized)
166
+
167
+# If running normally, start the core in the current process
168
+if options.daemon_command == None:
169
+  run()
170
+
171
+# If issued a daemon command, check what we're supposed to do
172
+else:
173
+  import plistlib, daemon, tempfile
174
+
175
+  # Read the plist to get the identifier
176
+  plist_path = os.path.join(bundle_path, 'Contents', 'Info.plist')
177
+  ident = plistlib.readPlist(plist_path)['CFBundleIdentifier']
178
+
179
+  class PluginDaemon(daemon.Daemon):
180
+    def run(self):
181
+      run(True)
182
+
183
+  # Make sure we have a pid file for this daemon
184
+  pid_config = dict(identifier = ident, port = config.socket_interface_port)
185
+  pid_dir = os.path.join(config.root_path, config.pid_files_dir)
186
+
187
+  # Create the pid files dir if it doesn't exist
188
+  if not os.path.exists(pid_dir):
189
+    try:
190
+      os.makedirs(pid_dir)
191
+    except:
192
+      pass
193
+
194
+  if not config.pid_file:
195
+    pid_file = os.path.join(pid_dir, '%(identifier)s.%(port)d.pid' % pid_config)
196
+  else:
197
+    pid_file = config.pid_file % pid_config
198
+
199
+  # Create the daemon object
200
+  d = PluginDaemon(pid_file)
201
+
202
+  # Decide which action to perform
203
+  if options.daemon_command == 'start':
204
+    d.start()
205
+  elif options.daemon_command == 'restart':
206
+    d.restart()
207
+  elif options.daemon_command == 'stop':
208
+    # Wait up to 60 seconds for the interface to stop
209
+    port = 0
210
+    try:
211
+      port = pid_config['port']
212
+      urllib2.urlopen('http://127.0.0.1:%d/:/shutdownInterface' % port, timeout=60)
213
+    except:
214
+      print "Error shutting down interface on port %d" % port
215
+    # Kill the daemon process
216
+    d.stop()
217
+  else:
218
+    print "Unknown command '%s'" % options.daemon_command
219
+

+ 113
- 0
plex_test_case/config.py View File

@@ -0,0 +1,113 @@
1
+#
2
+#  Plex Extension Framework
3
+#  Copyright (C) 2008-2012 Plex, Inc. (James Clarke, Elan Feingold). All Rights Reserved.
4
+#
5
+
6
+
7
+"""
8
+  Defaults
9
+"""
10
+
11
+default_locale                  = 'en-us'
12
+default_network_timeout         = 20.0
13
+module_whitelist                = ['re', 'string', 'datetime', 'time']
14
+
15
+
16
+
17
+"""
18
+  Framework config
19
+"""
20
+
21
+default_interface               = 'socket'
22
+log_level                       = 'Debug'
23
+console_logging                 = False
24
+enable_system_debugging         = False
25
+
26
+root_path                       = None
27
+plugin_support_dir_name         = None
28
+bundles_dir_name                = None
29
+
30
+# These variables are only used when app_support_path is defined
31
+pid_files_dir                   = 'pid'
32
+log_files_dir                   = 'log'
33
+plugin_support_files_dir        = 'support'
34
+bundle_files_dir                = 'bundle'
35
+
36
+# These variables override the app support path + individual dir variables if set
37
+pid_file                        = None
38
+log_file                        = None
39
+
40
+log_internal_component_usage    = False
41
+show_internal_traceback_frames  = True
42
+enable_external_debugging       = True
43
+
44
+messaging_keepalive_interval    = 20
45
+messaging_timeout               = 60
46
+
47
+taskpool_maximum_threads        = 8
48
+taskpool_priority_threads       = 2
49
+
50
+threadkit_parallel_limit        = 4
51
+agentservice_update_limit       = 3
52
+
53
+socket_interface_port           = 0
54
+
55
+http_cache_max_items            = 1024
56
+http_cache_max_items_grace      = 100
57
+http_cache_max_size             = 52428800
58
+
59
+flags                           = []
60
+
61
+ab_api_key = None
62
+
63
+cf_domain = None
64
+cf_token = None
65
+cf_rooms = {
66
+  'inc'   : '211292',
67
+  'dev'   : '213005',
68
+  'err'   : '521743',
69
+  'test'  : '442698',
70
+}
71
+
72
+
73
+"""
74
+  Platform config
75
+"""
76
+
77
+system_bundle_name              = 'System'
78
+system_bundle_identifier        = 'com.plexapp.system'
79
+services_bundle_name            = 'Services'
80
+services_bundle_identifier      = 'com.plexapp.system.services'
81
+services_bundle_path            = None
82
+framework_bundle_name           = 'Framework'
83
+framework_bundle_identifier     = 'com.plexapp.framework'
84
+use_node_for_url_lookups        = False
85
+
86
+server_version                  = None
87
+daemonized                      = None
88
+
89
+os_map = {
90
+  "Darwin"                      : "MacOSX",
91
+  "Linux"                       : "Linux",
92
+  "Windows"                     : "Windows",
93
+  "FreeBSD"                     : "FreeBSD",
94
+  "SunOS"                       : "SunOS",
95
+}
96
+
97
+cpu_map = {
98
+  "i386"                        : "i386",
99
+  "i686"                        : "i386",
100
+  "x86_64"                      : "i386",
101
+  "amd64"                       : "i386",
102
+  "3548b0-smp"                  : "MIPS",
103
+  "mips"                        : "MIPS",
104
+  "mips64"                      : "mips64",
105
+  "Win32"                       : "Win32",
106
+  "armv5tel"                    : "armv5tel",
107
+  "armv61"                      : "armv5tel",
108
+  "armv5tejl"                   : "armv5tel",
109
+  "armv6l"                      : "armv5tel",
110
+  "armv7l"                      : "armv5tel",
111
+  "ppc"                         : "ppc",
112
+  "sun4v"                       : "sun4v",
113
+}

+ 6
- 0
plex_test_case/mock.py View File

@@ -0,0 +1,6 @@
1
+import imp
2
+import os
3
+
4
+mock_path = os.path.join(os.path.dirname(__file__), 'mock')
5
+f, filename, desc = imp.find_module('mock', [mock_path])
6
+mock = imp.load_module('mock', f, filename, desc)

+ 20
- 0
plex_test_case/mock/.gitignore View File

@@ -0,0 +1,20 @@
1
+.*\.pyc
2
+*.rej
3
+html/
4
+mock\.egg-info/
5
+mock\.wpu
6
+\.tox/
7
+build
8
+dist
9
+latex
10
+.*\$py\.class
11
+runtox
12
+*~
13
+*.pyc
14
+.testrepository
15
+.*.swp
16
+AUTHORS
17
+ChangeLog
18
+.eggs
19
+README.saved
20
+README.html

+ 4
- 0
plex_test_case/mock/.testr.conf View File

@@ -0,0 +1,4 @@
1
+[DEFAULT]
2
+test_command=${PYTHON:-python} -m subunit.run discover . $LISTOPT $IDOPTION
3
+test_id_option=--load-list $IDFILE
4
+test_list_option=--list

+ 25
- 0
plex_test_case/mock/.travis.yml View File

@@ -0,0 +1,25 @@
1
+sudo: false
2
+language: python
3
+python:
4
+  - "2.6"
5
+  - "2.7"
6
+  - "3.3"
7
+  - "3.4"
8
+  - pypy
9
+  - pypy3
10
+matrix:
11
+  include:
12
+# Travis nightly look to be 3.5.0a4, b3 is out and the syntax error we see
13
+# doesn't happen in trunk.
14
+    - python: "nightly"
15
+      env: SKIP_DOCS=1
16
+install:
17
+ - pip install -U pip
18
+ - pip install -U wheel setuptools
19
+ - pip install -U .[docs,test]
20
+ - pip list
21
+ - python --version
22
+script:
23
+ - unit2
24
+ - if [ -z "$SKIP_DOCS" ]; then python setup.py build_sphinx; fi
25
+ - rst2html.py --strict README.rst README.html

+ 26
- 0
plex_test_case/mock/LICENSE.txt View File

@@ -0,0 +1,26 @@
1
+Copyright (c) 2003-2013, Michael Foord & the mock team
2
+All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are
6
+met:
7
+
8
+    * Redistributions of source code must retain the above copyright
9
+      notice, this list of conditions and the following disclaimer.
10
+
11
+    * Redistributions in binary form must reproduce the above
12
+      copyright notice, this list of conditions and the following
13
+      disclaimer in the documentation and/or other materials provided
14
+      with the distribution.
15
+
16
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 79
- 0
plex_test_case/mock/NEWS View File

@@ -0,0 +1,79 @@
1
+Library
2
+-------
3
+
4
+- Issue #26323: Add Mock.assert_called() and Mock.assert_called_once()
5
+  methods to unittest.mock. Patch written by Amit Saha.
6
+
7
+- Issue #22138: Fix mock.patch behavior when patching descriptors. Restore
8
+  original values after patching. Patch contributed by Sean McCully.
9
+
10
+- Issue #24857: Comparing call_args to a long sequence now correctly returns a
11
+  boolean result instead of raising an exception.  Patch by A Kaptur.
12
+
13
+- Issue #23004: mock_open() now reads binary data correctly when the type of
14
+  read_data is bytes.  Initial patch by Aaron Hill.
15
+
16
+- Issue #21750: mock_open.read_data can now be read from each instance, as it
17
+  could in Python 3.3.
18
+
19
+- Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely.
20
+  Patch from Nicola Palumbo and Laurent De Buyst.
21
+
22
+- Issue #23661: unittest.mock side_effects can now be exceptions again. This
23
+  was a regression vs Python 3.4. Patch from Ignacio Rossi
24
+
25
+- Issue #23310: Fix MagicMock's initializer to work with __methods__, just
26
+  like configure_mock().  Patch by Kasia Jachim.
27
+
28
+- Issue #23568: Add rdivmod support to MagicMock() objects.
29
+  Patch by Håkan Lövdahl.
30
+
31
+- Issue #23581: Add matmul support to MagicMock. Patch by Håkan Lövdahl.
32
+
33
+- Issue #23326: Removed __ne__ implementations.  Since fixing default __ne__
34
+  implementation in issue #21408 they are redundant. *** NOT BACKPORTED ***
35
+
36
+- Issue #21270: We now override tuple methods in mock.call objects so that
37
+  they can be used as normal call attributes.
38
+
39
+- Issue #21256: Printout of keyword args should be in deterministic order in
40
+  a mock function call. This will help to write better doctests.
41
+
42
+- Issue #21262: New method assert_not_called for Mock.
43
+  It raises AssertionError if the mock has been called.
44
+
45
+- Issue #21238: New keyword argument `unsafe` to Mock. It raises
46
+  `AttributeError` incase of an attribute startswith assert or assret.
47
+
48
+- Issue #21239: patch.stopall() didn't work deterministically when the same
49
+  name was patched more than once.
50
+
51
+- Issue #21222: Passing name keyword argument to mock.create_autospec now
52
+  works.
53
+
54
+- Issue #17826: setting an iterable side_effect on a mock function created by
55
+  create_autospec now works. Patch by Kushal Das.
56
+
57
+- Issue #17826: setting an iterable side_effect on a mock function created by
58
+  create_autospec now works. Patch by Kushal Das.
59
+
60
+- Issue #20968: unittest.mock.MagicMock now supports division.
61
+  Patch by Johannes Baiter.
62
+
63
+- Issue #20189: unittest.mock now no longer assumes that any object for
64
+  which it could get an inspect.Signature is a callable written in Python.
65
+  Fix courtesy of Michael Foord.
66
+
67
+- Issue #17467: add readline and readlines support to mock_open in
68
+  unittest.mock.
69
+
70
+- Issue #17015: When it has a spec, a Mock object now inspects its signature
71
+  when matching calls, so that arguments can be matched positionally or
72
+  by name.
73
+
74
+- Issue #15323: improve failure message of Mock.assert_called_once_with
75
+
76
+- Issue #14857: fix regression in references to PEP 3135 implicit __class__
77
+  closure variable (Reopens issue #12370)
78
+
79
+- Issue #14295: Add unittest.mock

+ 29
- 0
plex_test_case/mock/README.rst View File

@@ -0,0 +1,29 @@
1
+mock is a library for testing in Python. It allows you to replace parts of
2
+your system under test with mock objects and make assertions about how they
3
+have been used.
4
+
5
+mock is now part of the Python standard library, available as `unittest.mock
6
+<https://docs.python.org/dev/library/unittest.mock.html>`_ in Python 3.3
7
+onwards.
8
+
9
+This package contains a rolling backport of the standard library mock code
10
+compatible with Python 2.6 and up, and 3.3 and up.
11
+
12
+Please see the standard library documentation for more details.
13
+
14
+:Homepage: `Mock Homepage`_
15
+:Download: `Mock on PyPI`_
16
+:Documentation: `Python Docs`_
17
+:License: `BSD License`_
18
+:Support: `Mailing list (testing-in-python@lists.idyll.org)
19
+ <http://lists.idyll.org/listinfo/testing-in-python>`_
20
+:Issue tracker: `Github Issues
21
+ <https://github.com/testing-cabal/mock/issues>`_
22
+:Build status:
23
+  .. image:: https://travis-ci.org/testing-cabal/mock.svg?branch=master
24
+      :target: https://travis-ci.org/testing-cabal/mock
25
+
26
+.. _Mock Homepage: https://github.com/testing-cabal/mock
27
+.. _BSD License: http://github.com/testing-cabal/mock/blob/master/LICENSE.txt
28
+.. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html
29
+.. _mock on PyPI: http://pypi.python.org/pypi/mock

+ 1
- 0
plex_test_case/mock/docs/changelog.txt View File

@@ -0,0 +1 @@
1
+../ChangeLog

+ 210
- 0
plex_test_case/mock/docs/conf.py View File

@@ -0,0 +1,210 @@
1
+# -*- coding: utf-8 -*-
2
+#
3
+# Mock documentation build configuration file, created by
4
+# sphinx-quickstart on Mon Nov 17 18:12:00 2008.
5
+#
6
+# This file is execfile()d with the current directory set to its containing dir.
7
+#
8
+# The contents of this file are pickled, so don't put values in the namespace
9
+# that aren't pickleable (module imports are okay, they're removed automatically).
10
+#
11
+# All configuration values have a default value; values that are commented out
12
+# serve to show the default value.
13
+
14
+import sys, os
15
+sys.path.insert(0, os.path.abspath('..'))
16
+
17
+import mock
18
+
19
+# If your extensions are in another directory, add it here. If the directory
20
+# is relative to the documentation root, use os.path.abspath to make it
21
+# absolute, like shown here.
22
+#sys.path.append(os.path.abspath('some/directory'))
23
+
24
+# General configuration
25
+# ---------------------
26
+
27
+# Add any Sphinx extension module names here, as strings. They can be extensions
28
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
29
+extensions = ['sphinx.ext.doctest']
30
+
31
+doctest_global_setup = """
32
+import os
33
+import sys
34
+import mock
35
+from mock import * # yeah, I know :-/
36
+import unittest2
37
+import __main__
38
+
39
+if os.getcwd() not in sys.path:
40
+    sys.path.append(os.getcwd())
41
+
42
+# keep a reference to __main__
43
+sys.modules['__main'] = __main__
44
+
45
+class ProxyModule(object):
46
+    def __init__(self):
47
+        self.__dict__ = globals()
48
+
49
+sys.modules['__main__'] = ProxyModule()
50
+"""
51
+
52
+doctest_global_cleanup = """
53
+sys.modules['__main__'] = sys.modules['__main']
54
+"""
55
+
56
+html_theme = 'nature'
57
+html_theme_options = {}
58
+
59
+# Add any paths that contain templates here, relative to this directory.
60
+#templates_path = ['_templates']
61
+
62
+# The suffix of source filenames.
63
+source_suffix = '.txt'
64
+
65
+# The master toctree document.
66
+master_doc = 'index'
67
+
68
+# General substitutions.
69
+project = u'Mock'
70
+copyright = u'2007-2015, Michael Foord & the mock team'
71
+
72
+# The default replacements for |version| and |release|, also used in various
73
+# other places throughout the built documents. Supplied by pbr.
74
+#
75
+# The short X.Y version.
76
+version = mock.mock._v.brief_string()
77
+# The full version, including alpha/beta/rc tags.
78
+release = mock.__version__
79
+
80
+# There are two options for replacing |today|: either, you set today to some
81
+# non-false value, then it is used: (Set from pbr)
82
+today = ''
83
+# Else, today_fmt is used as the format for a strftime call.
84
+# today_fmt = '%B %d, %Y'
85
+
86
+# List of documents that shouldn't be included in the build.
87
+#unused_docs = []
88
+
89
+# List of directories, relative to source directories, that shouldn't be searched
90
+# for source files.
91
+exclude_trees = []
92
+
93
+# The reST default role (used for this markup: `text`) to use for all documents.
94
+#default_role = None
95
+
96
+# If true, '()' will be appended to :func: etc. cross-reference text.
97
+#add_function_parentheses = True
98
+
99
+# If true, the current module name will be prepended to all description
100
+# unit titles (such as .. function::).
101
+add_module_names = False
102
+
103
+# If true, sectionauthor and moduleauthor directives will be shown in the
104
+# output. They are ignored by default.
105
+#show_authors = False
106
+
107
+# The name of the Pygments (syntax highlighting) style to use.
108
+pygments_style = 'friendly'
109
+
110
+
111
+# Options for HTML output
112
+# -----------------------
113
+
114
+# The style sheet to use for HTML and HTML Help pages. A file of that name
115
+# must exist either in Sphinx' static/ path, or in one of the custom paths
116
+# given in html_static_path.
117
+#html_style = 'adctheme.css'
118
+
119
+# The name for this set of Sphinx documents.  If None, it defaults to
120
+# "<project> v<release> documentation".
121
+#html_title = None
122
+
123
+# A shorter title for the navigation bar.  Default is the same as html_title.
124
+#html_short_title = None
125
+
126
+# The name of an image file (relative to this directory) to place at the top
127
+# of the sidebar.
128
+#html_logo = None
129
+
130
+# The name of an image file (within the static path) to use as favicon of the
131
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
132
+# pixels large.
133
+#html_favicon = None
134
+
135
+# Add any paths that contain custom static files (such as style sheets) here,
136
+# relative to this directory. They are copied after the builtin static files,
137
+# so a file named "default.css" will overwrite the builtin "default.css".
138
+#html_static_path = ['_static']
139
+
140
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
141
+# using the given strftime format.
142
+html_last_updated_fmt = '%b %d, %Y'
143
+
144
+# If true, SmartyPants will be used to convert quotes and dashes to
145
+# typographically correct entities.
146
+#html_use_smartypants = True
147
+
148
+# Custom sidebar templates, maps document names to template names.
149
+#html_sidebars = {}
150
+
151
+# Additional templates that should be rendered to pages, maps page names to
152
+# template names.
153
+#html_additional_pages = {}
154
+
155
+# If false, no module index is generated.
156
+html_use_modindex = False
157
+
158
+# If false, no index is generated.
159
+#html_use_index = True
160
+
161
+# If true, the index is split into individual pages for each letter.
162
+#html_split_index = False
163
+
164
+# If true, the reST sources are included in the HTML build as _sources/<name>.
165
+#html_copy_source = True
166
+
167
+# If true, an OpenSearch description file will be output, and all pages will
168
+# contain a <link> tag referring to it.  The value of this option must be the
169
+# base URL from which the finished HTML is served.
170
+#html_use_opensearch = ''
171
+
172
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
173
+#html_file_suffix = ''
174
+
175
+# Output file base name for HTML help builder.
176
+htmlhelp_basename = 'Mockdoc'
177
+
178
+
179
+# Options for LaTeX output
180
+# ------------------------
181
+
182
+# The paper size ('letter' or 'a4').
183
+#latex_paper_size = 'letter'
184
+
185
+# The font size ('10pt', '11pt' or '12pt').
186
+latex_font_size = '12pt'
187
+
188
+# Grouping the document tree into LaTeX files. List of tuples
189
+# (source start file, target name, title, author, document class [howto/manual]).
190
+latex_documents = [
191
+  ('index', 'Mock.tex', u'Mock Documentation',
192
+   u'Michael Foord', 'manual'),
193
+]
194
+
195
+# The name of an image file (relative to this directory) to place at the top of
196
+# the title page.
197
+#latex_logo = None
198
+
199
+# For "manual" documents, if this is true, then toplevel headings are parts,
200
+# not chapters.
201
+#latex_use_parts = False
202
+
203
+# Additional stuff for the LaTeX preamble.
204
+#latex_preamble = ''
205
+
206
+# Documents to append as an appendix to all manuals.
207
+#latex_appendices = []
208
+
209
+# If false, no module index is generated.
210
+latex_use_modindex = False

+ 189
- 0
plex_test_case/mock/docs/index.txt View File

@@ -0,0 +1,189 @@
1
+====================================
2
+ Mock - Mocking and Testing Library
3
+====================================
4
+
5
+:Version: |release|
6
+:Date: |today|
7
+:Homepage: `Mock Homepage`_
8
+:Download: `Mock on PyPI`_
9
+:Documentation: `Python Docs`_
10
+:License: `BSD License`_
11
+:Support: `Mailing list (testing-in-python@lists.idyll.org)
12
+ <http://lists.idyll.org/listinfo/testing-in-python>`_
13
+:Issue tracker: `Github Issues
14
+ <https://github.com/testing-cabal/mock/issues>`_
15
+:Last sync: cb6aab1248c4aec4dd578bea717854505a6fb55d
16
+
17
+.. _Mock Homepage: https://github.com/testing-cabal/mock
18
+.. _BSD License: http://github.com/testing-cabal/mock/blob/master/LICENSE.txt
19
+.. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html
20
+
21
+.. module:: mock
22
+   :synopsis: Mock object and testing library.
23
+
24
+.. index:: introduction
25
+
26
+TOC
27
++++
28
+
29
+.. toctree::
30
+   :maxdepth: 2
31
+
32
+   changelog
33
+
34
+Introduction
35
+++++++++++++
36
+
37
+mock is a library for testing in Python. It allows you to replace parts of
38
+your system under test with mock objects and make assertions about how they
39
+have been used.
40
+
41
+mock is now part of the Python standard library, available as
42
+``unittest.mock`` in Python 3.3 onwards. However, if you are writing code that
43
+runs on multiple versions of Python the ``mock`` package is better, as you get
44
+the newest features from the latest release of Python available for all
45
+Pythons.
46
+
47
+The ``mock`` package contains a rolling backport of the standard library mock
48
+code compatible with Python 2.6 and up, and 3.3 and up. Python 3.2 is supported
49
+by mock 1.3.0 and below - with pip no longer supporting 3.2, we cannot test
50
+against that version anymore.
51
+
52
+Please see the standard library documentation for usage details.
53
+
54
+.. index:: installing
55
+.. _installing:
56
+
57
+Installing
58
+++++++++++
59
+
60
+The current version is |release|. Mock is stable and widely used.
61
+
62
+* `mock on PyPI <http://pypi.python.org/pypi/mock>`_
63
+
64
+.. index:: repository
65
+.. index:: git
66
+
67
+You can checkout the latest development version from Github
68
+repository with the following command:
69
+
70
+    ``git clone https://github.com/testing-cabal/mock``
71
+
72
+
73
+.. index:: pip
74
+
75
+You can install mock with pip:
76
+
77
+    | ``pip install -U mock``
78
+
79
+Alternatively you can download the mock distribution from PyPI and after
80
+unpacking run:
81
+
82
+   ``python setup.py install``
83
+
84
+
85
+.. index:: bug reports
86
+
87
+Bug Reports
88
++++++++++++
89
+
90
+Mock uses `unittest2 <http://pypi.python.org/pypi/unittest2>`_ for its own
91
+Issues with the backport process, such as compatibility with a particular
92
+Python, should be reported to the `bug tracker
93
+<https://github.com/testing-cabal/mock/issues>`_. Feature requests and issues
94
+with Mock functionality should be reported to the `Python bug tracker
95
+<https://bugs.python.org>`_.
96
+
97
+.. index:: python changes
98
+
99
+Python Changes
100
+++++++++++++++
101
+
102
+Python NEWS entries from cPython:
103
+
104
+.. include:: ../NEWS
105
+
106
+.. index:: older versions
107
+
108
+Older Versions of Python
109
+++++++++++++++++++++++++
110
+
111
+Version 1.0.1 is the last version compatible with Python < 2.6.
112
+
113
+.. index:: maintainer notes
114
+
115
+Maintainer Notes
116
+++++++++++++++++
117
+
118
+Development
119
+===========
120
+
121
+Checkout from git (see :ref:`installing`) and submit pull requests.
122
+
123
+Committers can just push as desired: since all semantic development takes
124
+place in cPython, the backport process is as lightweight as we can make it.
125
+
126
+mock is CI tested using Travis-CI on Python versions 2.6, 2.7, 3.3, 3.4,
127
+3.5, nightly Python 3 builds, pypy, pypy3. Jython support is desired, if
128
+someone could contribute a patch to .travis.yml to support it that would be
129
+excellent.
130
+
131
+Releasing
132
+=========
133
+
134
+NB: please use semver. Bump the major component on API breaks, minor on all
135
+non-bugfix changes, patch on bugfix only changes.
136
+
137
+1. tag -s, push --tags origin master
138
+2. setup.py sdist bdist_wheel upload -s
139
+
140
+
141
+Backporting rules
142
+=================
143
+
144
+isinstance checks in cPython to ``type`` need to check ``ClassTypes``.
145
+Code calling ``obj.isidentifier`` needs to change to ``_isidentifier(obj)``.
146
+
147
+Backporting process
148
+===================
149
+
150
+1. Patch your git am with `my patch <https://github.com/rbtcollins/git>`_.
151
+2. Install the applypatch-transform hook from tools/ to your .git hooks dir.
152
+3. Configure a pre-applypatch hook to test at least all the cPython versions
153
+   we support on each patch that is applied. I use containers, and a sample
154
+   script is in tools/pre-applypatch.
155
+4. Pull down the cPython git mirror: https://github.com/python/cpython.git
156
+5. Export the new revisions since the ``Last sync`` at the top of this
157
+   document::
158
+
159
+     revs=${lastsync}
160
+     rm migrate-export
161
+     git log --pretty="format:%H " $revs.. -- Lib/unittest/mock.py \
162
+       Lib/unittest/test/testmock/ > migrate-revs
163
+     tac migrate-revs > migrate-sorted-revs
164
+     for rev in $(< migrate-sorted-revs); do
165
+           git format-patch -1 $rev -k --stdout >> migrate-export;
166
+           done
167
+     echo NEW SYNC POINT: $(git rev-parse HEAD)
168
+
169
+6. Import into mock::
170
+
171
+     git am -k --reject $path-to-cpython/migrate-export
172
+
173
+   This will transform the patches automatically. Currently it will error
174
+   on every NEWS change as I haven't gotten around to making those patches
175
+   automatic. Fixup any errors that occur. When the patch is ready, do a ``git
176
+   add -u`` to update the index and then ``git am --continue`` to move onto
177
+   the next patch. If the patch is inappropriate e.g. the patch removing
178
+   __ne__ which would break older pythons, then either do ``git reset --hard;
179
+   git am --skip`` to discard any partially applied changes and skip over it,
180
+   or, if it has a NEWS entry thats worth preserving, edit it down to just
181
+   that, with a note such as we have for the ``__ne__`` patch, and continue on
182
+   from there.
183
+
184
+   The goal is that every patch work at all times.
185
+
186
+7. After the import is complete, update this document with the new sync point.
187
+
188
+8. Push to a personal branch and propose a PR to the main repo. This will make
189
+   Travis-CI test it. If it works, push to the main repo.

+ 1
- 0
plex_test_case/mock/extendmock.py View File

@@ -0,0 +1 @@
1
+# merged into mock.py in Mock 0.7

+ 26
- 0
plex_test_case/mock/mock.wpr View File

@@ -0,0 +1,26 @@
1
+#!wing
2
+#!version=4.0
3
+##################################################################
4
+# Wing IDE project file                                          #
5
+##################################################################
6
+[project attributes]
7
+proj.directory-list = [{'dirloc': loc('.'),
8
+                        'excludes': [u'latex',
9
+                                     u'.hg',
10
+                                     u'.tox',
11
+                                     u'dist',
12
+                                     u'htmlcov',
13
+                                     u'extendmock.py',
14
+                                     u'__pycache__',
15
+                                     u'html',
16
+                                     u'build',
17
+                                     u'mock.egg-info',
18
+                                     u'tests/__pycache__',
19
+                                     u'.hgignore',
20
+                                     u'.hgtags'],
21
+                        'filter': '*',
22
+                        'include_hidden': 0,
23
+                        'recursive': 1,
24
+                        'watch_for_changes': 1}]
25
+proj.file-type = 'shared'
26
+testing.auto-test-file-specs = ('test*.py',)

+ 4
- 0
plex_test_case/mock/mock/__init__.py View File

@@ -0,0 +1,4 @@
1
+from __future__ import absolute_import
2
+import mock.mock as _mock
3
+from mock.mock import *
4
+__all__ = _mock.__all__

+ 2556
- 0
plex_test_case/mock/mock/mock.py
File diff suppressed because it is too large
View File


+ 3
- 0
plex_test_case/mock/mock/tests/__init__.py View File

@@ -0,0 +1,3 @@
1
+# Copyright (C) 2007-2012 Michael Foord & the mock team
2
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
3
+# http://www.voidspace.org.uk/python/mock/

+ 18
- 0
plex_test_case/mock/mock/tests/__main__.py View File

@@ -0,0 +1,18 @@
1
+import os
2
+import unittest
3
+
4
+
5
+def load_tests(loader, standard_tests, pattern):
6
+    # top level directory cached on loader instance
7
+    this_dir = os.path.dirname(__file__)
8
+    pattern = pattern or "test*.py"
9
+    # We are inside unittest.test.testmock, so the top-level is three notches up
10
+    top_level_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir)))
11
+    package_tests = loader.discover(start_dir=this_dir, pattern=pattern,
12
+                                    top_level_dir=top_level_dir)
13
+    standard_tests.addTests(package_tests)
14
+    return standard_tests
15
+
16
+
17
+if __name__ == '__main__':
18
+    unittest.main()

+ 36
- 0
plex_test_case/mock/mock/tests/support.py View File

@@ -0,0 +1,36 @@
1
+import sys
2
+
3
+info = sys.version_info
4
+import unittest2
5
+
6
+
7
+try:
8
+    callable = callable
9
+except NameError:
10
+    def callable(obj):
11
+        return hasattr(obj, '__call__')
12
+
13
+
14
+with_available = sys.version_info[:2] >= (2, 5)
15
+
16
+
17
+def is_instance(obj, klass):
18
+    """Version of is_instance that doesn't access __class__"""
19
+    return issubclass(type(obj), klass)
20
+
21
+
22
+class SomeClass(object):
23
+    class_attribute = None
24
+
25
+    def wibble(self):
26
+        pass
27
+
28
+
29
+class X(object):
30
+    pass
31
+
32
+try:
33
+    next = next
34
+except NameError:
35
+    def next(obj):
36
+        return obj.next()

+ 158
- 0
plex_test_case/mock/mock/tests/testcallable.py View File

@@ -0,0 +1,158 @@
1
+# Copyright (C) 2007-2012 Michael Foord & the mock team
2
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
3
+# http://www.voidspace.org.uk/python/mock/
4
+
5
+import unittest2 as unittest
6
+from mock.tests.support import is_instance, X, SomeClass
7
+
8
+from mock import (
9
+    Mock, MagicMock, NonCallableMagicMock,
10
+    NonCallableMock, patch, create_autospec,
11
+    CallableMixin
12
+)
13
+
14
+
15
+
16
+class TestCallable(unittest.TestCase):
17
+
18
+    def assertNotCallable(self, mock):
19
+        self.assertTrue(is_instance(mock, NonCallableMagicMock))
20
+        self.assertFalse(is_instance(mock, CallableMixin))
21
+
22
+
23
+    def test_non_callable(self):
24
+        for mock in NonCallableMagicMock(), NonCallableMock():
25
+            self.assertRaises(TypeError, mock)
26
+            self.assertFalse(hasattr(mock, '__call__'))
27
+            self.assertIn(mock.__class__.__name__, repr(mock))
28
+
29
+
30
+    def test_heirarchy(self):
31
+        self.assertTrue(issubclass(MagicMock, Mock))
32
+        self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock))
33
+
34
+
35
+    def test_attributes(self):
36
+        one = NonCallableMock()
37
+        self.assertTrue(issubclass(type(one.one), Mock))
38
+
39
+        two = NonCallableMagicMock()
40
+        self.assertTrue(issubclass(type(two.two), MagicMock))
41
+
42
+
43
+    def test_subclasses(self):
44
+        class MockSub(Mock):
45
+            pass
46
+
47
+        one = MockSub()
48
+        self.assertTrue(issubclass(type(one.one), MockSub))
49
+
50
+        class MagicSub(MagicMock):
51
+            pass
52
+
53
+        two = MagicSub()
54
+        self.assertTrue(issubclass(type(two.two), MagicSub))
55
+
56
+
57
+    def test_patch_spec(self):
58
+        patcher = patch('%s.X' % __name__, spec=True)
59
+        mock = patcher.start()
60
+        self.addCleanup(patcher.stop)
61
+
62
+        instance = mock()
63
+        mock.assert_called_once_with()
64
+
65
+        self.assertNotCallable(instance)
66
+        self.assertRaises(TypeError, instance)
67
+
68
+
69
+    def test_patch_spec_set(self):
70
+        patcher = patch('%s.X' % __name__, spec_set=True)
71
+        mock = patcher.start()
72
+        self.addCleanup(patcher.stop)
73
+
74
+        instance = mock()
75
+        mock.assert_called_once_with()
76
+
77
+        self.assertNotCallable(instance)
78
+        self.assertRaises(TypeError, instance)
79
+
80
+
81
+    def test_patch_spec_instance(self):
82
+        patcher = patch('%s.X' % __name__, spec=X())
83
+        mock = patcher.start()
84
+        self.addCleanup(patcher.stop)
85
+
86
+        self.assertNotCallable(mock)
87
+        self.assertRaises(TypeError, mock)
88
+
89
+
90
+    def test_patch_spec_set_instance(self):
91
+        patcher = patch('%s.X' % __name__, spec_set=X())
92
+        mock = patcher.start()
93
+        self.addCleanup(patcher.stop)
94
+
95
+        self.assertNotCallable(mock)
96
+        self.assertRaises(TypeError, mock)
97
+
98
+
99
+    def test_patch_spec_callable_class(self):
100
+        class CallableX(X):
101
+            def __call__(self):
102
+                pass
103
+
104
+        class Sub(CallableX):
105
+            pass
106
+
107
+        class Multi(SomeClass, Sub):
108
+            pass
109
+
110
+        class OldStyle:
111
+            def __call__(self):
112
+                pass
113
+
114
+        class OldStyleSub(OldStyle):
115
+            pass
116
+
117
+        for arg in 'spec', 'spec_set':
118
+            for Klass in CallableX, Sub, Multi, OldStyle, OldStyleSub:
119
+                with patch('%s.X' % __name__, **{arg: Klass}) as mock:
120
+                    instance = mock()
121
+                    mock.assert_called_once_with()
122
+
123
+                    self.assertTrue(is_instance(instance, MagicMock))
124
+                    # inherited spec
125
+                    self.assertRaises(AttributeError, getattr, instance,
126
+                                      'foobarbaz')
127
+
128
+                    result = instance()
129
+                    # instance is callable, result has no spec
130
+                    instance.assert_called_once_with()
131
+
132
+                    result(3, 2, 1)
133
+                    result.assert_called_once_with(3, 2, 1)
134
+                    result.foo(3, 2, 1)
135
+                    result.foo.assert_called_once_with(3, 2, 1)
136
+
137
+
138
+    def test_create_autospec(self):
139
+        mock = create_autospec(X)
140
+        instance = mock()
141
+        self.assertRaises(TypeError, instance)
142
+
143
+        mock = create_autospec(X())
144
+        self.assertRaises(TypeError, mock)
145
+
146
+
147
+    def test_create_autospec_instance(self):
148
+        mock = create_autospec(SomeClass, instance=True)
149
+
150
+        self.assertRaises(TypeError, mock)
151
+        mock.wibble()
152
+        mock.wibble.assert_called_once_with()
153
+
154
+        self.assertRaises(TypeError, mock.wibble, 'some',  'args')
155
+
156
+
157
+if __name__ == "__main__":
158
+    unittest.main()

+ 975
- 0
plex_test_case/mock/mock/tests/testhelpers.py View File

@@ -0,0 +1,975 @@
1
+# Copyright (C) 2007-2012 Michael Foord & the mock team
2
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
3
+# http://www.voidspace.org.uk/python/mock/
4
+
5
+import six
6
+import unittest2 as unittest
7
+
8
+from mock import (
9
+    call, create_autospec, MagicMock,
10
+    Mock, ANY, patch, PropertyMock
11
+)
12
+from mock.mock import _Call, _CallList
13
+
14
+from datetime import datetime
15
+
16
+class SomeClass(object):
17
+    def one(self, a, b):
18
+        pass
19
+    def two(self):
20
+        pass
21
+    def three(self, a=None):
22
+        pass
23
+
24
+
25
+
26
+class AnyTest(unittest.TestCase):
27
+
28
+    def test_any(self):
29
+        self.assertEqual(ANY, object())
30
+
31
+        mock = Mock()
32
+        mock(ANY)
33
+        mock.assert_called_with(ANY)
34
+
35
+        mock = Mock()
36
+        mock(foo=ANY)
37
+        mock.assert_called_with(foo=ANY)
38
+
39
+    def test_repr(self):
40
+        self.assertEqual(repr(ANY), '<ANY>')
41
+        self.assertEqual(str(ANY), '<ANY>')
42
+
43
+
44
+    def test_any_and_datetime(self):
45
+        mock = Mock()
46
+        mock(datetime.now(), foo=datetime.now())
47
+
48
+        mock.assert_called_with(ANY, foo=ANY)
49
+
50
+
51
+    def test_any_mock_calls_comparison_order(self):
52
+        mock = Mock()
53
+        d = datetime.now()
54
+        class Foo(object):
55
+            def __eq__(self, other):
56
+                return False
57
+            def __ne__(self, other):
58
+                return True
59
+
60
+        for d in datetime.now(), Foo():
61
+            mock.reset_mock()
62
+
63
+            mock(d, foo=d, bar=d)
64
+            mock.method(d, zinga=d, alpha=d)
65
+            mock().method(a1=d, z99=d)
66
+
67
+            expected = [
68
+                call(ANY, foo=ANY, bar=ANY),
69
+                call.method(ANY, zinga=ANY, alpha=ANY),
70
+                call(), call().method(a1=ANY, z99=ANY)
71
+            ]
72
+            self.assertEqual(expected, mock.mock_calls)
73
+            self.assertEqual(mock.mock_calls, expected)
74
+
75
+
76
+
77
+class CallTest(unittest.TestCase):
78
+
79
+    def test_call_with_call(self):
80
+        kall = _Call()
81
+        self.assertEqual(kall, _Call())
82
+        self.assertEqual(kall, _Call(('',)))
83
+        self.assertEqual(kall, _Call(((),)))
84
+        self.assertEqual(kall, _Call(({},)))
85
+        self.assertEqual(kall, _Call(('', ())))
86
+        self.assertEqual(kall, _Call(('', {})))
87
+        self.assertEqual(kall, _Call(('', (), {})))
88
+        self.assertEqual(kall, _Call(('foo',)))
89
+        self.assertEqual(kall, _Call(('bar', ())))
90
+        self.assertEqual(kall, _Call(('baz', {})))
91
+        self.assertEqual(kall, _Call(('spam', (), {})))
92
+
93
+        kall = _Call(((1, 2, 3),))
94
+        self.assertEqual(kall, _Call(((1, 2, 3),)))
95
+        self.assertEqual(kall, _Call(('', (1, 2, 3))))
96
+        self.assertEqual(kall, _Call(((1, 2, 3), {})))
97
+        self.assertEqual(kall, _Call(('', (1, 2, 3), {})))
98
+
99
+        kall = _Call(((1, 2, 4),))
100
+        self.assertNotEqual(kall, _Call(('', (1, 2, 3))))
101
+        self.assertNotEqual(kall, _Call(('', (1, 2, 3), {})))
102
+
103
+        kall = _Call(('foo', (1, 2, 4),))
104
+        self.assertNotEqual(kall, _Call(('', (1, 2, 4))))
105
+        self.assertNotEqual(kall, _Call(('', (1, 2, 4), {})))
106
+        self.assertNotEqual(kall, _Call(('bar', (1, 2, 4))))
107
+        self.assertNotEqual(kall, _Call(('bar', (1, 2, 4), {})))
108
+
109
+        kall = _Call(({'a': 3},))
110
+        self.assertEqual(kall, _Call(('', (), {'a': 3})))
111
+        self.assertEqual(kall, _Call(('', {'a': 3})))
112
+        self.assertEqual(kall, _Call(((), {'a': 3})))
113
+        self.assertEqual(kall, _Call(({'a': 3},)))
114
+
115
+
116
+    def test_empty__Call(self):
117
+        args = _Call()
118
+
119
+        self.assertEqual(args, ())
120
+        self.assertEqual(args, ('foo',))
121
+        self.assertEqual(args, ((),))
122
+        self.assertEqual(args, ('foo', ()))
123
+        self.assertEqual(args, ('foo',(), {}))
124
+        self.assertEqual(args, ('foo', {}))
125
+        self.assertEqual(args, ({},))
126
+
127
+
128
+    def test_named_empty_call(self):
129
+        args = _Call(('foo', (), {}))
130
+
131
+        self.assertEqual(args, ('foo',))
132
+        self.assertEqual(args, ('foo', ()))
133
+        self.assertEqual(args, ('foo',(), {}))
134
+        self.assertEqual(args, ('foo', {}))
135
+
136
+        self.assertNotEqual(args, ((),))
137
+        self.assertNotEqual(args, ())
138
+        self.assertNotEqual(args, ({},))
139
+        self.assertNotEqual(args, ('bar',))
140
+        self.assertNotEqual(args, ('bar', ()))
141
+        self.assertNotEqual(args, ('bar', {}))
142
+
143
+
144
+    def test_call_with_args(self):
145
+        args = _Call(((1, 2, 3), {}))
146
+
147
+        self.assertEqual(args, ((1, 2, 3),))
148
+        self.assertEqual(args, ('foo', (1, 2, 3)))
149
+        self.assertEqual(args, ('foo', (1, 2, 3), {}))
150
+        self.assertEqual(args, ((1, 2, 3), {}))
151
+
152
+
153
+    def test_named_call_with_args(self):
154
+        args = _Call(('foo', (1, 2, 3), {}))
155
+
156
+        self.assertEqual(args, ('foo', (1, 2, 3)))
157
+        self.assertEqual(args, ('foo', (1, 2, 3), {}))
158
+
159
+        self.assertNotEqual(args, ((1, 2, 3),))
160
+        self.assertNotEqual(args, ((1, 2, 3), {}))
161
+
162
+
163
+    def test_call_with_kwargs(self):
164
+        args = _Call(((), dict(a=3, b=4)))
165
+
166
+        self.assertEqual(args, (dict(a=3, b=4),))
167
+        self.assertEqual(args, ('foo', dict(a=3, b=4)))
168
+        self.assertEqual(args, ('foo', (), dict(a=3, b=4)))
169
+        self.assertEqual(args, ((), dict(a=3, b=4)))
170
+
171
+
172
+    def test_named_call_with_kwargs(self):
173
+        args = _Call(('foo', (), dict(a=3, b=4)))
174
+
175
+        self.assertEqual(args, ('foo', dict(a=3, b=4)))
176
+        self.assertEqual(args, ('foo', (), dict(a=3, b=4)))
177
+
178
+        self.assertNotEqual(args, (dict(a=3, b=4),))
179
+        self.assertNotEqual(args, ((), dict(a=3, b=4)))
180
+
181
+
182
+    def test_call_with_args_call_empty_name(self):
183
+        args = _Call(((1, 2, 3), {}))
184
+        self.assertEqual(args, call(1, 2, 3))
185
+        self.assertEqual(call(1, 2, 3), args)
186
+        self.assertIn(call(1, 2, 3), [args])
187
+
188
+
189
+    def test_call_ne(self):
190
+        self.assertNotEqual(_Call(((1, 2, 3),)), call(1, 2))
191
+        self.assertFalse(_Call(((1, 2, 3),)) != call(1, 2, 3))
192
+        self.assertTrue(_Call(((1, 2), {})) != call(1, 2, 3))
193
+
194
+
195
+    def test_call_non_tuples(self):
196
+        kall = _Call(((1, 2, 3),))
197
+        for value in 1, None, self, int:
198
+            self.assertNotEqual(kall, value)
199
+            self.assertFalse(kall == value)
200
+
201
+
202
+    def test_repr(self):
203
+        self.assertEqual(repr(_Call()), 'call()')
204
+        self.assertEqual(repr(_Call(('foo',))), 'call.foo()')
205
+
206
+        self.assertEqual(repr(_Call(((1, 2, 3), {'a': 'b'}))),
207
+                         "call(1, 2, 3, a='b')")
208
+        self.assertEqual(repr(_Call(('bar', (1, 2, 3), {'a': 'b'}))),
209
+                         "call.bar(1, 2, 3, a='b')")
210
+
211
+        self.assertEqual(repr(call), 'call')
212
+        self.assertEqual(str(call), 'call')
213
+
214
+        self.assertEqual(repr(call()), 'call()')
215
+        self.assertEqual(repr(call(1)), 'call(1)')
216
+        self.assertEqual(repr(call(zz='thing')), "call(zz='thing')")
217
+
218
+        self.assertEqual(repr(call().foo), 'call().foo')
219
+        self.assertEqual(repr(call(1).foo.bar(a=3).bing),
220
+                         'call().foo.bar().bing')
221
+        self.assertEqual(
222
+            repr(call().foo(1, 2, a=3)),
223
+            "call().foo(1, 2, a=3)"
224
+        )
225
+        self.assertEqual(repr(call()()), "call()()")
226
+        self.assertEqual(repr(call(1)(2)), "call()(2)")
227
+        self.assertEqual(
228
+            repr(call()().bar().baz.beep(1)),
229
+            "call()().bar().baz.beep(1)"
230
+        )
231
+
232
+
233
+    def test_call(self):
234
+        self.assertEqual(call(), ('', (), {}))
235
+        self.assertEqual(call('foo', 'bar', one=3, two=4),
236
+                         ('', ('foo', 'bar'), {'one': 3, 'two': 4}))
237
+
238
+        mock = Mock()
239
+        mock(1, 2, 3)
240
+        mock(a=3, b=6)
241
+        self.assertEqual(mock.call_args_list,
242
+                         [call(1, 2, 3), call(a=3, b=6)])
243
+
244
+    def test_attribute_call(self):
245
+        self.assertEqual(call.foo(1), ('foo', (1,), {}))
246
+        self.assertEqual(call.bar.baz(fish='eggs'),
247
+                         ('bar.baz', (), {'fish': 'eggs'}))
248
+
249
+        mock = Mock()
250
+        mock.foo(1, 2 ,3)
251
+        mock.bar.baz(a=3, b=6)
252
+        self.assertEqual(mock.method_calls,
253
+                         [call.foo(1, 2, 3), call.bar.baz(a=3, b=6)])
254
+
255
+
256
+    def test_extended_call(self):
257
+        result = call(1).foo(2).bar(3, a=4)
258
+        self.assertEqual(result, ('().foo().bar', (3,), dict(a=4)))
259
+
260
+        mock = MagicMock()
261
+        mock(1, 2, a=3, b=4)
262
+        self.assertEqual(mock.call_args, call(1, 2, a=3, b=4))
263
+        self.assertNotEqual(mock.call_args, call(1, 2, 3))
264
+
265
+        self.assertEqual(mock.call_args_list, [call(1, 2, a=3, b=4)])
266
+        self.assertEqual(mock.mock_calls, [call(1, 2, a=3, b=4)])
267
+
268
+        mock = MagicMock()
269
+        mock.foo(1).bar()().baz.beep(a=6)
270
+
271
+        last_call = call.foo(1).bar()().baz.beep(a=6)
272
+        self.assertEqual(mock.mock_calls[-1], last_call)
273
+        self.assertEqual(mock.mock_calls, last_call.call_list())
274
+
275
+
276
+    def test_call_list(self):
277
+        mock = MagicMock()
278
+        mock(1)
279
+        self.assertEqual(call(1).call_list(), mock.mock_calls)
280
+
281
+        mock = MagicMock()
282
+        mock(1).method(2)
283
+        self.assertEqual(call(1).method(2).call_list(),
284
+                         mock.mock_calls)
285
+
286
+        mock = MagicMock()
287
+        mock(1).method(2)(3)
288
+        self.assertEqual(call(1).method(2)(3).call_list(),
289
+                         mock.mock_calls)
290
+
291
+        mock = MagicMock()
292
+        int(mock(1).method(2)(3).foo.bar.baz(4)(5))
293
+        kall = call(1).method(2)(3).foo.bar.baz(4)(5).__int__()
294
+        self.assertEqual(kall.call_list(), mock.mock_calls)
295
+
296
+
297
+    def test_call_any(self):
298
+        self.assertEqual(call, ANY)
299
+
300
+        m = MagicMock()
301
+        int(m)
302
+        self.assertEqual(m.mock_calls, [ANY])
303
+        self.assertEqual([ANY], m.mock_calls)
304
+
305
+
306
+    def test_two_args_call(self):
307
+        args = _Call(((1, 2), {'a': 3}), two=True)
308
+        self.assertEqual(len(args), 2)
309
+        self.assertEqual(args[0], (1, 2))
310
+        self.assertEqual(args[1], {'a': 3})
311
+
312
+        other_args = _Call(((1, 2), {'a': 3}))
313
+        self.assertEqual(args, other_args)
314
+
315
+
316
+class SpecSignatureTest(unittest.TestCase):
317
+
318
+    def _check_someclass_mock(self, mock):
319
+        self.assertRaises(AttributeError, getattr, mock, 'foo')
320
+        mock.one(1, 2)
321
+        mock.one.assert_called_with(1, 2)
322
+        self.assertRaises(AssertionError,
323
+                          mock.one.assert_called_with, 3, 4)
324
+        self.assertRaises(TypeError, mock.one, 1)
325
+
326
+        mock.two()
327
+        mock.two.assert_called_with()
328
+        self.assertRaises(AssertionError,
329
+                          mock.two.assert_called_with, 3)
330
+        self.assertRaises(TypeError, mock.two, 1)
331
+
332
+        mock.three()
333
+        mock.three.assert_called_with()
334
+        self.assertRaises(AssertionError,
335
+                          mock.three.assert_called_with, 3)
336
+        self.assertRaises(TypeError, mock.three, 3, 2)
337
+
338
+        mock.three(1)
339
+        mock.three.assert_called_with(1)
340
+
341
+        mock.three(a=1)
342
+        mock.three.assert_called_with(a=1)
343
+
344
+
345
+    def test_basic(self):
346
+        mock = create_autospec(SomeClass)
347
+        self._check_someclass_mock(mock)
348
+        mock = create_autospec(SomeClass())
349
+        self._check_someclass_mock(mock)
350
+
351
+
352
+    def test_create_autospec_return_value(self):
353
+        def f():
354
+            pass
355
+        mock = create_autospec(f, return_value='foo')
356
+        self.assertEqual(mock(), 'foo')
357
+
358
+        class Foo(object):
359
+            pass
360
+
361
+        mock = create_autospec(Foo, return_value='foo')
362
+        self.assertEqual(mock(), 'foo')
363
+
364
+
365
+    def test_autospec_reset_mock(self):
366
+        m = create_autospec(int)
367
+        int(m)
368
+        m.reset_mock()
369
+        self.assertEqual(m.__int__.call_count, 0)
370
+
371
+
372
+    def test_mocking_unbound_methods(self):
373
+        class Foo(object):
374
+            def foo(self, foo):
375
+                pass
376
+        p = patch.object(Foo, 'foo')
377
+        mock_foo = p.start()
378
+        Foo().foo(1)
379
+
380
+        mock_foo.assert_called_with(1)
381
+
382
+
383
+    @unittest.expectedFailure
384
+    def test_create_autospec_unbound_methods(self):
385
+        # see mock issue 128
386
+        class Foo(object):
387
+            def foo(self):
388
+                pass
389
+
390
+        klass = create_autospec(Foo)
391
+        instance = klass()
392
+        self.assertRaises(TypeError, instance.foo, 1)
393
+
394
+        # Note: no type checking on the "self" parameter
395
+        klass.foo(1)
396
+        klass.foo.assert_called_with(1)
397
+        self.assertRaises(TypeError, klass.foo)
398
+
399
+
400
+    def test_create_autospec_keyword_arguments(self):
401
+        class Foo(object):
402
+            a = 3
403
+        m = create_autospec(Foo, a='3')
404
+        self.assertEqual(m.a, '3')
405
+
406
+    @unittest.skipUnless(six.PY3, "Keyword only arguments Python 3 specific")
407
+    def test_create_autospec_keyword_only_arguments(self):
408
+        func_def = "def foo(a, *, b=None):\n    pass\n"
409
+        namespace = {}
410
+        exec (func_def, namespace)
411
+        foo = namespace['foo']
412
+
413
+        m = create_autospec(foo)
414
+        m(1)
415
+        m.assert_called_with(1)
416
+        self.assertRaises(TypeError, m, 1, 2)
417
+
418
+        m(2, b=3)
419
+        m.assert_called_with(2, b=3)
420
+
421
+    def test_function_as_instance_attribute(self):
422
+        obj = SomeClass()
423
+        def f(a):
424
+            pass
425
+        obj.f = f
426
+
427
+        mock = create_autospec(obj)
428
+        mock.f('bing')
429
+        mock.f.assert_called_with('bing')
430
+
431
+
432
+    def test_spec_as_list(self):
433
+        # because spec as a list of strings in the mock constructor means
434
+        # something very different we treat a list instance as the type.
435
+        mock = create_autospec([])
436
+        mock.append('foo')
437
+        mock.append.assert_called_with('foo')
438
+
439
+        self.assertRaises(AttributeError, getattr, mock, 'foo')
440
+
441
+        class Foo(object):
442
+            foo = []
443
+
444
+        mock = create_autospec(Foo)
445
+        mock.foo.append(3)
446
+        mock.foo.append.assert_called_with(3)
447
+        self.assertRaises(AttributeError, getattr, mock.foo, 'foo')
448
+
449
+
450
+    def test_attributes(self):
451
+        class Sub(SomeClass):
452
+            attr = SomeClass()
453
+
454
+        sub_mock = create_autospec(Sub)
455
+
456
+        for mock in (sub_mock, sub_mock.attr):
457
+            self._check_someclass_mock(mock)
458
+
459
+
460
+    def test_builtin_functions_types(self):
461
+        # we could replace builtin functions / methods with a function
462
+        # with *args / **kwargs signature. Using the builtin method type
463
+        # as a spec seems to work fairly well though.
464
+        class BuiltinSubclass(list):
465
+            def bar(self, arg):
466
+                pass
467
+            sorted = sorted
468
+            attr = {}
469
+
470
+        mock = create_autospec(BuiltinSubclass)
471
+        mock.append(3)
472
+        mock.append.assert_called_with(3)
473
+        self.assertRaises(AttributeError, getattr, mock.append, 'foo')
474
+
475
+        mock.bar('foo')
476
+        mock.bar.assert_called_with('foo')
477
+        self.assertRaises(TypeError, mock.bar, 'foo', 'bar')
478
+        self.assertRaises(AttributeError, getattr, mock.bar, 'foo')
479
+
480
+        mock.sorted([1, 2])
481
+        mock.sorted.assert_called_with([1, 2])
482
+        self.assertRaises(AttributeError, getattr, mock.sorted, 'foo')
483
+
484
+        mock.attr.pop(3)
485
+        mock.attr.pop.assert_called_with(3)
486
+        self.assertRaises(AttributeError, getattr, mock.attr, 'foo')
487
+
488
+
489
+    def test_method_calls(self):
490
+        class Sub(SomeClass):
491
+            attr = SomeClass()
492
+
493
+        mock = create_autospec(Sub)
494
+        mock.one(1, 2)
495
+        mock.two()
496
+        mock.three(3)
497
+
498
+        expected = [call.one(1, 2), call.two(), call.three(3)]
499
+        self.assertEqual(mock.method_calls, expected)
500
+
501
+        mock.attr.one(1, 2)
502
+        mock.attr.two()
503
+        mock.attr.three(3)
504
+
505
+        expected.extend(
506
+            [call.attr.one(1, 2), call.attr.two(), call.attr.three(3)]
507
+        )
508
+        self.assertEqual(mock.method_calls, expected)
509
+
510
+
511
+    def test_magic_methods(self):
512
+        class BuiltinSubclass(list):
513
+            attr = {}
514
+
515
+        mock = create_autospec(BuiltinSubclass)
516
+        self.assertEqual(list(mock), [])
517
+        self.assertRaises(TypeError, int, mock)
518
+        self.assertRaises(TypeError, int, mock.attr)
519
+        self.assertEqual(list(mock), [])
520
+
521
+        self.assertIsInstance(mock['foo'], MagicMock)
522
+        self.assertIsInstance(mock.attr['foo'], MagicMock)
523
+
524
+
525
+    def test_spec_set(self):
526
+        class Sub(SomeClass):
527
+            attr = SomeClass()
528
+
529
+        for spec in (Sub, Sub()):
530
+            mock = create_autospec(spec, spec_set=True)
531
+            self._check_someclass_mock(mock)
532
+
533
+            self.assertRaises(AttributeError, setattr, mock, 'foo', 'bar')
534
+            self.assertRaises(AttributeError, setattr, mock.attr, 'foo', 'bar')
535
+
536
+
537
+    def test_descriptors(self):
538
+        class Foo(object):
539
+            @classmethod
540
+            def f(cls, a, b):
541
+                pass
542
+            @staticmethod
543
+            def g(a, b):
544
+                pass
545
+
546
+        class Bar(Foo):
547
+            pass
548
+
549
+        class Baz(SomeClass, Bar):
550
+            pass
551
+
552
+        for spec in (Foo, Foo(), Bar, Bar(), Baz, Baz()):
553
+            mock = create_autospec(spec)
554
+            mock.f(1, 2)
555
+            mock.f.assert_called_once_with(1, 2)
556
+
557
+            mock.g(3, 4)
558
+            mock.g.assert_called_once_with(3, 4)
559
+
560
+
561
+    @unittest.skipIf(six.PY3, "No old style classes in Python 3")
562
+    def test_old_style_classes(self):
563
+        class Foo:
564
+            def f(self, a, b):
565
+                pass
566
+
567
+        class Bar(Foo):
568
+            g = Foo()
569
+
570
+        for spec in (Foo, Foo(), Bar, Bar()):
571
+            mock = create_autospec(spec)
572
+            mock.f(1, 2)
573
+            mock.f.assert_called_once_with(1, 2)
574
+
575
+            self.assertRaises(AttributeError, getattr, mock, 'foo')
576
+            self.assertRaises(AttributeError, getattr, mock.f, 'foo')
577
+
578
+        mock.g.f(1, 2)
579
+        mock.g.f.assert_called_once_with(1, 2)
580
+        self.assertRaises(AttributeError, getattr, mock.g, 'foo')
581
+
582
+
583
+    def test_recursive(self):
584
+        class A(object):
585
+            def a(self):
586
+                pass
587
+            foo = 'foo bar baz'
588
+            bar = foo
589
+
590
+        A.B = A
591
+        mock = create_autospec(A)
592
+
593
+        mock()
594
+        self.assertFalse(mock.B.called)
595
+
596
+        mock.a()
597
+        mock.B.a()
598
+        self.assertEqual(mock.method_calls, [call.a(), call.B.a()])
599
+
600
+        self.assertIs(A.foo, A.bar)
601
+        self.assertIsNot(mock.foo, mock.bar)
602
+        mock.foo.lower()
603
+        self.assertRaises(AssertionError, mock.bar.lower.assert_called_with)
604
+
605
+
606
+    def test_spec_inheritance_for_classes(self):
607
+        class Foo(object):
608
+            def a(self, x):
609
+                pass
610
+            class Bar(object):
611
+                def f(self, y):
612
+                    pass
613
+
614
+        class_mock = create_autospec(Foo)
615
+
616
+        self.assertIsNot(class_mock, class_mock())
617
+
618
+        for this_mock in class_mock, class_mock():
619
+            this_mock.a(x=5)
620
+            this_mock.a.assert_called_with(x=5)
621
+            this_mock.a.assert_called_with(5)
622
+            self.assertRaises(TypeError, this_mock.a, 'foo', 'bar')
623
+            self.assertRaises(AttributeError, getattr, this_mock, 'b')
624
+
625
+        instance_mock = create_autospec(Foo())
626
+        instance_mock.a(5)
627
+        instance_mock.a.assert_called_with(5)
628
+        instance_mock.a.assert_called_with(x=5)
629
+        self.assertRaises(TypeError, instance_mock.a, 'foo', 'bar')
630
+        self.assertRaises(AttributeError, getattr, instance_mock, 'b')
631
+
632
+        # The return value isn't isn't callable
633
+        self.assertRaises(TypeError, instance_mock)
634
+
635
+        instance_mock.Bar.f(6)
636
+        instance_mock.Bar.f.assert_called_with(6)
637
+        instance_mock.Bar.f.assert_called_with(y=6)
638
+        self.assertRaises(AttributeError, getattr, instance_mock.Bar, 'g')
639
+
640
+        instance_mock.Bar().f(6)
641
+        instance_mock.Bar().f.assert_called_with(6)
642
+        instance_mock.Bar().f.assert_called_with(y=6)
643
+        self.assertRaises(AttributeError, getattr, instance_mock.Bar(), 'g')
644
+
645
+
646
+    def test_inherit(self):
647
+        class Foo(object):
648
+            a = 3
649
+
650
+        Foo.Foo = Foo
651
+
652
+        # class
653
+        mock = create_autospec(Foo)
654
+        instance = mock()
655
+        self.assertRaises(AttributeError, getattr, instance, 'b')
656
+
657
+        attr_instance = mock.Foo()
658
+        self.assertRaises(AttributeError, getattr, attr_instance, 'b')
659
+
660
+        # instance
661
+        mock = create_autospec(Foo())
662
+        self.assertRaises(AttributeError, getattr, mock, 'b')
663
+        self.assertRaises(TypeError, mock)
664
+
665
+        # attribute instance
666
+        call_result = mock.Foo()
667
+        self.assertRaises(AttributeError, getattr, call_result, 'b')
668
+
669
+
670
+    def test_builtins(self):
671
+        # used to fail with infinite recursion
672
+        create_autospec(1)
673
+
674
+        create_autospec(int)
675
+        create_autospec('foo')
676
+        create_autospec(str)
677
+        create_autospec({})
678
+        create_autospec(dict)
679
+        create_autospec([])
680
+        create_autospec(list)
681
+        create_autospec(set())
682
+        create_autospec(set)
683
+        create_autospec(1.0)
684
+        create_autospec(float)
685
+        create_autospec(1j)
686
+        create_autospec(complex)
687
+        create_autospec(False)
688
+        create_autospec(True)
689
+
690
+
691
+    def test_function(self):
692
+        def f(a, b):
693
+            pass
694
+
695
+        mock = create_autospec(f)
696
+        self.assertRaises(TypeError, mock)
697
+        mock(1, 2)
698
+        mock.assert_called_with(1, 2)
699
+        mock.assert_called_with(1, b=2)
700
+        mock.assert_called_with(a=1, b=2)
701
+
702
+        f.f = f
703
+        mock = create_autospec(f)
704
+        self.assertRaises(TypeError, mock.f)
705
+        mock.f(3, 4)
706
+        mock.f.assert_called_with(3, 4)
707
+        mock.f.assert_called_with(a=3, b=4)
708
+
709
+
710
+    def test_skip_attributeerrors(self):
711
+        class Raiser(object):
712
+            def __get__(self, obj, type=None):
713
+                if obj is None:
714
+                    raise AttributeError('Can only be accessed via an instance')
715
+
716
+        class RaiserClass(object):
717
+            raiser = Raiser()
718
+
719
+            @staticmethod
720
+            def existing(a, b):
721
+                return a + b
722
+
723
+        s = create_autospec(RaiserClass)
724
+        self.assertRaises(TypeError, lambda x: s.existing(1, 2, 3))
725
+        s.existing(1, 2)
726
+        self.assertRaises(AttributeError, lambda: s.nonexisting)
727
+
728
+        # check we can fetch the raiser attribute and it has no spec
729
+        obj = s.raiser
730
+        obj.foo, obj.bar
731
+
732
+
733
+    def test_signature_class(self):
734
+        class Foo(object):
735
+            def __init__(self, a, b=3):
736
+                pass
737
+
738
+        mock = create_autospec(Foo)
739
+
740
+        self.assertRaises(TypeError, mock)
741
+        mock(1)
742
+        mock.assert_called_once_with(1)
743
+
744
+        mock(4, 5)
745
+        mock.assert_called_with(4, 5)
746
+
747
+
748
+    @unittest.skipIf(six.PY3, 'no old style classes in Python 3')
749
+    def test_signature_old_style_class(self):
750
+        class Foo:
751
+            def __init__(self, a, b=3):
752
+                pass
753
+
754
+        mock = create_autospec(Foo)
755
+
756
+        self.assertRaises(TypeError, mock)
757
+        mock(1)
758
+        mock.assert_called_once_with(1)
759
+        mock.assert_called_once_with(a=1)
760
+        self.assertRaises(AssertionError, mock.assert_called_once_with, 2)
761
+
762
+        mock(4, 5)
763
+        mock.assert_called_with(4, 5)
764
+        mock.assert_called_with(a=4, b=5)
765
+        self.assertRaises(AssertionError, mock.assert_called_with, a=5, b=4)
766
+
767
+
768
+    def test_class_with_no_init(self):
769
+        # this used to raise an exception
770
+        # due to trying to get a signature from object.__init__
771
+        class Foo(object):
772
+            pass
773
+        create_autospec(Foo)
774
+
775
+
776
+    @unittest.skipIf(six.PY3, 'no old style classes in Python 3')
777
+    def test_old_style_class_with_no_init(self):
778
+        # this used to raise an exception
779
+        # due to Foo.__init__ raising an AttributeError
780
+        class Foo:
781
+            pass
782
+        create_autospec(Foo)
783
+
784
+
785
+    def test_signature_callable(self):
786
+        class Callable(object):
787
+            def __init__(self, x, y):
788
+                pass
789
+            def __call__(self, a):
790
+                pass
791
+
792
+        mock = create_autospec(Callable)
793
+        mock(1, 2)
794
+        mock.assert_called_once_with(1, 2)
795
+        mock.assert_called_once_with(x=1, y=2)
796
+        self.assertRaises(TypeError, mock, 'a')
797
+
798
+        instance = mock(1, 2)
799
+        self.assertRaises(TypeError, instance)
800
+        instance(a='a')
801
+        instance.assert_called_once_with('a')
802
+        instance.assert_called_once_with(a='a')
803
+        instance('a')
804
+        instance.assert_called_with('a')
805
+        instance.assert_called_with(a='a')
806
+
807
+        mock = create_autospec(Callable(1, 2))
808
+        mock(a='a')
809
+        mock.assert_called_once_with(a='a')
810
+        self.assertRaises(TypeError, mock)
811
+        mock('a')
812
+        mock.assert_called_with('a')
813
+
814
+
815
+    def test_signature_noncallable(self):
816
+        class NonCallable(object):
817
+            def __init__(self):
818
+                pass
819
+
820
+        mock = create_autospec(NonCallable)
821
+        instance = mock()
822
+        mock.assert_called_once_with()
823
+        self.assertRaises(TypeError, mock, 'a')
824
+        self.assertRaises(TypeError, instance)
825
+        self.assertRaises(TypeError, instance, 'a')
826
+
827
+        mock = create_autospec(NonCallable())
828
+        self.assertRaises(TypeError, mock)
829
+        self.assertRaises(TypeError, mock, 'a')
830
+
831
+
832
+    def test_create_autospec_none(self):
833
+        class Foo(object):
834
+            bar = None
835
+
836
+        mock = create_autospec(Foo)
837
+        none = mock.bar
838
+        self.assertNotIsInstance(none, type(None))
839
+
840
+        none.foo()
841
+        none.foo.assert_called_once_with()
842
+
843
+
844
+    def test_autospec_functions_with_self_in_odd_place(self):
845
+        class Foo(object):
846
+            def f(a, self):
847
+                pass
848
+
849
+        a = create_autospec(Foo)
850
+        a.f(10)
851
+        a.f.assert_called_with(10)
852
+        a.f.assert_called_with(self=10)
853
+        a.f(self=10)
854
+        a.f.assert_called_with(10)
855
+        a.f.assert_called_with(self=10)
856
+
857
+
858
+    def test_autospec_property(self):
859
+        class Foo(object):
860
+            @property
861
+            def foo(self):
862
+                return 3
863
+
864
+        foo = create_autospec(Foo)
865
+        mock_property = foo.foo
866
+
867
+        # no spec on properties
868
+        self.assertIsInstance(mock_property, MagicMock)
869
+        mock_property(1, 2, 3)
870
+        mock_property.abc(4, 5, 6)
871
+        mock_property.assert_called_once_with(1, 2, 3)
872
+        mock_property.abc.assert_called_once_with(4, 5, 6)
873
+
874
+
875
+    def test_autospec_slots(self):
876
+        class Foo(object):
877
+            __slots__ = ['a']
878
+
879
+        foo = create_autospec(Foo)
880
+        mock_slot = foo.a
881
+
882
+        # no spec on slots
883
+        mock_slot(1, 2, 3)
884
+        mock_slot.abc(4, 5, 6)
885
+        mock_slot.assert_called_once_with(1, 2, 3)
886
+        mock_slot.abc.assert_called_once_with(4, 5, 6)
887
+
888
+
889
+class TestCallList(unittest.TestCase):
890
+
891
+    def test_args_list_contains_call_list(self):
892
+        mock = Mock()
893
+        self.assertIsInstance(mock.call_args_list, _CallList)
894
+
895
+        mock(1, 2)
896
+        mock(a=3)
897
+        mock(3, 4)
898
+        mock(b=6)
899
+
900
+        for kall in call(1, 2), call(a=3), call(3, 4), call(b=6):
901
+            self.assertIn(kall, mock.call_args_list)
902
+
903
+        calls = [call(a=3), call(3, 4)]
904
+        self.assertIn(calls, mock.call_args_list)
905
+        calls = [call(1, 2), call(a=3)]
906
+        self.assertIn(calls, mock.call_args_list)
907
+        calls = [call(3, 4), call(b=6)]
908
+        self.assertIn(calls, mock.call_args_list)
909
+        calls = [call(3, 4)]
910
+        self.assertIn(calls, mock.call_args_list)
911
+
912
+        self.assertNotIn(call('fish'), mock.call_args_list)
913
+        self.assertNotIn([call('fish')], mock.call_args_list)
914
+
915
+
916
+    def test_call_list_str(self):
917
+        mock = Mock()
918
+        mock(1, 2)
919
+        mock.foo(a=3)
920
+        mock.foo.bar().baz('fish', cat='dog')
921
+
922
+        expected = (
923
+            "[call(1, 2),\n"
924
+            " call.foo(a=3),\n"
925
+            " call.foo.bar(),\n"
926
+            " call.foo.bar().baz('fish', cat='dog')]"
927
+        )
928
+        self.assertEqual(str(mock.mock_calls), expected)
929
+
930
+
931
+    @unittest.skipIf(six.PY3, "Unicode is properly handled with Python 3")
932
+    def test_call_list_unicode(self):
933
+        # See github issue #328
934
+        mock = Mock()
935
+
936
+        class NonAsciiRepr(object):
937
+            def __repr__(self):
938
+                return "\xe9"
939
+
940
+        mock(**{unicode("a"): NonAsciiRepr()})
941
+
942
+        self.assertEqual(str(mock.mock_calls), "[call(a=\xe9)]")
943
+
944
+
945
+    def test_propertymock(self):
946
+        p = patch('%s.SomeClass.one' % __name__, new_callable=PropertyMock)
947
+        mock = p.start()
948
+        try:
949
+            SomeClass.one
950
+            mock.assert_called_once_with()
951
+
952
+            s = SomeClass()
953
+            s.one
954
+            mock.assert_called_with()
955
+            self.assertEqual(mock.mock_calls, [call(), call()])
956
+
957
+            s.one = 3
958
+            self.assertEqual(mock.mock_calls, [call(), call(), call(3)])
959
+        finally:
960
+            p.stop()
961
+
962
+
963
+    def test_propertymock_returnvalue(self):
964
+        m = MagicMock()
965
+        p = PropertyMock()
966
+        type(m).foo = p
967
+
968
+        returned = m.foo
969
+        p.assert_called_once_with()
970
+        self.assertIsInstance(returned, MagicMock)
971
+        self.assertNotIsInstance(returned, PropertyMock)
972
+
973
+
974
+if __name__ == '__main__':
975
+    unittest.main()

+ 533
- 0
plex_test_case/mock/mock/tests/testmagicmethods.py View File

@@ -0,0 +1,533 @@
1
+# Copyright (C) 2007-2012 Michael Foord & the mock team
2
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
3
+# http://www.voidspace.org.uk/python/mock/
4
+
5
+from __future__ import division
6
+
7
+try:
8
+    unicode
9
+except NameError:
10
+    # Python 3
11
+    unicode = str
12
+    long = int
13
+
14
+import inspect
15
+import sys
16
+import textwrap
17
+
18
+import six
19
+import unittest2 as unittest
20
+
21
+from mock import Mock, MagicMock
22
+from mock.mock import _magics
23
+
24
+
25
+
26
+class TestMockingMagicMethods(unittest.TestCase):
27
+
28
+    def test_deleting_magic_methods(self):
29
+        mock = Mock()
30
+        self.assertFalse(hasattr(mock, '__getitem__'))
31
+
32
+        mock.__getitem__ = Mock()
33
+        self.assertTrue(hasattr(mock, '__getitem__'))
34
+
35
+        del mock.__getitem__
36
+        self.assertFalse(hasattr(mock, '__getitem__'))
37
+
38
+
39
+    def test_magicmock_del(self):
40
+        mock = MagicMock()
41
+        # before using getitem
42
+        del mock.__getitem__
43
+        self.assertRaises(TypeError, lambda: mock['foo'])
44
+
45
+        mock = MagicMock()
46
+        # this time use it first
47
+        mock['foo']
48
+        del mock.__getitem__
49
+        self.assertRaises(TypeError, lambda: mock['foo'])
50
+
51
+
52
+    def test_magic_method_wrapping(self):
53
+        mock = Mock()
54
+        def f(self, name):
55
+            return self, 'fish'
56
+
57
+        mock.__getitem__ = f
58
+        self.assertIsNot(mock.__getitem__, f)
59
+        self.assertEqual(mock['foo'], (mock, 'fish'))
60
+        self.assertEqual(mock.__getitem__('foo'), (mock, 'fish'))
61
+
62
+        mock.__getitem__ = mock
63
+        self.assertIs(mock.__getitem__, mock)
64
+
65
+
66
+    def test_magic_methods_isolated_between_mocks(self):
67
+        mock1 = Mock()
68
+        mock2 = Mock()
69
+
70
+        mock1.__iter__ = Mock(return_value=iter([]))
71
+        self.assertEqual(list(mock1), [])
72
+        self.assertRaises(TypeError, lambda: list(mock2))
73
+
74
+
75
+    def test_repr(self):
76
+        mock = Mock()
77
+        self.assertEqual(repr(mock), "<Mock id='%s'>" % id(mock))
78
+        mock.__repr__ = lambda s: 'foo'
79
+        self.assertEqual(repr(mock), 'foo')
80
+
81
+
82
+    def test_str(self):
83
+        mock = Mock()
84
+        self.assertEqual(str(mock), object.__str__(mock))
85
+        mock.__str__ = lambda s: 'foo'
86
+        self.assertEqual(str(mock), 'foo')
87
+
88
+
89
+    @unittest.skipIf(six.PY3, "no unicode in Python 3")
90
+    def test_unicode(self):
91
+        mock = Mock()
92
+        self.assertEqual(unicode(mock), unicode(str(mock)))
93
+
94
+        mock.__unicode__ = lambda s: unicode('foo')
95
+        self.assertEqual(unicode(mock), unicode('foo'))
96
+
97
+
98
+    def test_dict_methods(self):
99
+        mock = Mock()
100
+
101
+        self.assertRaises(TypeError, lambda: mock['foo'])
102
+        def _del():
103
+            del mock['foo']
104
+        def _set():
105
+            mock['foo'] = 3
106
+        self.assertRaises(TypeError, _del)
107
+        self.assertRaises(TypeError, _set)
108
+
109
+        _dict = {}
110
+        def getitem(s, name):
111
+            return _dict[name]
112
+        def setitem(s, name, value):
113
+            _dict[name] = value
114
+        def delitem(s, name):
115
+            del _dict[name]
116
+
117
+        mock.__setitem__ = setitem
118
+        mock.__getitem__ = getitem
119
+        mock.__delitem__ = delitem
120
+
121
+        self.assertRaises(KeyError, lambda: mock['foo'])
122
+        mock['foo'] = 'bar'
123
+        self.assertEqual(_dict, {'foo': 'bar'})
124
+        self.assertEqual(mock['foo'], 'bar')
125
+        del mock['foo']
126
+        self.assertEqual(_dict, {})
127
+
128
+
129
+    def test_numeric(self):
130
+        original = mock = Mock()
131
+        mock.value = 0
132
+
133
+        self.assertRaises(TypeError, lambda: mock + 3)
134
+
135
+        def add(self, other):
136
+            mock.value += other
137
+            return self
138
+        mock.__add__ = add
139
+        self.assertEqual(mock + 3, mock)
140
+        self.assertEqual(mock.value, 3)
141
+
142
+        del mock.__add__
143
+        def iadd(mock):
144
+            mock += 3
145
+        self.assertRaises(TypeError, iadd, mock)
146
+        mock.__iadd__ = add
147
+        mock += 6
148
+        self.assertEqual(mock, original)
149
+        self.assertEqual(mock.value, 9)
150
+
151
+        self.assertRaises(TypeError, lambda: 3 + mock)
152
+        mock.__radd__ = add
153
+        self.assertEqual(7 + mock, mock)
154
+        self.assertEqual(mock.value, 16)
155
+
156
+    def test_division(self):
157
+        original = mock = Mock()
158
+        mock.value = 32
159
+        self.assertRaises(TypeError, lambda: mock / 2)
160
+
161
+        def truediv(self, other):
162
+            mock.value /= other
163
+            return self
164
+        mock.__truediv__ = truediv
165
+        self.assertEqual(mock / 2, mock)
166
+        self.assertEqual(mock.value, 16)
167
+
168
+        del mock.__truediv__
169
+        if six.PY3:
170
+            def itruediv(mock):
171
+                mock /= 4
172
+            self.assertRaises(TypeError, itruediv, mock)
173
+            mock.__itruediv__ = truediv
174
+            mock /= 8
175
+            self.assertEqual(mock, original)
176
+            self.assertEqual(mock.value, 2)
177
+        else:
178
+            mock.value = 2
179
+
180
+        self.assertRaises(TypeError, lambda: 8 / mock)
181
+        mock.__rtruediv__ = truediv
182
+        self.assertEqual(0.5 / mock, mock)
183
+        self.assertEqual(mock.value, 4)
184
+
185
+    def test_hash(self):
186
+        mock = Mock()
187
+        # test delegation
188
+        self.assertEqual(hash(mock), Mock.__hash__(mock))
189
+
190
+        def _hash(s):
191
+            return 3
192
+        mock.__hash__ = _hash
193
+        self.assertEqual(hash(mock), 3)
194
+
195
+
196
+    def test_nonzero(self):
197
+        m = Mock()
198
+        self.assertTrue(bool(m))
199
+
200
+        nonzero = lambda s: False
201
+        if six.PY2:
202
+            m.__nonzero__ = nonzero
203
+        else:
204
+            m.__bool__ = nonzero
205
+
206
+        self.assertFalse(bool(m))
207
+
208
+
209
+    def test_comparison(self):
210
+        mock = Mock()
211
+        def comp(s, o):
212
+            return True
213
+        mock.__lt__ = mock.__gt__ = mock.__le__ = mock.__ge__ = comp
214
+        self. assertTrue(mock < 3)
215
+        self. assertTrue(mock > 3)
216
+        self. assertTrue(mock <= 3)
217
+        self. assertTrue(mock >= 3)
218
+
219
+        if six.PY2:
220
+            # incomparable in Python 3
221
+            self.assertEqual(Mock() < 3, object() < 3)
222
+            self.assertEqual(Mock() > 3, object() > 3)
223
+            self.assertEqual(Mock() <= 3, object() <= 3)
224
+            self.assertEqual(Mock() >= 3, object() >= 3)
225
+        else:
226
+            self.assertRaises(TypeError, lambda: MagicMock() < object())
227
+            self.assertRaises(TypeError, lambda: object() < MagicMock())
228
+            self.assertRaises(TypeError, lambda: MagicMock() < MagicMock())
229
+            self.assertRaises(TypeError, lambda: MagicMock() > object())
230
+            self.assertRaises(TypeError, lambda: object() > MagicMock())
231
+            self.assertRaises(TypeError, lambda: MagicMock() > MagicMock())
232
+            self.assertRaises(TypeError, lambda: MagicMock() <= object())
233
+            self.assertRaises(TypeError, lambda: object() <= MagicMock())
234
+            self.assertRaises(TypeError, lambda: MagicMock() <= MagicMock())
235
+            self.assertRaises(TypeError, lambda: MagicMock() >= object())
236
+            self.assertRaises(TypeError, lambda: object() >= MagicMock())
237
+            self.assertRaises(TypeError, lambda: MagicMock() >= MagicMock())
238
+
239
+
240
+    def test_equality(self):
241
+        for mock in Mock(), MagicMock():
242
+            self.assertEqual(mock == mock, True)
243
+            self.assertIsInstance(mock == mock, bool)
244
+            self.assertEqual(mock != mock, False)
245
+            self.assertIsInstance(mock != mock, bool)
246
+            self.assertEqual(mock == object(), False)
247
+            self.assertEqual(mock != object(), True)
248
+
249
+            def eq(self, other):
250
+                return other == 3
251
+            mock.__eq__ = eq
252
+            self.assertTrue(mock == 3)
253
+            self.assertFalse(mock == 4)
254
+
255
+            def ne(self, other):
256
+                return other == 3
257
+            mock.__ne__ = ne
258
+            self.assertTrue(mock != 3)
259
+            self.assertFalse(mock != 4)
260
+
261
+        mock = MagicMock()
262
+        mock.__eq__.return_value = True
263
+        self.assertIsInstance(mock == 3, bool)
264
+        self.assertEqual(mock == 3, True)
265
+
266
+        mock.__ne__.return_value = False
267
+        self.assertIsInstance(mock != 3, bool)
268
+        self.assertEqual(mock != 3, False)
269
+
270
+
271
+    def test_len_contains_iter(self):
272
+        mock = Mock()
273
+
274
+        self.assertRaises(TypeError, len, mock)
275
+        self.assertRaises(TypeError, iter, mock)
276
+        self.assertRaises(TypeError, lambda: 'foo' in mock)
277
+
278
+        mock.__len__ = lambda s: 6
279
+        self.assertEqual(len(mock), 6)
280
+
281
+        mock.__contains__ = lambda s, o: o == 3
282
+        self.assertIn(3, mock)
283
+        self.assertNotIn(6, mock)
284
+
285
+        mock.__iter__ = lambda s: iter('foobarbaz')
286
+        self.assertEqual(list(mock), list('foobarbaz'))
287
+
288
+
289
+    def test_magicmock(self):
290
+        mock = MagicMock()
291
+
292
+        mock.__iter__.return_value = iter([1, 2, 3])
293
+        self.assertEqual(list(mock), [1, 2, 3])
294
+
295
+        name = '__nonzero__'
296
+        other = '__bool__'
297
+        if six.PY3:
298
+            name, other = other, name
299
+        getattr(mock, name).return_value = False
300
+        self.assertFalse(hasattr(mock, other))
301
+        self.assertFalse(bool(mock))
302
+
303
+        for entry in _magics:
304
+            self.assertTrue(hasattr(mock, entry))
305
+        self.assertFalse(hasattr(mock, '__imaginery__'))
306
+
307
+
308
+    def test_magic_mock_equality(self):
309
+        mock = MagicMock()
310
+        self.assertIsInstance(mock == object(), bool)
311
+        self.assertIsInstance(mock != object(), bool)
312
+
313
+        self.assertEqual(mock == object(), False)
314
+        self.assertEqual(mock != object(), True)
315
+        self.assertEqual(mock == mock, True)
316
+        self.assertEqual(mock != mock, False)
317
+
318
+
319
+    def test_magicmock_defaults(self):
320
+        mock = MagicMock()
321
+        self.assertEqual(int(mock), 1)
322
+        self.assertEqual(complex(mock), 1j)
323
+        self.assertEqual(float(mock), 1.0)
324
+        self.assertEqual(long(mock), long(1))
325
+        self.assertNotIn(object(), mock)
326
+        self.assertEqual(len(mock), 0)
327
+        self.assertEqual(list(mock), [])
328
+        self.assertEqual(hash(mock), object.__hash__(mock))
329
+        self.assertEqual(str(mock), object.__str__(mock))
330
+        self.assertEqual(unicode(mock), object.__str__(mock))
331
+        self.assertIsInstance(unicode(mock), unicode)
332
+        self.assertTrue(bool(mock))
333
+        if six.PY2:
334
+            self.assertEqual(oct(mock), '1')
335
+        else:
336
+            # in Python 3 oct and hex use __index__
337
+            # so these tests are for __index__ in py3k
338
+            self.assertEqual(oct(mock), '0o1')
339
+        self.assertEqual(hex(mock), '0x1')
340
+        # how to test __sizeof__ ?
341
+
342
+
343
+    @unittest.skipIf(six.PY3, "no __cmp__ in Python 3")
344
+    def test_non_default_magic_methods(self):
345
+        mock = MagicMock()
346
+        self.assertRaises(AttributeError, lambda: mock.__cmp__)
347
+
348
+        mock = Mock()
349
+        mock.__cmp__ = lambda s, o: 0
350
+
351
+        self.assertEqual(mock, object())
352
+
353
+
354
+    def test_magic_methods_and_spec(self):
355
+        class Iterable(object):
356
+            def __iter__(self):
357
+                pass
358
+
359
+        mock = Mock(spec=Iterable)
360
+        self.assertRaises(AttributeError, lambda: mock.__iter__)
361
+
362
+        mock.__iter__ = Mock(return_value=iter([]))
363
+        self.assertEqual(list(mock), [])
364
+
365
+        class NonIterable(object):
366
+            pass
367
+        mock = Mock(spec=NonIterable)
368
+        self.assertRaises(AttributeError, lambda: mock.__iter__)
369
+
370
+        def set_int():
371
+            mock.__int__ = Mock(return_value=iter([]))
372
+        self.assertRaises(AttributeError, set_int)
373
+
374
+        mock = MagicMock(spec=Iterable)
375
+        self.assertEqual(list(mock), [])
376
+        self.assertRaises(AttributeError, set_int)
377
+
378
+
379
+    def test_magic_methods_and_spec_set(self):
380
+        class Iterable(object):
381
+            def __iter__(self):
382
+                pass
383
+
384
+        mock = Mock(spec_set=Iterable)
385
+        self.assertRaises(AttributeError, lambda: mock.__iter__)
386
+
387
+        mock.__iter__ = Mock(return_value=iter([]))
388
+        self.assertEqual(list(mock), [])
389
+
390
+        class NonIterable(object):
391
+            pass
392
+        mock = Mock(spec_set=NonIterable)
393
+        self.assertRaises(AttributeError, lambda: mock.__iter__)
394
+
395
+        def set_int():
396
+            mock.__int__ = Mock(return_value=iter([]))
397
+        self.assertRaises(AttributeError, set_int)
398
+
399
+        mock = MagicMock(spec_set=Iterable)
400
+        self.assertEqual(list(mock), [])
401
+        self.assertRaises(AttributeError, set_int)
402
+
403
+
404
+    def test_setting_unsupported_magic_method(self):
405
+        mock = MagicMock()
406
+        def set_setattr():
407
+            mock.__setattr__ = lambda self, name: None
408
+        self.assertRaisesRegex(AttributeError,
409
+            "Attempting to set unsupported magic method '__setattr__'.",
410
+            set_setattr
411
+        )
412
+
413
+
414
+    def test_attributes_and_return_value(self):
415
+        mock = MagicMock()
416
+        attr = mock.foo
417
+        def _get_type(obj):
418
+            # the type of every mock (or magicmock) is a custom subclass
419
+            # so the real type is the second in the mro
420
+            return type(obj).__mro__[1]
421
+        self.assertEqual(_get_type(attr), MagicMock)
422
+
423
+        returned = mock()
424
+        self.assertEqual(_get_type(returned), MagicMock)
425
+
426
+
427
+    def test_magic_methods_are_magic_mocks(self):
428
+        mock = MagicMock()
429
+        self.assertIsInstance(mock.__getitem__, MagicMock)
430
+
431
+        mock[1][2].__getitem__.return_value = 3
432
+        self.assertEqual(mock[1][2][3], 3)
433
+
434
+
435
+    def test_magic_method_reset_mock(self):
436
+        mock = MagicMock()
437
+        str(mock)
438
+        self.assertTrue(mock.__str__.called)
439
+        mock.reset_mock()
440
+        self.assertFalse(mock.__str__.called)
441
+
442
+    def test_dir(self):
443
+        # overriding the default implementation
444
+        for mock in Mock(), MagicMock():
445
+            def _dir(self):
446
+                return ['foo']
447
+            mock.__dir__ = _dir
448
+            self.assertEqual(dir(mock), ['foo'])
449
+
450
+
451
+    @unittest.skipIf('PyPy' in sys.version, "This fails differently on pypy")
452
+    def test_bound_methods(self):
453
+        m = Mock()
454
+
455
+        # XXXX should this be an expected failure instead?
456
+
457
+        # this seems like it should work, but is hard to do without introducing
458
+        # other api inconsistencies. Failure message could be better though.
459
+        m.__iter__ = [3].__iter__
460
+        self.assertRaises(TypeError, iter, m)
461
+
462
+
463
+    def test_magic_method_type(self):
464
+        class Foo(MagicMock):
465
+            pass
466
+
467
+        foo = Foo()
468
+        self.assertIsInstance(foo.__int__, Foo)
469
+
470
+
471
+    def test_descriptor_from_class(self):
472
+        m = MagicMock()
473
+        type(m).__str__.return_value = 'foo'
474
+        self.assertEqual(str(m), 'foo')
475
+
476
+
477
+    def test_iterable_as_iter_return_value(self):
478
+        m = MagicMock()
479
+        m.__iter__.return_value = [1, 2, 3]
480
+        self.assertEqual(list(m), [1, 2, 3])
481
+        self.assertEqual(list(m), [1, 2, 3])
482
+
483
+        m.__iter__.return_value = iter([4, 5, 6])
484
+        self.assertEqual(list(m), [4, 5, 6])
485
+        self.assertEqual(list(m), [])
486
+
487
+    @unittest.skipIf(sys.version_info < (3, 5), "@ added in Python 3.5")
488
+    def test_matmul(self):
489
+        src = textwrap.dedent("""\
490
+            m = MagicMock()
491
+            self.assertIsInstance(m @ 1, MagicMock)
492
+            m.__matmul__.return_value = 42
493
+            m.__rmatmul__.return_value = 666
494
+            m.__imatmul__.return_value = 24
495
+            self.assertEqual(m @ 1, 42)
496
+            self.assertEqual(1 @ m, 666)
497
+            m @= 24
498
+            self.assertEqual(m, 24)
499
+        """)
500
+        exec(src)
501
+
502
+    def test_divmod_and_rdivmod(self):
503
+        m = MagicMock()
504
+        self.assertIsInstance(divmod(5, m), MagicMock)
505
+        m.__divmod__.return_value = (2, 1)
506
+        self.assertEqual(divmod(m, 2), (2, 1))
507
+        m = MagicMock()
508
+        foo = divmod(2, m)
509
+        self.assertIsInstance(foo, MagicMock)
510
+        foo_direct = m.__divmod__(2)
511
+        self.assertIsInstance(foo_direct, MagicMock)
512
+        bar = divmod(m, 2)
513
+        self.assertIsInstance(bar, MagicMock)
514
+        bar_direct = m.__rdivmod__(2)
515
+        self.assertIsInstance(bar_direct, MagicMock)
516
+
517
+    # http://bugs.python.org/issue23310
518
+    # Check if you can change behaviour of magic methds in MagicMock init
519
+    def test_magic_in_initialization(self):
520
+        m = MagicMock(**{'__str__.return_value': "12"})
521
+        self.assertEqual(str(m), "12")
522
+
523
+    def test_changing_magic_set_in_initialization(self):
524
+        m = MagicMock(**{'__str__.return_value': "12"})
525
+        m.__str__.return_value = "13"
526
+        self.assertEqual(str(m), "13")
527
+        m = MagicMock(**{'__str__.return_value': "12"})
528
+        m.configure_mock(**{'__str__.return_value': "14"})
529
+        self.assertEqual(str(m), "14")
530
+
531
+
532
+if __name__ == '__main__':
533
+    unittest.main()

+ 1593
- 0
plex_test_case/mock/mock/tests/testmock.py
File diff suppressed because it is too large
View File


+ 1883
- 0
plex_test_case/mock/mock/tests/testpatch.py
File diff suppressed because it is too large
View File


+ 33
- 0
plex_test_case/mock/mock/tests/testsentinel.py View File

@@ -0,0 +1,33 @@
1
+# Copyright (C) 2007-2012 Michael Foord & the mock team
2
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
3
+# http://www.voidspace.org.uk/python/mock/
4
+
5
+import unittest2 as unittest
6
+
7
+from mock import sentinel, DEFAULT
8
+
9
+
10
+class SentinelTest(unittest.TestCase):
11
+
12
+    def testSentinels(self):
13
+        self.assertEqual(sentinel.whatever, sentinel.whatever,
14
+                         'sentinel not stored')
15
+        self.assertNotEqual(sentinel.whatever, sentinel.whateverelse,
16
+                            'sentinel should be unique')
17
+
18
+
19
+    def testSentinelName(self):
20
+        self.assertEqual(str(sentinel.whatever), 'sentinel.whatever',
21
+                         'sentinel name incorrect')
22
+
23
+
24
+    def testDEFAULT(self):
25
+        self.assertIs(DEFAULT, sentinel.DEFAULT)
26
+
27
+    def testBases(self):
28
+        # If this doesn't raise an AttributeError then help(mock) is broken
29
+        self.assertRaises(AttributeError, lambda: sentinel.__bases__)
30
+
31
+
32
+if __name__ == '__main__':
33
+    unittest.main()

+ 306
- 0
plex_test_case/mock/mock/tests/testwith.py View File

@@ -0,0 +1,306 @@
1
+# Copyright (C) 2007-2012 Michael Foord & the mock team
2
+# E-mail: fuzzyman AT voidspace DOT org DOT uk
3
+# http://www.voidspace.org.uk/python/mock/
4
+
5
+from warnings import catch_warnings
6
+
7
+import unittest2 as unittest
8
+
9
+from mock.tests.support import is_instance
10
+from mock import MagicMock, Mock, patch, sentinel, mock_open, call
11
+
12
+
13
+something  = sentinel.Something
14
+something_else  = sentinel.SomethingElse
15
+
16
+
17
+
18
+class WithTest(unittest.TestCase):
19
+
20
+    def test_with_statement(self):
21
+        with patch('%s.something' % __name__, sentinel.Something2):
22
+            self.assertEqual(something, sentinel.Something2, "unpatched")
23
+        self.assertEqual(something, sentinel.Something)
24
+
25
+
26
+    def test_with_statement_exception(self):
27
+        try:
28
+            with patch('%s.something' % __name__, sentinel.Something2):
29
+                self.assertEqual(something, sentinel.Something2, "unpatched")
30
+                raise Exception('pow')
31
+        except Exception:
32
+            pass
33
+        else:
34
+            self.fail("patch swallowed exception")
35
+        self.assertEqual(something, sentinel.Something)
36
+
37
+
38
+    def test_with_statement_as(self):
39
+        with patch('%s.something' % __name__) as mock_something:
40
+            self.assertEqual(something, mock_something, "unpatched")
41
+            self.assertTrue(is_instance(mock_something, MagicMock),
42
+                            "patching wrong type")
43
+        self.assertEqual(something, sentinel.Something)
44
+
45
+
46
+    def test_patch_object_with_statement(self):
47
+        class Foo(object):
48
+            something = 'foo'
49
+        original = Foo.something
50
+        with patch.object(Foo, 'something'):
51
+            self.assertNotEqual(Foo.something, original, "unpatched")
52
+        self.assertEqual(Foo.something, original)
53
+
54
+
55
+    def test_with_statement_nested(self):
56
+        with catch_warnings(record=True):
57
+            with patch('%s.something' % __name__) as mock_something:
58
+                with patch('%s.something_else' % __name__) as mock_something_else:
59
+                    self.assertEqual(something, mock_something, "unpatched")
60
+                    self.assertEqual(something_else, mock_something_else,
61
+                                     "unpatched")
62
+
63
+        self.assertEqual(something, sentinel.Something)
64
+        self.assertEqual(something_else, sentinel.SomethingElse)
65
+
66
+
67
+    def test_with_statement_specified(self):
68
+        with patch('%s.something' % __name__, sentinel.Patched) as mock_something:
69
+            self.assertEqual(something, mock_something, "unpatched")
70
+            self.assertEqual(mock_something, sentinel.Patched, "wrong patch")
71
+        self.assertEqual(something, sentinel.Something)
72
+
73
+
74
+    def testContextManagerMocking(self):
75
+        mock = Mock()
76
+        mock.__enter__ = Mock()
77
+        mock.__exit__ = Mock()
78
+        mock.__exit__.return_value = False
79
+
80
+        with mock as m:
81
+            self.assertEqual(m, mock.__enter__.return_value)
82
+        mock.__enter__.assert_called_with()
83
+        mock.__exit__.assert_called_with(None, None, None)
84
+
85
+
86
+    def test_context_manager_with_magic_mock(self):
87
+        mock = MagicMock()
88
+
89
+        with self.assertRaises(TypeError):
90
+            with mock:
91
+                'foo' + 3
92
+        mock.__enter__.assert_called_with()
93
+        self.assertTrue(mock.__exit__.called)
94
+
95
+
96
+    def test_with_statement_same_attribute(self):
97
+        with patch('%s.something' % __name__, sentinel.Patched) as mock_something:
98
+            self.assertEqual(something, mock_something, "unpatched")
99
+
100
+            with patch('%s.something' % __name__) as mock_again:
101
+                self.assertEqual(something, mock_again, "unpatched")
102
+
103
+            self.assertEqual(something, mock_something,
104
+                             "restored with wrong instance")
105
+
106
+        self.assertEqual(something, sentinel.Something, "not restored")
107
+
108
+
109
+    def test_with_statement_imbricated(self):
110
+        with patch('%s.something' % __name__) as mock_something:
111
+            self.assertEqual(something, mock_something, "unpatched")
112
+
113
+            with patch('%s.something_else' % __name__) as mock_something_else:
114
+                self.assertEqual(something_else, mock_something_else,
115
+                                 "unpatched")
116
+
117
+        self.assertEqual(something, sentinel.Something)
118
+        self.assertEqual(something_else, sentinel.SomethingElse)
119
+
120
+
121
+    def test_dict_context_manager(self):
122
+        foo = {}
123
+        with patch.dict(foo, {'a': 'b'}):
124
+            self.assertEqual(foo, {'a': 'b'})
125
+        self.assertEqual(foo, {})
126
+
127
+        with self.assertRaises(NameError):
128
+            with patch.dict(foo, {'a': 'b'}):
129
+                self.assertEqual(foo, {'a': 'b'})
130
+                raise NameError('Konrad')
131
+
132
+        self.assertEqual(foo, {})
133
+
134
+
135
+
136
+class TestMockOpen(unittest.TestCase):
137
+
138
+    def test_mock_open(self):
139
+        mock = mock_open()
140
+        with patch('%s.open' % __name__, mock, create=True) as patched:
141
+            self.assertIs(patched, mock)
142
+            open('foo')
143
+
144
+        mock.assert_called_once_with('foo')
145
+
146
+
147
+    def test_mock_open_context_manager(self):
148
+        mock = mock_open()
149
+        handle = mock.return_value
150
+        with patch('%s.open' % __name__, mock, create=True):
151
+            with open('foo') as f:
152
+                f.read()
153
+
154
+        expected_calls = [call('foo'), call().__enter__(), call().read(),
155
+                          call().__exit__(None, None, None)]
156
+        self.assertEqual(mock.mock_calls, expected_calls)
157
+        self.assertIs(f, handle)
158
+
159
+    def test_mock_open_context_manager_multiple_times(self):
160
+        mock = mock_open()
161
+        with patch('%s.open' % __name__, mock, create=True):
162
+            with open('foo') as f:
163
+                f.read()
164
+            with open('bar') as f:
165
+                f.read()
166
+
167
+        expected_calls = [
168
+            call('foo'), call().__enter__(), call().read(),
169
+            call().__exit__(None, None, None),
170
+            call('bar'), call().__enter__(), call().read(),
171
+            call().__exit__(None, None, None)]
172
+        self.assertEqual(mock.mock_calls, expected_calls)
173
+
174
+    def test_explicit_mock(self):
175
+        mock = MagicMock()
176
+        mock_open(mock)
177
+
178
+        with patch('%s.open' % __name__, mock, create=True) as patched:
179
+            self.assertIs(patched, mock)
180
+            open('foo')
181
+
182
+        mock.assert_called_once_with('foo')
183
+
184
+
185
+    def test_read_data(self):
186
+        mock = mock_open(read_data='foo')
187
+        with patch('%s.open' % __name__, mock, create=True):
188
+            h = open('bar')
189
+            result = h.read()
190
+
191
+        self.assertEqual(result, 'foo')
192
+
193
+
194
+    def test_readline_data(self):
195
+        # Check that readline will return all the lines from the fake file
196
+        mock = mock_open(read_data='foo\nbar\nbaz\n')
197
+        with patch('%s.open' % __name__, mock, create=True):
198
+            h = open('bar')
199
+            line1 = h.readline()
200
+            line2 = h.readline()
201
+            line3 = h.readline()
202
+        self.assertEqual(line1, 'foo\n')
203
+        self.assertEqual(line2, 'bar\n')
204
+        self.assertEqual(line3, 'baz\n')
205
+
206
+        # Check that we properly emulate a file that doesn't end in a newline
207
+        mock = mock_open(read_data='foo')
208
+        with patch('%s.open' % __name__, mock, create=True):
209
+            h = open('bar')
210
+            result = h.readline()
211
+        self.assertEqual(result, 'foo')
212
+
213
+
214
+    def test_readlines_data(self):
215
+        # Test that emulating a file that ends in a newline character works
216
+        mock = mock_open(read_data='foo\nbar\nbaz\n')
217
+        with patch('%s.open' % __name__, mock, create=True):
218
+            h = open('bar')
219
+            result = h.readlines()
220
+        self.assertEqual(result, ['foo\n', 'bar\n', 'baz\n'])
221
+
222
+        # Test that files without a final newline will also be correctly
223
+        # emulated
224
+        mock = mock_open(read_data='foo\nbar\nbaz')
225
+        with patch('%s.open' % __name__, mock, create=True):
226
+            h = open('bar')
227
+            result = h.readlines()
228
+
229
+        self.assertEqual(result, ['foo\n', 'bar\n', 'baz'])
230
+
231
+
232
+    def test_read_bytes(self):
233
+        mock = mock_open(read_data=b'\xc6')
234
+        with patch('%s.open' % __name__, mock, create=True):
235
+            with open('abc', 'rb') as f:
236
+                result = f.read()
237
+        self.assertEqual(result, b'\xc6')
238
+
239
+
240
+    def test_readline_bytes(self):
241
+        m = mock_open(read_data=b'abc\ndef\nghi\n')
242
+        with patch('%s.open' % __name__, m, create=True):
243
+            with open('abc', 'rb') as f:
244
+                line1 = f.readline()
245
+                line2 = f.readline()
246
+                line3 = f.readline()
247
+        self.assertEqual(line1, b'abc\n')
248
+        self.assertEqual(line2, b'def\n')
249
+        self.assertEqual(line3, b'ghi\n')
250
+
251
+
252
+    def test_readlines_bytes(self):
253
+        m = mock_open(read_data=b'abc\ndef\nghi\n')
254
+        with patch('%s.open' % __name__, m, create=True):
255
+            with open('abc', 'rb') as f:
256
+                result = f.readlines()
257
+        self.assertEqual(result, [b'abc\n', b'def\n', b'ghi\n'])
258
+
259
+
260
+    def test_mock_open_read_with_argument(self):
261
+        # At one point calling read with an argument was broken
262
+        # for mocks returned by mock_open
263
+        some_data = 'foo\nbar\nbaz'
264
+        mock = mock_open(read_data=some_data)
265
+        self.assertEqual(mock().read(10), some_data)
266
+
267
+
268
+    def test_interleaved_reads(self):
269
+        # Test that calling read, readline, and readlines pulls data
270
+        # sequentially from the data we preload with
271
+        mock = mock_open(read_data='foo\nbar\nbaz\n')
272
+        with patch('%s.open' % __name__, mock, create=True):
273
+            h = open('bar')
274
+            line1 = h.readline()
275
+            rest = h.readlines()
276
+        self.assertEqual(line1, 'foo\n')
277
+        self.assertEqual(rest, ['bar\n', 'baz\n'])
278
+
279
+        mock = mock_open(read_data='foo\nbar\nbaz\n')
280
+        with patch('%s.open' % __name__, mock, create=True):
281
+            h = open('bar')
282
+            line1 = h.readline()
283
+            rest = h.read()
284
+        self.assertEqual(line1, 'foo\n')
285
+        self.assertEqual(rest, 'bar\nbaz\n')
286
+
287
+
288
+    def test_overriding_return_values(self):
289
+        mock = mock_open(read_data='foo')
290
+        handle = mock()
291
+
292
+        handle.read.return_value = 'bar'
293
+        handle.readline.return_value = 'bar'
294
+        handle.readlines.return_value = ['bar']
295
+
296
+        self.assertEqual(handle.read(), 'bar')
297
+        self.assertEqual(handle.readline(), 'bar')
298
+        self.assertEqual(handle.readlines(), ['bar'])
299
+
300
+        # call repeatedly to check that a StopIteration is not propagated
301
+        self.assertEqual(handle.readline(), 'bar')
302
+        self.assertEqual(handle.readline(), 'bar')
303
+
304
+
305
+if __name__ == '__main__':
306
+    unittest.main()

+ 6
- 0
plex_test_case/mock/requirements.txt View File

@@ -0,0 +1,6 @@
1
+funcsigs>=1;python_version<"3.3"
2
+# For runtime needs this is correct. For setup_requires needs, 1.2.0 is needed
3
+# but setuptools can't cope with conflicts in setup_requires, so thats
4
+# unversioned.
5
+pbr>=0.11
6
+six>=1.9

+ 45
- 0
plex_test_case/mock/setup.cfg View File

@@ -0,0 +1,45 @@
1
+[metadata]
2
+name = mock
3
+summary = Rolling backport of unittest.mock for all Pythons
4
+home-page = https://github.com/testing-cabal/mock
5
+description-file = README.rst
6
+author = Testing Cabal
7
+author-email = testing-in-python@lists.idyll.org
8
+classifier = 
9
+    Development Status :: 5 - Production/Stable
10
+    Environment :: Console
11
+    Intended Audience :: Developers
12
+    License :: OSI Approved :: BSD License
13
+    Operating System :: OS Independent
14
+    Programming Language :: Python
15
+    Programming Language :: Python :: 2
16
+    Programming Language :: Python :: 2.6
17
+    Programming Language :: Python :: 2.7
18
+    Programming Language :: Python :: 3
19
+    Programming Language :: Python :: 3.2
20
+    Programming Language :: Python :: 3.3
21
+    Programming Language :: Python :: 3.4
22
+    Programming Language :: Python :: 3.5
23
+    Programming Language :: Python :: Implementation :: CPython
24
+    Programming Language :: Python :: Implementation :: Jython
25
+    Programming Language :: Python :: Implementation :: PyPy
26
+    Topic :: Software Development :: Libraries
27
+    Topic :: Software Development :: Libraries :: Python Modules
28
+    Topic :: Software Development :: Testing
29
+keyword =
30
+    testing, test, mock, mocking, unittest, patching, stubs, fakes, doubles
31
+
32
+[extras]
33
+test =
34
+  unittest2>=1.1.0
35
+docs =
36
+  jinja2<2.7:python_version<"3.3" and python_version>="3"
37
+  Pygments<2:python_version<"3.3" and python_version>="3"
38
+  sphinx<1.3:python_version<"3.3" and python_version>="3"
39
+  sphinx:python_version<"3" or python_version>="3.3"
40
+
41
+[files]
42
+packages = mock
43
+
44
+[bdist_wheel]
45
+universal = 1

+ 6
- 0
plex_test_case/mock/setup.py View File

@@ -0,0 +1,6 @@
1
+#!/usr/bin/env python
2
+import setuptools
3
+
4
+setuptools.setup(
5
+    setup_requires=['pbr>=1.3', 'setuptools>=17.1'],
6
+    pbr=True)

+ 38
- 0
plex_test_case/mock/tools/applypatch-transform View File

@@ -0,0 +1,38 @@
1
+#!/bin/sh
2
+#
3
+# An example hook script to transform a patch taken from an email
4
+# by git am.
5
+#
6
+# The hook should exit with non-zero status after issuing an
7
+# appropriate message if it wants to stop the commit.  The hook is
8
+# allowed to edit the patch file.
9
+#
10
+# To enable this hook, rename this file to "applypatch-transform".
11
+#
12
+# This example changes the path of Lib/unittest/mock.py to mock.py
13
+# Lib/unittest/tests/testmock to tests and Misc/NEWS to NEWS, and
14
+# finally skips any patches that did not alter mock.py or its tests.
15
+
16
+set -eux
17
+
18
+patch_path=$1
19
+
20
+# Pull out mock.py
21
+filterdiff --clean --strip 3 --addprefix=a/mock/ -i 'a/Lib/unittest/mock.py' -i 'b/Lib/unittest/mock.py' $patch_path > $patch_path.mock
22
+# And the tests
23
+filterdiff --clean --strip 5 --addprefix=a/mock/tests/ -i 'a/Lib/unittest/test/testmock/*.py' -i 'b/Lib/unittest/test/testmock/*.py' $patch_path > $patch_path.tests
24
+# Lastly we want to pick up any NEWS entries.
25
+filterdiff --strip 2 --addprefix=a/ -i a/Misc/NEWS -i b/Misc/NEWS $patch_path > $patch_path.NEWS
26
+cp $patch_path $patch_path.orig
27
+# bash
28
+cat $patch_path.mock $patch_path.tests > $patch_path
29
+filtered=$(cat $patch_path)
30
+if [ -n "${filtered}" ]; then
31
+  cat $patch_path.NEWS >> $patch_path
32
+  exitcode=0
33
+else
34
+  exitcode=1
35
+fi
36
+
37
+rm $patch_path.mock $patch_path.tests $patch_path.NEWS
38
+exit $exitcode

+ 37
- 0
plex_test_case/mock/tools/pre-applypatch View File

@@ -0,0 +1,37 @@
1
+#!/bin/bash
2
+#
3
+# An example hook script to verify what is about to be committed
4
+# by applypatch from an e-mail message.
5
+#
6
+# The hook should exit with non-zero status after issuing an
7
+# appropriate message if it wants to stop the commit.
8
+#
9
+# To enable this hook, rename this file to "pre-applypatch".
10
+
11
+set -eu
12
+
13
+#. git-sh-setup
14
+echo "** in hook **"
15
+
16
+function test_version {
17
+  version=$1
18
+  host=$(ls ~/.virtualenvs/mock-$version-* -d | sed -e "s/^.*mock-$version-//")
19
+  if [ -z "$host" ]; then
20
+    echo "No host found for $version"
21
+    return 1
22
+  fi
23
+  echo testing $version in virtualenv mock-$version-$host on ssh host $host
24
+  ssh $host "cd work/mock && . ~/.virtualenvs/mock-$version-$host/bin/activate && pip install .[test] && unit2"
25
+}
26
+
27
+find . -name "*.pyc" -exec rm "{}" \;
28
+
29
+test_version 2.6
30
+test_version 2.7
31
+test_version 3.3
32
+test_version 3.4
33
+test_version 3.5
34
+test_version cpython
35
+test_version pypy
36
+
37
+echo '** pre-apply complete and successful **'

+ 40
- 0
plex_test_case/mock/tox.ini View File

@@ -0,0 +1,40 @@
1
+[tox]
2
+envlist = py25,py26,py27,py31,pypy,py32,py33,jython
3
+
4
+[testenv]
5
+deps=unittest2
6
+commands={envbindir}/unit2 discover []
7
+
8
+[testenv:py26]
9
+commands=
10
+    {envbindir}/unit2 discover []
11
+    {envbindir}/sphinx-build -E -b doctest docs html
12
+    {envbindir}/sphinx-build -E docs html
13
+deps =
14
+    unittest2
15
+    sphinx
16
+
17
+[testenv:py27]
18
+commands=
19
+    {envbindir}/unit2 discover []
20
+    {envbindir}/sphinx-build -E -b doctest docs html
21
+deps =
22
+    unittest2
23
+    sphinx
24
+
25
+[testenv:py31]
26
+deps =
27
+    unittest2py3k
28
+
29
+[testenv:py32]
30
+commands=
31
+    {envbindir}/python -m unittest discover []
32
+deps =
33
+
34
+[testenv:py33]
35
+commands=
36
+    {envbindir}/python -m unittest discover []
37
+deps =
38
+
39
+# note for jython. Execute in tests directory:
40
+# rm `find . -name '*$py.class'`

+ 95
- 0
plex_test_case/mock/unittest.cfg View File

@@ -0,0 +1,95 @@
1
+
2
+[unittest]
3
+plugins = 
4
+    unittest2.plugins.debugger
5
+    unittest2.plugins.checker
6
+    unittest2.plugins.doctestloader
7
+    unittest2.plugins.matchregexp
8
+    unittest2.plugins.moduleloading
9
+    unittest2.plugins.testcoverage
10
+    unittest2.plugins.growl
11
+    unittest2.plugins.filtertests
12
+    unittest2.plugins.junitxml
13
+    unittest2.plugins.timed
14
+    unittest2.plugins.counttests
15
+    unittest2.plugins.logchannels
16
+
17
+excluded-plugins =
18
+
19
+# 0, 1 or 2 (default is 1)
20
+# quiet, normal or verbose
21
+# can be overriden at command line
22
+verbosity = normal
23
+
24
+# true or false
25
+# even if false can be switched on at command line
26
+catch =
27
+buffer =
28
+failfast =
29
+
30
+
31
+[matchregexp]
32
+always-on = False
33
+full-path = True
34
+
35
+[debugger]
36
+always-on = False
37
+errors-only = True
38
+
39
+[coverage]
40
+always-on = False
41
+config =
42
+report-html = False
43
+# only used if report-html is false
44
+annotate = False
45
+# defaults to './htmlcov/'
46
+html-directory =
47
+# if unset will output to console
48
+text-file =
49
+branch = False
50
+timid = False
51
+cover-pylib = False
52
+exclude-lines = 
53
+    # Have to re-enable the standard pragma
54
+    pragma: no cover
55
+
56
+    # Don't complain about missing debug-only code:
57
+    def __repr__
58
+    if self\.debug
59
+
60
+    # Don't complain if tests don't hit defensive assertion code:
61
+    raise AssertionError
62
+    raise NotImplementedError
63
+
64
+    # Don't complain if non-runnable code isn't run:
65
+    if 0:
66
+    if __name__ == .__main__.
67
+    
68
+ignore-errors = False
69
+modules =
70
+
71
+[growl]
72
+always-on = False
73
+
74
+[doctest]
75
+always-on = False
76
+
77
+[module-loading]
78
+always-on = False
79
+
80
+[checker]
81
+always-on = False
82
+pep8 = False
83
+pyflakes = True
84
+
85
+[junit-xml]
86
+always-on = False
87
+path = junit.xml
88
+
89
+[timed]
90
+always-on = True
91
+threshold = 0.01
92
+
93
+[count]
94
+always-on = True
95
+enhanced = False

+ 186
- 0
plex_test_case/plex.py View File

@@ -0,0 +1,186 @@
1
+import httplib
2
+import inspect
3
+import logging
4
+import os
5
+import sys
6
+import urllib2
7
+from Framework import components, core
8
+from StringIO import StringIO
9
+
10
+
11
+GLOBAL_DEFAULT_TIMEOUT = components.networking.GLOBAL_DEFAULT_TIMEOUT
12
+
13
+class BasicRequest(object):
14
+
15
+    def __init__(self, uri, headers={}, method='GET', body=''):
16
+        self.uri = uri
17
+        self.headers = headers
18
+        self.method = method
19
+        self.body = body
20
+
21
+
22
+class FrameworkCore(core.FrameworkCore):
23
+    """
24
+        Framework core class extension to modify components in testing environment.
25
+    """
26
+    def _setup_storage(self):
27
+        # Don't configure output into real log file.
28
+        logging.basicConfig(level=logging.INFO)
29
+        #logging.setLoggerClass(core.FrameworkLogger)
30
+        logger = logging.getLogger()
31
+        #logger.handlers[0].addFilter(core.LogFilter())
32
+        #self.log = logging.getLogger(self.identifier)
33
+        self.log = logging.getLogger()
34
+        self._start_component(components.Storage)
35
+        self._values = {}
36
+        #self._values_file_path = os.path.join(self.plugin_support_path, 'Data', self.identifier, 'StoredValues')
37
+        self._values_file_path = os.path.join(self.bundle_path, 'Data', 'StoredValues')
38
+
39
+
40
+    def get_server_info(self):
41
+        # Don't do actual request to Plex server and return dummy values.
42
+        self.attributes['machineIdentifier'] = 'Unit testing'
43
+        self.attributes['serverVersion'] = None
44
+
45
+    def start(self):
46
+        # Same implementation as in parent class, just without error handling, to allow us to see what went wrong.
47
+        if self.init_code:
48
+            self.sandbox.execute(self.init_code)
49
+        self.sandbox.call_named_function(core.START_FUNCTION_NAME)
50
+
51
+    def log_exception(self, fmt, *args):
52
+        error = sys.exc_info()
53
+        if error:
54
+            # Catch a special case in the original `Runtime.handle_request`. If error is allowed to raise here, then
55
+            # the local variable `body` will not be set and the code fail on `UnboundLocalError` in `finally` later.
56
+            (frame, _, _, function_name, _, _) = inspect.getouterframes(inspect.currentframe())[1]
57
+            if function_name == 'handle_request' and frame.f_locals['self'].__class__ == Runtime:
58
+                self.runtime.request_error = error
59
+                return
60
+            # In general case, just raise the error.
61
+            raise error[0], error[1], error[2]
62
+
63
+    def _start_component(self, component):
64
+        # Replace runtime and networking components with our implementations.
65
+        if component == components.Runtime:
66
+            component = Runtime
67
+        elif component == components.Networking:
68
+            component = Networking
69
+        super(FrameworkCore, self)._start_component(component)
70
+
71
+
72
+class PreferenceManager(object):
73
+
74
+    def __init__(self, values={}):
75
+        #TODO ielasa no xml faila ja ir
76
+        self._dict = values
77
+
78
+    def get(self):
79
+        return self._dict
80
+
81
+
82
+class ResponseHeaders(object):
83
+
84
+    def __init__(self, headers):
85
+        self.dict = {k: v.lower() for k, v in headers.iteritems()}
86
+
87
+    def get(self, header, default=None):
88
+        lowercase = header.lower()
89
+        return self.dict[lowercase] if lowercase in self.dict else default
90
+
91
+
92
+class ResponseBody(StringIO):
93
+
94
+    def __init__(self, string):
95
+        StringIO.__init__(self, string)
96
+        self.recv = None
97
+
98
+    @property
99
+    def _sock(self):
100
+        return self
101
+
102
+
103
+class MockNetworkingHandler(object):
104
+
105
+    def __init__(self, networking):
106
+        self._networking = networking
107
+
108
+    def get_mock_response(self, req):
109
+        if self._networking.http_response_body is not None:
110
+            response = urllib2.addinfourl(ResponseBody(self._networking.http_response_body),
111
+                                          ResponseHeaders(self._networking.http_response_headers),
112
+                                          req.get_full_url())
113
+            response.code = 200
114
+            response.msg = "OK"
115
+            return response
116
+        raise RuntimeError('Unhandled HTTP request: %s' % req.get_full_url())
117
+
118
+
119
+class HTTPHandler(urllib2.HTTPHandler, MockNetworkingHandler):
120
+
121
+    def __init__(self, networking):
122
+        MockNetworkingHandler.__init__(self, networking)
123
+
124
+    def http_open(self, req):
125
+        return self.get_mock_response(req)
126
+
127
+
128
+if hasattr(httplib, 'HTTPS'):
129
+    class HTTPSHandler(urllib2.HTTPSHandler, MockNetworkingHandler):
130
+
131
+        def __init__(self, networking):
132
+            MockNetworkingHandler.__init__(self, networking)
133
+
134
+        def https_open(self, req):
135
+            return self.get_mock_response(req)
136
+
137
+
138
+class Networking(components.Networking):
139
+    """
140
+        Networking class extension to avoid sending actual requests in tests.
141
+    """
142
+    def _init(self):
143
+        super(Networking, self)._init()
144
+        self.http_response_body = None
145
+        self.http_response_headers = {}
146
+        self._core.policy.enable_http_caching = False
147
+
148
+    def build_opener(self, cookie_jar=None):
149
+        return urllib2.build_opener(HTTPHandler(self), HTTPSHandler(self))
150
+
151
+
152
+class Runtime(components.Runtime):
153
+    """
154
+        Runtime class extension to keep track of the framework's threads and locks.
155
+    """
156
+    def _init(self):
157
+        self._threads = []
158
+        super(Runtime, self)._init()
159
+
160
+    def create_thread(self, f, log=True, sandbox=None, globalize=True, *args, **kwargs):
161
+        # Store the created threads so that their status can be checked after tests have run.
162
+        thread = super(Runtime, self).create_thread(f, log, sandbox, globalize, *args, **kwargs)
163
+        self._threads.append(thread)
164
+        return thread
165
+
166
+    def check_threads(self):
167
+        # Check for alive non-daemon threads and throw error if found.
168
+        # Ignored threads are just slow but they don't run in a loop so they'll finish eventually.
169
+        ignored = ['_setup_shared_code_sandbox']
170
+        for thread in self._threads:
171
+            if thread.is_alive() and not thread.daemon and thread.name not in ignored:
172
+                raise RuntimeError('Alive non-daemon thread %s found, this will prevent shutdown.' % str(thread))
173
+
174
+    def handle_request(self, request):
175
+        # In case the parent method encounters an error reported using `_report_error` method, we'll raise it here.
176
+        self.request_error = None
177
+        status, headers, body = super(Runtime, self).handle_request(request)
178
+        if self.request_error is not None:
179
+            raise self.request_error[0], self.request_error[1], self.request_error[2]
180
+        return status, headers, body
181
+
182
+    def _report_error(self, request, controller, kwargs, e):
183
+        return
184
+
185
+    def get_resource_hashes(self):
186
+        return