TestPlacementConstraintParser.java
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.yarn.api.resource;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.hadoop.util.Sets;
import org.apache.hadoop.yarn.api.records.NodeAttributeOpCode;
import org.apache.hadoop.yarn.api.resource.PlacementConstraint.AbstractConstraint;
import org.apache.hadoop.yarn.api.resource.PlacementConstraint.And;
import org.apache.hadoop.yarn.api.resource.PlacementConstraint.Or;
import org.apache.hadoop.yarn.api.resource.PlacementConstraint.SingleConstraint;
import org.apache.hadoop.yarn.api.resource.PlacementConstraint.TargetExpression;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParseException;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.SourceTags;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.TargetConstraintParser;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.ConstraintParser;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.CardinalityConstraintParser;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.ConjunctionConstraintParser;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.MultipleConstraintsTokenizer;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.SourceTagsTokenizer;
import org.apache.hadoop.yarn.util.constraint.PlacementConstraintParser.ConstraintTokenizer;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.PlacementTargets.allocationTag;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.and;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.or;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.PlacementTargets;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.targetIn;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.targetNodeAttribute;
import static org.apache.hadoop.yarn.api.resource.PlacementConstraints.targetNotIn;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
/**
* Class to test placement constraint parser.
*/
class TestPlacementConstraintParser {
@Test
void testTargetExpressionParser() throws PlacementConstraintParseException {
String expressionStr;
ConstraintParser parser;
AbstractConstraint constraint;
SingleConstraint single;
// Anti-affinity with single target tag
// NOTIN,NODE,foo
expressionStr = "NOTIN, NODE, foo";
parser = new TargetConstraintParser(expressionStr);
constraint = parser.parse();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals("node", single.getScope());
assertEquals(0, single.getMinCardinality());
assertEquals(0, single.getMaxCardinality());
verifyConstraintToString(expressionStr, constraint);
// lower cases is also valid
expressionStr = "notin, node, foo";
parser = new TargetConstraintParser(expressionStr);
constraint = parser.parse();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals("node", single.getScope());
assertEquals(0, single.getMinCardinality());
assertEquals(0, single.getMaxCardinality());
verifyConstraintToString(expressionStr, constraint);
// Affinity with single target tag
// IN,NODE,foo
expressionStr = "IN, NODE, foo";
parser = new TargetConstraintParser(expressionStr);
constraint = parser.parse();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals("node", single.getScope());
assertEquals(1, single.getMinCardinality());
assertEquals(Integer.MAX_VALUE, single.getMaxCardinality());
verifyConstraintToString(expressionStr, constraint);
// Anti-affinity with multiple target tags
// NOTIN,NDOE,foo,bar,exp
expressionStr = "NOTIN, NODE, foo, bar, exp";
parser = new TargetConstraintParser(expressionStr);
constraint = parser.parse();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals("node", single.getScope());
assertEquals(0, single.getMinCardinality());
assertEquals(0, single.getMaxCardinality());
assertEquals(3, single.getTargetExpressions().size());
Set<TargetExpression> expectedTargetExpressions = Sets.newHashSet(
PlacementTargets.allocationTag("foo"),
PlacementTargets.allocationTag("bar"),
PlacementTargets.allocationTag("exp"));
assertTrue(Sets.difference(expectedTargetExpressions,
single.getTargetExpressions()).isEmpty());
verifyConstraintToString(expressionStr, constraint);
// Invalid OP
parser = new TargetConstraintParser("XYZ, NODE, foo");
try {
parser.parse();
} catch (Exception e) {
assertTrue(e instanceof PlacementConstraintParseException);
assertTrue(e.getMessage().contains("expecting in or notin"));
}
}
@Test
void testCardinalityConstraintParser()
throws PlacementConstraintParseException {
String expressionExpr;
ConstraintParser parser;
AbstractConstraint constraint;
SingleConstraint single;
// cardinality,NODE,foo,0,1
expressionExpr = "cardinality, NODE, foo, 0, 1";
parser = new CardinalityConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals("node", single.getScope());
assertEquals(0, single.getMinCardinality());
assertEquals(1, single.getMaxCardinality());
assertEquals(1, single.getTargetExpressions().size());
TargetExpression exp =
single.getTargetExpressions().iterator().next();
assertEquals("ALLOCATION_TAG", exp.getTargetType().toString());
assertEquals(1, exp.getTargetValues().size());
assertEquals("foo", exp.getTargetValues().iterator().next());
verifyConstraintToString(expressionExpr, constraint);
// cardinality,NODE,foo,bar,moo,0,1
expressionExpr = "cardinality,RACK,foo,bar,moo,0,1";
parser = new CardinalityConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals("rack", single.getScope());
assertEquals(0, single.getMinCardinality());
assertEquals(1, single.getMaxCardinality());
assertEquals(3, single.getTargetExpressions().size());
Set<TargetExpression> expectedTargetExpressions = Sets.newHashSet(
PlacementTargets.allocationTag("foo"),
PlacementTargets.allocationTag("bar"),
PlacementTargets.allocationTag("moo"));
assertTrue(Sets.difference(expectedTargetExpressions,
single.getTargetExpressions()).isEmpty());
verifyConstraintToString(expressionExpr, constraint);
// Invalid scope string
try {
parser = new CardinalityConstraintParser(
"cardinality,NOWHERE,foo,bar,moo,0,1");
parser.parse();
fail("Expecting a parsing failure!");
} catch (PlacementConstraintParseException e) {
assertTrue(e.getMessage()
.contains("expecting scope to node or rack, but met NOWHERE"));
}
// Invalid number of expression elements
try {
parser = new CardinalityConstraintParser(
"cardinality,NODE,0,1");
parser.parse();
fail("Expecting a parsing failure!");
} catch (PlacementConstraintParseException e) {
assertTrue(e.getMessage()
.contains("at least 5 elements, but only 4 is given"));
}
}
@Test
void testAndConstraintParser()
throws PlacementConstraintParseException {
String expressionExpr;
ConstraintParser parser;
AbstractConstraint constraint;
And and;
expressionExpr = "AND(NOTIN,NODE,foo:NOTIN,NODE,bar)";
parser = new ConjunctionConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof And);
and = (And) constraint;
assertEquals(2, and.getChildren().size());
verifyConstraintToString(expressionExpr, constraint);
expressionExpr = "AND(NOTIN,NODE,foo:cardinality,NODE,foo,0,1)";
parser = new ConjunctionConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof And);
assertEquals(2, and.getChildren().size());
verifyConstraintToString(expressionExpr, constraint);
expressionExpr =
"AND(NOTIN,NODE,foo:AND(NOTIN,NODE,foo:cardinality,NODE,foo,0,1))";
parser = new ConjunctionConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof And);
and = (And) constraint;
assertTrue(and.getChildren().get(0) instanceof SingleConstraint);
assertTrue(and.getChildren().get(1) instanceof And);
and = (And) and.getChildren().get(1);
assertEquals(2, and.getChildren().size());
verifyConstraintToString(expressionExpr, constraint);
}
@Test
void testOrConstraintParser()
throws PlacementConstraintParseException {
String expressionExpr;
ConstraintParser parser;
AbstractConstraint constraint;
Or or;
expressionExpr = "OR(NOTIN,NODE,foo:NOTIN,NODE,bar)";
parser = new ConjunctionConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof Or);
or = (Or) constraint;
assertEquals(2, or.getChildren().size());
verifyConstraintToString(expressionExpr, constraint);
expressionExpr = "OR(NOTIN,NODE,foo:cardinality,NODE,foo,0,1)";
parser = new ConjunctionConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof Or);
assertEquals(2, or.getChildren().size());
verifyConstraintToString(expressionExpr, constraint);
expressionExpr =
"OR(NOTIN,NODE,foo:OR(NOTIN,NODE,foo:cardinality,NODE,foo,0,1))";
parser = new ConjunctionConstraintParser(expressionExpr);
constraint = parser.parse();
assertTrue(constraint instanceof Or);
or = (Or) constraint;
assertTrue(or.getChildren().get(0) instanceof SingleConstraint);
assertTrue(or.getChildren().get(1) instanceof Or);
or = (Or) or.getChildren().get(1);
assertEquals(2, or.getChildren().size());
verifyConstraintToString(expressionExpr, constraint);
}
@Test
void testMultipleConstraintsTokenizer()
throws PlacementConstraintParseException {
MultipleConstraintsTokenizer ct;
SourceTagsTokenizer st;
TokenizerTester mp;
ct = new MultipleConstraintsTokenizer(
"foo(1),A1,A2,A3:bar(2),B1,B2:moo(3),C1,C2");
mp = new TokenizerTester(ct,
"foo(1),A1,A2,A3", "bar(2),B1,B2", "moo(3),C1,C2");
mp.verify();
ct = new MultipleConstraintsTokenizer(
"foo(1),AND(A2:A3):bar(2),OR(B1:AND(B2:B3)):moo(3),C1,C2");
mp = new TokenizerTester(ct,
"foo(1),AND(A2:A3)", "bar(2),OR(B1:AND(B2:B3))", "moo(3),C1,C2");
mp.verify();
ct = new MultipleConstraintsTokenizer("A:B:C");
mp = new TokenizerTester(ct, "A", "B", "C");
mp.verify();
ct = new MultipleConstraintsTokenizer("A:AND(B:C):D");
mp = new TokenizerTester(ct, "A", "AND(B:C)", "D");
mp.verify();
ct = new MultipleConstraintsTokenizer("A:AND(B:OR(C:D)):E");
mp = new TokenizerTester(ct, "A", "AND(B:OR(C:D))", "E");
mp.verify();
ct = new MultipleConstraintsTokenizer("A:AND(B:OR(C:D)):E");
mp = new TokenizerTester(ct, "A", "AND(B:OR(C:D))", "E");
mp.verify();
st = new SourceTagsTokenizer("A(4)");
mp = new TokenizerTester(st, "A", "4");
mp.verify();
try {
st = new SourceTagsTokenizer("A(B)");
mp = new TokenizerTester(st, "A", "B");
mp.verify();
fail("Expecting a parsing failure");
} catch (PlacementConstraintParseException e) {
assertTrue(e.getMessage()
.contains("Value of the expression must be an integer"));
}
}
private static class TokenizerTester {
private ConstraintTokenizer tokenizer;
private String[] expectedExtractions;
protected TokenizerTester(ConstraintTokenizer tk,
String... expctedStrings) {
this.tokenizer = tk;
this.expectedExtractions = expctedStrings;
}
void verify()
throws PlacementConstraintParseException {
tokenizer.validate();
int i = 0;
while (tokenizer.hasMoreElements()) {
String current = tokenizer.nextElement();
assertTrue(i < expectedExtractions.length);
assertEquals(expectedExtractions[i], current);
i++;
}
}
}
@Test
void testParsePlacementSpec()
throws PlacementConstraintParseException {
Map<SourceTags, PlacementConstraint> result;
PlacementConstraint expectedPc1, expectedPc2;
PlacementConstraint actualPc1, actualPc2;
SourceTags tag1, tag2;
// Only Source Tag without constraint
result = PlacementConstraintParser
.parsePlacementSpec("foo(3)");
assertEquals(1, result.size());
tag1 = result.keySet().iterator().next();
assertEquals("foo", tag1.getTag());
assertEquals(3, tag1.getNumOfAllocations());
expectedPc1 = null;
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1);
// A single anti-affinity constraint
result = PlacementConstraintParser
.parsePlacementSpec("foo(3),notin,node,foo");
assertEquals(1, result.size());
tag1 = result.keySet().iterator().next();
assertEquals("foo", tag1.getTag());
assertEquals(3, tag1.getNumOfAllocations());
expectedPc1 = targetNotIn("node", allocationTag("foo")).build();
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1);
// Upper case
result = PlacementConstraintParser
.parsePlacementSpec("foo(3),NOTIN,NODE,foo");
assertEquals(1, result.size());
tag1 = result.keySet().iterator().next();
assertEquals("foo", tag1.getTag());
assertEquals(3, tag1.getNumOfAllocations());
expectedPc1 = targetNotIn("node", allocationTag("foo")).build();
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1);
// A single cardinality constraint
result = PlacementConstraintParser
.parsePlacementSpec("foo(10),cardinality,node,foo,bar,0,100");
assertEquals(1, result.size());
tag1 = result.keySet().iterator().next();
assertEquals("foo", tag1.getTag());
assertEquals(10, tag1.getNumOfAllocations());
TargetExpression[] targetExpressions = new TargetExpression[] {
PlacementTargets.allocationTag("foo"),
PlacementTargets.allocationTag("bar")};
expectedPc1 = PlacementConstraints.targetCardinality("node", 0,
100, targetExpressions).build();
assertEquals(expectedPc1, result.values().iterator().next());
// Two constraint expressions
result = PlacementConstraintParser
.parsePlacementSpec("foo(3),notin,node,foo:bar(2),in,node,foo");
assertEquals(2, result.size());
Iterator<SourceTags> keyIt = result.keySet().iterator();
tag1 = keyIt.next();
assertEquals("foo", tag1.getTag());
assertEquals(3, tag1.getNumOfAllocations());
tag2 = keyIt.next();
assertEquals("bar", tag2.getTag());
assertEquals(2, tag2.getNumOfAllocations());
Iterator<PlacementConstraint> valueIt = result.values().iterator();
expectedPc1 = targetNotIn("node", allocationTag("foo")).build();
expectedPc2 = targetIn("node", allocationTag("foo")).build();
assertEquals(expectedPc1, valueIt.next());
assertEquals(expectedPc2, valueIt.next());
// And constraint
result = PlacementConstraintParser
.parsePlacementSpec("foo(1000),and(notin,node,bar:in,node,foo)");
assertEquals(1, result.size());
keyIt = result.keySet().iterator();
tag1 = keyIt.next();
assertEquals("foo", tag1.getTag());
assertEquals(1000, tag1.getNumOfAllocations());
actualPc1 = result.values().iterator().next();
expectedPc1 = and(targetNotIn("node", allocationTag("bar")),
targetIn("node", allocationTag("foo"))).build();
assertEquals(expectedPc1, actualPc1);
// Multiple constraints with nested forms.
result = PlacementConstraintParser.parsePlacementSpec(
"foo(1000),and(notin,node,bar:or(in,node,foo:in,node,moo))"
+ ":bar(200),notin,node,foo");
assertEquals(2, result.size());
keyIt = result.keySet().iterator();
tag1 = keyIt.next();
tag2 = keyIt.next();
assertEquals("foo", tag1.getTag());
assertEquals(1000, tag1.getNumOfAllocations());
assertEquals("bar", tag2.getTag());
assertEquals(200, tag2.getNumOfAllocations());
valueIt = result.values().iterator();
actualPc1 = valueIt.next();
actualPc2 = valueIt.next();
expectedPc1 = and(targetNotIn("node", allocationTag("bar")),
or(targetIn("node", allocationTag("foo")),
targetIn("node", allocationTag("moo")))).build();
assertEquals(actualPc1, expectedPc1);
expectedPc2 = targetNotIn("node", allocationTag("foo")).build();
assertEquals(expectedPc2, actualPc2);
// Failure Cases
String[] invalidSpecs = {"foo(3", "foo),bar", "foobar", "),java=1.7,1.8"};
for (String spec : invalidSpecs) {
try {
result = PlacementConstraintParser.parsePlacementSpec(spec);
fail("Expected a failure!");
} catch (Exception e) {
assertTrue(e instanceof PlacementConstraintParseException);
}
}
}
// We verify the toString result by parsing it again
// instead of raw string comparing. This is because internally
// we are not storing tags strictly to its original order, so
// the toString result might have different ordering with the
// input expression.
private void verifyConstraintToString(String inputExpr,
AbstractConstraint constraint) {
String constrainExpr = constraint.toString();
System.out.println("Input: " + inputExpr
.toLowerCase().replaceAll(" ", ""));
System.out.println("ToString: " + constrainExpr);
try {
PlacementConstraintParser.parseExpression(constrainExpr);
} catch (PlacementConstraintParseException e) {
fail("The parser is unable to parse the expression: "
+ constrainExpr + ", caused by: " + e.getMessage());
}
}
@Test
void testParseNodeAttributeSpec()
throws PlacementConstraintParseException {
Map<SourceTags, PlacementConstraint> result;
PlacementConstraint.AbstractConstraint expectedPc1, expectedPc2;
PlacementConstraint actualPc1, actualPc2;
// A single node attribute constraint
result = PlacementConstraintParser
.parsePlacementSpec("xyz(4),rm.yarn.io/foo=true");
assertEquals(1, result.size());
TargetExpression target = PlacementTargets
.nodeAttribute("rm.yarn.io/foo", "true");
expectedPc1 = targetNodeAttribute("node", NodeAttributeOpCode.EQ, target);
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1.getConstraintExpr());
// A single node attribute constraint
result = PlacementConstraintParser
.parsePlacementSpec("xyz(3),rm.yarn.io/foo!=abc");
assertEquals(1, result.size());
target = PlacementTargets
.nodeAttribute("rm.yarn.io/foo", "abc");
expectedPc1 = targetNodeAttribute("node", NodeAttributeOpCode.NE, target);
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1.getConstraintExpr());
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1.getConstraintExpr());
// A single node attribute constraint
result = PlacementConstraintParser
.parsePlacementSpec(
"xyz(1),rm.yarn.io/foo!=abc:zxy(1),rm.yarn.io/bar=true");
assertEquals(2, result.size());
target = PlacementTargets
.nodeAttribute("rm.yarn.io/foo", "abc");
expectedPc1 = targetNodeAttribute("node", NodeAttributeOpCode.NE, target);
target = PlacementTargets
.nodeAttribute("rm.yarn.io/bar", "true");
expectedPc2 = targetNodeAttribute("node", NodeAttributeOpCode.EQ, target);
Iterator<PlacementConstraint> valueIt = result.values().iterator();
actualPc1 = valueIt.next();
actualPc2 = valueIt.next();
assertEquals(expectedPc1, actualPc1.getConstraintExpr());
assertEquals(expectedPc2, actualPc2.getConstraintExpr());
// A single node attribute constraint w/o source tags
result = PlacementConstraintParser
.parsePlacementSpec("rm.yarn.io/foo=true");
assertEquals(1, result.size());
target = PlacementTargets.nodeAttribute("rm.yarn.io/foo", "true");
expectedPc1 = targetNodeAttribute("node", NodeAttributeOpCode.EQ, target);
SourceTags actualSourceTags = result.keySet().iterator().next();
assertTrue(actualSourceTags.isEmpty());
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1.getConstraintExpr());
// Node Attribute Constraint With Multiple Values
result = PlacementConstraintParser
.parsePlacementSpec("java=1.7,1.8");
assertEquals(1, result.size());
Set<String> constraintEntities = new TreeSet<>();
constraintEntities.add("1.7");
constraintEntities.add("1.8");
target = PlacementConstraints.PlacementTargets.nodeAttribute("java",
constraintEntities.toArray(new String[constraintEntities.size()]));
expectedPc1 = targetNodeAttribute("node", NodeAttributeOpCode.EQ, target);
actualSourceTags = result.keySet().iterator().next();
assertTrue(actualSourceTags.isEmpty());
actualPc1 = result.values().iterator().next();
assertEquals(expectedPc1, actualPc1.getConstraintExpr());
// If source tags is not specified for a node-attribute constraint,
// then this expression must be single constraint expression.
try {
PlacementConstraintParser
.parsePlacementSpec("rm.yarn.io/foo=true:xyz(1),notin,node,xyz");
fail("Expected a failure!");
} catch (Exception e) {
assertTrue(e instanceof PlacementConstraintParseException);
}
}
@Test
public void testParseIllegalExprShouldThrowException() {
// A single node attribute constraint w/o source tags, it should fail when multiple
// attribute kvs are specified.
try {
PlacementConstraintParser.parseExpression("rm.yarn.io/foo=true,rm.yarn.io/bar=true");
fail("Expected a failure!");
} catch (PlacementConstraintParseException e) {
// ignore
}
}
@Test
void testParseAllocationTagNameSpace()
throws PlacementConstraintParseException {
Map<SourceTags, PlacementConstraint> result;
// Constraint with Two Different NameSpaces
result = PlacementConstraintParser
.parsePlacementSpec("foo(2),notin,node,not-self/bar,all/moo");
assertEquals(1, result.size());
Set<TargetExpression> expectedTargetExpressions = Sets.newHashSet(
PlacementTargets.allocationTagWithNamespace("not-self", "bar"),
PlacementTargets.allocationTagWithNamespace("all", "moo"));
AbstractConstraint constraint = result.values().iterator().next().
getConstraintExpr();
assertTrue(constraint instanceof SingleConstraint);
SingleConstraint single = (SingleConstraint) constraint;
assertEquals(2, single.getTargetExpressions().size());
assertTrue(Sets.difference(expectedTargetExpressions,
single.getTargetExpressions()).isEmpty());
// Constraint With Default NameSpace SELF
result = PlacementConstraintParser
.parsePlacementSpec("foo(2),notin,node,moo");
assertEquals(1, result.size());
TargetExpression expectedTargetExpression = PlacementTargets.
allocationTagWithNamespace("self", "moo");
constraint = result.values().iterator().next().getConstraintExpr();
assertTrue(constraint instanceof SingleConstraint);
single = (SingleConstraint) constraint;
assertEquals(1, single.getTargetExpressions().size());
assertEquals(expectedTargetExpression,
single.getTargetExpressions().iterator().next());
// Constraint With Invalid NameSpace
boolean caughtException = false;
try {
result = PlacementConstraintParser
.parsePlacementSpec("foo(2),notin,node,bar/moo");
} catch(PlacementConstraintParseException e) {
caughtException = true;
}
assertTrue(caughtException,
"PlacementConstraintParseException is expected");
}
}