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

14 Commits
0.8 ... 0.10.0

Author SHA1 Message Date
f1f64cf4d4 Added WHDLoad support 2024-09-13 18:18:35 +02:00
a5606272cd Fix unit tests 2024-09-13 18:16:45 +02:00
463f6ed705 Fixed deduction of archive name.
When in different directory, the archive name and config need to be
treated differently. This commit unify behavior for both cases.

Also, changing a bit logging format and added some more debugging
messages.
2024-09-12 20:29:34 +02:00
148d28dac2 Make archive name and archiver for savestate optional. 2024-09-11 21:23:49 +02:00
60139d1728 Move to pyproject. 2024-09-11 19:53:52 +02:00
68b1f2a787 Correct homepage in setup.cfg 2022-10-02 14:57:38 +02:00
75d2cff96c Drop Python2 support. 2022-09-02 19:01:56 +02:00
114984cbee Go with setup.cfg instead of setup.py. 2022-09-02 18:47:17 +02:00
b015e443eb Fix tests for file path calculations 2021-05-02 09:36:14 +02:00
874213aef9 Prevent from nonexistent paths in config.
In case of defining paths in config, which wrapper is trying to
normalize, sometimes it may happen, that paths can be either local or
remote - depending on the intention, i.e. one can add:

cdrom_drive_0: foo.iso

where foo can be either an unpacked file or just some companion image,
which exists within config file.

In this patch calculated path is checked against file existence, and if
it doesn't exists, original value is preserved.
2021-03-20 13:12:58 +01:00
1559591417 Changed python3 interpreter to default.
Currently, as a python3 interpreter, specific version has been chosen.
As fs-uae-wrapper doesn't use any special features from latest version
of the python3, it doesn't make sense to have fixed version of it. Just
let operating system pick right python 3 interpreter.
2019-10-06 18:09:32 +02:00
193e487bb9 Fix download link and typo in readme 2017-06-19 19:55:02 +02:00
eb9012f3fa Readme update 2017-01-12 21:09:12 +01:00
efbb5f6f05 Readme update 2017-01-09 06:41:16 +01:00
22 changed files with 730 additions and 211 deletions

View File

@@ -2,7 +2,10 @@ language: python
env:
- TOXENV=py27
- TOXENV=py27-flake8
- TOXENV=py34
- TOXENV=py34-flake8
- TOXENV=py3
- TOXENV=py3-flake8
install: pip install tox
script: tox
before_install:
- sudo apt-get update
- sudo apt-get install -y python-tk python3-tk

View File

