Home » Modeling » TMF (Xtext) » Inferring XMemberFeatureCall
Inferring XMemberFeatureCall [message #1735382] |
Sat, 18 June 2016 01:44 |
Mansour Al Messages: 44 Registered: June 2016 |
Member |
|
|
I have this grammar snippet
/* The documentation for the action */
Action:
type=JvmTypeReference config=ActionConfiguration ";";
ActionConfiguration:
{ActionConfiguration} (target=XMemberFeatureCall)? (":" data+=Datum+)?;
Datum:
content=PropertyRef | Value;
I have this model project section:
Click MyPage.element ;
Fill MyPage.text : "How to use this" ;
// Click MyPage.getValues(3); // error: no viable alternative at input '3'
The first question is how to fix the error on the third line ?
When inferred, I am getting this output:
@Test
public void testSearch() {
(new Click (
<XFeatureCallImplCustom>.element
)).exec();
(new Fill (
<XFeatureCallImplCustom>.text
)).exec();
}
What I am looking for, is:
@Test
public void testSearch() {
(new Click ( MyPage.element )).exec();
(new Fill ( MyPage.text , "How to use this" )).exec();
(new Click ( MyPage.getValues(3) )).exec();
}
This is my infer code:
def JvmOperation createTestMethod(TestDefinition test) {
test.toMethod(test.name, typeRef(void)) [
annotations += annotationRef("org.junit.Test");
body = '''
«FOR a : test.actions»
(new «a.type » (
«IF a.config.target != null»
«a.config.target»
«ENDIF»
)).exec();
«ENDFOR»
'''
]
}
So basically, what do I need to modify in the grammar, and the inferer in order to get a copy of the ActionConfiguration.target ?
Finally, I am not sure when to use Generator or Inferrer. My understanding is Generator is more suitable if I am not producing a language that will work with JVM. For example, if I am generating a shell script, then a generator is more appropriate. Please correct me if I am wrong.
Thank you in advance
|
|
| | |
Re: Inferring XMemberFeatureCall [message #1735406 is a reply to message #1735403] |
Sat, 18 June 2016 14:38 |
|
yes you will need an extra method for that
@Test
public void testSearch() {
(new Click ( helper1() )).exec();
(new Fill ( helper2(), "How to use this" )).exec();
(new Click ( MyPage.getValues(3) )).exec();
}
private ClickTarget helper1() {
return MyPage.element;
}
private FillTarget helper2() {
return MyPage.text;
}
.....
or you have to turn your complete thingy into a own XExpression with adtoption to type computer and xbasecompiler etc. (this is a advanced task !)
if you want to do more stuff e.g. calling methods XMeberFeatureCall might not be approporiate. you may need to use XBlockExpression or XExpression XFeatureCall or ....
normally xexpressions should work but i cannot tell what you have else in your grammar that interfers with xbase supergrammar so plase share a minimal grammar and inferrer
i can copy and paste
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Day Job: https://www.everest-systems.com
[Updated on: Sat, 18 June 2016 14:45] Report message to a moderator
|
|
| |
Re: Inferring XMemberFeatureCall [message #1735419 is a reply to message #1735408] |
Sun, 19 June 2016 01:44 |
Mansour Al Messages: 44 Registered: June 2016 |
Member |
|
|
Hello Christian,
Thank you a lot for your help. I have committed the dsl project here https://github.com/malakeel/myDsl
and a model project here https://github.com/malakeel/myDsl-demo
The grammar file is located: https://github.com/malakeel/myDsl/blob/master/org.xtext.example.mydsl/src/org/xtext/example/mydsl/MyDsl.xtext
The relevant parts:
Domainmodel:
// moduleName=ModuleDeclaration
"package" name=QualifiedName ";" //
importSection=XImportSection? //
suite=SuiteDeclaration;
SuiteDeclaration:
'suite' name=ValidID ('using' '(' configFiles=Files ')')? '{' handlers+=EventHandlerDefinition* beforeActions+=Action*
prepare=PrepareDeclaraion afterActions+=(Action)* '}';
PrepareDeclaraion:
'prepare' ('using' '(' configFiles=Files ')')? '{' handlers+=EventHandlerDefinition* beforeActions+=Action*
testCases+=TestDefinition+ afterActions+=Action* '}';
TestDefinition:
"test" name=ValidID ('using' '(' configFiles=Files ')')? '{' actions+=(Action)+ '}';
Action:
type=JvmTypeReference config=ActionConfiguration ";";
ActionConfiguration:
{ActionConfiguration} targets+=(UIElement)* (":" data+=Datum+)?;
UIElement: //; returns xbase::XExpression:
comp=(XMemberFeatureCall);
as you can tell from the code, and from this thread, I am trying to do method calls inside each "test" TestDefinition.
I tried what you suggested, and introduced XExpression from xbase, however I lost support in the validator for imports and none of the calls could be resolved.
The difficulty I am facing now, is how to do method calls, like:
test myTestCaseSample using (some.xml ) {
Click MyPage.element ;
Fill MyPage.text : "How to use this" ;
Fill new Xpath.id("username") : "myUserName" ;
Click XpathBuilder.element("input").parent("div").att("id" , "submit") ;
Click MyPage.getValues(3); // error: no viable alternative at input '3'
DragNDrop Xpath.id("product-1") XpathBuilder.element("div").att("id" , "basket") ;
Validate XpathBuilder.id("message") "Thank you for placing your order" ;
}
As you said, XMemberFeatureCall looks limited. Unfortunately, I was not able to find documentation about what each of these elements mean and how do they act (XMemberFeatureCall, XExpression, XFeatureCall ... etc).
So I am not sure which the best one for my case. The only way for me to find out is trail and error, and ask here for help. I will be trying with XExpression, but again, i am loosing the "import" support in my model project.
I will worry about "imports" and IDE later. For now, I need to get the grammar rectified, and have more understanding for the XbaseComplier and Type Computer.
Thank you.
|
|
| |
Re: Inferring XMemberFeatureCall [message #1735435 is a reply to message #1735422] |
Sun, 19 June 2016 13:26 |
Mansour Al Messages: 44 Registered: June 2016 |
Member |
|
|
Christian, I have not used XExpression or XBlockExpression before, and couldn't find documentation on when and how to use them. Here's what I have now, but this is not working.
TestDefinition :
"test" name=ValidID ('using' '(' configFiles=Files ')')? '{' block = TestBlock '}';
TestBlock returns xbase::XBlockExpression :
expressions+=ActionExpression+
;
ActionExpression returns xbase::XExpression:
Action
;
Action:
type=JvmTypeReference config=ActionConfiguration ";";
ActionConfiguration:
{ActionConfiguration} targets+=(UIElement)* (":" data+=Datum+)?;
UIElement:
{UIElement} block=XExpression;
Now this grammar is generating code with NO ERRORS.
What I understand is the XExpression is just a wrapper around what ever ParserRule in order to invoke the compiler and type computer. Am I right here ?
From here I believe I need to write the compiler and type computer. Am I right ?
A sample will be great.
Thank you
[Updated on: Sun, 19 June 2016 13:35] Report message to a moderator
|
|
| | |
Re: Inferring XMemberFeatureCall [message #1735441 is a reply to message #1735437] |
Sun, 19 June 2016 15:10 |
Mansour Al Messages: 44 Registered: June 2016 |
Member |
|
|
LOL
I am not expecting you to do the work for me.
A few lines of an example may clarify the concept, and make it easier to communicate the idea. I am sorry if you think I am expecting a lot.
The solution you suggested may work, however I still need to track the number of helper methods generated, and add them to the class. The number of helper methods needs to be set and incremented inside the class (in my case, the suite block). So in my inferrer I need to keep a reference to the class that has the current number of helpers, to use it as an index. Additionally, I don't know of anyway to generate the suite with a placeholder variable like "helperCount".
In all cases, this is working:
TestDefinition:
"test" name=ValidID ('using' '(' configFiles=Files ')')? '{' block=TestBlock '}';
TestBlock returns xbase::XBlockExpression:
expressions+=Action+;
Action returns xbase::XExpression:
{Action} type=JvmTypeReference config=ActionConfiguration ";";
ActionConfiguration:
{ActionConfiguration} targets+=(UIElement)* (":" data+=Datum+)?;
I am using the example you linked to, and using ExpressionHelper.
The compiler and Type Computer are not called. I have set some break points, but none is being reached or hit. This is the problem I am facing now.
If you or someone else, have time to look at the issue, and guide me to the right path, it will be great.
Thank you for all the help.
|
|
| | | | |
Re: Inferring XMemberFeatureCall [message #1735456 is a reply to message #1735452] |
Mon, 20 June 2016 02:12 |
Mansour Al Messages: 44 Registered: June 2016 |
Member |
|
|
Ok, I think I know the source of confusion now.
For me, I was still lost between XbaseCompiler, Type Computer, Inferrer, not know what is the role for each. I think now, things are clearer.
Here's what I have (and it's working fine) so far:
TestDefinition:
"test" name=ValidID ('using' '(' configFiles=Files ')')? '{' block=TestBlock '}';
TestBlock returns xbase::XBlockExpression:
{TestBlock} expressions+=Action+;
Action returns xbase::XExpression:
{Action} type=JvmTypeReference config=ActionConfiguration ";";
ActionConfiguration:
{ActionConfiguration} targets+=(UIElement)* (":" data+=Datum+)?;
UIElement:
{UIElement} block=XExpression;
Inferrer
def JvmOperation createTestMethod(TestDefinition test) {
test.toMethod(test.name, typeRef(void)) [
annotations += annotationRef("org.junit.Test");
body = test.block
]
}
Finally, compiler:
override protected doInternalToJavaStatement(XExpression obj, ITreeAppendable appendable, boolean isReferenced) {
if (obj instanceof TestBlock) {
val block = obj as TestBlock;
for (exp : block.expressions)
doInternalToJavaStatement(exp, appendable, isReferenced);
return;
} else if (obj instanceof Action) {
appendable.newLine
val action = obj as Action;
appendable.trace(action)
appendable.append("(new ");
appendable.append(action.type.qualifiedName + "(");
for (target : action.config.targets) {
internalToJavaExpression(target.block, appendable);
// internalToJavaExpression(target.block, appendable);
if (!action.config.targets.last.equals(target)) {
appendable.append(",");
}
}
appendable.append(").exec();")
appendable.newLine
println("Processed compiler expression")
return
} else {
super.internalToConvertedExpression(obj, appendable)
}
}
So, elements that can not be inferred, are sent to the compiler. This is now clear to me. The remaining issue is producing the correct reference. This code in the DSL:
test myTestCaseSample using (some.xml ) {
Click MyPage.element ;
Fill MyPage.text : "How to use this" ;
Fill ; /*What ever*/
Click MyPage.getValues(3); // error: no viable alternative at input '3' ;
}
Is producing:
@Test
public void myTestCaseSample() {
(new actions.Click(/* name is null */./* name is null */).exec();
(new actions.Fill(/* name is null */./* name is null */).exec();
(new actions.Fill().exec();
(new actions.Click(/* name is null */./* name is null */).exec();
}
The only problem is the references, when called internalToJavaExpression(target.block, appendable); is producing the /*name is null*/ annotations !!
Thank you
[Updated on: Mon, 20 June 2016 02:13] Report message to a moderator
|
|
|
Re: Inferring XMemberFeatureCall [message #1735458 is a reply to message #1735456] |
Mon, 20 June 2016 03:56 |
|
I cannot follow this. maybe this is cause i dont have the complete grammar and the current type computer is missing.
here is what i got stubbed together in 10 mins i have
// i dont want all this before after whatever
SuiteDeclaration:
'suite' name=ValidID ('using' '(' configFiles=Files ')')? '{' testCases+=TestDefinition* '}';
Inferrer
for (TestDefinition test : suite.testCases) {
members += this.createTestMethod(test);
}
....
def JvmOperation createTestMethod(TestDefinition test) {
test.toMethod(test.name, typeRef(void)) [
annotations += annotationRef("org.junit.Test");
// visibility = JvmVisibility.PRIVATE ;
body = test.block
]
}
class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
//
def Class<? extends ITypeComputer> bindITypeComputer() {
return MyDslTypeComputer
}
def Class<? extends XbaseCompiler> bindXbaseCompiler() {
return MyDslCompiler
}
}
class MyDslTypeComputer extends XbaseTypeComputer {
override computeTypes(XExpression expression, ITypeComputationState state) {
if (expression instanceof Action) {
_computeTypes(expression as Action, state);
} else {
super.computeTypes(expression, state)
}
}
protected def void _computeTypes(Action object, ITypeComputationState state) {
for (t : object.config.targets) {
println("TODO better logic here ?!?")
val iaction = state.getReferenceOwner().getServices().getTypeReferences().findDeclaredType("org.example.IAction", state.referenceOwner.contextResourceSet)
state.withExpectation(
state.referenceOwner.newParameterizedTypeReference(iaction)
).computeTypes(t.block)
}
state.acceptActualType(getTypeForName(Void.TYPE, state), ConformanceFlags.CHECKED_SUCCESS)
}
// protected def void _computeTypes(UIElement object, ITypeComputationState state) {
// state.withExpectation(getPrimitiveVoid(state)).computeTypes(object.comp)
// state.acceptActualType(getTypeForName(Runnable, state), ConformanceFlags.CHECKED_SUCCESS)
// }
}
dont have complete iaction call etc ...
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Day Job: https://www.everest-systems.com
|
|
| |
Re: Inferring XMemberFeatureCall [message #1735541 is a reply to message #1735459] |
Tue, 21 June 2016 03:30 |
Mansour Al Messages: 44 Registered: June 2016 |
Member |
|
|
You are right. The source of confusion was related to me not understanding the inferring and the compiling phases, and how to relate to them. I though:
body = "my code <<myexpression>>" would give me the results I was looking for, but as you said, it doesn't work. I couldn't understand how each of them is called.
However, the TypeComputer code you gave me was the missing part, and allowed me to continue working. Thank you a lot. This saved me weeks of pain
So here's the computer part:
protected def void _computeTypes(Action action, ITypeComputationState state) {
for (t : action.config.targets) {
println("TODO better logic here ?!?")
val iaction = state.getReferenceOwner().getServices().getTypeReferences().findDeclaredType(
"org.example.ILocator", state.referenceOwner.contextResourceSet)
state.withExpectation(
state.referenceOwner.newParameterizedTypeReference(iaction)
).computeTypes(t.block)
}
state.acceptActualType(getTypeForName(Void.TYPE, state), ConformanceFlags.CHECKED_SUCCESS)
}
I had to dig, and struggled to find my way in the compiler to write this:
public override dispatch void toJavaStatement(XAbstractFeatureCall expr, ITreeAppendable b, boolean isReferenced) {
if (expr instanceof XMemberFeatureCall) {
featureCalltoJavaExpression(expr, b, false);
} else {
toJavaStatement(expr as XAbstractFeatureCall, b, isReferenced);
}
}
Now, this DSL code:
test myTestCaseSample using (some.xml ) {
Click MyOtherPage.element ;
Fill MyPage.text : "How to use this" ;
Fill ; /*What ever*/
Click MyPage.getValues(3) : "Hello";
Is producing the desired results:
@Test
public void myTestCaseSample() {
(new actions.Click(MyOtherPage.element)).exec() ;
(new actions.Fill(MyPage.text)).exec();
(new actions.Fill()).exec();
(new actions.Click(MyPage.getValues(3))).exec();
}
So things are working fine. I will continue working on it as soon as I get time. This is an open source UI testing DSL that I am writing, and I can put real efforts only on weekends.
Thank you a lot Christian for all the help and the patience.
|
|
|
Goto Forum:
Current Time: Thu Dec 05 23:32:14 GMT 2024
Powered by FUDForum. Page generated in 0.05058 seconds
|