1
0
mirror of https://github.com/gryf/fs-uae-wrapper.git synced 2026-02-03 06:45:49 +01:00

10 Commits
0.4 ... 0.7

Author SHA1 Message Date
334b56bad3 Version bump 2017-01-07 09:45:37 +01:00
6d84fc4b8a Make wrapper_archiver option mandatory in savestare module 2017-01-07 09:43:53 +01:00
c5ce27e637 Added savestate wrapper module 2017-01-07 09:31:59 +01:00
ae6e00ea1c Added gitignore 2017-01-07 09:29:18 +01:00
f1fb43ca64 Extract new base class for archive wrapper module 2017-01-07 09:10:03 +01:00
77cf10cee7 Fixed issue with compressing files with tar.*
This changeset fixes tar behavior on creating archive, like:

  tar cf foo.tar .

which include also archive file itself, so that tar reports an error
`file changed as we read it` during appending archive file to itself.

This changest is fixing that by replacing dot with list of items to be
added to the archive (similar as in RarArchive.create).
2017-01-06 16:53:49 +01:00
250b1c4c2c Version bump 2017-01-03 20:07:01 +01:00
0cbe6fc9d0 Added wrapper_save_state option 2017-01-03 19:34:42 +01:00
0b831e5b10 Setting assets paths in base class 2017-01-03 18:49:18 +01:00
1d35436dee Fixed tests for wrapper modules
Exchanged os.rename with shutil.move since there was a problem with
moving files between different filesystems.
2017-01-03 06:01:30 +01:00
13 changed files with 544 additions and 197 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
*.pyc
*.egg-info
.cache
.coverage
.tox
MANIFEST
build
dist

View File

@@ -106,6 +106,7 @@ Currently, three wrapper modules are available:
- plain - plain
- cd32 - cd32
- archive - archive
- savestate
plain plain
----- -----
@@ -126,19 +127,24 @@ Options used:
* ``wrapper`` (required) with ``cd32`` as an value * ``wrapper`` (required) with ``cd32`` as an value
* ``wrapper_archive`` (required) path to the archive with CD32 iso/cue/wav * ``wrapper_archive`` (required) path to the archive with CD32 iso/cue/wav
* ``wrapper_archiver`` (required) archiver to use for storage save state * ``wrapper_archiver`` (conditionally required) archiver to use for storage
save state
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical * ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
message during extracting files message during extracting files
* ``wrapper_save_state`` (optional) if set to "1", will load/archive save state
directory, defined as ``$CONFIG/[save-state-dir-name]`` using provided
``wrapper_archiver`` archiver. If this option is enabled,
``wrapper_archiver`` will be required.
Let's see some sample config for a game, which is saved as Let's see some sample config for a game, which is saved as
``ChaosEngine.fs-uae``: ``ChaosEngine.fs-uae``:
.. code:: ini .. code:: ini
:number-lines:
[config] [config]
wrapper = cd32 wrapper = cd32
wrapper_archive = ChaosEngine.7z wrapper_archive = ChaosEngine.7z
wrapper_archiver = 7z
wrapper_gui_msg = 1 wrapper_gui_msg = 1
amiga_model = CD32 amiga_model = CD32
@@ -159,20 +165,20 @@ Next, the invocation of the wrapper would be as follows:
Now, there several thing will happen: Now, there several thing will happen:
- Config file will be read, and wrapper module will be find (because we already - Config file will be read, and wrapper module will be found
put it on line 2)
- New temporary directory will be created - New temporary directory will be created
- Archive with game assists will be extracted in that directory - Archive with game assists will be extracted in that directory
- Configuration file will be copied into that directory, and renamed to - Configuration file will be copied into that directory, and renamed to
``Config.fs-uae`` ``Config.fs-uae``
- If there is saved state, it also would be extracted there - If ``wrapper_save_state`` is set, and there is saved state archive, it also
would be extracted there
- ``fs-uae`` will be launched inside that directory - ``fs-uae`` will be launched inside that directory
Next, after ``fs-uae`` quit, there will: Next, after ``fs-uae`` quit, there will:
- Create archive containing save state with name like the configuration file - Optionally create archive containing save state with name like the
with additional ``_save`` suffix. In this example it would be configuration file with additional ``_save`` suffix. In this example it would
``ChaosEngine_save.7z``. be ``ChaosEngine_save.7z``.
- Wipe out temporary directory - Wipe out temporary directory
archive archive
@@ -180,28 +186,40 @@ archive
Options used: Options used:
* ``wrapper`` (required) with ``cd32`` as an value * ``wrapper`` (required) with ``archive`` as an value
* ``wrapper_archive`` (required) path to the archive with assets (usually means * ``wrapper_archive`` (required) path to the archive with assets (usually means
* ``wrapper_archiver`` (required) archiver to use for storage save state
whole system directories, floppies or hard disk images) whole system directories, floppies or hard disk images)
* ``wrapper_archiver`` (conditionally required) archiver to use for storage
save state
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical * ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
message during extracting files message during extracting files
* ``wrapper_persist_data`` (optional) if set to "1", will compress (possibly * ``wrapper_persist_data`` (optional) if set to "1", will compress (possibly
changed) data, replacing original archive changed) data, replacing original archive
* ``wrapper_save_state`` (optional) if set to "1", will archive save state
directory, defined as ``$CONFIG/[save-state-dir-name]`` using provided
``wrapper_archiver`` archiver. If this option is enabled,
``wrapper_archiver`` will be required.
Example configuration: Example configuration:
.. code:: ini .. code:: ini
:number-lines:
[config] [config]
wrapper = archive wrapper = archive
wrapper_archive = Workbench_3.1.tar.bz2 wrapper_archive = Workbench_3.1.tar.bz2
wrapper_archiver = lha
wrapper_gui_msg = 1 wrapper_gui_msg = 1
wrapper_persist_data = 1 wrapper_persist_data = 1
wrapper_save_state = 1
... ...
This module is quite useful in two use cases. First is a usual work with
Workbench, where there is a need to keep changes of filesystem. Second is the
opposite - if there is a need to test some software, but not necessary keep it
in a Workbench, than it will act as a temporary copy of the system, so that
next time fs-uae will be run, there will be no files of tested software
cluttering around.
And execution is as usual: And execution is as usual:
.. code:: shell-session .. code:: shell-session
@@ -212,20 +230,58 @@ This module will do several steps (similar as with ``cd32`` wrapper):
- create temporary directory - create temporary directory
- extract provided in configuration archive - extract provided in configuration archive
- extract save state (if exists) - extract save state (if ``wrapper_save_state`` is set to ``1`` and archive
with save exists)
- copy configuration under name ``Config.fs-uae`` - copy configuration under name ``Config.fs-uae``
- run the fs-uae emulator - run the fs-uae emulator
- create archive with save state (if save state directory place is *not* a - optionally create archive with save state (if save state directory place is
global one) *not* a global one)
- optionally create new archive under the same name as the original one and - optionally create new archive under the same name as the original one and
replace it with original one. replace it with original one.
This module is quite useful in two use cases. First is a usual work with savestate
Workbench, where there is a need to keep changes of filesystem. Second is the ---------
opposite - if there is a need to test some software, but not necessary keep it
in a Workbench, than it will act as a temporary copy of the system, so that Options used:
next time fs-uae will be run, there will be no files of tested software
cluttering around. * ``wrapper`` (required) with ``archive`` as an value
* ``wrapper_archiver`` (required) archiver to use for storage save state
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
message during extracting files
This module is primarily used to run emulator with read only media attached
(like images of floppies or uncompressed CD-ROMs) and its purpose is to
preserve save state which will be created as an archive alongside with original
configuration file in selected archive format. Note, that there is required to
provide ``wrapper_archiver``, since option ``wrapper_save_state`` is implicitly
set to value ``1`` in this module.
Example configuration:
.. code:: ini
:number-lines:
[config]
wrapper = savestate
wrapper_archiver = 7z
wrapper_gui_msg = 1
...
And execution is as usual:
.. code:: shell-session
$ fs-uae-wrapper Sanity-Arte.fs-uae
The steps would be as follows:
- create temporary directory
- extract save state (if ``wrapper_save_state`` is set to ``1`` and archive
with save exists)
- copy configuration under name ``Config.fs-uae``
- run the fs-uae emulator
- optionally create archive with save state (if save state directory place is
*not* a global one)
License License
======= =======