@@ -12,7 +12,7 @@ This little utility is a wrapper on great FS-UAE_ emulator, to perform some
actions, like uncompressing archived files (CD ROMs images, filesystems),
launch the emulator and archive emulator save state.
As an example, if there is a collection of CD³² game files and one want to
As an example, if there is a collection of CD³² game files and you want to
provide configuration for each game, but want to keep ISO images with
corresponding files in archive (due to size of those images) than FS-UAE
Wrapper is a way to achieve this.
@@ -25,11 +25,10 @@ preferably in a archive file vs a bunch of files.
Requirements
============
- Python (2 or 3)
- Python 3
- `fs-uae`_ (obviously :)
Also, there should be archivers programs available for compress/decompress
archives. Fs-uae-wrapper supports several types of archives:
Fs-uae-wrapper supports several types of archives:
- `7z`_
- `lha`_
@@ -43,25 +42,35 @@ archives. Fs-uae-wrapper supports several types of archives:
- `zip`_
There are several archive types which are supported, ranging from tar
(compressed with gzip, bzip2 and xz), 7z, rar, zip. lha and lzx. All of those
formats should have corresponding decompressors available in the system,
otherwise archive extraction will fail.
All of those formats should have corresponding software available in the
system, otherwise archive extraction/compression will fail.
Installation
============
Just perform (preferably in ``virtualenv``) a command:
FS-UAE Wrapper is available on `CheeseShop`_ (or python package index if you
will). To install it, you can simply invoke (preferably in ``virtualenv``) a
command:
.. code:: shell-session
$ pip install fs-uae-wrapper
Note, that if ``virtualenv`` was used, there is no need for activating it every
time, since if invoke wrapper like this:
.. code:: shell-session
$ /path/to/virtualenv/bin/fs-uae-wrapper
you should be able run the wrapper properly. *Tested only on Linux :)*.
Usage
=====
After installation you should be able to access new command
``fs-uae-wrapper``, and its invocation is identical like ``fs-uae`` binary:
After installation you should be able to access new command ``fs-uae-wrapper``
(or use the full path to the ``virtualenv`` like on the section above), and it's
invocation is identical like ``fs-uae`` binary:
.. code:: shell-session
@@ -73,9 +82,9 @@ directly in fs-uae configuration or passed as an option:
.. code:: ini
[config]
...
# ...
wrapper = cd32
...
# ...
or
@@ -89,15 +98,19 @@ specific module will be loaded and depending on the module, there would be
performed specific tasks before ``fs-uae`` is launched and after it.
Assumption is, that configuration file are written in portable way, so the are
saved as `relative configuration file`_ (hence the name ``Config.fs-uae``, even
if the are named differently. If certain wrapper is specified, it will create
temporary directory and place the configuration file there as
saved as `relative configuration file`_ (hence the name ``Config.fs-uae``),
even if the are named differently. If certain wrapper is specified, it will
create temporary directory and place the configuration file there as
``Config.fs-uae``.
If no ``wrapper`` option would be passed either as an config option or
command line argument, all command line options will be passed to the fs-uae
executable as-is.
Note, that you can also pass all *wrapper* options via commandline in the very
same way as you can pass config options to `fs-uae`, so you don't have to
modify original configuration if you don't want to.
There is also new config variable introduced: ``$WRAPPER`` which have the same
role as ``$CONFIG``, but apply for copied config. For instance - in module
archive there are filesystem extracted to new location - to access this
@@ -108,7 +121,7 @@ a config option:
[config]
wrapper = archive
...
# ...
hard_drive_0 = $WRAPPER/my_hardrive
@@ -118,7 +131,7 @@ directory, where configuration will be copied.
Modules
=======
Currently, three wrapper modules are available:
Currently, couple of wrapper modules are available:
- plain
- cd32
@@ -144,12 +157,12 @@ Options used:
* ``wrapper`` (required) with ``cd32`` as an value
* ``wrapper_archive`` (required) path to the archive with CD32 iso/cue/wav
* ``wrapper_archiver`` (conditionally required) archiver to use for storage
save state
* ``wrapper_archiver`` (optional) archiver to use for storage save state -
default ``7z``.
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
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
directory, defined as ``$WRAPPER/[save-state-dir-name]`` using provided
``wrapper_archiver`` archiver. If this option is enabled,
``wrapper_archiver`` will be required.
@@ -162,7 +175,7 @@ fragment of configuration file is saved as ``ChaosEngine.fs-uae``:
[config]
wrapper = cd32
wrapper_archive = ChaosEngine.7z
wrapper_archiver = 7z
wrapper_archiver = zip
wrapper_gui_msg = 1
amiga_model = CD32
@@ -170,11 +183,11 @@ fragment of configuration file is saved as ``ChaosEngine.fs-uae``:
cdrom_drive_0 = Chaos Engine, The (1994)(Renegade)(M4)[!][CDD3445].cue
save_states_dir = $CONFIG/fs-uae-save/
save_states_dir = $WRAPPER/fs-uae-save/
joystick_port_1_mode = cd32 gamepad
platform = cd32
...
# ...
Command line invocation of the wrapper would be as follows:
@@ -186,7 +199,7 @@ Now, there several thing will happen:
- Config file will be read, and wrapper module will be found
- New temporary directory will be created
- Archive with game assists will be extracted in that directory
- Archive with game assets will be extracted in that directory
- Configuration file will be copied into that directory, and renamed to
``Config.fs-uae``
- If ``wrapper_save_state`` is set, and there is saved state archive, it also
@@ -206,16 +219,18 @@ archive
Options used:
* ``wrapper`` (required) with ``archive`` as an value
* ``wrapper_archive`` (required) path to the archive with assets (usually means
whole system directories, floppies or hard disk images)
* ``wrapper_archiver`` (conditionally required) archiver to use for storage
save state
* ``wrapper_archive`` (optional) path to the archive with assets (usually means
whole system directories, floppies or hard disk images), defaults to same
name as configuration file with some detected archive extension. Note, that
name is case sensitive
* ``wrapper_archiver`` (optional) archiver to use for storage save state -
default ``7z``.
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
message during extracting files
* ``wrapper_persist_data`` (optional) if set to "1", will compress (possibly
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
directory, defined as ``$WRAPPER/[save-state-dir-name]`` using provided
``wrapper_archiver`` archiver. If this option is enabled,
``wrapper_archiver`` will be required.
@@ -237,7 +252,7 @@ Example configuration:
wrapper_gui_msg = 1
wrapper_persist_data = 1
wrapper_save_state = 1
...
# ...
And execution is as usual:
@@ -264,7 +279,8 @@ savestate
Options used:
* ``wrapper`` (required) with ``archive`` as an value
* ``wrapper_archiver`` (required) archiver to use for storage save state
* ``wrapper_archiver`` (optional) archiver to use for storage save state -
default ``7z``.
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
@@ -276,12 +292,11 @@ set to value ``1`` in this module.
Example configuration:
.. code:: ini
:number-lines:
[config]
wrapper = savestate
wrapper_archiver = 7z
...
# ...
And execution is as usual:
@@ -299,6 +314,155 @@ The steps would be as follows:
- optionally create archive with save state (if save state directory place is
*not* a global one)
whdload
-------
Options used:
* ``wrapper`` (required) with ``whdload`` as an value
* ``wrapper_whdload_base`` (required) path to the whdload base system. Usually
it's minimal system containing at least whdload executables in C, and config
in S. Read on below for further details.
* ``wrapper_archive`` (optional) path to the whdload archive, defaults to same
name as configuration file with some detected archive extension. Note, that
name is case sensitive
* ``wrapper_archiver`` (optional) archiver to use for storage save state -
default ``7z``.
This module is solely used with whdload distributed games (not just whdload
slave files, but whole games, which can be found on several places on the
internet).
Base image
~~~~~~~~~~
To make it work, first the minimal system archive need to be prepared. There
are few dependences to be included in such small system:
- `WHDLoad`_ 18.9
- `uaequit`_
- `SetPatch`_ 43.6
- ``Excecute``, ``Assign`` and whatever commands you'll be use in scripts from
your copy of Workbench
- `kgiconload`_ - tool for reading icon and executing *default tool* with
optionally defined tool types as parameters (in this case: WHDLoad)
- `SKick`_ optionally - for kickstart relocations. Also images of corresponding
kickstart ROM images will be needed.
Now, the tree for the minimal image could look like that:
.. code::
.
├── C
│   ├── Assign
│   ├── DIC
│   ├── Execute
│   ├── kgiconload
│   ├── Patcher
│   ├── RawDIC
│   ├── SetPatch
│   ├── UAEquit
│   ├── WHDLoad
│   └── WHDLoadCD32
└── S
├── startup-sequence
└── WHDLoad.prefs
to use relocation tables you'll need to place ``Kickstarts`` drawer into Devs
drawer, so it'll looks like this:
.. code::
.
├── C
│   ├── Assign
│   ├── …
│   └── WHDLoadCD32
├── Devs
│   └── Kickstarts
│   ├── 39115_ROMKick.PAT
│   ├── 39115_ROMKick.RTB
│   ├── …
│   ├── kick40068.A4000.PAT
│   └── kick40068.A4000.RTB
└── S
├── startup-sequence
└── WHDLoad.prefs
Important: You'll need to prepare archive with base OS without top directory,
i.e. suppose you have prepared all the files in ``/tmp/baseos``:
.. code:: shell-session
$ pwd
/tmp
$ cd baseos
$ pwd
/tmp/basos
$ ls
C S
$ zip -r /tmp/base.zip .
adding: C/ (stored 0%)
adding: C/Assign (deflated 31%)
adding: C/WHDLoadCD32 (deflated 26%)
adding: C/RawDIC (deflated 46%)
adding: C/UAEquit (deflated 39%)
adding: C/Execute (deflated 42%)
adding: C/Patcher (deflated 56%)
adding: C/DIC (deflated 33%)
adding: C/kgiconload (deflated 49%)
adding: C/SetPatch (deflated 39%)
adding: C/WHDLoad (deflated 23%)
adding: S/ (stored 0%)
adding: S/startup-sequence (deflated 36%)
adding: S/WHDLoad.prefs (deflated 51%)
You can do it with other archivers as well, like 7z: ``7z a /tmp/base.7z .`` or
tar with different compressions: ``tar Jcf /tmp/base.tar.xz .``, ``tar zcf
/tmp/base.tgz .``, ``tar jcf /tmp/base.tar.bz2 .``. It should work with all
mentioned at the beginning of this document archivers.
Starting point is in ``S/startup-sequence`` file, where eventually
``S/whdload-startup`` is executed, which will be created by fs-uae-warpper
before execution by fs-uae.
Configuration
~~~~~~~~~~~~~
Now, to use whdload module with any of the WHDLoad game, you'll need to prepare
configuration for the wrapper.
Example configuration:
.. code:: ini
[config]
wrapper = whdload
wrapper_whdload_base = $CONFIG/whdload_base.7z
# ...
And execution is as usual:
.. code:: shell-session
$ fs-uae-wrapper ChaosEngine_v1.2_0106.fs-uae
Now, similar to the archive module, it will create temporary directory, unpack
base image there, unpack WHDLoad game archive, search for slave file, and
preapre ``s:whdload-startup``, and finally pass all the configuration to
fs-uae.
Limitations
===========
There is one limitation when using save ``wrapper_save_state`` option. In case
of floppies it should work without any issues, although save state for running
Workbench or WHDLoad games may or may not work. In the past there was an issue
with `fs-uae`_ where saving state was causing data corruption on the emulated
system. Use it with caution!
License
=======
@@ -312,3 +476,9 @@ This work is licensed on 3-clause BSD license. See LICENSE file for details.
.. _lzx: http://aminet.net/package/misc/unix/unlzx.c.readme
.. _tar: https://www.gnu.org/software/tar/
.. _zip: http://www.info-zip.org
.. _CheeseShop: https://pypi.python.org/pypi/fs-/fs-uae-wrapperuae-wrapper
.. _WHDLoad: https://www.whdload.de
.. _uaequit: https://aminet.net/package/misc/emu/UAEquit
.. _SKick: https://aminet.net/package/util/boot/skick346
.. _SetPatch: https://aminet.net/package/util/boot/SetPatch_43.6b
.. _kgiconload: https://eab.abime.net/showpost.php?p=733614&postcount=92

View File

@@ -42,6 +42,7 @@ class Base(object):
- run the emulation
- archive save state
"""
logging.debug("run")
if not self._validate_options():
return False
@@ -176,13 +177,14 @@ class Base(object):
Configuration file will be placed in new directory, therefore it is
needed to calculate new paths so that emulator can find assets.
"""
options = ['wrapper_archive', 'accelerator_rom', 'base_dir',
'cdrom_drive_0', 'cdroms_dir', 'controllers_dir',
'cpuboard_flash_ext_file', 'cpuboard_flash_file',
'floppies_dir', 'floppy_overlays_dir', 'fmv_rom',
'graphics_card_rom', 'hard_drives_dir', 'kickstart_file',
'kickstarts_dir', 'logs_dir', 'save_states_dir',
'screenshots_output_dir']
logging.debug("_normalize_options")
options = ['wrapper_archive', 'wrapper_whdload_base',
'accelerator_rom', 'base_dir', 'cdrom_drive_0',
'cdroms_dir', 'controllers_dir', 'cpuboard_flash_ext_file',
'cpuboard_flash_file', 'floppies_dir',
'floppy_overlays_dir', 'fmv_rom', 'graphics_card_rom',
'hard_drives_dir', 'kickstart_file', 'kickstarts_dir',
'logs_dir', 'save_states_dir', 'screenshots_output_dir']
for num in range(20):
options.append('cdrom_image_%d' % num)
@@ -217,9 +219,14 @@ class Base(object):
if val.startswith('$CONFIG'):
abspath = utils.interpolate_variables(val, self.conf_file)
changed_options[key] = abspath
logging.info("%s: %s => %s", key, val, abspath)
continue
changed_options[key] = os.path.abspath(val)
_val = os.path.abspath(val)
if os.path.exists(_val):
changed_options[key] = _val
else:
changed_options[key] = val
self.fsuae_options.update(changed_options)
@@ -233,9 +240,9 @@ class Base(object):
return True
if 'wrapper_archiver' not in self.all_options:
logging.error("Configuration lacks of required "
"`wrapper_archiver' option.")
return False
logging.warning("Configuration lacks of optional "
"`wrapper_archiver' option, fall back to 7z")
self.all_options['wrapper_archiver'] = "7z"
if not path.which(self.all_options['wrapper_archiver']):
logging.error("Cannot find archiver `%s'.",
@@ -278,6 +285,7 @@ class ArchiveBase(Base):
def _extract(self):
"""Extract archive to temp dir"""
logging.debug("_extract")
title = self._get_title()
curdir = os.path.abspath('.')
@@ -287,12 +295,36 @@ class ArchiveBase(Base):
return result
def _validate_options(self):
logging.debug("_validate_options")
validation_result = super(ArchiveBase, self)._validate_options()
if not validation_result:
return False
if 'wrapper_archive' not in self.all_options:
sys.stderr.write("Configuration lacks of required "
"`wrapper_archive' option.\n")
validation_result = False
logging.warning("Configuration lacks of optional `wrapper_archive'"
" option.\n")
wrapper_archive = self._get_wrapper_archive_name()
if wrapper_archive is None:
logging.error("Configuration lacks of optional "
"`wrapper_archive', cannot deduct the name by "
"configuration file name.\n")
validation_result = False
self.all_options['wrapper_archive'] = wrapper_archive
return validation_result
def _get_wrapper_archive_name(self):
"""
Return full path to the archive name using configuration file
basename and appending one of the expected archive extensions.
"""
basename = os.path.splitext(os.path.basename(self.conf_file))[0]
file_list = os.listdir(os.path.dirname(self.conf_file))
for fname in file_list:
for ext in ('.7z', '.lha', '.lzx', '.zip', '.rar', '.tar', '.tgz',
'.tar.gz', '.tar.bz2', '.tar.xz'):
if ((basename + ext).lower() == fname.lower() and
basename == os.path.splitext(fname)[0]):
return fname
return None

View File

@@ -3,19 +3,15 @@ Display message in separate process
"""
import multiprocessing as mp
import sys
try:
import tkinter as tk
from tkinter import ttk
except ImportError:
import Tkinter as tk
import ttk
import tkinter
import tkinter.ttk
class MessageGui(tk.Tk):
class MessageGui(tkinter.Tk):
"""Simple gui for displaying a message"""
def __init__(self, parent=None, msg=""):
tk.Tk.__init__(self, parent)
tkinter.Tk.__init__(self, parent)
self.grid()
self.resizable(False, False)
@@ -26,10 +22,11 @@ class MessageGui(tk.Tk):
self.frame = ttk.Frame(self, padding=5, borderwidth=0)
self.frame.grid()
ttk.Label(self.frame, text=msg, relief="ridge", padding=10).grid()
tkinter.ttk.Label(self.frame, text=msg, relief="ridge",
padding=10).grid()
if 'linux' in sys.platform:
style = ttk.Style()
style = tkinter.ttk.Style()
style.theme_use('clam')
def __call__(self):

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Simple class for executing fs-uae with specified parameters. This is a
failsafe class for running fs-uae.

View File

@@ -1,14 +1,12 @@
"""
Misc utilities
"""
from distutils import spawn
import configparser
import logging
import os
import pathlib
import shutil
import subprocess
try:
import configparser
except ImportError:
import ConfigParser as configparser
from fs_uae_wrapper import message
from fs_uae_wrapper import file_archive
@@ -22,8 +20,8 @@ class CmdOption(dict):
def add(self, option):
"""parse and add option to the dictionary"""
if not option.startswith('--'):
raise AttributeError("Cannot add option `%s' to the dictionary" %
option)
raise AttributeError(f"Cannot add option {option} to the "
f"dictionary")
if '=' in option:
key, val = option.split('=', 1)
key = key[2:].strip()
@@ -38,15 +36,15 @@ class CmdOption(dict):
ret_list = []
for key, val in self.items():
if val != '1':
ret_list.append('--%(k)s=%(v)s' % {'k': key, 'v': val})
ret_list.append(f'--{key}={val}')
else:
ret_list.append('--%(k)s' % {'k': key})
ret_list.append(f'--{key}')
return ret_list
def get_config_options(conf):
"""Read config file and return options as a dict"""
parser = configparser.SafeConfigParser()
parser = configparser.ConfigParser()
try:
parser.read(conf)
except configparser.ParsingError:
@@ -90,7 +88,7 @@ def create_archive(arch_name, title='', params=None):
"""
msg = ''
if title:
msg = "Creating archive for `%s'. Please be patient" % title
msg = f"Creating archive for `{title}'. Please be patient"
return operate_archive(arch_name, 'create', msg, params)
@@ -100,7 +98,7 @@ def extract_archive(arch_name, title='', params=None):
"""
msg = ''
if title:
msg = "Extracting files for `%s'. Please be patient" % title
msg = f"Extracting files for `{title}'. Please be patient"
return operate_archive(arch_name, 'extract', msg, params)
@@ -144,18 +142,20 @@ def interpolate_variables(string, config_path, base=None):
- $BASE
"""
_string = string
if '$CONFIG' in string:
conf_path = os.path.dirname(os.path.abspath(config_path))
string = os.path.abspath(string.replace('$CONFIG', conf_path))
conf_path = pathlib.Path(config_path).resolve().parent
string = str(pathlib.Path(string.replace('$CONFIG', str(conf_path)))
.resolve())
if '$HOME' in string:
string = string.replace('$HOME', os.path.expandvars('$HOME'))
if '$EXE' in string:
string = string.replace('$EXE', spawn.find_executable('fs-uae'))
string = string.replace('$EXE', shutil.which('fs-uae'))
if '$APP' in string:
string = string.replace('$APP', spawn.find_executable('fs-uae'))
string = string.replace('$APP', shutil.which('fs-uae'))
if '$DOCUMENTS' in string:
xdg_docs = os.getenv('XDG_DOCUMENTS_DIR',
@@ -166,7 +166,9 @@ def interpolate_variables(string, config_path, base=None):
if '$BASE' in string:
string = string.replace('$BASE', base)
return string
if os.path.exists(string):
return string
return _string
def get_config(conf_file):

115
fs_uae_wrapper/whdload.py Normal file
View File

@@ -0,0 +1,115 @@
"""
Run fs-uae with WHDLoad games
It will use compressed base image and compressed directories.
"""
import logging
import os
import shutil
from fs_uae_wrapper import base
from fs_uae_wrapper import utils
class Wrapper(base.ArchiveBase):
"""
Class for performing extracting archive, copying emulator files, and
cleaning it back again
"""
def __init__(self, conf_file, fsuae_options, configuration):
super(Wrapper, self).__init__(conf_file, fsuae_options, configuration)
self.archive_type = None
def run(self):
"""
Main function which accepts configuration file for FS-UAE
It will do as follows:
- extract base image and archive file
- copy configuration
- run the emulation
"""
logging.debug("run")
if not super().run():
return False
if not self._extract():
return False
if not self._copy_conf():
return False
return self._run_emulator()
def _validate_options(self):
"""
Do the validation for the options, additionally check if there is
mandatory WHDLoad base OS images set.
"""
if not super()._validate_options():
return False
if not self.all_options.get('wrapper_whdload_base'):
logging.error("wrapper_whdload_base is not set in configuration, "
"exiting.")
return False
return True
def _extract(self):
"""Extract base image and then WHDLoad archive"""
base_image = self.fsuae_options['wrapper_whdload_base']
if not os.path.exists(base_image):
logging.error("Base image `%s` does't exists in provided "
"location.", base_image)
return False
title = self._get_title()
curdir = os.path.abspath('.')
os.chdir(self.dir)
result = utils.extract_archive(base_image)
os.chdir(curdir)
if not result:
return False
if not super()._extract():
return False
return self._find_slave()
def _find_slave(self):
"""Find Slave file and create apropriate entry in S:whdload-startup"""
curdir = os.path.abspath('.')
os.chdir(self.dir)
# find slave name
slave_fname = None
slave_path = None
for root, dirs, fnames in os.walk('.'):
for fname in fnames:
if fname.lower().endswith('.slave'):
slave_path, slave_fname = os.path.normpath(root), fname
break
if slave_fname is None:
logging.error("Cannot find .slave file in archive.")
return False
# find corresponfing info (an icon) fname
icon_fname = None
for fname in os.listdir(slave_path):
if (fname.lower().endswith('.info') and
os.path.splitext(slave_fname)[0].lower() ==
os.path.splitext(fname)[0].lower()):
icon_fname = fname
break
if icon_fname is None:
logging.error("Cannot find .info file corresponding to %s in "
"archive.", slave_fname)
return False
# Write startup file
with open("S/whdload-startup", "w") as fobj:
fobj.write(f"cd {slave_path}\n")
fobj.write(f"C:kgiconload {icon_fname}\n")
os.chdir(curdir)
return True

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
"""
Wrapper for FS-UAE to perform some actions before and or after running the
emulator, if appropriate option is enabled.
@@ -28,7 +27,8 @@ def setup_logger(options):
level = logging.DEBUG
logging.basicConfig(level=level,
format="%(asctime)s %(levelname)s: %(message)s")
format="%(asctime)s %(levelname)s\t%(filename)s:"
"%(lineno)d:\t\t%(message)s")
def parse_args():
@@ -119,7 +119,3 @@ def run():
if not exit_code:
sys.exit(4)
if __name__ == "__main__":
run()

40
pyproject.toml Normal file
View File

@@ -0,0 +1,40 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "fs-uae-wrapper"
authors = [{name = "Roman Dobosz", email = "gryf73@gmail.com"}]
license = {text = "BSD"}
description = "Automate archives support and state saves for fs-uae"
readme = "README.rst"
requires-python = ">=3.8"
keywords = ["uae", "fs-uae", "amiga", "emulator", "wrapper"]
version = "0.10.0"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: System :: Emulators",
"Topic :: Games/Entertainment"
]
[project.urls]
Homepage = "https://github.com/gryf/fs-uae-wrapper"
[project.scripts]
fs-uae-wrapper = "fs_uae_wrapper.wrapper:run"
[tool.setuptools]
py-modules = ["fs_uae_wrapper"]
[tool.distutils.bdist_wheel]
universal = true

View File

@@ -1,16 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Run CD32 games using fsuae
"""
from fs_uae_wrapper import wrapper
def main():
"""run wrapper"""
wrapper.run()
if __name__ == "__main__":
main()

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env python
"""
Setup for the fs-uae-wrapper
"""
from setuptools import setup
setup(name='fs-uae-wrapper',
packages=['fs_uae_wrapper'],
version='0.8',
description='Automate archives and state for fs-uae',
author='Roman Dobosz',
author_email='gryf73@gmail.com',
url='https://github.com/gryf/fs-uea-wrapper',
download_url='https://github.com/gryf/pygtktalog.git',
keywords=['uae', 'fs-uae', 'amiga', 'emulator', 'wrapper'],
scripts=['script/fs-uae-wrapper'],
classifiers=['Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: End Users/Desktop',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Topic :: System :: Emulators',
'Topic :: Games/Entertainment'],
long_description=open('README.rst').read(),
options={'test': {'verbose': False,
'coverage': False}})

View File

@@ -2,11 +2,7 @@ import os
import shutil
from tempfile import mkdtemp
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import archive
from fs_uae_wrapper import utils
@@ -26,23 +22,26 @@ class TestArchive(TestCase):
except OSError:
pass
@mock.patch('fs_uae_wrapper.base.ArchiveBase._get_wrapper_archive_name')
@mock.patch('fs_uae_wrapper.path.which')
def test_validate_options(self, which):
def test_validate_options(self, which, get_wrapper_arch_name):
which.return_value = 'unrar'
arch = archive.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(arch._validate_options())
arch.all_options = {'wrapper': 'archive'}
arch.all_options['wrapper'] = 'archive'
get_wrapper_arch_name.return_value = None
arch.all_options = {'wrapper': 'archive'}
self.assertFalse(arch._validate_options())
get_wrapper_arch_name.return_value = 'fake_arch_filename'
arch.all_options['wrapper_archive'] = 'rar'
self.assertTrue(arch._validate_options())
@mock.patch('tempfile.mkdtemp')
@mock.patch('fs_uae_wrapper.path.which')
@mock.patch('fs_uae_wrapper.archive.Wrapper._make_archive')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._get_wrapper_archive_name')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._save_save')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._get_saves_dir')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._run_emulator')
@@ -50,7 +49,8 @@ class TestArchive(TestCase):
@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, run_emulator,
get_save_dir, save_state, make_arch, which, mkdtemp):
get_save_dir, save_state, get_wrapper_arch_name, make_arch,
which, mkdtemp):
extract.return_value = False
load_save.return_value = False
@@ -58,6 +58,7 @@ class TestArchive(TestCase):
run_emulator.return_value = False
get_save_dir.return_value = False
save_state.return_value = False
get_wrapper_arch_name.return_value = "fake_arch_filename"
make_arch.return_value = False
which.return_value = 'rar'

