Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Virtualenv files
bin/
lib/
Lib/
Scripts/
include/
Include/
pip-selfcheck.json
selenium/
share/
.env
.python-version

# Python Files
*.Python
__pycache__/
*.pyd
*.pyc
.functional.backup
.ipynb_checkpoints
environment.pickle

# IDE Files
.idea
.cache
.vscode
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ PyModel: Model-based testing in Python

**PyModel** is an open-source model-based testing framework in Python.

(July 2021: The fork at <https://github.com/zlorb/PyModel> adds Python 3.x
support. They have submitted a pull request but I have not yet merged it.)

In model-based testing, you code a *model* that can generate as many
test cases as needed. The model also checks the test outcomes.
Model-based testing is helpful where so many test cases are needed
Expand Down Expand Up @@ -86,4 +83,14 @@ PyModel is covered by the
Code and documents are copyright (C) 2009-2013, Jonathan Jacky.


Revised May 2013 (note about pull request added Jul 2021)
Updates
=======

Python 3.x support, and additional updates and fixes:
- @jproudlo
- @philscrace2
- @zlorb


Revised Feb 2022

4 changes: 2 additions & 2 deletions bin/pma
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
This is pma command in bin directory, runs pma.py module in pymodel directory
"""

import pma # find pma.py anywhere on Python path
import pymodel.pma # find pma.py anywhere on Python path

pma.main()
pymodel.pma.main()
4 changes: 2 additions & 2 deletions bin/pmg
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
This is pmg command in bin directory, runs pmg.py module in pymodel directory
"""

import pmg # find pmg.py anywhere on Python path
import pymodel.pmg # find pmg.py anywhere on Python path

pmg.main()
pymodel.pmg.main()
4 changes: 2 additions & 2 deletions bin/pmt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
This is pmt command in bin directory, runs pmt.py module in pymodel directory
"""

import pmt # find pmt.py anywhere on Python path
import pymodel.pmt # find pmt.py anywhere on Python path

pmt.main()
pymodel.pmt.main()
4 changes: 2 additions & 2 deletions bin/pmv
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
This is pmv command in bin directory, runs pmv.py module in pymodel directory
"""

import pmv # find pmv.py anywhere on Python path
import pymodel.pmv # find pmv.py anywhere on Python path

pmv.main()
pymodel.pmv.main()
2 changes: 1 addition & 1 deletion bin/trun
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
This is trun command in bin directory, runs trun.py module in pymodel directory
"""

import trun # find and execute trun.py anywhere on Python path
import pymodel.trun # find and execute trun.py anywhere on Python path
4 changes: 2 additions & 2 deletions bin/wsgirunner
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
This is wsgirunner command in bin directory, runs wsgirunner.py module in pymodel directory
"""

import wsgirunner # find wsgirunner.py anywhere on Python path
import pymodel.wsgirunner # find wsgirunner.py anywhere on Python path

