Changes between Version 2 and Version 3 of TestManagerForTracPluginWorkflow


Ignore:
Timestamp:
Oct 9, 2010, 4:39:33 PM (5 years ago)
Author:
seccanj
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • TestManagerForTracPluginWorkflow

    v2 v3  
    11= Test Manager for Trac - Generic Workflow Engine =
    22
    3 
    4 
    5 
    6 '''To be documented.'''
     3The Test Manager plugin is comprised of four plugins, one of which is a Generic Workflow Engine plugin for any Trac resource.
     4
     5The following figure shows a sample workflow added to Test Cases with custom sample operations. No built-in operation is currently implemented but the sample one shown here, named 'sample_operation', which logs a debug message with the text input by the User.
     6
     7Every object which has a workflow defined is created in a "new" state, so every transition should consider this as the first state in the state machine.
     8
     9
     10== How to implement Workflow on Test Cases ==
     11
     12The following is a cookbook to implement Workflow on Test Cases.
     13
     14=== 1) Declare the desired workflow steps ===
     15
     16First thing to do is to define the "state machine". Think of it as the definition of the steps your artifacts should go through in their transitions between a state and another.
     17
     18A state machine indeed defines three things:
     19 * States - the 'steps' of the workflow. Note that the first step of any workflow will always be "new".
     20 * Transition actions - the User actions avaiable in each state, which may optionally move the artifact from that state to another.
     21 * Operations - what the system must perform on the artifact (but not only) when a certain transition action is triggered.
     22 
     23The three elements above are specified in the trac.ini file with the following syntax.
     24
     25=== Definition of a workflow for a resource type ===
     26
     27First of all you should define that a particular resource type, e.g. Test Cases, should be managed through a workflow.
     28
     29To do this, you must add to the trac.ini file a new section, named after the resource type followed by "-resource_workflow".
     30
     31For resource type we mean the "realm" string used for resource registration into the Trac environment.
     32
     33Resource type names for the Test Manager plugin artifacts are the following:
     34 * testcatalog
     35 * testcase
     36 * testcaseinplan
     37 * testplan
     38
     39For example, to start the definition of a state machine for Test Case, add the following into the trac.ini file:
     40[testcase-resource_workflow]
     41
     42 
     43=== Definition of transition actions and states: ===
     44
     45In the section started with the row above you can define the state machine for the specified artifact.
     46
     47To define states of the workflow and actions that allow for moving artifacts from one state to another - so said transition actions, use the following syntax:
     48
     49{{{
     50  '''action''' = '''old state''' -> '''new state'''
     51}}}
     52
     53This is sufficient to:
     54 * Define a state named '''old state'''
     55 * Define a state named '''new state'''
     56 * Define a transition action '''action''' that should be available in the state '''old state''' and that, when chosen by the User, will move the artifact into the '''new state'''.
     57 
     58Special cases:
     59 * You can specify that an action should be available in all states by using the "* -> *" syntax for the state transition part (i.e. right side of the equal sign).
     60 * You can specify that an action should be available in more than one state by separating them with a comma, as in "asleep,calm -> dead".
     61
     62
     63=== Definition of operations to be performed along with specified actions ===
     64
     65You can specify that every time an artifact is moved from one state to another by means of a transition action, the system should perfomr one or more operations.
     66
     67There is a set of [wiki:TestManagerPluginWorkflowOperations out-of-the-box operations] available, but more operations can be provided with your or other plugins by implementing the IWorkflowOperationProvider interface.
     68
     69To specify one or more operations to be performed along with a transition action, use the following syntax:
     70
     71{{{
     72  '''action'''.operations = '''operation1''','''operation2''',...
     73}}}
     74
     75This specifies that the operations named '''operation1''' etcetera should be performed after the User has chosen the transition action named '''action'''.
     76
     77You can specify one or more operations this way, separating them with commas.
     78
     79=== Other properties ===
     80
     81There are other optional properties of a workflow that let you customize its behavior.
     82
     83 * Permissions: you can specify which permissions a User must have to be able to fire any particular transition action. To do this, use the following syntax:
     84   '''action'''.permissions = '''permission1''','''permission2''',...
     85 
     86 
     87=== Example ===
     88
     89This is a sample content of the trac.ini file to associate a workflow to the Test Case object.
     90
     91The workflow is shown in the following figure:
     92
     93[[BR]]
     94[[BR]]
     95[[Image(sample_workflow.png)]]
     96[[BR]]
     97'''Sample workflow for Test Cases'''
     98[[BR]]
     99[[BR]]
     100
     101{{{
     102  [testcase-resource_workflow]
     103  sleep = new -> asleep
     104  sleep.permissions = TEST_MODIFY
     105
     106  sing = new -> singing
     107  sing.permissions = TEST_MODIFY
     108  sing.operations = sample_operation
     109
     110  calmdown = singing -> calm
     111  calmdown.permissions = TEST_MODIFY
     112
     113  kill = asleep,calm -> dead
     114  kill.permissions = TICKET_MODIFY
     115}}}
     116
     117=== 2) Implement any custom operations ===
     118
     119A set of predefined, [wiki:TestManagerPluginWorkflowOperations out-of-the-box operations] is available, but you may want to create your own operations to fit your needs.
     120
     121To do this, you must write python code into a plugin, and deploy it along with the TracGenericClass and TracGenericWorkflow plugins.
     122
     123To be able to provide custom workflow operations, your Trac Component must implement the IWorkflowOperationProvider interface.
     124
     125Let's take a look at a sample operation provider, which is included in the TestManager plugin.
     126
     127{{{
     128from tracgenericworkflow.api import IWorkflowOperationProvider
     129
     130class TestManagerWorkflowInterface(Component):
     131    """Adds workflow capabilities to the TestManager plugin."""
     132   
     133    implements(IWorkflowOperationProvider)
     134
     135    # IWorkflowOperationProvider methods
     136    # Just a sample operation
     137    def get_implemented_operations(self):
     138        yield 'sample_operation'
     139
     140    def get_operation_control(self, req, action, operation, res_wf_state, resource):
     141        if operation == 'sample_operation':
     142            id = 'action_%s_operation_%s' % (action, operation)
     143            speech = 'Hello World!'
     144
     145            control = tag.input(type='text', id=id, name=id,
     146                                    value=speech)
     147            hint = "Will sing %s" % speech
     148           
     149            return control, hint
     150       
     151        return None, ''
     152       
     153    def perform_operation(self, req, action, operation, old_state, new_state, res_wf_state, resource):
     154        self.log.debug("---> Performing operation %s while transitioning from %s to %s."
     155            % (operation, old_state, new_state))
     156
     157        speech = req.args.get('action_%s_operation_%s' % (action, operation), 'Not found!')
     158
     159        self.log.debug("        The speech is %s" % speech)
     160}}}
     161
     162
     163As you can see, it's not much code to write. Let's go through it.
     164
     165The IWorkflowOperationProvider interface prescribes three methods that a provider must implement:
     166
     167 * get_implemented_operations(): Used by the workflow engine to ask the provider the names of the operations it provides.
     168
     169   * This method must return a basestring generator with the operation names.
     170
     171 * get_operation_control(req, action, operation, res_wf_state, resource): Called right before the web user interface for one of the operations the provider supports must be displayed.
     172
     173   The workflow engine passes to the provider:
     174   * The http request.
     175   * The name of the transition action that is associated with this operation in the trac.ini file.
     176   * The name of the operation to be rendered (this in case the provider has stated to provide more than once).
     177   * The ResourceWorkflowState object representing the current state of the resource in the workflow.
     178   * The Resource object representing the artifact instance being subject to the workflow.
     179   
     180   This method must return two results:
     181   * A Genshi tag, containing the markup to render the controls necessary to let the User give any parameter for the operation. This controls will be put inside a form, and they will be available to retrieve User input by means of the request parameters in the perform_operation() method, described next.
     182   * A string, containing a textual short description of what the operation does.
     183
     184 * perform_operation(req, action, operation, old_state, new_state, res_wf_state, resource): Called after the User has selected the specified operation, to actually perform it.
     185
     186   The workflow engine passes to the provider:
     187   * The http request, from which the provider can retrieve form values for the input fields it may have provided with the get_operation_control() method.
     188   * The name of the transition action that is associated with this operation in the trac.ini file.
     189   * The name of the operation to be performed (this in case the provider has stated to provide more than once).
     190   * The name of the old workflow state from which the artifact is being moved.
     191   * The name of the new workflow state to which the artifact is being moved.
     192   * The ResourceWorkflowState object representing the current state of the resource in the workflow.
     193   * The Resource object representing the artifact instance being subject to the workflow.
     194 
     195   There are no requirements as to what the provider must or can do inside this method, except from what's specified next.
     196   It is '''NOT allowed''' to modify the resource object.
     197   It is '''NOT allowed''' to modify the res_wf_state object.
     198   
     199   
     200=== 3) Add workflow support to any of your artifacts ===
     201
     202Test artifacts already have workflow support. To turn it on, all you must do is define the desired workflow in the trac.ini file, as described above.
     203
     204But there is more you can do with the TracGenericWorkflow standalone plugin (remember it requires TracGenericClass plugin to be installed, anyway), you can add workflow support to any of your artifacts inside Trac.
     205
     206To do that, you should do the following steps:
     207
     208 1. Define your desired workflow - explained above
     209 1. Define any custom operations you may provide on your artifacts - explained above
     210 1. '''Display the workflow markup into your web pages'''. This is what this section is about.
     211
     212There are several ways how you can provide markup to web pages in Trac. Here, I'll explain the way I do it in the TestManager plugin, which is by means of the ITemplateStreamFilter.
     213
     214To do this, you must:
     215 
     216 1. Implement the ITemplateStreamFilter interface
     217 1. When you need to display the markup for the workflow support - i.e. the list of available transition actions in the current resource state and the associated operations - call the ResourceWorkflowSystem.get_workflow_markup() method to get the markup, then just add it to your page.
     218 
     219You may ask... this is it???
     220
     221Yes! You don't have to do anything to:
     222 * Manage you resource states,
     223 * Handle transitions,
     224 * Manage operations
     225
     226all this is automatically procided by the plugin.
     227
     228So, let's take a look at how the TestManager plugin incorporates this web interface support.
     229
     230{{{
     231from trac.web.api import ITemplateStreamFilter
     232from tracgenericworkflow.api import ResourceWorkflowSystem
     233
     234class TestManagerWorkflowInterface(Component):
     235    """Adds workflow capabilities to the TestManager plugin."""
     236   
     237    implements(ITemplateStreamFilter)
     238
     239    # ITemplateStreamFilter methods
     240    def filter_stream(self, req, method, filename, stream, data):
     241        page_name = req.args.get('page', 'WikiStart')
     242        planid = req.args.get('planid', '-1')
     243
     244        if page_name == 'TC':
     245            # The root catalog does not have workflows
     246            return stream
     247
     248        if page_name.startswith('TC') and filename == 'wiki_view.html':
     249            req.perm.require('TEST_VIEW')
     250           
     251            # Determine which object is being displayed (i.e. realm),
     252            # based on Wiki page name and the presence of the planid
     253            # request parameter.
     254            realm = None
     255            if page_name.find('_TC') >= 0:
     256                if not planid or planid == '-1':
     257                    realm = 'testcase'
     258                    key = {'id': page_name.rpartition('_TC')[2]}
     259                else:
     260                    realm = 'testcaseinplan'
     261                    key = {'id': page_name.rpartition('_TC')[2], 'planid': planid}
     262            else:
     263                if not planid or planid == '-1':
     264                    realm = 'testcatalog'
     265                    key = {'id': page_name.rpartition('_TT')[2]}
     266                else:
     267                    realm = 'testplan'
     268                    key = {'id': planid}
     269
     270            id = get_string_from_dictionary(key)
     271            res = Resource(realm, id)
     272
     273            workflow_markup = ResourceWorkflowSystem(self.env).get_workflow_markup(req, '..', realm, res)
     274           
     275            return stream | Transformer('//div[contains(@class,"wikipage")]').after(workflow_markup)
     276
     277        return stream
     278}}}
     279
     280Let's take a look at the ResourceWorkflowSystem.get_workflow_markup() method.
     281
     282{{{
     283  get_workflow_markup(self, req, base_href, realm, resource):
     284}}}
     285
     286It takes the following arguments:
     287
     288 * req: the http request
     289 * base_href: an href string pointing to the base of your project - e.g. in the context of a wiki page, '..' returns up to your project's base URL.
     290 * realm: the artifact's resource type - e.g. 'testcase' in the examples above.
     291 * resource: the actual Trac resource instance object of the workflow.
     292
     293Note: TestManager artifact resources are made so that their resource ID is a string representation of their key properties, in the form of a JSON dictionary.
     294This is why you see the following code, where to build a Trac Resource corresponding to a TestManager aretifact, I first build the Resource ID as the string representation of the artifact's key properties, then use it to create the Resource:
     295
     296{{{
     297id = get_string_from_dictionary(key)
     298res = Resource(realm, id)
     299}}}