View File

@@ -3,11 +3,7 @@ import sys
import shutil
from tempfile import mkstemp, mkdtemp
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import base
from fs_uae_wrapper import utils
@@ -47,9 +43,11 @@ class TestBase(TestCase):
bobj.clean()
self.assertFalse(os.path.exists(self.dirname))
@mock.patch('os.path.exists')
@mock.patch('fs_uae_wrapper.utils.get_config')
def test_normalize_options(self, get_config):
def test_normalize_options(self, get_config, os_exists):
os_exists.return_value = True
bobj = base.Base('Config.fs-uae', utils.CmdOption(), {})
get_config.return_value = {'kickstarts_dir': '/some/path'}
@@ -92,6 +90,33 @@ class TestBase(TestCase):
bobj._normalize_options()
self.assertDictEqual(bobj.fsuae_options, {})
get_config.return_value = {'random_item': 10}
bobj.fsuae_options = utils.CmdOption()
bobj._normalize_options()
self.assertDictEqual(bobj.fsuae_options, {})
@mock.patch('os.path.exists')
@mock.patch('fs_uae_wrapper.utils.get_config')
def test_normalize_options_path_not_exists(self, get_config, os_exists):
os_exists.return_value = False
bobj = base.Base('Config.fs-uae', utils.CmdOption(), {})
get_config.return_value = {'kickstarts_dir': '/some/path'}
bobj._normalize_options()
self.assertDictEqual(bobj.fsuae_options, {})
os.chdir(self.dirname)
get_config.return_value = {'fmv_rom': 'bar'}
bobj._normalize_options()
self.assertDictEqual(bobj.fsuae_options, {'fmv_rom': 'bar'})
get_config.return_value = {'floppies_dir': '../some/path'}
bobj.fsuae_options = utils.CmdOption()
bobj._normalize_options()
self.assertDictEqual(bobj.fsuae_options,
{'floppies_dir': '../some/path'})
def test_set_assets_paths(self):
bobj = base.Base('Config.fs-uae', utils.CmdOption(), {})
@@ -262,7 +287,7 @@ class TestBase(TestCase):
which.return_value = '7z'
bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1'}
self.assertFalse(bobj._validate_options())
self.assertTrue(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1',
@@ -366,16 +391,42 @@ class TestArchiveBase(TestCase):
self.assertFalse(bobj._extract())
utils_extract.assert_called_once_with(self.fname, '')
def test_validate_options(self):
@mock.patch('fs_uae_wrapper.base.ArchiveBase._get_wrapper_archive_name')
def test_validate_options(self, get_wrapper_arch_name):
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
bobj.all_options = {}
self.assertFalse(bobj._validate_options())
get_wrapper_arch_name.return_value = None
bobj.all_options = {'wrapper': 'dummy'}
self.assertFalse(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy',
'wrapper_archive': 'myarchive.7z'}
self.assertTrue(bobj._validate_options())
@mock.patch('os.listdir')
def test_get_wrapper_archive_name(self, os_listdir):
os_listdir.return_value = 'no archive among other files'.split()
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
bobj.all_options = {'wrapper': 'dummy'}
self.assertIsNone(bobj._get_wrapper_archive_name())
os_listdir.return_value = 'no config.rar among other files'.split()
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
bobj.all_options = {'wrapper': 'dummy'}
self.assertIsNone(bobj._get_wrapper_archive_name())
os_listdir.return_value = 'file Config.TAR among other files'.split()
bobj = base.ArchiveBase('Config.fs-uae', utils.CmdOption(), {})
bobj.all_options = {'wrapper': 'dummy'}
self.assertEqual(bobj._get_wrapper_archive_name(), 'Config.TAR')
os_listdir.return_value = 'Config.lha FooBar_1.24b_20202.7z'.split()
bobj = base.ArchiveBase('FooBar_1.24b_20202.fs-uae',
utils.CmdOption(), {})
bobj.all_options = {'wrapper': 'dummy'}
self.assertEqual(bobj._get_wrapper_archive_name(),
'FooBar_1.24b_20202.7z')

View File

@@ -1,9 +1,5 @@
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import cd32
from fs_uae_wrapper import utils
@@ -13,6 +9,7 @@ class TestCD32(TestCase):
@mock.patch('tempfile.mkdtemp')
@mock.patch('fs_uae_wrapper.path.which')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._get_wrapper_archive_name')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._save_save')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._get_saves_dir')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._run_emulator')
@@ -20,7 +17,8 @@ class TestCD32(TestCase):
@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, run_emulator,
get_save_dir, save_state, which, mkdtemp):
get_save_dir, save_state, get_wrapper_arch_name, which,
mkdtemp):
extract.return_value = False
copy_conf.return_value = False
@@ -28,6 +26,7 @@ class TestCD32(TestCase):
run_emulator.return_value = False
get_save_dir.return_value = False
save_state.return_value = False
get_wrapper_arch_name.return_value = "fake_arch_filename"
which.return_value = 'unrar'
acd32 = cd32.Wrapper('Config.fs-uae', utils.CmdOption(), {})