wsgirunner.main()
pymodel.wsgirunner.main()
206 changes: 103 additions & 103 deletions pymodel/FSM.py
Original file line number Diff line number Diff line change
@@ -1,103 +1,103 @@
"""
Interface to an FSM module (graph) used by ProductModelProgram
"""
from model import Model
class FSM(Model):
def __init__(self, module, exclude, include):
Model.__init__(self, module, exclude, include)
def post_init(self):
"""
Now that all modules have been imported and executed their __init__
do a postprocessing pass
to process metadata that might be affected by configuration modules
"""
# Do all of this work here rather than in __init__
# so it can include the effects of any pymodel config modules
# Make copies of collections that may be altered later
# self.actions is not used in this module outside this __init__
# BUT it is used in several places in Model and ProductModelProgram
# EnabledTransitions below works directly on self.module.graph,
# not self.actions
if not hasattr(self.module, 'actions'):
self.actions = list(self.actions_in_graph()) # default, make copy
else:
self.actions = list(self.module.actions) # copy
Model.post_init(self) # uses self.actions
# Construct self.graph like self.module.graph
# BUT also accounts for include, exclude via self.actions
self.graph = [ (current, (action,args,result), next)
for (current, (action,args,result), next) in
self.module.graph if action in self.actions ]
# prepare for first run
self.current = self.module.initial # raise exception if module is not FSM
def actions_in_graph(self):
return tuple(set([ action for (current, (action,args,result), next) in
self.module.graph])) # not self.graph, here ONLY
def make_properties(self, state):
return { 'accepting': state in self.module.accepting, 'statefilter': True,
'stateinvariant': True }
def Properties(self):
return self.make_properties(self.current)
def Reset(self): # needed by stepper
self.current = self.module.initial
def CleanupGraph(self, cleanup=False):
"""
if cleanup, return the graph with just the cleanup transitions
"""
if cleanup:
graph = [(current,(action,args,result),next)
for (current,(action,args,result),next) in self.graph
if action in self.module.cleanup]
else:
graph = self.graph
return graph
def ActionEnabled(self, a, args):
"""
action a with args is enabled in the current state
"""
# no cleanup check here
# any args matches empty arguments in FSM
return any([(a == action and (not arguments or args == arguments))
for (current,(action, arguments, result),next)
in self.graph
if current == self.current ])
def EnabledTransitions(self, cleanup=False):
"""
Return list tuples for all enabled actions:
(action, args, next state, properties)
"""
graph = self.CleanupGraph(cleanup)
return [(action, args, result, next, self.make_properties(next))
for (current,(action,args,result),next) in graph
if current == self.current ]
def DoAction(self, a, arguments):
ts = [(current,(action,args,result),next)
for (current,(action,args,result),next) in self.graph
if current == self.current and action == a
and args == arguments[0:len(args)]] #ignore extra trailing args
# print 'List ts %s' % ts # DEBUG
# Might be nondet. FSM: for now, just pick first transition that matches
current, (action,args,result), self.current = ts[0]
return result
def Current(self):
return self.current
def Restore(self, state):
self.current = state
# GetNext not needed
"""
Interface to an FSM module (graph) used by ProductModelProgram
"""

from pymodel.model import Model

class FSM(Model):

def __init__(self, module, exclude, include):
Model.__init__(self, module, exclude, include)

def post_init(self):
"""
Now that all modules have been imported and executed their __init__
do a postprocessing pass
to process metadata that might be affected by configuration modules
"""
# Do all of this work here rather than in __init__
# so it can include the effects of any pymodel config modules

# Make copies of collections that may be altered later
# self.actions is not used in this module outside this __init__
# BUT it is used in several places in Model and ProductModelProgram
# EnabledTransitions below works directly on self.module.graph,
# not self.actions
if not hasattr(self.module, 'actions'):
self.actions = list(self.actions_in_graph()) # default, make copy
else:
self.actions = list(self.module.actions) # copy
Model.post_init(self) # uses self.actions
# Construct self.graph like self.module.graph
# BUT also accounts for include, exclude via self.actions
self.graph = [ (current, (action,args,result), next)
for (current, (action,args,result), next) in
self.module.graph if action in self.actions ]
# prepare for first run
self.current = self.module.initial # raise exception if module is not FSM


def actions_in_graph(self):
return tuple(set([ action for (current, (action,args,result), next) in
self.module.graph])) # not self.graph, here ONLY

def make_properties(self, state):
return { 'accepting': state in self.module.accepting, 'statefilter': True,
'stateinvariant': True }

def Properties(self):
return self.make_properties(self.current)

def Reset(self): # needed by stepper
self.current = self.module.initial

def CleanupGraph(self, cleanup=False):
"""
if cleanup, return the graph with just the cleanup transitions
"""
if cleanup:
graph = [(current,(action,args,result),next)
for (current,(action,args,result),next) in self.graph
if action in self.module.cleanup]
else:
graph = self.graph
return graph

def ActionEnabled(self, a, args):
"""
action a with args is enabled in the current state
"""
# no cleanup check here
# any args matches empty arguments in FSM
return any([(a == action and (not arguments or args == arguments))
for (current,(action, arguments, result),next)
in self.graph
if current == self.current ])

def EnabledTransitions(self, cleanup=False):
"""
Return list tuples for all enabled actions:
(action, args, next state, properties)
"""
graph = self.CleanupGraph(cleanup)
return [(action, args, result, next, self.make_properties(next))
for (current,(action,args,result),next) in graph
if current == self.current ]

def DoAction(self, a, arguments):
ts = [(current,(action,args,result),next)
for (current,(action,args,result),next) in self.graph
if current == self.current and action == a
and args == arguments[0:len(args)]] #ignore extra trailing args
# print 'List ts %s' % ts # DEBUG
# Might be nondet. FSM: for now, just pick first transition that matches
current, (action,args,result), self.current = ts[0]
return result

def Current(self):
return self.current

def Restore(self, state):
self.current = state

# GetNext not needed
Loading