View File

@@ -6,13 +6,12 @@ the temporary one.
""" """
import os import os
import shutil import shutil
import sys
from fs_uae_wrapper import base from fs_uae_wrapper import base
from fs_uae_wrapper import utils from fs_uae_wrapper import utils
class Archive(base.Base): class Archive(base.ArchiveBase):
""" """
Class for performing extracting archive, copying emulator files, and Class for performing extracting archive, copying emulator files, and
cleaning it back again cleaning it back again
@@ -30,17 +29,10 @@ class Archive(base.Base):
- copy configuration - copy configuration
- run the emulation - run the emulation
- optionally make archive save state - optionally make archive save state
Params:
conf_file: a relative path to provided configuration file
fsuae_options: is an CmdOption object created out of command line
parameters
configuration: is config dictionary created out of config file
""" """
if not super(Archive, self).run(): if not super(Archive, self).run():
return False return False
self._set_assets_paths()
if not self._extract(): if not self._extract():
return False return False
@@ -62,20 +54,6 @@ class Archive(base.Base):
return self._make_archive() return self._make_archive()
def _validate_options(self):
validation_result = super(Archive, self)._validate_options()
if not super(Archive, self)._validate_options():
validation_result = False
if 'wrapper_archive' not in self.all_options:
sys.stderr.write("Configuration lacks of required "
"`wrapper_archive' option.\n")
validation_result = False
return validation_result
def _make_archive(self): def _make_archive(self):
""" """
Produce archive and save it back. Than remove old one. Produce archive and save it back. Than remove old one.
@@ -97,7 +75,7 @@ class Archive(base.Base):
if not utils.create_archive(arch, title): if not utils.create_archive(arch, title):
return False return False
os.rename(arch, self.arch_filepath) shutil.move(arch, self.arch_filepath)
os.chdir(curdir) os.chdir(curdir)
return True return True

View File