View File

@@ -2,11 +2,7 @@ import os
import shutil
from tempfile import mkdtemp
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import file_archive

View File

@@ -1,20 +1,12 @@
from unittest import TestCase
import os
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import message
if os.environ.get('DISPLAY'):
try:
import tkinter as tk
from tkinter import ttk
except ImportError:
import Tkinter as tk
import ttk
import tkinter as tk
from tkinter import ttk
class TestMessage(TestCase):

View File

@@ -1,9 +1,5 @@
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import plain
from fs_uae_wrapper import utils

View File

@@ -2,11 +2,7 @@ import os
import shutil
from tempfile import mkdtemp
from unittest import TestCase
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import savestate
from fs_uae_wrapper import utils
@@ -73,7 +69,7 @@ class TestSaveState(TestCase):
self.assertFalse(arch._validate_options())
arch.all_options['wrapper'] = 'savestate'
self.assertFalse(arch._validate_options())
self.assertTrue(arch._validate_options())
arch.all_options['wrapper_archiver'] = 'rar'
self.assertTrue(arch._validate_options())

View File

@@ -3,11 +3,7 @@ import sys
from tempfile import mkstemp, mkdtemp
from unittest import TestCase
import shutil
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import utils
@@ -234,11 +230,13 @@ class TestCmdOptions(TestCase):
self.assertListEqual(sorted(cmd.list()),
['--fast_memory=4096', '--fullscreen'])
@mock.patch('os.path.exists')
@mock.patch('os.getenv')
@mock.patch('os.path.expandvars')
@mock.patch('distutils.spawn.find_executable')
def test_interpolate_variables(self, find_exe, expandv, getenv):
@mock.patch('shutil.which')
def test_interpolate_variables(self, which, expandv, getenv, os_exists):
os_exists.return_value = True
itrpl = utils.interpolate_variables
string = '$CONFIG/../path/to/smth'
@@ -250,7 +248,7 @@ class TestCmdOptions(TestCase):
'/home/user')
string = '$APP/$EXE'
find_exe.return_value = '/usr/bin/fs-uae'
which.return_value = '/usr/bin/fs-uae'
self.assertEqual(itrpl(string, '/home/user/Config.fs-uae'),
'/usr/bin/fs-uae//usr/bin/fs-uae')
@@ -264,3 +262,11 @@ class TestCmdOptions(TestCase):
'$BASE')
self.assertEqual(itrpl(string, '/home/user/Config.fs-uae', 'base'),
'base')
@mock.patch('os.getenv')
@mock.patch('os.path.expandvars')
def test_interpolate_variables_path_not_exists(self, expandv, getenv):
itrpl = utils.interpolate_variables
string = '$CONFIG/../path/to/smth'
self.assertEqual(itrpl(string, '/home/user/Config.fs-uae'), string)

