/** Copyright (C) Conformiq Software Ltd. | |
* All rights reserved. | |
* | |
* Created Wed Aug 27 16:49:21 2008. | |
* | |
* @file TTCNScripter.java | |
* | |
* @author Conformiq Software Ltd. | |
* | |
* | |
*/ | |
package com.conformiq.adaptation.ttcn; | |
import java.io.BufferedWriter; | |
import java.io.File; | |
import java.io.FileWriter; | |
import java.io.IOException; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Queue; | |
import java.util.Set; | |
import java.util.StringTokenizer; | |
import java.util.Vector; | |
import com.conformiq.adaptation.ttcn.Utils.Banner; | |
import com.conformiq.adaptation.ttcn.Utils.CheckPointInfo; | |
import com.conformiq.adaptation.ttcn.Utils.TemplateDefinition; | |
import com.conformiq.qtronic2.Checkpoint; | |
import com.conformiq.qtronic2.MetaDataDictionary; | |
import com.conformiq.qtronic2.NotificationSink; | |
import com.conformiq.qtronic2.QMLArray; | |
import com.conformiq.qtronic2.QMLBoolean; | |
import com.conformiq.qtronic2.QMLNumber; | |
import com.conformiq.qtronic2.QMLOptional; | |
import com.conformiq.qtronic2.QMLRecord; | |
import com.conformiq.qtronic2.QMLRecordType; | |
import com.conformiq.qtronic2.QMLRecordTypeField; | |
import com.conformiq.qtronic2.QMLString; | |
import com.conformiq.qtronic2.QMLUnionType; | |
import com.conformiq.qtronic2.QMLValue; | |
import com.conformiq.qtronic2.QMLValueVisitor; | |
import com.conformiq.qtronic2.ScriptBackend; | |
import com.conformiq.qtronic2.TimeStamp; | |
import com.conformiq.qtronic2.Checkpoint.CheckpointStatus; | |
public class TTCNScripter extends ScriptBackend | |
{ | |
public TTCNScripter() | |
{ | |
this.mTestCaseIndex = 0; | |
this.mTemplateIndex = 0; | |
this.mTimeStamp = new TimeStamp(); | |
this.mTimeStamp.seconds = 0; | |
this.mTimeStamp.nanoseconds = 0; | |
this.mIsFirstStep = false; | |
this.mGeneratedTemplates = new HashMap<QMLRecord, Integer>(); | |
this.mPostponedTemplates = new Vector<TemplateDefinition>(); | |
this.mTestCaseNames = new Vector<String>(); | |
this.collectedCheckpoints = null; | |
} | |
@Override | |
public void setNotificationSink(NotificationSink sink) | |
{ | |
mNotifications = sink; | |
} | |
@Override | |
public boolean setMetaData(MetaDataDictionary dict) | |
{ | |
mMetadata = dict; | |
extractPortsMetaData(); | |
return true; | |
} | |
@Override | |
public boolean setConfigurationOption(java.lang.String property, | |
java.lang.String value) | |
{ | |
return mConfiguration.setConfigurationOption(property, value); | |
} | |
@Override | |
public boolean beginScript(String testsuiteName) | |
{ | |
this.mTestsuiteName = testsuiteName; | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("info", mScripterName | |
+ ": Export test cases for test design configuration " | |
+ testsuiteName); | |
} | |
String msg = null; | |
if (mConfiguration.getSuiteFile().getAbsolutePath().equals("")) | |
{ | |
msg = "TTCN-3 backend must be configured with a file name."; | |
} | |
else | |
{ | |
try | |
{ | |
mOut = new PrettyPrinter(new BufferedWriter(new FileWriter( | |
mConfiguration.getSuiteFile()))); | |
} catch (IOException e) | |
{ | |
StringBuffer msgbuf = new StringBuffer(); | |
msgbuf.append("Failed to open file '"); | |
msgbuf.append(mConfiguration.getSuiteFile()); | |
msgbuf.append("' for writing TTCN-3 script."); | |
msg = msgbuf.toString(); | |
} | |
} | |
if (msg != null) | |
{ | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", mScripterName + ": " + msg); | |
} | |
else | |
{ | |
System.err.println(msg); | |
} | |
return false; | |
} | |
{ | |
String[] desc = { | |
"This file contains all test cases generated from the " | |
+ "Conformiq '" + mConfiguration.getProjectName() | |
+ "' project", | |
"with '" + testsuiteName + "' design configuration." }; | |
String[] remark = { | |
"WARNING! This file has been automatically generated using the", | |
"Conformiq TTCN-3 scripting backend. DO NOT EDIT." }; | |
printHeader(mConfiguration.getSuiteFile(), desc, remark); | |
} | |
mOut.print("module "); | |
mOut.print(mConfiguration.getSuiteModuleName()); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
if (mConfiguration.getTestSystemModuleName() != null) | |
{ | |
mOut.print("import from "); | |
mOut.print(mConfiguration.getTestSystemModuleName()); | |
mOut.print(" all;"); | |
mOut.endl(); | |
} | |
if (mConfiguration.getDataTypesModuleName() != null) | |
{ | |
mOut.print("import from "); | |
mOut.print(mConfiguration.getDataTypesModuleName()); | |
mOut.print(" all;"); | |
mOut.endl(); | |
} | |
mOut.print("/* Customized imports begin */"); | |
mOut.endl(); | |
mOut.print(mConfiguration.getExtraImports()); | |
mOut.endl(); | |
mOut.println("/* Customized imports end */"); | |
mOut.emptyline(); | |
mOut.print("modulepar float " + mSlackVarName + " := "); | |
mOut.print(mConfiguration.getCommSlack()); | |
mOut.println(";"); | |
mOut.emptyline(); | |
printDebugModulepars(); | |
mOut.emptyline(); | |
// print out the test harness template | |
try | |
{ | |
// should the harness template be written | |
if (mConfiguration.isGenerateTestHarness()) | |
{ | |
PrettyPrinter harness = buildPrettyPrinter(mConfiguration | |
.getTestHarnessFile(), "harness template", false); | |
if (harness != null) | |
{ | |
dumpTestHarness(harness); | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("info", mScripterName | |
+ ": Generated TTCN-3 test harness template to " | |
+ "<hyperlink=\"" | |
+ mConfiguration.getTestHarnessFile() | |
.getAbsolutePath() | |
+ "\">" | |
+ mConfiguration.getTestHarnessFile() | |
.getAbsolutePath() + "</hyperlink>."); | |
} | |
} | |
} | |
} catch (Exception e) | |
{ | |
mOut.print("/* EXCEPTION CAUGHT */"); | |
mOut.endl(); | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", mScripterName | |
+ ": Caught exception while rendering TTCN test harness" | |
+ " template. Please reload model and try again (" | |
+ e.getMessage() + ")"); | |
} | |
return false; | |
} | |
if (mConfiguration.isGenerateDataTypes()) | |
{ | |
/* First dump all the types used in the module. */ | |
try | |
{ | |
dumpDataTypes(); | |
} catch (Exception e) | |
{ | |
mOut.print("/* EXCEPTION CAUGHT */"); | |
mOut.endl(); | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", mScripterName | |
+ ": Caught exception while " | |
+ "rendering TTCN type definitions. Please reload " | |
+ "model and try again (" + e.getMessage() + ")"); | |
} | |
return false; | |
} | |
} | |
if (mConfiguration.isGenerateTestSystemFile()) | |
{ | |
/* First dump all the types used in the module. */ | |
try | |
{ | |
dumpTestComponentFile(); | |
} catch (Exception e) | |
{ | |
mOut.print("/* EXCEPTION CAUGHT */"); | |
mOut.endl(); | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", mScripterName | |
+ ": Caught exception while " | |
+ "rendering TTCN type definitions. Please reload " | |
+ "model and try again (" + e.getMessage() + ")"); | |
} | |
return false; | |
} | |
} | |
deleteCopies(); | |
mTemplateIndex = 0; | |
mTestCaseIndex = 0; | |
mIsFirstStep = false; | |
return true; | |
} | |
@Override | |
public boolean beginCase(String testcaseName) | |
{ | |
collectedCheckpoints = new LinkedList<CheckPointInfo>(); | |
mCaseStepIndex = 0; | |
mIsFirstStep = true; | |
mTestCaseIndex++; | |
Banner banner = new Banner(); | |
banner.addTag("desc", "Generated test case #" + mTestCaseIndex); | |
banner.print(mOut); | |
mOut.print("testcase "); | |
mCrrentTestCaseName = recordAndRenderTestCaseName(testcaseName); | |
mOut.print(mCrrentTestCaseName); | |
mOut.println("()"); | |
mOut.print("runs on "); | |
mOut.print(mConfiguration.getRunsOnName()); | |
if (mConfiguration.getSystemTypeName() != null | |
&& !mConfiguration.getSystemTypeName().equals("")) | |
{ | |
mOut.print(" system "); | |
mOut.print(mConfiguration.getSystemTypeName()); | |
} | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
printLog(LogMessageType.DEBUG, "Starting execution of test case: '" | |
+ mCrrentTestCaseName + "'"); | |
mOut.print("var float " + mOldTimerName + " := " | |
+ Utils.TimeStampToString(mTimeStamp) + ";"); | |
mOut.endl(); | |
if (!mConfiguration.getDefaultName().equals("")) | |
{ | |
mOut.print("var default " + mConfiguration.getDefaultRefName() | |
+ ";"); | |
mOut.endl(); | |
} | |
if (!mConfiguration.getStartHook().equals("")) | |
{ | |
mOut.emptyline(); | |
mOut.print("/***** set up test configuration, TTCN-3 harness, " | |
+ "and adapter *****/"); | |
mOut.endl(); | |
mOut.print(mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getStartHook()); | |
mOut.print(";"); | |
mOut.endl(); | |
} | |
if (!mConfiguration.getDefaultName().equals("")) | |
{ | |
mOut.println("// default handles waiting beyond maximum response " | |
+ "time and reception of any"); | |
mOut.println("// other than the expected message with setting a" | |
+ " fail verdict and stopping the test"); | |
mOut.print(mConfiguration.getDefaultRefName() + " := activate("); | |
mOut.print(mConfiguration.getDefaultName()); | |
mOut.print(");"); | |
mOut.endl(); | |
} | |
return true; | |
} | |
// @Override | |
public void caseProbability(double probability) | |
{ | |
} | |
@Override | |
public boolean checkpointInfo(Checkpoint checkpoint, int status, | |
TimeStamp ts) | |
{ | |
if (!mIsFirstStep || status == CheckpointStatus.UNCOVERED | |
|| status == CheckpointStatus.MAYBE_COVERED) | |
{ | |
return true; | |
} | |
collectedCheckpoints.offer(new CheckPointInfo(checkpoint, status, ts)); | |
return true; | |
} | |
@Override | |
public boolean endCase() | |
{ | |
printCheckpoints(); | |
mOut.emptyline(); | |
mOut.println("setverdict(pass);"); | |
/* | |
* Default is used only if the provided name is different from "", so if | |
* the default is not used let's not deactivate it either. | |
*/ | |
if (!mConfiguration.getDefaultName().equals("")) | |
{ | |
mOut.print("deactivate(" + mConfiguration.getDefaultRefName() | |
+ ");"); | |
mOut.endl(); | |
} | |
if (!mConfiguration.getEndHook().equals("")) | |
{ | |
mOut.emptyline(); | |
mOut.print("/***** " | |
+ "tear down test configuration, TTCN-3 harness, and adapter" | |
+ " *****/"); | |
mOut.endl(); | |
mOut.print(mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getEndHook()); | |
mOut.print(";"); | |
mOut.endl(); | |
} | |
mOut.emptyline(); | |
printLog(LogMessageType.DEBUG, "Ending execution of " | |
+ mCrrentTestCaseName); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
/* Generate templates out of the datums stored in the TestStep. */ | |
printTemplates(); | |
mPostponedTemplates.clear(); | |
return true; | |
} | |
@Override | |
public boolean endScript() | |
{ | |
prindAltstepDefaultDecl(); | |
printSleepFunction(); | |
mOut.emptyline(); | |
printDebugMessageTypeEnum(); | |
mOut.emptyline(); | |
printDebugFunctionDecl(); | |
printControl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
try | |
{ | |
mOut.flush(); | |
mOut.close(); | |
} catch (Exception e) | |
{ | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", mScripterName + ": " | |
+ e.toString()); | |
} | |
} | |
mOut = null; | |
StringBuffer ss = new StringBuffer(); | |
ss.append("Generated TTCN-3 script to <hyperlink=\""); | |
ss.append(mConfiguration.getSuiteFile().getAbsolutePath()); | |
ss.append("\">"); | |
ss.append(mConfiguration.getSuiteFile().getAbsolutePath()); | |
ss.append("</hyperlink>."); | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("info", mScripterName + ": " + ss.toString()); | |
} | |
else | |
{ | |
System.out.println(ss.toString()); | |
} | |
mTestCaseNames.clear(); | |
return true; | |
} | |
@Override | |
public boolean testStep(QMLRecord r, String thread, String port, | |
boolean isFromTester, TimeStamp ts) | |
{ | |
/* | |
* We have an altstep defined: | |
* | |
* altstep CQDefaultAlt() runs on SystemType { [] any port.receive { | |
* mytimer.stop; setverdict(fail); stop; } [] mytimer.timeout { | |
* setverdict(fail); stop; } } | |
* | |
* which removes the need for "[] any port.receive" and timeout from alt | |
* statements. | |
*/ | |
mCaseStepIndex++; | |
mOut.endl(); | |
mOut.emptyline(); | |
mOut.println("/***** Step " + mCaseStepIndex + "; t = " | |
+ Utils.TimeStampToString(ts) + " *****/"); | |
mOut.endl(); | |
printLog(LogMessageType.DEBUG, mCrrentTestCaseName + ": Step " | |
+ mCaseStepIndex); | |
printCheckpoints(); | |
// Remember the datum so that we can generate a template out of it. | |
// Generate a new template only if it has not been generated before. | |
String name; | |
Integer idx = mGeneratedTemplates.get(r); | |
if (idx != null) | |
{ | |
StringBuffer ss = new StringBuffer(); | |
ss.append(r.getName()); | |
ss.append("Template"); | |
ss.append(idx); | |
name = ss.toString(); | |
} | |
else | |
{ | |
++mTemplateIndex; | |
StringBuffer ss = new StringBuffer(); | |
ss.append(r.getName()); | |
ss.append("Template"); | |
ss.append(mTemplateIndex); | |
name = ss.toString(); | |
if (!isFromTester) | |
{ | |
name = "expected" + name; | |
} | |
mPostponedTemplates.add( | |
new TemplateDefinition(r.getName(), name, r)); | |
mGeneratedTemplates.put(r, mTemplateIndex); | |
} | |
if (isFromTester) | |
{ | |
/* | |
* Send datum to SUT. Generate: | |
* | |
* mytimer.start(<send time>); alt { [] mytimer.timeout { // OK } } | |
* <port>.send(template_<index>); | |
*/ | |
if (ts.seconds > mTimeStamp.seconds | |
|| ts.nanoseconds > mTimeStamp.nanoseconds) | |
{ | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(".start("); | |
mOut.print(ts.seconds); | |
mOut.print("."); | |
mOut.print(ts.nanoseconds); | |
mOut.print(" - " + mOldTimerName + ");"); | |
mOut.endl(); | |
mOut.print("alt"); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.print("[] "); | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(".timeout"); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(".stop;"); | |
mOut.endl(); | |
} | |
mOut.endl(); | |
mOut.print(mConfiguration.getFunctionsPrefix() + "send_"); | |
mOut.print(r.getName()); | |
mOut.print("_to_"); | |
mOut.print(Utils.makeValidTTCN3Identifier(port)); | |
mOut.print("("); | |
mOut.print("m_" + name); | |
mOut.print(");"); | |
mOut.endl(); | |
} | |
else | |
{ | |
final String timeStamp = "" + ts.seconds + "." + ts.nanoseconds; | |
if (!Utils.EqualTimeStamps(this.mTimeStamp, ts)) | |
{ | |
mOut.println(mConfiguration.getFunctionsPrefix() | |
+ mSleepFunctionName + "(" + timeStamp + " - " | |
+ mOldTimerName + ");"); | |
} | |
mOut.println(mConfiguration.getTimerName() + ".start(" | |
+ mSlackVarName + ");"); | |
mOut.println("// Note: In below receive " | |
+ mConfiguration.getDefaultRefName() + "() is active!"); | |
mOut.print(mConfiguration.getFunctionsPrefix() + "receive_"); | |
mOut.print(r.getName()); | |
mOut.print("_from_"); | |
mOut.print(Utils.makeValidTTCN3Identifier(port)); | |
mOut.print("("); | |
mOut.print("m_" + name); | |
mOut.print(");"); | |
mOut.endl(); | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(".stop;"); | |
mOut.endl(); | |
} | |
if (!Utils.EqualTimeStamps(this.mTimeStamp, ts)) | |
{ | |
mOut.print(mOldTimerName + " := "); | |
mOut.print(ts.seconds); | |
mOut.print("."); | |
mOut.print(ts.nanoseconds); | |
mOut.println(";"); | |
} | |
this.mTimeStamp.seconds = ts.seconds; | |
this.mTimeStamp.nanoseconds = ts.nanoseconds; | |
this.collectedCheckpoints = new LinkedList<CheckPointInfo>(); | |
return true; | |
} | |
@Override | |
public boolean internalCommunicationsInfo(QMLRecord datum, String sender, | |
String receiver, String port, TimeStamp time) | |
{ | |
return true; | |
} | |
@Override | |
public boolean trace(String message, TimeStamp time) | |
{ | |
return true; | |
} | |
private PrettyPrinter buildPrettyPrinter(final File f, final String desc, | |
final boolean overWrite) | |
{ | |
String name = f.getAbsolutePath(); | |
if (f.exists() && !f.isDirectory() && !overWrite) | |
{ | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("info", mScripterName + ": File " | |
+ "<hyperlink=\"" + name + "\">" + name + "</hyperlink>." | |
+ " exists, NOT overwriting it."); | |
} | |
return null; | |
} | |
if (name.equals("") || f.isDirectory()) | |
{ | |
mNotifications.notify("error", | |
"TTCN-3 backend must be configured with a valid " + desc | |
+ " file name."); | |
return null; | |
} | |
try | |
{ | |
return new PrettyPrinter(new BufferedWriter(new FileWriter(f))); | |
} catch (IOException e) | |
{ | |
mNotifications.notify("error", "Failed to open file '" + name | |
+ "' for writing" + desc + "."); | |
return null; | |
} | |
} | |
private void dumpTestComponentFile() throws Exception | |
{ | |
String msg = null; | |
Exception rethrowMe = null; | |
PrettyPrinter oldOut = mOut; | |
if (mConfiguration.getTestSystemFile().getAbsolutePath().equals("")) | |
{ | |
msg = "TTCN-3 backend must be configured with a test system file " | |
+ "name."; | |
} | |
else | |
{ | |
try | |
{ | |
mOut = new PrettyPrinter(new BufferedWriter(new FileWriter( | |
mConfiguration.getTestSystemFile()))); | |
} catch (IOException e) | |
{ | |
StringBuffer msgbuf = new StringBuffer(); | |
msgbuf.append("Failed to open file '"); | |
msgbuf.append( | |
mConfiguration.getTestSystemFile().getAbsolutePath()); | |
msgbuf.append("' for writing test system information."); | |
msg = msgbuf.toString(); | |
rethrowMe = e; | |
} | |
} | |
if (msg != null) | |
{ | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", msg); | |
} | |
else | |
{ | |
System.err.println(msg); | |
} | |
mOut = oldOut; | |
// Pass the exception forward for proper error handling further | |
// up the call stack. | |
throw (rethrowMe); | |
} | |
{ | |
String[] desc = { | |
"This file contains port type definitions and test component " | |
+ "generated by Conformiq", | |
"with '" + mTestsuiteName + "' design configuration for '" | |
+ mConfiguration.getProjectName() + "' project." }; | |
String[] remark = { | |
"WARNING! This file has been automatically generated using the", | |
"Conformiq TTCN-3 scripting backend. DO NOT EDIT." }; | |
printHeader(mConfiguration.getDataTypesFile(), desc, remark); | |
} | |
mOut.print("module "); | |
mOut.print(mConfiguration.getTestSystemModuleName()); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
if (mConfiguration.getDataTypesModuleName() != null) | |
{ | |
mOut.println("import from " | |
+ mConfiguration.getDataTypesModuleName() + " all;"); | |
mOut.emptyline(); | |
} | |
// system mapping | |
if (!mConfiguration.getSystemTypeName().equals("") && !mConfiguration.isEptfEnable()) | |
{ | |
mOut.emptyline(); | |
Banner banner = new Banner(); | |
banner.addTag("desc", new String[] { | |
"This TTCN-3 component type specifies the interface of the", | |
"test cases towards the SUT more specifically the SUT " | |
+ "adapter." }); | |
banner.print(mOut); | |
mOut.print("type component "); | |
mOut.print(mConfiguration.getSystemTypeName()); | |
mOut.endl(); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.println("// Add all port instances here"); | |
mOut.resume(); | |
mOut.println("}"); | |
} | |
try | |
{ | |
if (mConfiguration.isGenerateTestSystemFile()) | |
{ | |
for (Port p : externalPorts) | |
{ | |
if (mConfiguration.isEptfEnable() && | |
(p.name.equals(mConfiguration.getEptfInboundPort()) || | |
p.name.equals(mConfiguration.getEptfOutboundPort())) | |
) | |
{ | |
} | |
else | |
{ | |
mOut.print("type port "); | |
mOut.print(p.name); | |
mOut.print("Port message"); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
for (QMLRecordType record : p.records) | |
{ | |
String dir = p.isInbound ? "in" : "out"; | |
if (mConfiguration.isEptfBidirectionalPort()) | |
dir = "inout"; | |
mOut.println(dir + " " + record.getTypeName() + ";"); | |
} | |
mOut.resume(); | |
mOut.print("}"); | |
if (!mConfiguration.getPortExtensions().equals("")) | |
{ | |
mOut.print(" with {extension \""); | |
mOut.print(mConfiguration.getPortExtensions()); | |
mOut.print("\"}"); | |
} | |
mOut.emptyline(); | |
} | |
} | |
} | |
mOut.emptyline(); | |
Banner banner = new Banner(); | |
banner.addTag("desc", "The test component (MTC) on which all " | |
+ "generated test cases run on"); | |
banner.print(mOut); | |
mOut.print("type component "); | |
mOut.print(mConfiguration.getRunsOnName()); | |
if (!mConfiguration.getExtendsComponent().equals("")) | |
{ | |
mOut.print(" extends "); | |
mOut.print(mConfiguration.getExtendsComponent()); | |
} | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
for (Port p : externalPorts) | |
{ | |
mOut.print("port "); | |
mOut.print(p.name); | |
mOut.print("Port "); | |
mOut.print(p.name); | |
mOut.print(";"); | |
mOut.endl(); | |
} | |
mOut.print("timer "); | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(" := 0.0;"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.flush(); | |
mOut.close(); | |
mOut = oldOut; | |
} catch (Exception e) | |
{ | |
mOut.flush(); | |
mOut.close(); | |
mOut = oldOut; | |
} | |
mNotifications.notify("info", mScripterName | |
+ ": Generated TTCN-3 port and component types file " | |
+ "<hyperlink=\"" | |
+ mConfiguration.getTestSystemFile().getAbsolutePath() + "\">" | |
+ mConfiguration.getTestSystemFile().getAbsolutePath() | |
+ "</hyperlink>."); | |
} | |
/** | |
* prints out the test harness template, that contains: | |
* | |
* - import from CQTypes; | |
* | |
* - type component for the harness system | |
* | |
* - MTC component the tests should run on | |
* | |
* - harness alt step | |
* | |
* - start and end test hook calls | |
* | |
* - port and message mapping functions | |
*/ | |
private void dumpTestHarness(final PrettyPrinter pp) throws Exception | |
{ | |
assert (mMetadata != null); | |
PrettyPrinter oldOut = this.mOut; | |
this.mOut = pp; | |
{ | |
String[] desc = { | |
"This is a template file with TTCN-3 function stubs for the " | |
+ "implementation ", | |
"of test harness code for the test suites generated from the " | |
+ "Conformiq '", | |
mConfiguration.getProjectName() + "' project." }; | |
String[] remark = { | |
"All function definitions must be edited as instructed in " | |
+ "their comments in", | |
" order for the test suite to execute properly." }; | |
printHeader(mConfiguration.getTestHarnessFile(), desc, remark); | |
} | |
mOut.print("module "); | |
mOut.print(Utils.buildModuleNameFromFileName( | |
mConfiguration.getTestHarnessFile())); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
// import all data types | |
if (mConfiguration.getDataTypesModuleName() != null) | |
{ | |
mOut.print(mIndent); | |
mOut.print("import from "); | |
mOut.print(mConfiguration.getDataTypesModuleName()); | |
mOut.print(" all;"); | |
mOut.endl(); | |
} | |
if (mConfiguration.getTestSystemModuleName() != null) | |
{ | |
mOut.print(mIndent); | |
mOut.print("import from "); | |
mOut.print(mConfiguration.getTestSystemModuleName()); | |
mOut.print(" all;"); | |
mOut.endl(); | |
} | |
mOut.emptyline(); | |
// debug function | |
mOut.print(mIndent); | |
mOut.block(); | |
// MTC port definitions | |
if (!mConfiguration.getRunsOnName().equals("")) | |
{ | |
mOut.resume(); | |
// default start and stop functions | |
mOut.print(mIndent); | |
mOut.block(); | |
if (!mConfiguration.getStartHook().equals("") && !mConfiguration.isEptfEnable()) | |
{ | |
mOut.emptyline(); | |
Banner banner = new Banner(); | |
banner.addTag("desc", new String[] { | |
"This function sets up the test configuration, maps all " | |
+ "mtc", | |
"to system component ports, and configures (if needed)", | |
"TTCN-3 harness and test system adapter" }); | |
banner.print(mOut); | |
mOut.print("function "); | |
mOut.print(mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getStartHook()); | |
mOut.print(" runs on "); | |
mOut.print(mConfiguration.getRunsOnName()); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.println("// Specify here map operations between MTC and " | |
+ "abstract test"); | |
mOut.println("// system interface or update and uncomment " | |
+ "generated code below"); | |
for (Port p : externalPorts) | |
{ | |
mOut.println("// map(" + mConfiguration.getRunsOnName() | |
+ ": " + p.name + ", system: <some port>);"); | |
} | |
if (mConfiguration.isDoLogCQInfoMessages()) | |
{ | |
mOut.println("// Remove or comment the following generated " | |
+ "code"); | |
} | |
printLog(LogMessageType.INFO, "Warning: " | |
+ mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getStartHook() + ": " | |
+ "function is not implemented"); | |
mOut.resume(); | |
mOut.println("}"); | |
} | |
if (!mConfiguration.getEndHook().equals("") && !mConfiguration.isEptfEnable()) | |
{ | |
Banner banner = new Banner(); | |
banner.addTag("desc", new String[] { | |
"This function tears down the test configuration", | |
"and unmaps all mtc to system component ports" }); | |
banner.print(mOut); | |
mOut.print("function "); | |
mOut.print(mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getEndHook()); | |
mOut.print(" runs on "); | |
mOut.println(mConfiguration.getRunsOnName()); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.println("//Specify here unmap operations between MTC and" | |
+ " abstract test"); | |
mOut.println("// system interface or update and uncomment" | |
+ " generated code below"); | |
for (Port p : externalPorts) | |
{ | |
mOut.println("// unmap(" + mConfiguration.getRunsOnName() | |
+ ": " + p.name + ", system: <some port>);"); | |
} | |
if (mConfiguration.isDoLogCQInfoMessages()) | |
{ | |
mOut.println("// Remove or comment the following generated " | |
+ "code"); | |
} | |
printLog(LogMessageType.INFO, "Warning: " | |
+ mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getEndHook() + ": " | |
+ "function is not implemented"); | |
mOut.resume(); | |
mOut.println("}"); | |
} | |
mOut.resume(); | |
// port mapping | |
try | |
{ | |
mOut.print(mIndent); | |
mOut.block(); | |
for (Port p : externalPorts) | |
{ | |
for (QMLRecordType type : p.records) | |
{ | |
String record = type.getTypeName(); | |
String argName = null; | |
if (p.isInbound) | |
{ | |
argName = "p_" + "expected" + record; | |
} | |
else | |
{ | |
argName = "p_" + record; | |
} | |
// (p.isInbound ? "tmplToMatch" : "msgToSend"); | |
String functionName = | |
mConfiguration.getFunctionsPrefix() | |
+ (p.isInbound ? "receive_" : "send_") | |
+ record | |
+ (p.isInbound ? "_from_" : "_to_") + p.name; | |
String functionDecl = "function " + functionName | |
+ "(template " + record + " " + argName + ")"; | |
mOut.emptyline(); | |
Banner banner = new Banner(); | |
if (p.isInbound) | |
{ | |
banner.addTag("desc", new String[] { | |
"This function receives a TTCN-3 value " | |
+ "corresponding to a " + record, | |
"via the abstract test system interface from " | |
+ "the SUT, performs any", | |
"manipulation and transformation needed to " | |
+ "convert it to a " + record, | |
"value, and then attempts to match it to the " | |
+ argName + " generated by CQ Designer" }); | |
banner.addTag("param", new String[] { argName | |
+ "Expected data generated by CQ Designer" }); | |
// * from the SUT to */ | |
} | |
else | |
{ | |
banner.addTag("desc", new String[] { | |
"This function performs manipulation needed" | |
+ " and" + " sends a " + argName, | |
"via the abstract test interface to the" | |
+ " SUT." }); | |
banner.addTag("param", argName | |
+ " Message data generated by CQ Designer" | |
+ "to be sent to the SUT"); | |
} | |
banner.print(mOut); | |
mOut.println(functionDecl + " runs on " | |
+ mConfiguration.getRunsOnName()); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
if (p.isInbound) | |
{ | |
mOut.println("//Steps that need to be implemented" | |
+ " here are:"); | |
mOut.println("//1. receive and store (any) TTCN-3" | |
+ " data value via TTCN-3 port which " | |
+ "corresponds to the " + p.name + " model " | |
+ "port"); | |
mOut.println("// var <T3_" + record | |
+ "Type> v_recvT3" + record + ";"); | |
mOut.println("// " + p.name + ".receive(<T3_" | |
+ record + "Type>:?) -> value v_recvT3" | |
+ record + ";"); | |
mOut.println("// 2. replace real with symbolic " | |
+ "values (if any) in the received TTCN-3 data" | |
+ " value"); | |
mOut.println("// 3. transform data from a TTCN-3 " | |
+ "to " + record + " data value (if needed)"); | |
mOut.println("// var" + record + "v_recv" + record | |
+ " = " + "f_transform" + record | |
+ "T3toCQ(v_recvT3" + record + ");"); | |
mOut.println("// 4. set the verdict to fail if if" | |
+ " there is a mismatch of the " | |
+ "transformed value and " + argName); | |
mOut.println("// if ( !match( v_recv" + record | |
+ " " + argName + ") {"); | |
mOut.println("// log(CQ_INFO: " + functionName | |
+ ": FAIL: Mismatch in received and expected " | |
+ record + " values. Stopping test case.�);"); | |
mOut.println("// setverdict(fail);"); | |
mOut.println("// " | |
+ mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getEndHook() | |
+ ";"); | |
mOut.println("// stop; }"); | |
if (mConfiguration.isEptfEnable()) | |
{ | |
if (p.name.equals(mConfiguration.getEptfInboundPort()) | |
|| p.name.equals(mConfiguration.getEptfOutboundPort())) | |
{ | |
mOut.println(" EPTF_MBT_TESTER_PCO.receive("+argName+") from vc_lgen;"); | |
} | |
else | |
{ | |
mOut.println(" " + p.name + ".receive("+argName+");"); | |
} | |
} | |
} | |
else | |
{ | |
mOut.println("// Steps that need to be implemented" | |
+ " here are:"); | |
mOut.println("// (modify and uncomment example " | |
+ "code as needed)"); | |
mOut.println("// 1. transform data from a " | |
+ record + " to the TTCN-3 data value" | |
+ " used by the test harness (if needed)"); | |
mOut.println("// var <T3" + record + "Type> v_T3" | |
+ record + " := " + "f_transform" + record | |
+ "CQtoT3(" + argName + ");"); | |
mOut.println("// 2. replace symbolic values " | |
+ "(if any) with real values in TTCN-3" | |
+ " data value"); | |
mOut.println("// 3. send TTCN-3 data value via " | |
+ "TTCN-3 port which corresponds to" + p.name | |
+ "model port"); | |
mOut.println("// " + p.name + ".send(v_T3" + record | |
+ ");"); | |
if (mConfiguration.isEptfEnable()) | |
{ | |
if (p.name.equals(mConfiguration.getEptfInboundPort()) | |
|| p.name.equals(mConfiguration.getEptfOutboundPort())) | |
{ | |
mOut.println(" EPTF_MBT_TESTER_PCO.send("+argName+") to vc_lgen;"); | |
} | |
else | |
{ | |
mOut.println(" " + p.name + ".send("+argName+");"); | |
} | |
} | |
} | |
if (mConfiguration.isDoLogCQInfoMessages()) | |
{ | |
mOut.println("// Remove or comment the following " | |
+ "generated code"); | |
} | |
printLog(LogMessageType.INFO, "Warning: " | |
+ functionName + ": " | |
+ "function is not implemented)"); | |
mOut.resume(); | |
mOut.println("}"); | |
} | |
} | |
mOut.resume(); | |
} catch (Exception e) | |
{ | |
mOut.print("/* EXCEPTION CAUGHT in dumpTestHarness */"); | |
mOut.flush(); | |
mOut.close(); | |
this.mOut = oldOut; | |
throw e; | |
} | |
} | |
// Must close the module block. | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.flush(); | |
mOut.close(); | |
this.mOut = oldOut; | |
} | |
private void dumpDataTypes() throws Exception | |
{ | |
assert (mMetadata != null); | |
PrettyPrinter oldOut = mOut; | |
String msg = null; | |
Exception rethrowMe = null; | |
if (mConfiguration.getDataTypesFile().getAbsolutePath().equals("")) | |
{ | |
msg = "TTCN-3 backend must be configured with a data type file " | |
+ "name."; | |
} | |
else | |
{ | |
try | |
{ | |
mOut = new PrettyPrinter(new BufferedWriter(new FileWriter( | |
mConfiguration.getDataTypesFile()))); | |
} catch (IOException e) | |
{ | |
StringBuffer msgbuf = new StringBuffer(); | |
msgbuf.append("Failed to open file '"); | |
msgbuf.append(mConfiguration.getDataTypesFile() | |
.getAbsolutePath()); | |
msgbuf.append("' for writing data types."); | |
msg = msgbuf.toString(); | |
rethrowMe = e; | |
} | |
} | |
if (msg != null) | |
{ | |
if (mNotifications != null) | |
{ | |
mNotifications.notify("error", msg); | |
} | |
else | |
{ | |
System.err.println(msg); | |
} | |
mOut = oldOut; | |
// Pass the exception forward for proper error handling further | |
// up the call stack. | |
throw (rethrowMe); | |
} | |
{ | |
String[] desc = { | |
"This file contains all data type definitions exported from " | |
+ "the Conformiq " + mConfiguration.getProjectName() | |
+ "' project.", | |
"with '" + mTestsuiteName + "' design configuration.'"}; | |
String[] remark = { | |
"WARNING! This file has been automatically generated using the", | |
"Conformiq TTCN-3 scripting backend. DO NOT EDIT." }; | |
printHeader(mConfiguration.getDataTypesFile(), desc, remark); | |
} | |
mOut.print("module "); | |
mOut.print(Utils.buildModuleNameFromFileName(mConfiguration | |
.getDataTypesFile())); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
try | |
{ | |
if (mConfiguration.isGenerateDataTypes()) | |
{ | |
final Set<QMLRecordType> alreadyDumped = | |
new HashSet<QMLRecordType>(); | |
final Queue<QMLRecordType> workList = | |
new LinkedList<QMLRecordType>(); | |
final Set<String> arrays = new HashSet<String>(); | |
for (Port p : externalPorts) | |
{ | |
for (QMLRecordType t : p.records) | |
{ | |
workList.offer(t); | |
} | |
} | |
while (!workList.isEmpty()) | |
{ | |
QMLRecordType type = workList.poll(); | |
if (alreadyDumped.contains(type) || | |
( | |
mConfiguration.isEptfEnable() && | |
type.getTypeName().length() >= 5 && | |
type.getTypeName().substring(0, 5).equals("EPTF_") | |
) | |
) | |
{ | |
// Skipping type generation | |
} | |
else | |
{ | |
alreadyDumped.add(type); | |
if (type instanceof QMLUnionType) | |
{ | |
mOut.print("type union "); | |
} | |
else | |
{ | |
mOut.print("type record "); | |
} | |
mOut.println(type.getTypeName()); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
for (int i = 0; i < type.getNumberOfFields(); i++) | |
{ | |
QMLRecordTypeField field = type.getField(i); | |
TTCNTypeName v = new TTCNTypeName(workList, | |
alreadyDumped, arrays); | |
field.getType().accept(v); | |
if (field.getType() instanceof QMLRecordType) | |
{ | |
String fieldType = | |
Utils.makeValidTTCN3Identifier( | |
field.getType().getTypeName()); | |
if (!alreadyDumped.contains(fieldType)) | |
{ | |
workList.offer( | |
(QMLRecordType)field.getType()); | |
} | |
} | |
assert (!v.getName().equals("")); | |
if (i != 0) | |
{ | |
mOut.print(","); | |
mOut.endl(); | |
} | |
mOut.print(v.getName()); | |
mOut.print(" "); | |
mOut.print(Utils.makeValidTTCN3Identifier( | |
field.getFieldName())); | |
if (v.getOptional()) | |
{ | |
mOut.print(" optional"); | |
} | |
} | |
mOut.resume(); | |
mOut.endl(); | |
mOut.print("}"); | |
mOut.endl(); | |
} | |
} | |
Iterator<String> it = arrays.iterator(); | |
while (it.hasNext()) | |
{ | |
String x = it.next(); | |
// TODO: reconsider changing this because now both | |
// TTCNTypeName class and this piece of code here has | |
// to know how array names are constructed. | |
mOut.print("type record of "); | |
mOut.print(x); | |
mOut.print(" "); | |
mOut.print(x); | |
mOut.print("Array;"); | |
mOut.endl(); | |
} | |
} | |
/* | |
* altstep CQDefaultAlt() runs on SystemType { [] any port.receive { | |
* mytimer.stop; setverdict(fail); stop; } [] mytimer.timeout { | |
* setverdict(fail); stop; } } | |
*/ | |
// Must close the module block. | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.flush(); | |
mOut.close(); | |
mOut = oldOut; | |
} catch (Exception e) | |
{ | |
mOut.flush(); | |
mOut.close(); | |
mOut = oldOut; | |
} | |
mNotifications.notify("info", mScripterName | |
+ ": Generated TTCN-3 type definitions " + "<hyperlink=\"" | |
+ mConfiguration.getDataTypesFile() + "\">" | |
+ mConfiguration.getDataTypesFile() + "</hyperlink>."); | |
} | |
private void printControl() | |
{ | |
mOut.print("control"); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
for (int i = 1; i <= mTestCaseIndex; i++) | |
{ | |
mOut.print("execute("); | |
mOut.print(getTestCaseName(i)); | |
mOut.print("());"); | |
mOut.endl(); | |
} | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.emptyline(); | |
} | |
public void reportError(String msg) | |
{ | |
mNotifications.notify("error", mScripterName + ": " + msg); | |
} | |
public void reportInfo(String msg) | |
{ | |
mNotifications.notify("info", mScripterName + ": " + msg); | |
} | |
private void extractPortsMetaData() | |
{ | |
final String portinfo = "portinfo:"; | |
String key = mMetadata.getNextKey(portinfo); | |
externalPorts = new LinkedList<Port>(); | |
while (key != null) | |
{ | |
String data = key; | |
if (!data.startsWith(portinfo)) | |
{ | |
break; | |
} | |
final QMLValue definition = mMetadata.get(key); | |
assert (definition != null); | |
QMLArray tuple = (QMLArray) definition; | |
if (tuple != null) | |
{ | |
Port port = new Port(key, tuple); | |
externalPorts.add(port); | |
} | |
key = mMetadata.getNextKey(key); | |
} | |
} | |
private void printSleepFunction() | |
{ | |
Banner banner = new Banner(); | |
banner.addTag("desc", | |
"This function blocks the execution for the specified time"); | |
banner.addTag("param", "p_duration The specified time in seconds"); | |
banner.print(mOut); | |
mOut.println("function " + mConfiguration.getFunctionsPrefix() | |
+ mSleepFunctionName + "(float p_duration)"); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.println("timer t;"); | |
mOut.println("t.start(p_duration);"); | |
mOut.println("t.timeout; // Note that any active default behavior " | |
+ "may interrupt this waiting!"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
} | |
private void printAltStepBody() | |
{ | |
mOut.print(mIndent); | |
mOut.block(); | |
if (!mConfiguration.isEptfEnable()) | |
{ | |
mOut.print("[] any port.receive"); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(".stop;"); | |
mOut.endl(); | |
mOut.print("setverdict(fail);"); | |
mOut.endl(); | |
printLog(LogMessageType.DEBUG, mConfiguration.getDefaultName() | |
+ ": FAIL: Stopping test case after receiving unexpected " | |
+ "message in default: " + mConfiguration.getDefaultName() + "!"); | |
if (!mConfiguration.getEndHook().equals("")) | |
{ | |
mOut.print(mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getEndHook()); | |
mOut.print(";"); | |
mOut.endl(); | |
} | |
mOut.print("stop;"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
} | |
mOut.print("[] "); | |
mOut.print(mConfiguration.getTimerName()); | |
mOut.print(".timeout"); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
mOut.print("setverdict(fail);"); | |
mOut.endl(); | |
printLog(LogMessageType.DEBUG, mConfiguration.getDefaultName() | |
+ ": FAIL: Stopping test case after " + "time out of timer: " | |
+ mConfiguration.getTimerName() + "!"); | |
if (!mConfiguration.getEndHook().equals("")) | |
{ | |
mOut.print(mConfiguration.getFunctionsPrefix() | |
+ mConfiguration.getEndHook()); | |
mOut.print(";"); | |
mOut.endl(); | |
} | |
mOut.print("stop;"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
mOut.resume(); | |
mOut.endl(); | |
} | |
private void printTemplates() | |
{ | |
if (!mPostponedTemplates.isEmpty()) | |
{ | |
mOut.println("/***** Template definitions generated for " | |
+ mCrrentTestCaseName + "*****/"); | |
Iterator<TemplateDefinition> it = mPostponedTemplates.iterator(); | |
while (it.hasNext()) | |
{ | |
final TemplateDefinition def = it.next(); | |
mOut.print("template "); | |
mOut.print(Utils.makeValidTTCN3Identifier(def.getTypeName())); | |
mOut.print(" "); | |
mOut.print("m_" + def.getTemplateName()); | |
mOut.ws(); | |
mOut.print(":="); | |
mOut.endl(); | |
QMLRecord r = def.getRecord(); | |
TemplateDumper t = new TemplateDumper(mOut, mConfiguration | |
.isUseFractions()); | |
r.accept(t); | |
mOut.endl(); | |
mOut.emptyline(); | |
} | |
} | |
} | |
private void prindAltstepDefaultDecl() | |
{ | |
mOut.emptyline(); | |
Banner banner = new Banner(); | |
banner.addTag("desc", new String[] { | |
"This altstep handles terminating after waiting up to", | |
"the maximum response time and receiving any other", | |
"than the expected message with setting a fail ", | |
"verdict and stopping the test" }); | |
banner.print(mOut); | |
mOut.print("altstep "); | |
mOut.print(mConfiguration.getDefaultName()); | |
mOut.print(" runs on "); | |
mOut.print(mConfiguration.getRunsOnName()); | |
mOut.endl(); | |
mOut.print("{"); | |
mOut.endl(); | |
mOut.print(mIndent); | |
mOut.block(); | |
printAltStepBody(); | |
mOut.resume(); | |
mOut.println("}"); | |
} | |
private void printDebugModulepars() | |
{ | |
mOut.println("/***** Logging verbosity flags *****/"); | |
for (DebugMessageType t : DebugMessageType.enabledValues()) | |
{ | |
Banner banner = new Banner(); | |
banner.addTag("desc", t.modeuleparComment); | |
banner.print(mOut); | |
printModulepar("boolean", t.enablingFlag, "" + t.defaultFlagState); | |
} | |
} | |
private void printModulepar(final String type, final String name, | |
final String init) | |
{ | |
mOut.println("modulepar " + type + " " + name + " := " + init + ";"); | |
} | |
private void printDebugMessageTypeEnum() | |
{ | |
mOut.println("type enumerated " + mDebugMessageTypeEnumName); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
DebugMessageType[] debugMessageTypes = DebugMessageType.enabledValues(); | |
for (int i = 0; i < debugMessageTypes.length; i++) | |
{ | |
mOut.print(debugMessageTypes[i].dispatchingEnumItemName); | |
if (i != debugMessageTypes.length - 1) | |
{ | |
mOut.print(","); | |
} | |
mOut.endl(); | |
} | |
mOut.resume(); | |
mOut.endl(); | |
mOut.print("}"); | |
mOut.endl(); | |
} | |
private void printDebugFunctionDecl() | |
{ | |
String firstArg = "p_description"; | |
String secondArg = "p_" + mDebugMessageTypeEnumName; | |
Banner banner = new Banner(); | |
List<String> descBody = new LinkedList<String>(); | |
descBody.add("This function logs target description if the module" | |
+ " parameter"); | |
descBody.add("related to the target type is set to true."); | |
descBody.add("Module parameters and target type dependencies are" | |
+ " as follows:"); | |
for (DebugMessageType t : DebugMessageType.enabledValues()) | |
{ | |
descBody.add(mIndent + t.enablingFlag | |
+ " controls logging of targets type " | |
+ t.dispatchingEnumItemName); | |
} | |
banner.addTag("desc", descBody.toArray(new String[0])); | |
banner.addTag("param", new String[] { firstArg | |
+ " The textual target description to be logged" }); | |
banner.addTag("param ", new String[] { secondArg | |
+ " The type of target covered by a test" }); | |
banner.print(mOut); | |
mOut.print("function " + mConfiguration.getFunctionsPrefix() | |
+ mSmartLogCommand + " ("); | |
mOut.print("charstring " + firstArg + ", " + mDebugMessageTypeEnumName | |
+ " " + secondArg); | |
mOut.print(") runs on " + mConfiguration.getRunsOnName()); | |
mOut.endl(); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
DebugMessageType types[] = DebugMessageType.enabledValues(); | |
for (int i = 0; i < types.length; i++) | |
{ | |
DebugMessageType t = types[i]; | |
mOut.print("if ((" + secondArg + " == " + t.dispatchingEnumItemName | |
+ ")"); | |
mOut.print(" and "); | |
mOut.print(t.enablingFlag + ")"); | |
mOut.endl(); | |
mOut.println("{"); | |
mOut.print(mIndent); | |
mOut.block(); | |
printLogVar(firstArg); | |
mOut.resume(); | |
mOut.println("}"); | |
if (i != types.length - 1) | |
{ | |
mOut.println("else"); | |
} | |
} | |
mOut.resume(); | |
mOut.print("}"); | |
mOut.endl(); | |
} | |
private void printLog(LogMessageType type, String msg) | |
{ | |
String messageHeader; | |
switch (type) { | |
case DEBUG: | |
if (!mConfiguration.isDoLogCQDEbugMessages()) | |
{ | |
return; | |
} | |
messageHeader = "DEBUG"; | |
break; | |
case INFO: | |
if (!mConfiguration.isDoLogCQInfoMessages()) | |
{ | |
return; | |
} | |
messageHeader = "INFO"; | |
break; | |
default: | |
return; | |
} | |
mOut.print("log"); | |
mOut.print("(\"CQ_" + messageHeader + ": " + msg + "\");"); | |
mOut.endl(); | |
} | |
private void printLogVar(String varName) | |
{ | |
mOut.print("log"); | |
mOut.print("(\"CQ_INFO: \" & " + varName + ");"); | |
mOut.endl(); | |
} | |
private void printHeader(File file, String[] desc, String[] remark) | |
{ | |
assert (mOut != null); | |
mOut.print("/* -*- ttcn-3 -*- */"); | |
mOut.endl(); | |
mOut.emptyline(); | |
Banner banner = new Banner(); | |
banner.addTag("file", file.getAbsolutePath()); | |
banner.addTag("author", "Conformiq TTCN-3 scripting backend"); | |
banner.addTag("version", new Date().toString()); | |
if (desc != null) | |
{ | |
banner.addTag("desc", desc); | |
} | |
if (remark != null) | |
{ | |
banner.addTag("remark", remark); | |
} | |
banner.print(mOut); | |
} | |
private void deleteCopies() | |
{ | |
mGeneratedTemplates.clear(); | |
} | |
private void printCheckpoints() | |
{ | |
for (CheckPointInfo cp : collectedCheckpoints) | |
{ | |
printCheckPoint(cp); | |
} | |
} | |
private void printCheckPoint(CheckPointInfo cp) | |
{ | |
String name = cp.checkpoint.getName(); | |
for (DebugMessageType t : DebugMessageType.values()) | |
{ | |
for (String p : t.checkpointPrefixes) | |
{ | |
if (name.startsWith(p) && t.enabled) | |
{ | |
mOut.print(mConfiguration.getFunctionsPrefix() + | |
mSmartLogCommand); | |
mOut.print("(\"Covered "); | |
mOut.print(Utils.escapeString(name)); | |
mOut.print("\""); | |
mOut.print(", "); | |
mOut.print(t.dispatchingEnumItemName); | |
mOut.print(");"); | |
mOut.endl(); | |
return; | |
} | |
} | |
} | |
} | |
private String recordAndRenderTestCaseName(String testCaseName) | |
{ | |
// Remove whitespace | |
StringBuffer rendered = new StringBuffer("tc_"); | |
for (int i = 0; i < testCaseName.length(); i++) | |
{ | |
// Inverted the logic here to be safer by replacing everything | |
// but valid identifier chars with an underscore. According to the | |
// spec: "TTCN-3 identifiers are case sensitive and may | |
// only contain lowercase letters (a-z) uppercase letters | |
// (A-Z) and numeric digits (0-9). Use of the underscore ( | |
// _ ) symbol is also allowed. An identifier shall begin | |
// with a letter (i.e., not a number and not an | |
// underscore)." | |
char c = testCaseName.substring(i, i + 1).charAt(0); | |
// If the original test case name starts with a digit, we want | |
// to preserve that but need to prefix the test case name with | |
// an underscore. | |
if (c >= '0' && c <= '9' && i == 0) | |
rendered.append('_'); | |
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') | |
|| (c >= '0' && c <= '9') || c == '_') | |
rendered.append(c); | |
else | |
rendered.append('_'); | |
} | |
// Put into test case name list | |
mTestCaseNames.add(rendered.toString()); | |
return rendered.toString(); | |
} | |
private String getTestCaseName(int i) | |
{ | |
// Get test case name at index i of the test case list | |
return mTestCaseNames.get(i - 1); | |
} | |
private static final String mIndent = " "; | |
private static final String mDebugMessageTypeEnumName = "target_type"; | |
private static final String mSmartLogCommand = "log_target"; | |
static public final String mScripterName = "TTCN-3 scripter"; | |
static private final String mSlackVarName = "mp_max_response_time"; | |
static private final String mOldTimerName = "v_last_wait_timeout"; | |
static private final String mSleepFunctionName = "sleep"; | |
private PrettyPrinter mOut; | |
private NotificationSink mNotifications; | |
private MetaDataDictionary mMetadata; | |
private int mTestCaseIndex; | |
private int mTemplateIndex; | |
private String mCrrentTestCaseName; | |
private int mCaseStepIndex; | |
private TimeStamp mTimeStamp; | |
private boolean mIsFirstStep; | |
private Map<QMLRecord, Integer> mGeneratedTemplates; | |
private Vector<TemplateDefinition> mPostponedTemplates; | |
private Vector<String> mTestCaseNames; | |
private Configuration mConfiguration = new Configuration(); | |
private String mTestsuiteName; | |
private Queue<Utils.CheckPointInfo> collectedCheckpoints; | |
enum LogMessageType { | |
DEBUG, INFO | |
} | |
public class Port | |
{ | |
public Port(final String key, final QMLArray tuple) | |
{ | |
final StringTokenizer tokenizer = new StringTokenizer(key, ":"); | |
final String potinfo = tokenizer.nextToken(); | |
assert potinfo == "portinfo"; | |
final String dir = tokenizer.nextToken(); | |
isInbound = !dir.equals("inbound"); | |
final boolean isOutbound = !dir.equals("outbound"); | |
assert isInbound != isOutbound; | |
final String origname = tokenizer.nextToken(); | |
name = Utils.makeValidTTCN3Identifier(origname); | |
assert !tokenizer.hasMoreElements(); | |
this.records = new LinkedList<QMLRecordType>(); | |
tuple.accept(new PortDataExtractor()); | |
} | |
final List<QMLRecordType> records; | |
final boolean isInbound; | |
final String name; | |
private class PortDataExtractor implements QMLValueVisitor | |
{ | |
public PortDataExtractor() | |
{ | |
} | |
@Override | |
public void visit(QMLArray x) | |
{ | |
final int size = x.getNumberOfElements(); | |
for (int i = 0; i < size; i++) | |
{ | |
QMLString portname = (QMLString) x.getValue(i); | |
if (portname != null) | |
{ | |
final String origname = portname.getValue(); | |
QMLRecordType type = mMetadata.getType(origname); | |
if (type == null) | |
{ | |
mNotifications.notify("info", "Warning: Nested r" + | |
"records in port declarations types are " + | |
"not yet supported by the scripter"); | |
continue; | |
} | |
records.add(type); | |
} | |
} | |
} | |
@Override | |
public void visit(QMLBoolean b) | |
{ | |
} | |
@Override | |
public void visit(QMLNumber n) | |
{ | |
} | |
@Override | |
public void visit(QMLRecord r) | |
{ | |
} | |
@Override | |
public void visit(QMLString s) | |
{ | |
} | |
@Override | |
public void visit(QMLOptional p) | |
{ | |
} | |
} | |
} | |
private List<Port> externalPorts; | |
} |