@@ -29,7 +29,6 @@ class Base(object):
fsuae_options) fsuae_options)
self.dir = None self.dir = None
self.save_filename = None self.save_filename = None
self.arch_filepath = None
def run(self): def run(self):
""" """
@@ -46,6 +45,7 @@ class Base(object):
return False return False
self.dir = tempfile.mkdtemp() self.dir = tempfile.mkdtemp()
self._set_assets_paths()
return True return True
@@ -91,15 +91,11 @@ class Base(object):
conf_base = os.path.basename(self.conf_file) conf_base = os.path.basename(self.conf_file)
conf_base = os.path.splitext(conf_base)[0] conf_base = os.path.splitext(conf_base)[0]
arch = self.all_options['wrapper_archive']
if os.path.isabs(arch):
self.arch_filepath = arch
else:
self.arch_filepath = os.path.join(conf_abs_dir, arch)
# set optional save_state # set optional save_state
arch_ext = utils.get_arch_ext(self.all_options['wrapper_archiver']) arch_ext = utils.get_arch_ext(self.all_options.get('wrapper_archiver'))
self.save_filename = os.path.join(conf_abs_dir, conf_base + '_save' + if arch_ext:
arch_ext) self.save_filename = os.path.join(conf_abs_dir, conf_base +
'_save' + arch_ext)
def _copy_conf(self): def _copy_conf(self):
"""copy provided configuration as Config.fs-uae""" """copy provided configuration as Config.fs-uae"""
@@ -108,16 +104,6 @@ class Base(object):
os.path.join(self.dir, 'Config.fs-uae')) os.path.join(self.dir, 'Config.fs-uae'))
return True return True
def _extract(self):
"""Extract archive to temp dir"""
title = self._get_title()
curdir = os.path.abspath('.')
os.chdir(self.dir)
result = utils.extract_archive(self.arch_filepath, title)
os.chdir(curdir)
return result
def _run_emulator(self, fsuae_options): def _run_emulator(self, fsuae_options):
"""execute fs-uae in provided directory""" """execute fs-uae in provided directory"""
curdir = os.path.abspath('.') curdir = os.path.abspath('.')
@@ -143,6 +129,9 @@ class Base(object):
""" """
Get the saves from emulator and store it where configuration is placed Get the saves from emulator and store it where configuration is placed
""" """
if self.all_options.get('wrapper_save_state', '0') != '1':
return True
os.chdir(self.dir) os.chdir(self.dir)
save_path = self._get_saves_dir() save_path = self._get_saves_dir()
if not save_path: if not save_path:
@@ -165,6 +154,9 @@ class Base(object):
""" """
Put the saves (if exists) to the temp directory. Put the saves (if exists) to the temp directory.
""" """
if self.all_options.get('wrapper_save_state', '0') != '1':
return True
if not os.path.exists(self.save_filename): if not os.path.exists(self.save_filename):
return True return True
@@ -208,6 +200,9 @@ class Base(object):
"`wrapper' option.\n") "`wrapper' option.\n")
return False return False
if self.all_options.get('wrapper_save_state', '0') == '0':
return True
if 'wrapper_archiver' not in self.all_options: if 'wrapper_archiver' not in self.all_options:
sys.stderr.write("Configuration lacks of required " sys.stderr.write("Configuration lacks of required "
"`wrapper_archiver' option.\n") "`wrapper_archiver' option.\n")
@@ -219,3 +214,56 @@ class Base(object):
return False return False
return True return True
class ArchiveBase(Base):
"""
Base class for archive based wrapper modules
"""
def __init__(self, conf_file, fsuae_options, configuration):
"""
Params:
conf_file: a relative path to provided configuration file
fsuae_options: is an CmdOption object created out of command line
parameters
configuration: is config dictionary created out of config file
"""
super(ArchiveBase, self).__init__(conf_file, fsuae_options,
configuration)
self.arch_filepath = None
def _set_assets_paths(self):
"""
Set full paths for archive file (without extension) and for save state
archive file
"""
super(ArchiveBase, self)._set_assets_paths()
conf_abs_dir = os.path.dirname(os.path.abspath(self.conf_file))
arch = self.all_options.get('wrapper_archive')
if arch:
if os.path.isabs(arch):
self.arch_filepath = arch
else:
self.arch_filepath = os.path.join(conf_abs_dir, arch)
def _extract(self):
"""Extract archive to temp dir"""
title = self._get_title()
curdir = os.path.abspath('.')
os.chdir(self.dir)
result = utils.extract_archive(self.arch_filepath, title)
os.chdir(curdir)
return result
def _validate_options(self):
validation_result = super(ArchiveBase, self)._validate_options()
if 'wrapper_archive' not in self.all_options:
sys.stderr.write("Configuration lacks of required "
"`wrapper_archive' option.\n")
validation_result = False
return validation_result

View File

