/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.properties.editor.test;
import java.util.ArrayList;
import java.util.regex.Pattern;
import org.springframework.ide.eclipse.editor.support.util.DocumentUtil;
import org.springframework.ide.eclipse.editor.support.yaml.path.YamlPath;
import org.springframework.ide.eclipse.editor.support.yaml.path.YamlPathSegment;
import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureParser;
import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureParser.SChildBearingNode;
import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureParser.SDocNode;
import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureParser.SKeyNode;
import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureParser.SNode;
import org.springframework.ide.eclipse.editor.support.yaml.structure.YamlStructureParser.SRootNode;
public class YamlStructureParserTest extends ApplicationYamlEditorTestHarness {
public void testSimple() throws Exception {
MockYamlEditor editor = new YamlEditor(
"hello:\n"+
" world:\n" +
" message\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): hello:",
" KEY(2): world:",
" RAW(4): message",
" RAW(-1): "
);
}
public void assertParse(MockYamlEditor editor, String... expectDumpLines) throws Exception {
StringBuilder expected = new StringBuilder();
for (String line : expectDumpLines) {
expected.append(line);
expected.append("\n");
}
assertEquals(expected.toString().trim(), editor.parseStructure().toString().trim());
}
public void assertParseOneDoc(MockYamlEditor editor, String... expectDumpLines) throws Exception {
StringBuilder expected = new StringBuilder();
for (String line : expectDumpLines) {
expected.append(line);
expected.append("\n");
}
assertEquals(expected.toString().trim(), getOnlyDocument(editor.parseStructure()).toString().trim());
}
public void testComments() throws Exception {
MockYamlEditor editor = new YamlEditor(
"#A comment\n" +
"hello:\n"+
" #Another comment\n" +
" world:\n" +
" message\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" RAW(-1): #A comment",
" KEY(0): hello:",
" RAW(-1): #Another comment",
" KEY(2): world:",
" RAW(4): message",
" RAW(-1): "
);
}
public void testSiblings() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): world:",
" KEY(2): europe:",
" KEY(4): france:",
" RAW(6): cheese",
" KEY(4): belgium:",
" RAW(4): beer",
" KEY(2): canada:",
" KEY(4): montreal: poutine",
" KEY(4): vancouver:",
" RAW(6): salmon",
" KEY(0): moon:",
" KEY(2): moonbase-alfa:",
" RAW(4): moonstone",
" RAW(-1): "
);
}
public void testMultiDocs() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
"---\n"+
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"---\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n" +
"...\n"
);
assertParse(editor,
"ROOT(0): ",
" DOC(0): ",
" KEY(0): world:",
" KEY(2): europe:",
" KEY(4): france:",
" RAW(6): cheese",
" KEY(4): belgium:",
" RAW(4): beer",
" DOC(0): ---",
" KEY(2): canada:",
" KEY(4): montreal: poutine",
" KEY(4): vancouver:",
" RAW(6): salmon",
" DOC(0): ---",
" KEY(0): moon:",
" KEY(2): moonbase-alfa:",
" RAW(4): moonstone",
" DOC(0): ...",
" RAW(-1): "
);
}
public void testSequenceBasic() throws Exception {
MockYamlEditor editor;
//Sequence at root level
editor = new YamlEditor(
"- foo\n" +
"- bar\n" +
"- zor"
);
assertParseOneDoc(editor,
"DOC(0): ",
" SEQ(0): - foo",
" SEQ(0): - bar",
" SEQ(0): - zor"
);
//Sequences nested in map without indent
editor = new YamlEditor(
"something:\n" +
"- foo\n" +
"- bar\n" +
"- zor\n" +
"else:\n" +
"- a\n" +
"- def"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): something:" ,
" SEQ(0): - foo",
" SEQ(0): - bar",
" SEQ(0): - zor",
" KEY(0): else:",
" SEQ(0): - a",
" SEQ(0): - def"
);
//Sequences nested in map without indent
editor = new YamlEditor(
"higher:\n" +
" something:\n" +
" - foo\n" +
" - bar\n" +
" - zor\n" +
" else:\n" +
" - a\n" +
" - def"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): higher:",
" KEY(2): something:" ,
" SEQ(2): - foo",
" SEQ(2): - bar",
" SEQ(2): - zor",
" KEY(2): else:",
" SEQ(2): - a",
" SEQ(2): - def"
);
//Sequences nested in map with indent
editor = new YamlEditor(
"something:\n" +
" - foo\n" +
" - bar\n" +
" - zor\n" +
"else:\n" +
" - a\n" +
" - def"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): something:" ,
" SEQ(2): - foo",
" SEQ(2): - bar",
" SEQ(2): - zor",
" KEY(0): else:",
" SEQ(2): - a",
" SEQ(2): - def"
);
}
public void testKeyWithADot() throws Exception {
MockYamlEditor editor;
//First try without a '.'
editor = new YamlEditor(
"logging:\n" +
" level:\n" +
" somepackage: "
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): logging:",
" KEY(2): level:",
" KEY(4): somepackage:"
);
editor = new YamlEditor(
"logging:\n" +
" level:\n" +
" some.package: "
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): logging:",
" KEY(2): level:",
" KEY(4): some.package:"
);
}
public void testSequenceWithNestedSequence() throws Exception {
MockYamlEditor editor;
editor = new YamlEditor(
"- - a\n" +
" - b\n" +
"- - c\n" +
" - d\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" SEQ(0): - - a",
" SEQ(2): - a",
" SEQ(2): - b",
" SEQ(0): - - c",
" SEQ(2): - c",
" SEQ(2): - d",
" RAW(-1): "
);
editor = new YamlEditor(
"foo:\n" +
"- - a\n" +
" - b\n" +
"- - c\n" +
" - d"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): foo:",
" SEQ(0): - - a",
" SEQ(2): - a",
" SEQ(2): - b",
" SEQ(0): - - c",
" SEQ(2): - c",
" SEQ(2): - d"
);
editor = new YamlEditor(
"foo:\n" +
"- - a\n" +
" - b\n" +
"bar:\n" +
"- - c\n" +
" - d\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): foo:",
" SEQ(0): - - a",
" SEQ(2): - a",
" SEQ(2): - b",
" KEY(0): bar:",
" SEQ(0): - - c",
" SEQ(2): - c",
" SEQ(2): - d",
" RAW(-1): "
);
editor = new YamlEditor(
"foo:\n" +
"- \n" +
" - a\n" +
" - b\n" +
"-\n" +
" - c\n" +
" - d"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): foo:",
" SEQ(0): - ",
" SEQ(2): - a",
" SEQ(2): - b",
" SEQ(0): -",
" SEQ(2): - c",
" SEQ(2): - d"
);
editor = new YamlEditor(
"foo:\n" +
"- - - - a\n" +
" - b\n" +
" - c\n" +
" - d\n" +
"- e\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): foo:",
" SEQ(0): - - - - a",
" SEQ(2): - - - a",
" SEQ(4): - - a",
" SEQ(6): - a",
" SEQ(6): - b",
" SEQ(4): - c",
" SEQ(2): - d",
" SEQ(0): - e",
" RAW(-1): "
);
editor = new YamlEditor(
"foo:\n" +
"- - - - a\n" +
" - c\n" +
"- e\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): foo:",
" SEQ(0): - - - - a",
" SEQ(2): - - - a",
" SEQ(4): - - a",
" SEQ(6): - a",
" SEQ(4): - c",
" SEQ(0): - e",
" RAW(-1): "
);
}
public void testSequenceWithNestedMap() throws Exception {
MockYamlEditor editor;
// A map nested in a sequence may start on the same line
editor = new YamlEditor(
"- foo: is foo\n" +
" bar: is bar\n" +
" junk\n" +
"- a: aaa\n" +
" b: bbb\n"
);
assertParseOneDoc(editor,
"DOC(0): ",
" SEQ(0): - foo: is foo",
" KEY(2): foo: is foo",
" KEY(2): bar: is bar",
" RAW(4): junk",
" SEQ(0): - a: aaa",
" KEY(2): a: aaa",
" KEY(2): b: bbb",
" RAW(-1): "
);
//A map nested in a sequence may start on a new line
editor = new YamlEditor(
"-\n"+ //without space
" foo: is foo\n" +
" bar: is bar\n" +
" junk\n" +
"- \n" + //with space
" a: aaa\n" +
" b: bbb"
);
assertParseOneDoc(editor,
"DOC(0): ",
" SEQ(0): -",
" KEY(2): foo: is foo",
" KEY(2): bar: is bar",
" RAW(4): junk",
" SEQ(0): - ", //with space
" KEY(2): a: aaa",
" KEY(2): b: bbb"
);
editor = new YamlEditor(
"foo:\n" +
"-\n"+ //without space
" foo: is foo\n" +
" bar: is bar\n" +
" junk\n" +
"- \n" + //with space
" a: aaa\n" +
" b: bbb"
);
assertParseOneDoc(editor,
"DOC(0): ",
" KEY(0): foo:",
" SEQ(0): -",
" KEY(2): foo: is foo",
" KEY(2): bar: is bar",
" RAW(4): junk",
" SEQ(0): - ", //with space
" KEY(2): a: aaa",
" KEY(2): b: bbb"
);
}
public void testTraverseSeq() throws Exception {
MockYamlEditor editor = new YamlEditor(
"foo:\n" +
"- - - - a\n" +
" - c\n" +
"- e"
);
SRootNode root = editor.parseStructure();
YamlPath path;
path = pathWith(0, "foo", 0, 0, 0, 0);
assertEquals(
"SEQ(6): - a\n",
path.traverse((SNode)root).toString());
path = pathWith(0, "foo", -1);
assertNull(path.traverse((SNode)root));
path = pathWith(0, "foo", 1);
assertEquals(
"SEQ(0): - e\n",
path.traverse((SNode)root).toString());
path = pathWith(0, "foo", 2);
assertNull(path.traverse((SNode)root));
}
public void testFindAndTraverseSeqNode() throws Exception {
MockYamlEditor editor;
editor = new YamlEditor(
"foo:\n"+
"- abc\n" +
"- def\n" +
"- ghi\n"
);
findAndTraversPathPath(editor, "abc");
findAndTraversPathPath(editor, "def");
findAndTraversPathPath(editor, "ghi");
// nodes are position sensitive make sure that generated positions agree
// with traverse interpretation, even in case where it is not so well-defined
// how the indices should be interpreted:
editor = new YamlEditor(
"foo:\n"+
" garbage\n" +
" - abc\n" +
" junk\n" +
" - def\n" +
" crap\n" +
" - ghi\n"
);
findAndTraversPathPath(editor, "abc");
findAndTraversPathPath(editor, "def");
findAndTraversPathPath(editor, "ghi");
}
private void findAndTraversPathPath(MockYamlEditor editor, String snippet) throws Exception {
SRootNode root = editor.parseStructure();
SNode node = root.find(editor.startOf(snippet));
assertNotNull(node);
YamlPath path = node.getPath();
SNode actualNode = path.traverse((SNode)root);
assertEquals(node, actualNode);
}
public void testTraverseSeqKey() throws Exception {
MockYamlEditor editor = new YamlEditor(
"foo:\n" +
"- bar:\n" +
" - a\n" +
" - key: lol\n" +
"- e\n"
);
SRootNode root = editor.parseStructure();
YamlPath path;
path = pathWith(0, "foo", 0, "bar", 1, "key");
assertEquals(
"KEY(4): key: lol\n",
path.traverse((SNode)root).toString());
}
public void testTreeEnd() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
SRootNode root = editor.parseStructure();
SNode node = getNodeAtPath(root, 0, 0, 1);
assertTreeText(editor, node,
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n"
);
node = getNodeAtPath(root, 0, 0, 0, 1, 0);
assertTreeText(editor, node,
"beer"
);
}
public void testTreeEndKeyNodeNoChildren() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
SRootNode root = editor.parseStructure();
SNode node = getNodeAtPath(root, 0, 0, 0);
assertTreeText(editor, node,
" europe:"
);
}
public void testFind() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
SRootNode root = editor.parseStructure();
assertFind(editor, root, "world:", 0, 0);
assertFind(editor, root, "europe:", 0, 0, 0);
assertFind(editor, root, "france:", 0, 0, 0, 0);
assertFind(editor, root, "cheese", 0, 0, 0, 0, 0);
assertFind(editor, root, "belgium:", 0, 0, 0, 1);
assertFind(editor, root, "beer", 0, 0, 0, 1, 0);
assertFind(editor, root, "canada:", 0, 0, 1);
assertFind(editor, root, "montreal: poutine", 0, 0, 1, 0);
assertFind(editor, root, "vancouver:", 0, 0, 1, 1);
assertFind(editor, root, "salmon", 0, 0, 1, 1, 0);
assertFind(editor, root, "moon:", 0, 1);
assertFind(editor, root, "moonbase-alfa:", 0, 1, 0);
assertFind(editor, root, "moonstone", 0, 1, 0, 0);
assertFindStart(editor, root, " europe:", 0, 0);
}
public void testFindInMultiDoc() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
"---\n" +
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"---\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n" +
"..."
);
SRootNode root = editor.parseStructure();
assertFind(editor, root, "world:", 0, 0);
assertFind(editor, root, "europe:", 0, 0, 0);
assertFind(editor, root, "france:", 0, 0, 0, 0);
assertFind(editor, root, "cheese", 0, 0, 0, 0, 0);
assertFind(editor, root, "belgium:", 0, 0, 0, 1);
assertFind(editor, root, "beer", 0, 0, 0, 1, 0);
assertFind(editor, root, "canada:", 1, 0);
assertFind(editor, root, "montreal: poutine", 1, 0, 0);
assertFind(editor, root, "vancouver:", 1, 0, 1);
assertFind(editor, root, "salmon", 1, 0, 1, 0);
assertFind(editor, root, "moon:", 2, 0);
assertFind(editor, root, "moonbase-alfa:", 2, 0, 0);
assertFind(editor, root, "moonstone", 2, 0, 0, 0);
assertFindStart(editor, root, " canada:", 1);
}
public void testFindInSequence() throws Exception {
MockYamlEditor editor = new YamlEditor(
"foo:\n" +
"- alchemy\n" +
"- bistro\n" +
"bar:\n" +
"- - - nice: text\n"+
"zor:\n" +
" - - - very: good\n" +
"end: END"
);
SRootNode root = editor.parseStructure();
assertFind (editor, root, "foo:", 0, 0);
assertFind (editor, root, "- alchemy", 0, 0, 0);
assertFind (editor, root, "- bistro", 0, 0, 1);
assertFind (editor, root, "bar:", 0, 1);
assertFindStart(editor, root, "- - - nice: text", 0, 1, 0);
assertFindStart(editor, root, " - - nice: text", 0, 1, 0);
assertFindStart(editor, root, "- - nice: text", 0, 1, 0, 0);
assertFindStart(editor, root, " - nice: text", 0, 1, 0, 0);
assertFindStart(editor, root, "- nice: text", 0, 1, 0, 0, 0);
assertFindStart(editor, root, " nice: text", 0, 1, 0, 0, 0);
assertFind (editor, root, "nice: text", 0, 1, 0, 0, 0, 0);
assertFind (editor, root, "zor:", 0, 2);
assertFindStart(editor, root, " - - - very: good", 0, 2);
assertFindStart(editor, root, " - - - very: good", 0, 2);
assertFindStart(editor, root, "- - - very: good", 0, 2, 0);
assertFindStart(editor, root, " - - very: good", 0, 2, 0);
assertFindStart(editor, root, "- - very: good", 0, 2, 0, 0);
assertFindStart(editor, root, " - very: good", 0, 2, 0, 0);
assertFindStart(editor, root, "- very: good", 0, 2, 0, 0, 0);
assertFindStart(editor, root, " very: good", 0, 2, 0, 0, 0);
assertFind (editor, root, "very: good", 0, 2, 0, 0, 0, 0);
}
public void testGetKey() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
SRootNode root = editor.parseStructure();
assertKey(editor, root, "world:", "world");
assertKey(editor, root, "europe:", "europe");
assertKey(editor, root, "montreal: poutine", "montreal");
}
public void testIsInValue() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"foo:\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
SRootNode root = editor.parseStructure();
assertValueRange(editor, root, "montreal: poutine", " poutine");
assertValueRange(editor, root, "europe:", "\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer");
assertValueRange(editor, root, "foo:", null);
}
private void assertValueRange(MockEditor editor, SRootNode root, String nodeText, String expectedValue) throws Exception {
int start = editor.getText().indexOf(nodeText);
SKeyNode node = (SKeyNode) root.find(start);
int valueRangeStart;
int valueRangeEnd;
if (expectedValue==null) {
valueRangeStart = valueRangeEnd = start+nodeText.length();
} else {
valueRangeStart = editor.getRawText().lastIndexOf(expectedValue);
valueRangeEnd = valueRangeStart+expectedValue.length();
assertEquals(expectedValue, editor.textBetween(valueRangeStart, valueRangeEnd));
}
assertTrue(node.isInValue(valueRangeStart));
assertFalse(node.isInValue(valueRangeStart-1));
assertTrue(node.isInValue(valueRangeEnd));
assertFalse(node.isInValue(valueRangeEnd+1));
}
public void testTraverse() throws Exception {
MockYamlEditor editor = new YamlEditor(
"world:\n" +
" europe:\n" +
" france:\n" +
" cheese\n" +
" belgium:\n" +
" beer\n" + //At same level as key, technically this is a syntax error but we tolerate it
" canada:\n" +
" montreal: poutine\n" +
" vancouver:\n" +
" salmon\n" +
"moon:\n" +
" moonbase-alfa:\n" +
" moonstone\n"
);
SRootNode root = editor.parseStructure();
YamlPath pathToFrance = pathWith(
0, "world", "europe", "france"
);
assertEquals(
"KEY(4): france:\n"+
" RAW(6): cheese\n",
pathToFrance.traverse((SNode)root).toString());
assertNull(pathWith(0, "world", "europe", "bogus").traverse((SNode)root));
}
public void testGetFirstRealChild() throws Exception {
MockYamlEditor editor = new YamlEditor(
"no-children:\n" +
"unreal-children:\n" +
" #Unreal\n" +
"\n" +
" #comment only\n" +
"real-child:\n" +
" abc\n" +
"mixed-children:\n" +
"\n" +
"#comment\n" +
" def"
);
assertFirstRealChild(editor, "no-children", null);
assertFirstRealChild(editor, "unreal-children", null);
assertFirstRealChild(editor, "real-child", "abc");
assertFirstRealChild(editor, "mixed-children", "def");
}
public void testDocumentSeparatorRegexp() throws Exception {
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "---");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "...");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "--- ");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "... ");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "--- #The next doc starts here");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "... #The previous doc ends here");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "---#The next doc starts here");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "...#The previous doc ends here");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "---#");
assertMatch(YamlStructureParser.DOCUMENT_SEPERATOR, "...#");
}
private void assertMatch(Pattern pat, String string) {
assertTrue("Doesn't match: '"+string+"'", pat.matcher(string).matches());
}
private void assertFirstRealChild(MockYamlEditor editor, String testNodeName, String expectedNodeSnippet) throws Exception {
SDocNode doc = getOnlyDocument(editor.parseStructure());
SKeyNode testNode = doc.getChildWithKey(testNodeName);
assertNotNull(testNode);
SNode expected = null;
if (expectedNodeSnippet!=null) {
int offset = editor.getRawText().indexOf(expectedNodeSnippet);
expected = doc.find(offset);
assertTrue(editor.textUnder(expected).contains(expectedNodeSnippet));
}
assertEquals(expected, testNode.getFirstRealChild());
}
private SDocNode getOnlyDocument(SRootNode root) {
assertEquals(1, root.getChildren().size());
return (SDocNode) root.getChildren().get(0);
}
private YamlPath pathWith(Object... keysOrIndexes) {
ArrayList<YamlPathSegment> segments = new ArrayList<YamlPathSegment>();
for (Object keyOrIndex : keysOrIndexes) {
if (keyOrIndex instanceof String) {
segments.add(YamlPathSegment.valueAt((String)keyOrIndex));
} else if (keyOrIndex instanceof Integer) {
segments.add(YamlPathSegment.valueAt((Integer)keyOrIndex));
} else {
fail("Unknown type of path element: "+keyOrIndex);
}
}
return new YamlPath(segments);
}
private void assertKey(MockEditor editor, SRootNode root, String nodeText, String expectedKey) throws Exception {
int start = editor.getText().indexOf(nodeText);
SKeyNode node = (SKeyNode) root.find(start);
String key = node.getKey();
assertEquals(expectedKey, key);
//test the key range as well
int startOfKeyRange = node.getStart();
int keyRangeLen = key.length();
int endOfKeyRange = startOfKeyRange + keyRangeLen;
assertTrue(node.isInKey(startOfKeyRange));
assertFalse(node.isInKey(startOfKeyRange-1));
assertTrue(node.isInKey(endOfKeyRange));
assertFalse(node.isInKey(endOfKeyRange+1));
}
private void assertFind(MockEditor editor, SRootNode root, String snippet, int... expectPath) {
int start = editor.getRawText().indexOf(snippet);
int end = start+snippet.length();
int middle = (start+end) / 2;
SNode expectNode = getNodeAtPath(root, expectPath);
assertEquals(expectNode, root.find(start));
assertEquals(expectNode, root.find(middle));
assertEquals(expectNode, root.find(end));
}
private void assertFindStart(MockEditor editor, SRootNode root, String snippet, int... expectPath) {
int start = editor.getRawText().indexOf(snippet);
SNode expectNode = getNodeAtPath(root, expectPath);
assertEquals(expectNode, root.find(start));
}
private void assertTreeText(MockEditor editor, SNode node, String expected) throws Exception {
String actual = editor.textBetween(node.getStart(), node.getTreeEnd());
assertEquals(expected.trim(), actual.trim());
}
private SNode getNodeAtPath(SNode node, int... childindices) {
int i = 0;
while (i<childindices.length) {
int child = childindices[i];
node = ((SChildBearingNode)node).getChildren().get(child);
i++;
}
return node;
}
}