Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Note

Advised: Read at your (and possibly my) own risk. There are some program languages that have native capabilities to more gracefully solve the issues outlined below. However, since SIL is a sudo language it does not necessarily have all the bells and whistles and a complete programming language. What is presented below are methods that can be used to solve the issues right now using the tools that are currently available. If you disagree with these suggestions please suppress the urge to hunt me down in person and share your opinions with your fists (or oversized purse like the last time). Instead, please feel free to send us your thoughts and opinions at anovaproducts@appfire.com.

When writing large and complicated scripts it can become hard to debug the script. Either it becomes difficult to determine where in the large script the point of failure is contained or it is just difficult to identify why the code is not working. A simple solution to this is to add logic to your script that allows you to break it into smaller sections for easier debugging. Or, perhaps you do not wish to perform certain actions while debugging, like sending emails to users. A "test mode" concept will allow you to execute the script without performing this specific actions.

Constructing the flag

The basic idea behind a testing mode is to use flags to identify if the whole script should be run in test mode, or perhaps which sections of the script should be run in test mode. This is done with simple if/then statements. However, it is a good idea to use a struct to create a single flag that has multiple attributes. Whenever I create a test mode flag I usually start with a structure like this:

Code Block
struct debug {
	boolean debug;
	boolean useLogging;
	boolean sendEmails;
	boolean codeAction1;
	boolean codeAction2;
	boolean codeAction3;
}

This allows me to create a single flag that controls multiple actions.

Initializing the flag

To start using the structure you must initialize it like the code below. When I create the actual flag (instead of the struct) I do not call it "debug" or "test" because that becomes redundant with the "debug" or "test" property. For example, I don't want the flag to look like "debug.debug" or "test.test". For this reason I call the main flag something else. And, for whatever reason, I typically use "goldBug" from the Richard Scarry children books.

Code Block
debug goldBug;
goldBug.debug = true;

Using the flag

Now that the flag has been initialized it can be used as a wrapper around your actual code like this:

Code Block
if(goldBug.debug != true) {
	//do some live, non-test action
}
else {
	//do some test/debug related action
}

Setting up the modes

I typically use the "debug" or "test" property to determine if the script is indeed running in test mode. If it is running in test mode you may have specific settings or sections of code that should or should not run. I use the main flag to help set the test mode as a whole. Also, the flag can be used to set the script to live or production mode as well. In the example below logging will be used in test mode and emails will not be sent to the users. However, in production mode logging will not be used but emails will be sent.

Code Block
if(goldBug.debug == true) {
	//test mode
	goldBug.useLogging = true;
	goldBug.sendEmails = false;
	goldBug.codeAction1 = true;
	goldBug.codeAction2 = true;
	goldBug.codeAction3 = false;
}
else {
	//live mode
	goldBug.useLogging = false;
	goldBug.sendEmails = true;
	goldBug.codeAction1 = true;
	goldBug.codeAction2 = true;
	goldBug.codeAction3 = true;
}

Automatically trigger test mode

The potential of forgetting to switch test mode off is very high. However, there are some ways to automatically trigger test mode. Generally, the logical place to test the execution of scripts is directly from the SIL Manager in order to take advantage of the output console and viewing the logs. The concept behind the automatic triggering of test mode is that when the script is run from the SIL Manager itself it will be run in test mode but when the script is triggered by other methods it will be run in live or production mode.

There are two methods of automatically triggering test mode and they are based on the method of execution:

  1. Method 1 - Issue context; This is used for the following types of scripts:

    • Conditions

    • Validators

    • Post functions

    • Listeners

    • Custom fields

  2. Method 2 - Arguments; This is used for the following types of scripts:

    • SIL Runner dashboard gadget

    • SIL Scheduler

Issue Context

The general idea behind this method is that when running in the SIL Manager the script will not be in the context of an issue. This, of course, assumes that an issue has not been added to the Run Configuration setting. If the script is run by executing an issue action (transitions the issue, updating the issue, etc.) it will be in the context of that issue. By using the isIssueContext() routine we can determine if the script is running in the context of an issue.

Code Block
if(isIssueContext() == true) {
	goldBug.debug = false; //production mode
}
else {
	goldBug.debug = true; //test mode
}

Arguments

When running scripts from the SIL Runner dashboard gadget or through the SIL Scheduler the script can be passed arguments. By detecting if the arguments are present we can trigger test mode. Of course, there are times when the script does not require arguments. For these instances we can just pass a flag as a bogus parameter that indicates the script is running in production mode.

Code Block
if(size(argv) > 0) {
	goldBug.debug = false; //production mode
}
else {
	goldBug.debug = true; //test mode
}

What is different about test mode?

Issue context

One valuable use of test mode is switching into the context of a test issue or test project. This way, if the action does not perform correctly it will not update a live issue or potentially notify other end users.