192
tests/test_whdload.py Normal file
View File

@@ -0,0 +1,192 @@
import os
import shutil
from tempfile import mkdtemp
from unittest import TestCase
from unittest import mock
from fs_uae_wrapper import whdload
from fs_uae_wrapper import utils
class TestWHDLoad(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('fs_uae_wrapper.base.ArchiveBase._validate_options')
def test_validate_options_arch_validation_fail(self, base_valid):
base_valid.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper._validate_options())
@mock.patch('fs_uae_wrapper.base.ArchiveBase._validate_options')
def test_validate_options_no_base_image(self, base_valid):
base_valid.return_value = True
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper._validate_options())
@mock.patch('fs_uae_wrapper.base.ArchiveBase._validate_options')
def test_validate_options_with_base_image_set(self, base_valid):
base_valid.return_value = True
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.all_options['wrapper_whdload_base'] = 'fake_base_fname.7z'
self.assertTrue(wrapper._validate_options())
@mock.patch('fs_uae_wrapper.base.ArchiveBase.run')
def test_run_base_run_fail(self, run):
run.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper.run())
@mock.patch('fs_uae_wrapper.whdload.Wrapper._extract')
@mock.patch('fs_uae_wrapper.base.ArchiveBase.run')
def test_run_extract_fail(self, run, extract):
run.return_value = True
extract.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.all_options = {'wrapper': 'whdload',
'wrapper_archive': 'fake.tgz',
'wrapper_archiver': 'rar'}
self.assertFalse(wrapper.run())
@mock.patch('fs_uae_wrapper.base.ArchiveBase._copy_conf')
@mock.patch('fs_uae_wrapper.whdload.Wrapper._extract')
@mock.patch('fs_uae_wrapper.base.ArchiveBase.run')
def test_run_copy_conf_fail(self, run, extract, copy_conf):
run.return_value = True
extract.return_value = True
copy_conf.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper.run())
@mock.patch('fs_uae_wrapper.base.ArchiveBase._run_emulator')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._copy_conf')
@mock.patch('fs_uae_wrapper.whdload.Wrapper._extract')
@mock.patch('fs_uae_wrapper.base.ArchiveBase.run')
def test_run_emulator_fail(self, run, extract, copy_conf, run_emulator):
run.return_value = True
extract.return_value = True
copy_conf.return_value = True
run_emulator.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper.run())
@mock.patch('fs_uae_wrapper.base.ArchiveBase._run_emulator')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._copy_conf')
@mock.patch('fs_uae_wrapper.whdload.Wrapper._extract')
@mock.patch('fs_uae_wrapper.base.ArchiveBase.run')
def test_run_success(self, run, extract, copy_conf, run_emulator):
run.return_value = True
extract.return_value = True
copy_conf.return_value = True
run_emulator.return_value = True
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertTrue(wrapper.run())
@mock.patch('os.path.exists')
def test_extract_nonexistent_image(self, exists):
exists.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.fsuae_options['wrapper_whdload_base'] = 'fakefilename'
self.assertFalse(wrapper._extract())
@mock.patch('os.chdir')
@mock.patch('os.path.exists')
def test_extract_extraction_failed(self, exists, chdir):
exists.return_value = True
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.fsuae_options['wrapper_whdload_base'] = 'fakefilename.7z'
self.assertFalse(wrapper._extract())
@mock.patch('fs_uae_wrapper.base.ArchiveBase._extract')
@mock.patch('fs_uae_wrapper.utils.extract_archive')
@mock.patch('os.chdir')
@mock.patch('os.path.exists')
def test_extract_extraction_of_whdload_arch_failed(self, exists, chdir,
image_extract,
arch_extract):
exists.return_value = True
image_extract.return_value = True
arch_extract.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.fsuae_options['wrapper_whdload_base'] = 'fakefilename'
self.assertFalse(wrapper._extract())
@mock.patch('fs_uae_wrapper.whdload.Wrapper._find_slave')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._extract')
@mock.patch('fs_uae_wrapper.utils.extract_archive')
@mock.patch('os.chdir')
@mock.patch('os.path.exists')
def test_extract_slave_not_found(self, exists, chdir, image_extract,
arch_extract, find_slave):
exists.return_value = True
image_extract.return_value = True
arch_extract.return_value = True
find_slave.return_value = False
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.fsuae_options['wrapper_whdload_base'] = 'fakefilename'
self.assertFalse(wrapper._extract())
@mock.patch('fs_uae_wrapper.whdload.Wrapper._find_slave')
@mock.patch('fs_uae_wrapper.base.ArchiveBase._extract')
@mock.patch('fs_uae_wrapper.utils.extract_archive')
@mock.patch('os.chdir')
@mock.patch('os.path.exists')
def test_extract_success(self, exists, chdir, image_extract, arch_extract,
find_slave):
exists.return_value = True
image_extract.return_value = True
arch_extract.return_value = True
find_slave.return_value = True
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
wrapper.fsuae_options['wrapper_whdload_base'] = 'fakefilename'
self.assertTrue(wrapper._extract())
@mock.patch('os.walk')
@mock.patch('os.chdir')
def test_find_slave_no_slave_file(self, chdir, walk):
walk.return_value = [(".", ('game'), ()),
('./game', (), ('foo', 'bar', 'baz'))]
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper._find_slave())
@mock.patch('os.listdir')
@mock.patch('os.walk')
@mock.patch('os.chdir')
def test_find_slave_no_corresponding_icon(self, chdir, walk, listdir):
contents = ('foo', 'bar', 'baz.slave')
walk.return_value = [(".", ('game'), ()),
('./game', (), contents)]
listdir.return_value = contents
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertFalse(wrapper._find_slave())
@mock.patch('builtins.open')
@mock.patch('os.listdir')
@mock.patch('os.walk')
@mock.patch('os.chdir')
def test_find_slave_success(self, chdir, walk, listdir, bopen):
contents = ('foo', 'bar', 'baz.slave', 'baz.info')
walk.return_value = [(".", ('game'), ()),
('./game', (), contents)]
listdir.return_value = contents
wrapper = whdload.Wrapper('Config.fs-uae', utils.CmdOption(), {})
self.assertTrue(wrapper._find_slave())
bopen.assert_called_once()

