1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2008 The University of Melbourne
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# Module: TestFilespace
19
# Author: Dilshan Angampitiya
20
# Steven Bird (revisions)
21
# David Coles (revisions and moved to module)
28
Our dummy file system which is accessed by code being tested.
29
Implemented as a dictionary which maps filenames to strings
31
def __init__(self, files=None):
32
"Initialise, optionally with filename-filedata pairs"
37
# dict mapping files to strings
39
self._files.update(files)
41
self._modified_files = set([])
42
# dict mapping files to stringIO objects
45
def add_file(self, filename, data):
46
" Add a file to the filespace "
47
self._files[filename] = data
49
def openfile(self, filename, mode='r'):
50
""" Open a file from the filespace with the given mode.
51
Return a StringIO subclass object with the file contents.
53
# currently very messy, needs to be cleaned up
54
# Probably most of this should be in the initialiser to the TestStringIO
58
if filename in self._open_files:
59
raise IOError("File already open: %s" %filename)
61
if not re.compile("[rwa][+b]{0,2}").match(mode):
62
raise IOError("invalid mode %s" %mode)
64
## TODO: validate filename?
68
# initialise the file properly (truncate/create if required)
70
self._files[filename] = ''
71
self._modified_files.add(filename)
72
elif filename not in self._files:
74
self._files[filename] = ''
75
self._modified_files.add(filename)
77
raise IOError(2, "Access to file denied: %s" %filename)
79
# for append mode, remember the existing data
81
existing_data = self._files[filename]
85
# determine what operations are allowed
86
reading_ok = (len(mode) == 2 or mode[0] == 'r')
87
writing_ok = (len(mode) == 2 or mode[0] in 'wa')
89
# for all writing modes, start off with blank file
93
initial_data = self._files[filename]
95
file_object = TestStringIO(initial_data, filename, self, reading_ok, writing_ok, existing_data)
96
self._open_files[filename] = file_object
101
""" Flush all open files
103
for file_object in self._open_files.values():
106
def updatefile(self,filename, data):
107
""" Callback function used by an open file to inform when it has been updated.
109
if filename in self._open_files:
110
self._files[filename] = data
111
if self._open_files[filename].is_modified():
112
self._modified_files.add(filename)
114
raise IOError(2, "Access to file denied: %s" %filename)
116
def closefile(self, filename):
117
""" Callback function used by an open file to inform when it has been closed.
119
if filename in self._open_files:
120
del self._open_files[filename]
122
def get_modified_files(self):
123
"""" A subset of the filespace containing only those files which have been
127
for filename in self._modified_files:
128
modified_files[filename] = self._files[filename]
130
return modified_files
132
def get_open_files(self):
133
" Return the names of all open files "
134
return self._open_files.keys()
137
""" Return a copy of the current filespace.
138
Only the files are copied, not the modified or open file lists.
141
return TestFilespace(self._files)
143
class TestStringIO(StringIO.StringIO):
145
A subclass of StringIO which acts as a file in our dummy file system
147
def __init__(self, string, filename, filespace, reading_ok, writing_ok, existing_data):
148
""" Initialise with the filedata, file name and infomation on what ops are
150
StringIO.StringIO.__init__(self, string)
151
self._filename = filename
152
self._filespace = filespace
153
self._reading_ok = reading_ok
154
self._writing_ok = writing_ok
155
self._existing_data = existing_data
156
self._modified = False
159
# Override all standard file ops. Make sure that they are valid with the given
160
# permissions and if so then call the corresponding method in StringIO
162
def read(self, *args):
163
if not self._reading_ok:
164
raise IOError(9, "Bad file descriptor")
166
return StringIO.StringIO.read(self, *args)
168
def readline(self, *args):
169
if not self._reading_ok:
170
raise IOError(9, "Bad file descriptor")
172
return StringIO.StringIO.readline(self, *args)
174
def readlines(self, *args):
175
if not self._reading_ok:
176
raise IOError(9, "Bad file descriptor")
178
return StringIO.StringIO.readlines(self, *args)
180
def seek(self, *args):
181
if not self._reading_ok:
182
raise IOError(9, "Bad file descriptor")
184
return StringIO.StringIO.seek(self, *args)
186
def truncate(self, *args):
187
self._modified = True
188
if not self._writing_ok:
189
raise IOError(9, "Bad file descriptor")
191
return StringIO.StringIO.truncate(self, *args)
193
def write(self, *args):
194
self._modified = True
195
if not self._writing_ok:
196
raise IOError(9, "Bad file descriptor")
198
return StringIO.StringIO.write(self, *args)
200
def writelines(self, *args):
201
self._modified = True
202
if not self._writing_ok:
203
raise IOError(9, "Bad file descriptor")
205
return StringIO.StringIO.writelines(self, *args)
207
def is_modified(self):
208
" Return true if the file has been written to, or truncated"
209
return self._modified
212
" Update the contents of the filespace with the new data "
213
self._filespace.updatefile(self._filename, self._existing_data+self.getvalue())
214
return StringIO.StringIO.flush(self)
217
" Flush the file and close it "
219
self._filespace.closefile(self._filename)
220
return StringIO.StringIO.close(self)