Code Block
string _key;


if(goldBug.debug == false) {
	_key = key; //live issue key
}
else {
	_key = "TST-1234"; //test issue
}


//example use of using conditional issue context
%_key%.summary = "New summary text";

Output to the console

Often times you may want more information displayed in the output console when debugging then other times. This is helpful for getting the value of custom fields while the script is running.

Code Block
if(goldBug.debug == false) { //test mode
	//write output to console
	runnerLog(key + ": " + summary);
}

Logging

Just like displaying information to the console you might want to write information to the logs when running in test mode.

Code Block
if(goldBug.debug == false) {
	//write output to the log
	logPrint(currentUser() + " performed some action on " + currentDate(), "INFO");
}

Restricting actions

There may be times were you don't want an action to occur when testing, like sending an email to a user or writing data to a database. And, there may also be times when you do want this action to occur so you can test that it is behaving correctly. In these instances the action can be controlled using separate flags.

Code Block
JEmailMessage email;
email.to = {"testJiraUser1", "testEmail@cprime.com", "testJiraUser2"};
email.subject = "Email to Santa";
email.message = "Dear Santa, I want a train.";
 
for(string a in attach) {  
    JEmailAttachment att;
    att.name = a;
    att.file = getAttachmentPath(key, a);
    email.attachments += att;
}

if(goldBug.sendEmail == false) { //live mode
	sendEmail(email); //send email
}
else { //test mode
	//don't spam me bro!
}

Compound flag conditions

Like the previous example, there may be times where you want to toggle an action on or off while testing. However, can you be sure that the flag is set back to the proper setting before going live with the script changes. By using compound conditions with the flags we can ensure that the action is only getting toggled on or off in test mode but will always be on in live mode.

Code Block
if(goldBug.debug == false) { //live mode
	sendEmail(email);
}
else if(goldBug.debug == true && goldBug.sendEmail == true) { //test mode
	sendEmail(email);
}
else {
	//don't send email
}

This example will always send the email in live mode but will only send the email in test mode when the sendEmail flag is set to true.

The example above can be consolidated into a single statement:

Code Block
if(goldBug.debug == false || (goldBug.debug == true && goldBug.sendEmail == true)) {
	sendEmail(email);
}

Complete test mode script template

Here is a complete test script template for you to get started with:

Code Block
struct debug { //define the flag
	boolean debug;
	boolean useLogging;
	boolean sendEmails;
	boolean codeAction1;
	boolean codeAction2;
	boolean codeAction3;
}


debug goldBug; //initialize

//automatically set the flag
if(isIssueContext() == true) {
	goldBug.debug = false; //production mode
}
else {
	goldBug.debug = true; //test mode
}


//build out the modes
if(goldBug.debug == true) {
	//test mode
	goldBug.useLogging = true;
	goldBug.sendEmails = false;
	goldBug.codeAction1 = true;
	goldBug.codeAction2 = true;
	goldBug.codeAction3 = false;
}
else {
	//live mode
	goldBug.useLogging = false;
	goldBug.sendEmails = true;
	goldBug.codeAction1 = true;
	goldBug.codeAction2 = true;
	goldBug.codeAction3 = true;
}


/*----------------------------------------
-----------End Include File---------------
-----------------------------------------*/


//use the flag in the script
if(goldBug.debug == false || (goldBug.debug == true && goldBug.codeAction1 == true)) {
	
	//do something
	
	//test output
	if(goldBug.debug == true) { //test mode only
		runnerLog("See something");
	}


	//write to log
	if(goldBug,useLogging == true) { //test mode only
		logPrint(currentUser() + " performed some action on " + currentDate(), "INFO");
	}
}


if(goldBug.debug == false || (goldBug.debug == true && goldBug.codeAction2 == true)) {
	//do something else
}


if(goldBug.debug == false || (goldBug.debug == true && goldBug.codeAction3 == true)) {

	//do something else
}

Or, use an include file

In order to make the script smaller and more readable the bulk of the test mode script can be contained in a separate include file and reused in other script as well. In this example the script from the beginning up to the line that says "End Include File" is stored off as a separate file called testMode.incl.

Code Block
include "testMode.incl";


if(goldBug.debug == false || (goldBug.debug == true && goldBug.codeAction1 == true)) {
	
	//do something
	
	//test output
	if(goldBug.debug == true) { //test mode only
		runnerLog("See something");
	}

	//write to log
	if(goldBug,useLogging == true) { //test mode only
		logPrint(currentUser() + " performed some action on " + currentDate(), "INFO");
	}
}


if(goldBug.debug == false || (goldBug.debug == true && goldBug.codeAction2 == true)) {
	//do something else
}


if(goldBug.debug == false || (goldBug.debug == true && goldBug.codeAction3 == true)) {

	//do something else
}

Contents

Table of Contents