View File

@@ -3,11 +3,7 @@ import sys
from tempfile import mkstemp, mkdtemp
from unittest import TestCase
import shutil
try:
from unittest import mock
except ImportError:
import mock
from unittest import mock
from fs_uae_wrapper import wrapper
@@ -47,10 +43,7 @@ class TestWrapper(TestCase):
fobj.write('\n')
wrapper.run()
mock_plain_run.called_once_with('Config.fs-uae',
['--fullscreen',
'--fade_out_duration=0'],
[])
mock_plain_run.assert_called_once()
# This will obviously fail for nonexistent module
sys.argv.append('--wrapper=dummy_wrapper')

15
tox.ini
View File

@@ -1,5 +1,5 @@
[tox]
envlist = py27,py34,py27-flake8,py34-flake8
envlist = py3,py3-flake8
usedevelop = True
@@ -10,16 +10,7 @@ commands = py.test --cov=fs_uae_wrapper --cov-report=term-missing
deps = -r{toxinidir}/test-requirements.txt
[testenv:py27]
deps = {[testenv]deps}
mock
[testenv:py34-flake8]
basepython = python3.4
deps = flake8
commands = flake8 {posargs}
[testenv:py27-flake8]
basepython = python2.7
[testenv:py3-flake8]
basepython = python3
deps = flake8
commands = flake8 {posargs}