NAME
TestRail::API - Provides an interface to TestRail's REST api via HTTP
VERSION
version 0.043
SYNOPSIS
use TestRail::API;
my ($username,$password,$host) = ('foo','bar','http://testrail.baz.foo');
my $tr = TestRail::API->new($host, $username, $password);
DESCRIPTION
TestRail::API
provides methods to access an existing TestRail account using API v2. You can then do things like look up tests, set statuses and create runs from lists of cases. It is by no means exhaustively implementing every TestRail API function.
IMPORTANT
All the methods aside from the constructor should not die, but return a false value upon failure (see exceptions below). When the server is not responsive, expect a -500 response, and retry accordingly by setting the num_tries parameter in the constructor.
Also, all *ByName methods are vulnerable to duplicate naming issues. Try not to use the same name for:
* projects
* testsuites within the same project
* sections within the same testsuite that are peers
* test cases
* test plans and runs outside of plans which are not completed
* configurations
To do so will result in the first of said item found being returned rather than an array of possibilities to choose from.
There are two exceptions to this, in the case of 401 and 403 responses, as these failing generally mean your program has no chance of success anyways.
CONSTRUCTOR
new (api_url, user, password, encoding, debug, do_post_redirect)
Creates new TestRail::API
object.
- STRING
API URL
- base url for your TestRail api server. - STRING
USER
- Your TestRail User. - STRING
PASSWORD
- Your TestRail password, or a valid API key (TestRail 4.2 and above). - STRING
ENCODING
- The character encoding used by the caller. Defaults to 'UTF-8', see Encode::Supported and for supported encodings. - BOOLEAN
DEBUG
(optional) - Print the JSON responses from TL with your requests. Default false. - BOOLEAN
DO_POST_REDIRECT
(optional) - Follow redirects on POST requests (most add/edit/delete calls are POSTs). Default false. - INTEGER
MAX_TRIES
(optional) - Try requests up to X number of times if they fail with anything other than 401/403. Useful with flaky external authenticators, or timeout issues. Default 1.
Returns TestRail::API
object if login is successful.
my $tr = TestRail::API->new('http://tr.test/testrail', 'moo','M000000!');
Dies on all communication errors with the TestRail server. Does not do above checks if debug is passed.
GETTERS
apiurl
debug
Accessors for these parameters you pass into the constructor, in case you forget.
retry_delay
There is no getter/setter for this parameter, but it is worth mentioning. This is the number of seconds to wait between failed request retries when max_retries > 1.
#Do something other than the default of 5s, like spam the server mercilessly
$tr->{retry_delay} = 0;
...
USER METHODS
getUsers ()
Get all the user definitions for the provided Test Rail install. Returns ARRAYREF of user definition HASHREFs.
getUserByID(id)
getUserByName(name)
getUserByEmail(email)
Get user definition hash by ID, Name or Email. Returns user def HASHREF.
For efficiency's sake, these methods cache the result of getUsers until you explicitly run it again.
userNamesToIds(names)
Convenience method to translate a list of user names to TestRail user IDs.
Returns ARRAY of user IDs.
Throws an exception in the case of one (or more) of the names not corresponding to a valid username.
PROJECT METHODS
createProject (name, [description,send_announcement])
Creates new Project (Database of testsuites/tests). Optionally specify an announcement to go out to the users. Requires TestRail admin login.
- STRING
NAME
- Desired name of project. - STRING
DESCRIPTION
(optional) - Description of project. Default value is 'res ipsa loquiter'. - BOOLEAN
SEND ANNOUNCEMENT
(optional) - Whether to confront users with an announcement about your awesome project on next login. Default false.
Returns project definition HASHREF on success, false otherwise.
$tl->createProject('Widgetronic 4000', 'Tests for the whiz-bang new product', true);
deleteProject (id)
Deletes specified project by ID. Requires TestRail admin login.
Returns BOOLEAN.
$success = $tl->deleteProject(1);
getProjects ()
Get all available projects
Returns array of project definition HASHREFs, false otherwise.
$projects = $tl->getProjects;
getProjectByName ($project)
Gets some project definition hash by it's name
Returns desired project def HASHREF, false otherwise.
$project = $tl->getProjectByName('FunProject');
getProjectByID ($project)
Gets some project definition hash by it's ID
Returns desired project def HASHREF, false otherwise.
$projects = $tl->getProjectByID(222);
TESTSUITE METHODS
createTestSuite (project_id, name, [description])
Creates new TestSuite (folder of tests) in the database of test specifications under given project id having given name and details.
- INTEGER
PROJECT ID
- ID of project this test suite should be under. - STRING
NAME
- Desired name of test suite. - STRING
DESCRIPTION
(optional) - Description of test suite. Default value is 'res ipsa loquiter'.
Returns TS definition HASHREF on success, false otherwise.
$tl->createTestSuite(1, 'broken tests', 'Tests that should be reviewed');
deleteTestSuite (suite_id)
Deletes specified testsuite.
Returns BOOLEAN.
$tl->deleteTestSuite(1);
getTestSuites (project_id)
Gets the testsuites for a project
Returns ARRAYREF of testsuite definition HASHREFs, 0 on error.
$suites = $tl->getTestSuites(123);
getTestSuiteByName (project_id,testsuite_name)
Gets the testsuite that matches the given name inside of given project.
- STRING
PROJECT ID
- ID of project holding this testsuite - STRING
TESTSUITE NAME
- desired parent testsuite name
Returns desired testsuite definition HASHREF, false otherwise.
$suites = $tl->getTestSuitesByName(321, 'hugSuite');
getTestSuiteByID (testsuite_id)
Gets the testsuite with the given ID.
Returns desired testsuite definition HASHREF, false otherwise.
$tests = $tl->getTestSuiteByID(123);
SECTION METHODS
createSection(project_id,suite_id,name,[parent_id])
Creates a section.
- INTEGER
PROJECT ID
- Parent Project ID. - INTEGER
SUITE ID
- Parent TestSuite ID. - STRING
NAME
- desired section name. - INTEGER
PARENT ID
(optional) - parent section id
Returns new section definition HASHREF, false otherwise.
$section = $tr->createSection(1,1,'nugs',1);
deleteSection (section_id)
Deletes specified section.
Returns BOOLEAN.
$tr->deleteSection(1);
getSections (project_id,suite_id)
Gets sections for a given project and suite.
Returns ARRAYREF of section definition HASHREFs.
$tr->getSections(1,2);
getSectionByID (section_id)
Gets desired section.
Returns section definition HASHREF.
$tr->getSectionByID(344);
getSectionByName (project_id,suite_id,name)
Gets desired section.
- INTEGER
PROJECT ID
- ID of parent project. - INTEGER
SUITE ID
- ID of suite to get section for. - STRING
NAME
- name of section to get
Returns section definition HASHREF.
$tr->getSectionByName(1,2,'nugs');
getChildSections ($project_id, section)
Gets desired section's child sections.
Returns ARRAYREF of section definition HASHREF. ARRAYREF is empty if there are none.
Recursively searches for children, so the children of child sections will be returned as well.
$tr->getChildSections($section);
sectionNamesToIds(project_id,suite_id,names)
Convenience method to translate a list of section names to TestRail section IDs.
- INTEGER
PROJECT ID
- ID of parent project. - INTEGER
SUITE ID
- ID of parent suite. - ARRAY
NAMES
- Array of section names to translate to IDs.
Returns ARRAY of section IDs.
Throws an exception in the case of one (or more) of the names not corresponding to a valid section name.
CASE METHODS
getCaseTypes ()
Gets possible case types.
Returns ARRAYREF of case type definition HASHREFs.
$tr->getCaseTypes();
getCaseTypeByName (name)
Gets case type by name.
Returns case type definition HASHREF. Dies if named case type does not exist.
$tr->getCaseTypeByName();
typeNamesToIds(names)
Convenience method to translate a list of case type names to TestRail case type IDs.
Returns ARRAY of type IDs in the same order as the type names passed.
Throws an exception in the case of one (or more) of the names not corresponding to a valid case type.
createCase(section_id,title,type_id,options,extra_options)
Creates a test case.
- INTEGER
SECTION ID
- Parent Section ID. - STRING
TITLE
- Case title. - INTEGER
TYPE_ID
(optional) - desired test type's ID. Defaults to whatever your TR install considers the default type. - HASHREF
OPTIONS
(optional) - Custom fields in the case are the keys, set to the values provided. See TestRail API documentation for more info. - HASHREF
EXTRA OPTIONS
(optional) - contains priority_id, estimate, milestone_id and refs as possible keys. See TestRail API documentation for more info.
Returns new case definition HASHREF, false otherwise.
$custom_opts = {
preconds => "Test harness installed",
steps => "Do the needful",
expected => "cubicle environment transforms into Dali painting"
};
$other_opts = {
priority_id => 4,
milestone_id => 666,
estimate => '2m 45s',
refs => ['TRACE-22','ON-166'] #ARRAYREF of bug IDs.
}
$case = $tr->createCase(1,'Do some stuff',3,$custom_opts,$other_opts);
updateCase(case_id,options)
Updates a test case.
- INTEGER
CASE ID
- Case ID. - HASHREF
OPTIONS
- Various things about a case to set. Everything except section_id in the output of getCaseBy* methods is a valid input here.
Returns new case definition HASHREF, false otherwise.
deleteCase (case_id)
Deletes specified test case.
Returns BOOLEAN.
$tr->deleteCase(1324);
getCases (project_id,suite_id,filters)
Gets cases for provided section.
- INTEGER
PROJECT ID
- ID of parent project. - INTEGER
SUITE ID
- ID of parent suite. - HASHREF
FILTERS
(optional) - HASHREF describing parameters to filter cases by.
See:
L<http://docs.gurock.com/testrail-api2/reference-cases#get_cases>
for details as to the allowable filter keys.
If the section ID is omitted, all cases for the suite will be returned. Returns ARRAYREF of test case definition HASHREFs.
$tr->getCases(1,2, {'section_id' => 3} );
getCaseByName (project_id,suite_id,name,filters)
Gets case by name.
- INTEGER
PROJECT ID
- ID of parent project. - INTEGER
SUITE ID
- ID of parent suite. - STRING
NAME
- Name of desired test case. - HASHREF
FILTERS
- Filter dictionary acceptable to getCases.
Returns test case definition HASHREF.
$tr->getCaseByName(1,2,'nugs', {'section_id' => 3});
getCaseByID (case_id)
Gets case by ID.
Returns test case definition HASHREF.
$tr->getCaseByID(1345);
RUN METHODS
createRun (project_id,suite_id,name,description,milestone_id,assigned_to_id,case_ids)
Create a run.
- INTEGER
PROJECT ID
- ID of parent project. - INTEGER
SUITE ID
- ID of suite to base run on - STRING
NAME
- Name of run - STRING
DESCRIPTION
(optional) - Description of run - INTEGER
MILESTONE ID
(optional) - ID of milestone - INTEGER
ASSIGNED TO ID
(optional) - User to assign the run to - ARRAYREF
CASE IDS
(optional) - Array of case IDs in case you don't want to use the whole testsuite when making the build.
Returns run definition HASHREF.
$tr->createRun(1,1345,'RUN AWAY','SO FAR AWAY',22,3,[3,4,5,6]);
deleteRun (run_id)
Deletes specified run.
Returns BOOLEAN.
$tr->deleteRun(1324);
getRuns (project_id)
Get all runs for specified project. To do this, it must make (no. of runs/250) HTTP requests. This is due to the maximum result set limit enforced by testrail.
Returns ARRAYREF of run definition HASHREFs.
$allRuns = $tr->getRuns(6969);
getRunsPaginated (project_id,limit,offset)
Get some runs for specified project.
- INTEGER
PROJECT_ID
- ID of parent project - INTEGER
LIMIT
- Number of runs to return. - INTEGER
OFFSET
- Page of runs to return.
Returns ARRAYREF of run definition HASHREFs.
$someRuns = $tr->getRunsPaginated(6969,22,4);
getRunByName (project_id,name)
Gets run by name.
Returns run definition HASHREF.
$tr->getRunByName(1,'R2');
getRunByID (run_id)
Gets run by ID.
Returns run definition HASHREF.
$tr->getRunByID(7779311);
closeRun (run_id)
Close the specified run.
Returns run definition HASHREF on success, false on failure.
$tr->closeRun(90210);
getRunSummary(runs)
Returns array of hashrefs describing the # of tests in the run(s) with the available statuses. Translates custom_statuses into their system names for you.
Returns ARRAY of run HASHREFs with the added key 'run_status' holding a hashref where status_name => count.
$tr->getRunSummary($run,$run2);
getRunResults(run_id)
Returns array of hashrefs describing the results of the run.
Warning: This only returns the most recent results of a run. If you want to know about the tortured journey a test may have taken to get to it's final status, you will need to use getTestResults.
getRunResultsPaginated(run_id,limit,offset)
RUN AS CHILD OF PLAN METHODS
getChildRuns(plan)
Extract the child runs from a plan. Convenient, as the structure of this hash is deep, and correct error handling can be tedious.
Returns ARRAYREF of run definition HASHREFs. Returns 0 upon failure to extract the data.
getChildRunByName(plan,name,configurations,testsuite_id)
- HASHREF
PLAN
- Test Plan definition HASHREF returned by any of the PLAN methods below. - STRING
NAME
- Name of run to search for within plan. - ARRAYREF
CONFIGURATIONS
(optional) - Names of configurations to filter runs by. - INTEGER
TESTSUITE_ID
(optional) - Filter by the provided Testsuite ID. Helpful for when child runs have duplicate names, but are from differing testsuites.
Returns run definition HASHREF, or false if no such run is found. Convenience method using getChildRuns.
Will throw a fatal error if one or more of the configurations passed does not exist in the project.
PLAN METHODS
createPlan (project_id,name,description,milestone_id,entries)
Create a test plan.
- INTEGER
PROJECT ID
- ID of parent project. - STRING
NAME
- Name of plan - STRING
DESCRIPTION
(optional) - Description of plan - INTEGER
MILESTONE_ID
(optional) - ID of milestone - ARRAYREF
ENTRIES
(optional) - New Runs to initially populate the plan with -- See TestRail API documentation for more advanced inputs here.
Returns test plan definition HASHREF, or false on failure.
$entries = [{
suite_id => 345,
include_all => 1,
assignedto_id => 1
}];
$tr->createPlan(1,'Gosplan','Robo-Signed Soviet 5-year plan',22,$entries);
deletePlan (plan_id)
Deletes specified plan.
Returns BOOLEAN.
$tr->deletePlan(8675309);
getPlans (project_id,filters)
Gets all test plans in specified project. Like getRuns, must make multiple HTTP requests when the number of results exceeds 250.
- INTEGER
PROJECT ID
- ID of parent project. - HASHREF
FILTERS
- (optional) dictionary of filters, with keys corresponding to the documented filters for get_plans (other than limit/offset).
Returns ARRAYREF of all plan definition HASHREFs in a project.
$tr->getPlans(8);
Does not contain any information about child test runs. Use getPlanByID or getPlanByName if you want that, in particular if you are interested in using getChildRunByName.
Possible filters:
- created_after (UNIX timestamp)
- created_before (UNIX timestamp)
- created_by (csv of ints) IDs of users plans were created by
- is_completed (bool)
- milestone_id (csv of ints) IDs of milestone assigned to plans
getPlansPaginated (project_id,limit,offset,filters)
Get some plans for specified project.
- INTEGER
PROJECT_ID
- ID of parent project - INTEGER
LIMIT
- Number of plans to return. - INTEGER
OFFSET
- Page of plans to return. - HASHREF
FILTERS
- (optional) other filters to apply to the requests. See getPlans for more information.
Returns ARRAYREF of plan definition HASHREFs.
$someRuns = $tr->getPlansPaginated(6969,222,44);
getPlanByName (project_id,name)
Gets specified plan by name.
Returns plan definition HASHREF.
$tr->getPlanByName(8,'GosPlan');
getPlanByID (plan_id)
Gets specified plan by ID.
Returns plan definition HASHREF.
$tr->getPlanByID(2);
getPlanSummary(plan_ID)
Returns hashref describing the various pass, fail, etc. percentages for tests in the plan. The 'totals' key has total cases in each status ('status' => count) The 'percentages' key has the same, but as a percentage of the total.
$tr->getPlanSummary($plan_id);
createRunInPlan (plan_id,suite_id,name,milestone_id,assigned_to_id,config_ids,case_ids)
Create a run in a plan.
- INTEGER
PLAN ID
- ID of parent project. - INTEGER
SUITE ID
- ID of suite to base run on - STRING
NAME
- Name of run - INTEGER
ASSIGNED TO ID
(optional) - User to assign the run to - ARRAYREF
CONFIG IDS
(optional) - Array of Configuration IDs (see getConfigurations) to apply to the created run - ARRAYREF
CASE IDS
(optional) - Array of case IDs in case you don't want to use the whole testsuite when making the build.
Returns run definition HASHREF.
$tr->createRun(1,1345,'PlannedRun',3,[1,4,77],[3,4,5,6]);
closePlan (plan_id)
Close the specified plan.
Returns plan definition HASHREF on success, false on failure.
$tr->closePlan(75020);
MILESTONE METHODS
createMilestone (project_id,name,description,due_on)
Create a milestone.
- INTEGER
PROJECT ID
- ID of parent project. - STRING
NAME
- Name of milestone - STRING
DESCRIPTION
(optional) - Description of milestone - INTEGER
DUE_ON
- Date at which milestone should be completed. Unix Timestamp.
Returns milestone definition HASHREF, or false on failure.
$tr->createMilestone(1,'Patriotic victory of world perlism','Accomplish by Robo-Signed Soviet 5-year plan',time()+157788000);
deleteMilestone (milestone_id)
Deletes specified milestone.
Returns BOOLEAN.
$tr->deleteMilestone(86);
getMilestones (project_id)
Get milestones for some project.
Returns ARRAYREF of milestone definition HASHREFs.
$tr->getMilestones(8);
getMilestoneByName (project_id,name)
Gets specified milestone by name.
Returns milestone definition HASHREF.
$tr->getMilestoneByName(8,'whee');
getMilestoneByID (milestone_id)
Gets specified milestone by ID.
Returns milestone definition HASHREF.
$tr->getMilestoneByID(2);
TEST METHODS
getTests (run_id,status_ids,assignedto_ids)
Get tests for some run. Optionally filter by provided status_ids and assigned_to ids.
- INTEGER
RUN ID
- ID of parent run. - ARRAYREF
STATUS IDS
(optional) - IDs of relevant test statuses to filter by. Get with getPossibleTestStatuses. - ARRAYREF
ASSIGNEDTO IDS
(optional) - IDs of users assigned to test to filter by. Get with getUsers.
Returns ARRAYREF of test definition HASHREFs.
$tr->getTests(8,[1,2,3],[2]);
getTestByName (run_id,name)
Gets specified test by name.
This is done by getting the list of all tests in the run and then picking out the relevant test. As such, for efficiency the list of tests is cached. The cache may be refreshed, or restricted by running getTests (with optional restrictions, such as assignedto_ids, etc).
Returns test definition HASHREF.
$tr->getTestByName(36,'wheeTest');
getTestByID (test_id)
Gets specified test by ID.
Returns test definition HASHREF.
$tr->getTestByID(222222);
getTestResultFields()
Gets custom fields that can be set for tests.
Returns ARRAYREF of result definition HASHREFs.
getTestResultFieldByName(SYSTEM_NAME,PROJECT_ID)
Gets a test result field by it's system name. Optionally filter by project ID.
- SYSTEM NAME - STRING: system name of a result field.
- PROJECT ID - INTEGER (optional): Filter by whether or not the field is enabled for said project
Returns a value less than 0 if unsuccessful.
getPossibleTestStatuses()
Gets all possible statuses a test can be set to.
Returns ARRAYREF of status definition HASHREFs.
Caches the result for the lifetime of the TestRail::API object.
statusNamesToIds(names)
Convenience method to translate a list of statuses to TestRail status IDs. The names referred to here are 'internal names' rather than the labels shown in TestRail.
Returns ARRAY of status IDs in the same order as the status names passed.
Throws an exception in the case of one (or more) of the names not corresponding to a valid test status.
statusNamesToLabels(names)
Convenience method to translate a list of statuses to TestRail status labels (the 'nice' form of status names). This is useful when interacting with getRunSummary or getPlanSummary, which uses these labels as hash keys.
Returns ARRAY of status labels in the same order as the status names passed.
Throws an exception in the case of one (or more) of the names not corresponding to a valid test status.
createTestResults(test_id,status_id,comment,options,custom_options)
Creates a result entry for a test.
- INTEGER
TEST_ID
- ID of desired test - INTEGER
STATUS_ID
- ID of desired test result status - STRING
COMMENT
(optional) - Any comments about this result - HASHREF
OPTIONS
(optional) - Various "Baked-In" options that can be set for test results. See TR docs for more information. - HASHREF
CUSTOM OPTIONS
(optional) - Options to set for custom fields. See buildStepResults for a simple way to post up custom steps.
Returns result definition HASHREF.
$options = {
elapsed => '30m 22s',
defects => ['TSR-3','BOOM-44'],
version => '6969'
};
$custom_options = {
step_results => [
{
content => 'Step 1',
expected => "Bought Groceries",
actual => "No Dinero!",
status_id => 2
},
{
content => 'Step 2',
expected => 'Ate Dinner',
actual => 'Went Hungry',
status_id => 2
}
]
};
$res = $tr->createTestResults(1,2,'Test failed because it was all like WAAAAAAA when I poked it',$options,$custom_options);
bulkAddResults(run_id,results)
Add multiple results to a run, where each result is a HASHREF with keys as outlined in the get_results API call documentation.
- INTEGER
RUN_ID
- ID of desired run to add results to - ARRAYREF
RESULTS
- Array of result HASHREFs to upload.
Returns ARRAYREF of result definition HASHREFs.
getTestResults(test_id,limit,offset)
Get the recorded results for desired test, limiting output to 'limit' entries.
- INTEGER
TEST_ID
- ID of desired test - POSITIVE INTEGER
LIMIT
(OPTIONAL) - provide no more than this number of results. - INTEGER
OFFSET
(OPTIONAL) - Offset to begin viewing result set at.
Returns ARRAYREF of result definition HASHREFs.
CONFIGURATION METHODS
getConfigurationGroups(project_id)
Gets the available configuration groups for a project, with their configurations as children.
Returns ARRAYREF of configuration group definition HASHREFs.
getConfigurationGroupByName(project_id,name)
Get the provided configuration group by name.
Returns false if the configuration group could not be found.
addConfigurationGroup(project_id,name)
New in TestRail 5.2.
Add a configuration group to the specified project.
Returns HASHREF with new configuration group.
editConfigurationGroup(config_group_id,name)
New in TestRail 5.2.
Change the name of a configuration group.
- INTEGER
CONFIG_GROUP_ID
- ID of relevant configuration group - STRING
NAME
- Name for new configuration Group.
Returns HASHREF with new configuration group.
deleteConfigurationGroup(config_group_id)
New in TestRail 5.2.
Delete a configuration group.
Returns BOOL.
getConfigurations(project_id)
Gets the available configurations for a project. Mostly for convenience (no need to write a boilerplate loop over the groups).
Returns ARRAYREF of configuration definition HASHREFs. Returns result of getConfigurationGroups (likely -500) in the event that call fails.
addConfiguration(configuration_group_id,name)
New in TestRail 5.2.
Add a configuration to the specified configuration group.
- INTEGER
CONFIGURATION_GROUP_ID
- ID of relevant configuration group - STRING
NAME
- Name for new configuration.
Returns HASHREF with new configuration.
editConfiguration(config_id,name)
New in TestRail 5.2.
Change the name of a configuration.
Returns HASHREF with new configuration group.
deleteConfiguration(config_id)
New in TestRail 5.2.
Delete a configuration.
Returns BOOL.
translateConfigNamesToIds(project_id,configs)
Transforms a list of configuration names into a list of config IDs.
Returns ARRAY of configuration names, with undef values for unknown configuration names.
STATIC METHODS
buildStepResults(content,expected,actual,status_id)
Convenience method to build the stepResult hashes seen in the custom options for getTestResults.
- STRING
CONTENT
(optional) - The step itself. - STRING
EXPECTED
(optional) - Expected result of test step. - STRING
ACTUAL
(optional) - Actual result of test step - INTEGER
STATUS ID
(optional) - Status ID of result
SEE ALSO
http://docs.gurock.com/testrail-api2/start
SPECIAL THANKS
Thanks to cPanel Inc, for graciously funding the creation of this module.
AUTHOR
George S. Baugh <teodesian@cpan.org>
CONTRIBUTORS
Mohammad S Anwar <mohammad.anwar@yahoo.com>
Neil Bowers <neil@bowers.com>
Ryan Sherer <ryan.sherer@cpanel.net>
SOURCE
The development version is on github at http://https://github.com/teodesian/TestRail-Perl and may be cloned from git://https://github.com/teodesian/TestRail-Perl.git
COPYRIGHT AND LICENSE
This software is copyright (c) 2018 by George S. Baugh.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.