@@ -8,12 +8,10 @@ used as a base for save state (it will append '_save.7z' to the archive file
name. name.
""" """
import sys
from fs_uae_wrapper import base from fs_uae_wrapper import base
class CD32(base.Base): class CD32(base.ArchiveBase):
""" """
Class for performing extracting archive, copying emulator files, and Class for performing extracting archive, copying emulator files, and
cleaning it back again cleaning it back again
@@ -34,7 +32,6 @@ class CD32(base.Base):
if not self._validate_options(): if not self._validate_options():
return False return False
self._set_assets_paths()
if not self._extract(): if not self._extract():
return False return False
@@ -46,21 +43,14 @@ class CD32(base.Base):
if kick_opts: if kick_opts:
self.fsuae_options.update(kick_opts) self.fsuae_options.update(kick_opts)
if self._run_emulator(self.fsuae_options.list()): if not self._run_emulator(self.fsuae_options.list()):
return False
if self._get_saves_dir():
return self._save_save() return self._save_save()
return True return True
def _validate_options(self):
validation_result = super(CD32, self)._validate_options()
if 'wrapper_archive' not in self.all_options:
sys.stderr.write("Configuration lacks of required "
"`wrapper_archive' option.\n")
validation_result = False
return validation_result
def run(config_file, fsuae_options, configuration): def run(config_file, fsuae_options, configuration):
"""Run fs-uae with provided config file and options""" """Run fs-uae with provided config file and options"""

View File

@@ -53,6 +53,15 @@ class TarArchive(Archive):
EXTRACT = ['xf'] EXTRACT = ['xf']
ARCH = 'tar' ARCH = 'tar'
def create(self, arch_name, files=None):
files = files if files else sorted(os.listdir('.'))
result = subprocess.call([self._compess] + self.ADD + [arch_name] +
files)
if result != 0:
sys.stderr.write("Unable to create archive `%s'\n" % arch_name)
return False
return True
class TarGzipArchive(TarArchive): class TarGzipArchive(TarArchive):
ADD = ['zcf'] ADD = ['zcf']

View File

@@ -0,0 +1,63 @@
"""
Wrapper module with preserved save state in archive file.
"""
from fs_uae_wrapper import base
class SaveState(base.Base):
"""
Preserve save state.
"""
def __init__(self, conf_file, fsuae_options, configuration):
"""
Params:
conf_file: a relative path to provided configuration file
fsuae_options: is an CmdOption object created out of command line
parameters
configuration: is config dictionary created out of config file
"""
super(SaveState, self).__init__(conf_file, fsuae_options,
configuration)
self.arch_filepath = None
self.all_options['wrapper_save_state'] = '1'
def run(self):
"""
Main function which accepts configuration file for FS-UAE
It will do as follows:
- set needed full path for asset files
- copy configuration
- optionally extract save state archive
- run the emulation
- optionally make archive save state
"""
if not super(SaveState, self).run():
return False
self._load_save()
if not self._copy_conf():
return False
kick_opts = self._kickstart_option()
if kick_opts:
self.fsuae_options.update(kick_opts)
if not self._run_emulator(self.fsuae_options.list()):
return False
if self._get_saves_dir():
if not self._save_save():
return False
return True
def run(config_file, fsuae_options, configuration):
"""Run fs-uae with provided config file and options"""
runner = SaveState(config_file, fsuae_options, configuration)
try:
return runner.run()
finally:
runner.clean()

View File

@@ -2,12 +2,12 @@
""" """
Setup for the fs-uae-wrapper Setup for the fs-uae-wrapper
""" """
from distutils.core import setup from setuptools import setup
setup(name='fs-uae-wrapper', setup(name='fs-uae-wrapper',
packages=['fs_uae_wrapper'], packages=['fs_uae_wrapper'],
version='0.4', version='0.7',
description='Automate archives and state for fs-uae', description='Automate archives and state for fs-uae',
author='Roman Dobosz', author='Roman Dobosz',
author_email='gryf73@gmail.com', author_email='gryf73@gmail.com',

View File

@@ -26,35 +26,43 @@ class TestArchive(TestCase):
except OSError: except OSError:
pass pass
def test_validate_options(self): @mock.patch('fs_uae_wrapper.path.which')
def test_validate_options(self, which):
which.return_value = 'unrar'
arch = archive.Archive('Config.fs-uae', utils.CmdOption(), {}) arch = archive.Archive('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(arch._validate_options()) self.assertFalse(arch._validate_options())
arch.all_options = {'wrapper': 'archive'}
arch.all_options['wrapper'] = 'archive' arch.all_options['wrapper'] = 'archive'
self.assertFalse(arch._validate_options()) self.assertFalse(arch._validate_options())
arch.all_options['wrapper_archive'] = 'fake.tgz' arch.all_options['wrapper_archive'] = 'rar'
self.assertFalse(arch._validate_options())
arch.all_options['wrapper_archiver'] = 'rar'
self.assertTrue(arch._validate_options()) self.assertTrue(arch._validate_options())
@mock.patch('tempfile.mkdtemp') @mock.patch('tempfile.mkdtemp')
@mock.patch('fs_uae_wrapper.path.which')
@mock.patch('fs_uae_wrapper.archive.Archive._make_archive') @mock.patch('fs_uae_wrapper.archive.Archive._make_archive')
@mock.patch('fs_uae_wrapper.base.Base._run_emulator') @mock.patch('fs_uae_wrapper.base.ArchiveBase._save_save')
@mock.patch('fs_uae_wrapper.base.Base._kickstart_option') @mock.patch('fs_uae_wrapper.base.ArchiveBase._get_saves_dir')
@mock.patch('fs_uae_wrapper.base.Base._copy_conf') @mock.patch('fs_uae_wrapper.base.ArchiveBase._run_emulator')
@mock.patch('fs_uae_wrapper.base.Base._load_save') @mock.patch('fs_uae_wrapper.base.ArchiveBase._kickstart_option')
@mock.patch('fs_uae_wrapper.base.Base._extract') @mock.patch('fs_uae_wrapper.base.ArchiveBase._copy_conf')
def test_run(self, extr, load, copy, kick, run, march, mkdtemp): @mock.patch('fs_uae_wrapper.base.ArchiveBase._load_save')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._extract')
def test_run(self, extract, load_save, copy_conf, kick_option,
run_emulator, get_save_dir, save_state, make_arch, which,
mkdtemp):
extr.return_value = False extract.return_value = False
load.return_value = False load_save.return_value = False
copy.return_value = False copy_conf.return_value = False
kick.return_value = False kick_option.return_value = False
run.return_value = False run_emulator.return_value = False
march.return_value = False get_save_dir.return_value = False
save_state.return_value = False
make_arch.return_value = False
which.return_value = 'rar'
arch = archive.Archive('Config.fs-uae', utils.CmdOption(), {}) arch = archive.Archive('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(arch.run()) self.assertFalse(arch.run())
@@ -65,31 +73,37 @@ class TestArchive(TestCase):
self.assertFalse(arch.run()) self.assertFalse(arch.run())
extr.return_value = True extract.return_value = True
self.assertFalse(arch.run()) self.assertFalse(arch.run())
load.return_value = True load_save.return_value = True
self.assertFalse(arch.run()) self.assertFalse(arch.run())
copy.return_value = True copy_conf.return_value = True
self.assertFalse(arch.run()) self.assertFalse(arch.run())
kick.return_value = {'foo': 'bar'} kick_option.return_value = {'foo': 'bar'}
self.assertFalse(arch.run()) self.assertFalse(arch.run())
self.assertDictEqual(arch.fsuae_options, {'foo': 'bar'}) self.assertDictEqual(arch.fsuae_options, {'foo': 'bar'})
run.return_value = True run_emulator.return_value = True
self.assertFalse(arch.run()) self.assertFalse(arch.run())
march.return_value = True get_save_dir.return_value = True
self.assertFalse(arch.run())
save_state.return_value = True
self.assertFalse(arch.run())
make_arch.return_value = True
self.assertTrue(arch.run()) self.assertTrue(arch.run())
@mock.patch('os.rename') @mock.patch('os.rename')
@mock.patch('os.unlink') @mock.patch('os.unlink')
@mock.patch('shutil.rmtree') @mock.patch('shutil.rmtree')
@mock.patch('fs_uae_wrapper.utils.create_archive') @mock.patch('fs_uae_wrapper.utils.create_archive')
@mock.patch('fs_uae_wrapper.base.Base._get_title') @mock.patch('fs_uae_wrapper.base.ArchiveBase._get_title')
@mock.patch('fs_uae_wrapper.base.Base._get_saves_dir') @mock.patch('fs_uae_wrapper.base.ArchiveBase._get_saves_dir')
def test_make_archive(self, sdir, title, carch, rmt, unlink, rename): def test_make_archive(self, sdir, title, carch, rmt, unlink, rename):
sdir.return_value = None sdir.return_value = None

View File

@@ -99,34 +99,6 @@ class TestBase(TestCase):
self.assertTrue(os.path.exists(os.path.join(self.dirname, self.assertTrue(os.path.exists(os.path.join(self.dirname,
'Config.fs-uae'))) 'Config.fs-uae')))
@mock.patch('fs_uae_wrapper.utils.extract_archive')
def test_extract(self, utils_extract):
bobj = base.Base('Config.fs-uae', utils.CmdOption(), {})
bobj.arch_filepath = self.fname
bobj.dir = self.dirname
utils_extract.return_value = False
# message for the gui is taken from title in fs-uae conf or, if there
# is no such entry, use archive name, which is mandatory to provide
bobj.all_options = {'title': 'foo_game', 'wrapper_gui_msg': '1'}
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, 'foo_game')
utils_extract.reset_mock()
bobj.all_options = {'wrapper_archive': 'arch.tar',
'wrapper_gui_msg': '1'}
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, 'arch.tar')
# lets pretend, the extracting has failed
utils_extract.reset_mock()
bobj.all_options = {'wrapper_gui_msg': '0'}
utils_extract.return_value = False
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, '')
@mock.patch('fs_uae_wrapper.utils.run_command') @mock.patch('fs_uae_wrapper.utils.run_command')
def test_run_emulator(self, run): def test_run_emulator(self, run):
@@ -146,21 +118,27 @@ class TestBase(TestCase):
@mock.patch('fs_uae_wrapper.utils.create_archive') @mock.patch('fs_uae_wrapper.utils.create_archive')
def test_save_save(self, carch, saves_dir): def test_save_save(self, carch, saves_dir):
bobj = base.Base('Config.fs-uae', utils.CmdOption(), {}) os.chdir(self.confdir)
bobj = base.Base('myconf.fs-uae', utils.CmdOption(), {})
bobj.dir = self.dirname bobj.dir = self.dirname
bobj.save_filename = 'foobar_save.7z' bobj.save_filename = os.path.join(self.confdir, 'myconf_save.7z')
saves_dir.bobj.save_filenamereturn_value = None
saves_dir.return_value = None
carch.return_value = True carch.return_value = True
self.assertTrue(bobj._save_save()) self.assertTrue(bobj._save_save(),
'there is assumption, that wrapper_save_state is'
' false by default. Here it was true.')
bobj.all_options['wrapper_save_state'] = '1'
self.assertTrue(bobj._save_save(),
'unexpected save_state directory found')
saves_dir.return_value = bobj.save_filename saves_dir.return_value = bobj.save_filename
os.chdir(self.confdir)
with open(bobj.save_filename, 'w') as fobj: with open(bobj.save_filename, 'w') as fobj:
fobj.write('asd') fobj.write('asd')
self.assertTrue(bobj._save_save())
os.mkdir(os.path.join(self.dirname, 'fs-uae-save')) os.mkdir(os.path.join(self.dirname, 'fs-uae-save'))
self.assertTrue(bobj._save_save()) self.assertTrue(bobj._save_save())
@@ -175,6 +153,12 @@ class TestBase(TestCase):
bobj.save_filename = "foobar_save.7z" bobj.save_filename = "foobar_save.7z"
earch.return_value = 0 earch.return_value = 0
# By default there would be no save state persistence
self.assertTrue(bobj._load_save())
# set wrapper_save_state option, so we can proceed with test
bobj.all_options['wrapper_save_state'] = '1'
# fail to load save is not fatal # fail to load save is not fatal
self.assertTrue(bobj._load_save()) self.assertTrue(bobj._load_save())
@@ -218,6 +202,9 @@ class TestBase(TestCase):
os.mkdir(path) os.mkdir(path)
self.assertEqual(bobj._get_saves_dir(), 'saves') self.assertEqual(bobj._get_saves_dir(), 'saves')
bobj.all_options['save_states_dir'] = '$CONFIG/saves/'
self.assertEqual(bobj._get_saves_dir(), 'saves')
@mock.patch('fs_uae_wrapper.path.which') @mock.patch('fs_uae_wrapper.path.which')
def test_validate_options(self, which): def test_validate_options(self, which):
@@ -229,18 +216,34 @@ class TestBase(TestCase):
self.assertFalse(bobj._validate_options()) self.assertFalse(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy'} bobj.all_options = {'wrapper': 'dummy'}
self.assertFalse(bobj._validate_options()) self.assertTrue(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy', bobj.all_options = {'wrapper': 'dummy',
'wrapper_archiver': 'myarchiver'} 'wrapper_save_state': '0'}
self.assertTrue(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy',
'wrapper_archiver': 'rar'}
self.assertTrue(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1'}
self.assertFalse(bobj._validate_options()) self.assertFalse(bobj._validate_options())
which.return_value = '7z' which.return_value = '7z'
bobj.all_options = {'wrapper': 'dummy', bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1'}
self.assertFalse(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1',
'wrapper_archiver': '7z'} 'wrapper_archiver': '7z'}
self.assertTrue(bobj._validate_options()) self.assertTrue(bobj._validate_options())
def test_run_clean(self): @mock.patch('fs_uae_wrapper.path.which')
def test_run_clean(self, which):
which.return_value = 'rar'
bobj = base.Base('Config.fs-uae', utils.CmdOption(), {}) bobj = base.Base('Config.fs-uae', utils.CmdOption(), {})
bobj.all_options = {} bobj.all_options = {}
@@ -248,9 +251,96 @@ class TestBase(TestCase):
self.assertFalse(bobj.run()) self.assertFalse(bobj.run())
bobj.all_options = {'wrapper': 'dummy', bobj.all_options = {'wrapper': 'dummy',
'wrapper_archiver': 'rar'} 'wrapper_archiver': 'rar',
'wrapper_archive': 'foo.7z'}
try: try:
self.assertTrue(bobj.run()) self.assertTrue(bobj.run())
self.assertTrue(os.path.exists(bobj.dir)) self.assertTrue(os.path.exists(bobj.dir))
finally: finally:
bobj.clean() bobj.clean()
class TestArchiveBase(TestCase):
def setUp(self):
fd, self.fname = mkstemp()
self.dirname = mkdtemp()
self.confdir = mkdtemp()
os.close(fd)
self._argv = sys.argv[:]
sys.argv = ['fs-uae-wrapper']
self.curdir = os.path.abspath(os.curdir)
def tearDown(self):
os.chdir(self.curdir)
try:
shutil.rmtree(self.dirname)
except OSError:
pass
try:
shutil.rmtree(self.confdir)
except OSError:
pass
os.unlink(self.fname)
sys.argv = self._argv[:]
def test_set_assets_paths(self):
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
os.chdir(self.dirname)
bobj.conf_file = 'Config.fs-uae'
bobj.all_options = {'wrapper_archive': 'foo.7z',
'wrapper_archiver': '7z'}
bobj._set_assets_paths()
full_path = os.path.join(self.dirname, 'Config_save.7z')
self.assertEqual(bobj.save_filename, full_path)
bobj.all_options = {'wrapper_archive': '/home/user/foo.7z',
'wrapper_archiver': '7z'}
bobj._set_assets_paths()
full_path = os.path.join(self.dirname, 'Config_save.7z')
self.assertEqual(bobj.save_filename, full_path)
@mock.patch('fs_uae_wrapper.utils.extract_archive')
def test_extract(self, utils_extract):
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
bobj.arch_filepath = self.fname
bobj.dir = self.dirname
utils_extract.return_value = False
# message for the gui is taken from title in fs-uae conf or, if there
# is no such entry, use archive name, which is mandatory to provide
bobj.all_options = {'title': 'foo_game', 'wrapper_gui_msg': '1'}
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, 'foo_game')
utils_extract.reset_mock()
bobj.all_options = {'wrapper_archive': 'arch.tar',
'wrapper_gui_msg': '1'}
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, 'arch.tar')
# lets pretend, the extracting has failed
utils_extract.reset_mock()
bobj.all_options = {'wrapper_gui_msg': '0'}
utils_extract.return_value = False
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, '')
def test_validate_options(self):
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
bobj.all_options = {}
self.assertFalse(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy'}
self.assertFalse(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy',
'wrapper_archive': 'myarchive.7z'}
self.assertTrue(bobj._validate_options())

View File

@@ -11,35 +11,26 @@ from fs_uae_wrapper import utils
class TestCD32(TestCase): class TestCD32(TestCase):
def test_validate_options(self):
acd32 = cd32.CD32('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(acd32._validate_options())
acd32.all_options['wrapper'] = 'cd32'
self.assertFalse(acd32._validate_options())
acd32.all_options['wrapper_archive'] = 'fake.tgz'
self.assertFalse(acd32._validate_options())
acd32.all_options['wrapper_archiver'] = 'rar'
self.assertTrue(acd32._validate_options())
@mock.patch('tempfile.mkdtemp') @mock.patch('tempfile.mkdtemp')
@mock.patch('fs_uae_wrapper.base.Base._save_save') @mock.patch('fs_uae_wrapper.path.which')
@mock.patch('fs_uae_wrapper.base.Base._run_emulator') @mock.patch('fs_uae_wrapper.base.ArchiveBase._save_save')
@mock.patch('fs_uae_wrapper.base.Base._kickstart_option') @mock.patch('fs_uae_wrapper.base.ArchiveBase._get_saves_dir')
@mock.patch('fs_uae_wrapper.base.Base._load_save') @mock.patch('fs_uae_wrapper.base.ArchiveBase._run_emulator')
@mock.patch('fs_uae_wrapper.base.Base._copy_conf') @mock.patch('fs_uae_wrapper.base.ArchiveBase._kickstart_option')
@mock.patch('fs_uae_wrapper.base.Base._extract') @mock.patch('fs_uae_wrapper.base.ArchiveBase._copy_conf')
def test_run(self, extr, cconf, lsave, kick, runemul, ssave, mkdtemp): @mock.patch('fs_uae_wrapper.base.ArchiveBase._load_save')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._extract')
def test_run(self, extract, load_save, copy_conf, kick_option,
run_emulator, get_save_dir, save_state, which, mkdtemp):
extr.return_value = False extract.return_value = False
cconf.return_value = False copy_conf.return_value = False
lsave.return_value = False load_save.return_value = False
kick.return_value = {} kick_option.return_value = {}
runemul.return_value = False run_emulator.return_value = False
ssave.return_value = False get_save_dir.return_value = False
save_state.return_value = False
which.return_value = 'unrar'
acd32 = cd32.CD32('Config.fs-uae', utils.CmdOption(), {}) acd32 = cd32.CD32('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(acd32.run()) self.assertFalse(acd32.run())
@@ -50,21 +41,24 @@ class TestCD32(TestCase):
self.assertFalse(acd32.run()) self.assertFalse(acd32.run())
extr.return_value = True extract.return_value = True
self.assertFalse(acd32.run()) self.assertFalse(acd32.run())
cconf.return_value = True copy_conf.return_value = True
self.assertFalse(acd32.run()) self.assertFalse(acd32.run())
lsave.return_value = True load_save.return_value = True
self.assertTrue(acd32.run()) self.assertFalse(acd32.run())
kick.return_value = {'foo': 'bar'} kick_option.return_value = {'foo': 'bar'}
self.assertTrue(acd32.run()) self.assertFalse(acd32.run())
self.assertDictEqual(acd32.fsuae_options, {'foo': 'bar'}) self.assertDictEqual(acd32.fsuae_options, {'foo': 'bar'})
runemul.return_value = True run_emulator.return_value = True
self.assertTrue(acd32.run())
get_save_dir.return_value = True
self.assertFalse(acd32.run()) self.assertFalse(acd32.run())
ssave.return_value = True save_state.return_value = True
self.assertTrue(acd32.run()) self.assertTrue(acd32.run())

View File

@@ -60,61 +60,73 @@ class TestArchive(TestCase):
self.assertFalse(arch.extract('foo')) self.assertFalse(arch.extract('foo'))
call.assert_called_once_with(['false', 'x', 'foo']) call.assert_called_once_with(['false', 'x', 'foo'])
@mock.patch('os.path.exists')
@mock.patch('fs_uae_wrapper.path.which') @mock.patch('fs_uae_wrapper.path.which')
@mock.patch('subprocess.call') @mock.patch('subprocess.call')
def test_tar(self, call, which): def test_tar(self, call, which, exists):
with open('foo', 'w') as fobj: with open('foo', 'w') as fobj:
fobj.write('\n') fobj.write('\n')
which.return_value = 'tar' which.return_value = 'tar'
exists.return_value = True
arch = file_archive.TarArchive() arch = file_archive.TarArchive()
arch.archiver = 'tar' arch.archiver = 'tar'
call.return_value = 0 call.return_value = 0
self.assertTrue(arch.create('foo')) self.assertTrue(arch.create('foo.tar'))
call.assert_called_once_with(['tar', 'cf', 'foo', '.']) call.assert_called_once_with(['tar', 'cf', 'foo.tar', 'foo'])
call.reset_mock() call.reset_mock()
call.return_value = 1 call.return_value = 1
self.assertFalse(arch.extract('foo')) self.assertFalse(arch.extract('foo.tar'))
call.assert_called_once_with(['tar', 'xf', 'foo']) call.assert_called_once_with(['tar', 'xf', 'foo.tar'])
call.reset_mock() call.reset_mock()
arch = file_archive.TarGzipArchive() arch = file_archive.TarGzipArchive()
arch.archiver = 'tar' arch.archiver = 'tar'
call.return_value = 0 call.return_value = 0
self.assertTrue(arch.create('foo')) self.assertTrue(arch.create('foo.tgz'))
call.assert_called_once_with(['tar', 'zcf', 'foo', '.']) call.assert_called_once_with(['tar', 'zcf', 'foo.tgz', 'foo'])
call.reset_mock() call.reset_mock()
call.return_value = 1 call.return_value = 1
self.assertFalse(arch.extract('foo')) self.assertFalse(arch.extract('foo.tgz'))
call.assert_called_once_with(['tar', 'xf', 'foo']) call.assert_called_once_with(['tar', 'xf', 'foo.tgz'])
call.reset_mock() call.reset_mock()
arch = file_archive.TarBzip2Archive() arch = file_archive.TarBzip2Archive()
arch.archiver = 'tar' arch.archiver = 'tar'
call.return_value = 0 call.return_value = 0
self.assertTrue(arch.create('foo')) self.assertTrue(arch.create('foo.tar.bz2'))
call.assert_called_once_with(['tar', 'jcf', 'foo', '.']) call.assert_called_once_with(['tar', 'jcf', 'foo.tar.bz2', 'foo'])
call.reset_mock() call.reset_mock()
call.return_value = 1 call.return_value = 1
self.assertFalse(arch.extract('foo')) self.assertFalse(arch.extract('foo.tar.bz2'))
call.assert_called_once_with(['tar', 'xf', 'foo']) call.assert_called_once_with(['tar', 'xf', 'foo.tar.bz2'])
call.reset_mock() call.reset_mock()
arch = file_archive.TarXzArchive() arch = file_archive.TarXzArchive()
arch.archiver = 'tar' arch.archiver = 'tar'
call.return_value = 0 call.return_value = 0
self.assertTrue(arch.create('foo')) self.assertTrue(arch.create('foo.tar.xz'))
call.assert_called_once_with(['tar', 'Jcf', 'foo', '.']) call.assert_called_once_with(['tar', 'Jcf', 'foo.tar.xz', 'foo'])
call.reset_mock() call.reset_mock()
call.return_value = 1 call.return_value = 1
self.assertFalse(arch.extract('foo')) self.assertFalse(arch.extract('foo.tar.xz'))
call.assert_called_once_with(['tar', 'xf', 'foo']) call.assert_called_once_with(['tar', 'xf', 'foo.tar.xz'])
with open('bar', 'w') as fobj:
fobj.write('\n')
call.reset_mock()
arch = file_archive.TarGzipArchive()
arch.archiver = 'tar'
call.return_value = 0
self.assertTrue(arch.create('foo.tgz'))
call.assert_called_once_with(['tar', 'zcf', 'foo.tgz', 'bar', 'foo'])
@mock.patch('fs_uae_wrapper.path.which') @mock.patch('fs_uae_wrapper.path.which')
@mock.patch('subprocess.call') @mock.patch('subprocess.call')

85
tests/test_savestate.py Normal file
View File

@@ -0,0 +1,85 @@
import os
import shutil
from tempfile import mkdtemp
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from fs_uae_wrapper import savestate
from fs_uae_wrapper import utils
class TestArchive(TestCase):
def setUp(self):
self.dirname = mkdtemp()
self.curdir = os.path.abspath(os.curdir)
os.chdir(self.dirname)
def tearDown(self):
os.chdir(self.curdir)
try:
shutil.rmtree(self.dirname)
except OSError:
pass
@mock.patch('tempfile.mkdtemp')
@mock.patch('fs_uae_wrapper.path.which')
@mock.patch('fs_uae_wrapper.base.Base._save_save')
@mock.patch('fs_uae_wrapper.base.Base._get_saves_dir')
@mock.patch('fs_uae_wrapper.base.Base._run_emulator')
@mock.patch('fs_uae_wrapper.base.Base._kickstart_option')
@mock.patch('fs_uae_wrapper.base.Base._copy_conf')
@mock.patch('fs_uae_wrapper.base.Base._load_save')
def test_run(self, load_save, copy_conf, kick_option, run_emulator,
get_save_dir, save_state, which, mkdtemp):
copy_conf.return_value = False
kick_option.return_value = False
run_emulator.return_value = False
get_save_dir.return_value = False
save_state.return_value = False
which.return_value = 'rar'
arch = savestate.SaveState('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(arch.run())
arch.all_options = {'wrapper': 'savestate',
'wrapper_save_state': '1',
'wrapper_archiver': 'rar'}
self.assertFalse(arch.run())
self.assertFalse(arch.run())
copy_conf.return_value = True
self.assertFalse(arch.run())
kick_option.return_value = {'foo': 'bar'}
self.assertFalse(arch.run())
self.assertDictEqual(arch.fsuae_options, {'foo': 'bar'})
run_emulator.return_value = True
self.assertTrue(arch.run())
get_save_dir.return_value = True
self.assertFalse(arch.run())
save_state.return_value = True
self.assertTrue(arch.run())
@mock.patch('fs_uae_wrapper.path.which')
def test_validate_options(self, which):
which.return_value = 'unrar'
arch = savestate.SaveState('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(arch._validate_options())
arch.all_options['wrapper'] = 'savestate'
self.assertFalse(arch._validate_options())
arch.all_options['wrapper_archiver'] = 'rar'
self.assertTrue(arch._validate_options())