diff --git a/flightrec/decoder/build.gradle b/flightrec/decoder/build.gradle index f1d3cae..d658f5d 100644 --- a/flightrec/decoder/build.gradle +++ b/flightrec/decoder/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'java' +apply plugin: 'groovy' group = 'org.rehlds.flightrec' version = rootProject.version @@ -11,6 +12,7 @@ repositories { } dependencies { + testCompile 'org.codehaus.groovy:groovy-all:2.4.5' testCompile "junit:junit:4.12" compile project(':flightrec/decoder_api') } diff --git a/flightrec/decoder/pub/extDecoders/.keep b/flightrec/decoder/pub/extDecoders/.keep new file mode 100644 index 0000000..e69de29 diff --git a/flightrec/decoder/src/main/java/org/rehlds/flightrec/logtree/FlightLogTreeBuilder.java b/flightrec/decoder/src/main/java/org/rehlds/flightrec/logtree/FlightLogTreeBuilder.java index 9648822..d7332af 100644 --- a/flightrec/decoder/src/main/java/org/rehlds/flightrec/logtree/FlightLogTreeBuilder.java +++ b/flightrec/decoder/src/main/java/org/rehlds/flightrec/logtree/FlightLogTreeBuilder.java @@ -17,7 +17,8 @@ public class FlightLogTreeBuilder { void handleLeaveMessage(FlightrecMessage msg) { if (currentNode == rootNode) { - rootNode = new LogTreeNodeComplex(null, null, msg); + currentNode.leaveMsg = msg; + rootNode = new LogTreeNodeComplex(null, null, null); rootNode.addChild(currentNode); currentNode.setParent(rootNode); currentNode = rootNode; diff --git a/flightrec/decoder/src/main/java/org/rehlds/flightrec/main/FlightRecorder.java b/flightrec/decoder/src/main/java/org/rehlds/flightrec/main/FlightRecorder.java index 231ae8a..f6fd5cc 100644 --- a/flightrec/decoder/src/main/java/org/rehlds/flightrec/main/FlightRecorder.java +++ b/flightrec/decoder/src/main/java/org/rehlds/flightrec/main/FlightRecorder.java @@ -46,17 +46,17 @@ public class FlightRecorder { FileScanResult scanResult = FlightRecFileScanner.scan(f); System.out.println("Dump file scan results: "); - for (HeaderScanResult metaHeader : scanResult.metaHeaders) { - System.out.print("\tMeta header @ 0x" + Long.toHexString(metaHeader.pos) + "; valid: " + (metaHeader.error == null)); - if (metaHeader.error != null) { - System.out.print("; error: " + metaHeader.error); + for (HeaderScanResult hdr : scanResult.metaHeaders) { + System.out.print(String.format("\tMeta header @ 0x%08X; len=%d; version=%d; valid=%s", hdr.pos, hdr.len, hdr.version, "" + (hdr.error == null))); + if (hdr.error != null) { + System.out.print("; error: " + hdr.error); } System.out.println(); } - for (HeaderScanResult dataHeader : scanResult.dataHeaders) { - System.out.print("\tData header @ 0x" + Long.toHexString(dataHeader.pos) + "; valid: " + (dataHeader.error == null)); - if (dataHeader.error != null) { - System.out.print("; error: " + dataHeader.error); + for (HeaderScanResult hdr : scanResult.dataHeaders) { + System.out.print(String.format("\tData header @ 0x%08X; len=%d; version=%d; valid=%s", hdr.pos, hdr.len, hdr.version, "" + (hdr.error == null))); + if (hdr.error != null) { + System.out.print("; error: " + hdr.error); } System.out.println(); } @@ -133,6 +133,7 @@ public class FlightRecorder { return false; } + System.out.println("Read " + messages.size() + " messages from '" + cfg.dumpFile.getAbsolutePath() + "'"); LogTreeNodeComplex treeRootNode = buildTree(messages); if (treeRootNode == null) { return false; diff --git a/flightrec/decoder/src/main/java/org/rehlds/flightrec/textlogwriter/TextLogWriter.java b/flightrec/decoder/src/main/java/org/rehlds/flightrec/textlogwriter/TextLogWriter.java index 4ec3e2b..31effe8 100644 --- a/flightrec/decoder/src/main/java/org/rehlds/flightrec/textlogwriter/TextLogWriter.java +++ b/flightrec/decoder/src/main/java/org/rehlds/flightrec/textlogwriter/TextLogWriter.java @@ -55,7 +55,10 @@ public class TextLogWriter { } String escapeString(String s) { - return s.replace("\"", "\\\"").replace("'", "\\'"); + return s.replace("\"", "\\\"") + .replace("'", "\\'") + .replace("\n", "\\n") + .replace("\r", "\\r"); } String generateIndent() { @@ -144,7 +147,7 @@ public class TextLogWriter { if (node.enterMsg != null) { writeMessage(node.enterMsg); } else { - writer.write(generateIndent() + ">> [Unknown]"); + writer.write(generateIndent() + ">> [Unknown]\n"); } indent++; @@ -154,7 +157,7 @@ public class TextLogWriter { if (node.leaveMsg != null) { writeMessage(node.leaveMsg); } else { - writer.write(generateIndent() + "<< [Unknown]"); + writer.write(generateIndent() + "<< [Unknown]\n"); } } diff --git a/flightrec/decoder/src/test/groovy/org/rehlds/flightrec/logtree/FlightLogTreeBuilderTest.groovy b/flightrec/decoder/src/test/groovy/org/rehlds/flightrec/logtree/FlightLogTreeBuilderTest.groovy new file mode 100644 index 0000000..40edd56 --- /dev/null +++ b/flightrec/decoder/src/test/groovy/org/rehlds/flightrec/logtree/FlightLogTreeBuilderTest.groovy @@ -0,0 +1,181 @@ +package org.rehlds.flightrec.logtree + +import org.junit.Test +import org.rehlds.flightrec.api.EntranceKind +import org.rehlds.flightrec.api.FlightrecMessage +import org.rehlds.flightrec.api.FlightrecMessageType + +class FlightLogTreeBuilderTest { + static final FlightrecMessageType hierarchyMsgType1 = new FlightrecMessageType('test', 'hmsg1', 1, true); + static final FlightrecMessageType hierarchyMsgType2 = new FlightrecMessageType('test', 'hmsg1', 1, true); + static final FlightrecMessageType flatMsgType1 = new FlightrecMessageType('test', 'flatmsg1', 1, true); + static final FlightrecMessageType flatMsgType2 = new FlightrecMessageType('test', 'flatmsg2', 1, true); + + static FlightrecMessage enterMsg(FlightrecMessageType type) { + return new FlightrecMessage(type, EntranceKind.ENTRANCE_ENTER, null, 0, 0); + } + + static FlightrecMessage leaveMsg(FlightrecMessageType type) { + return new FlightrecMessage(type, EntranceKind.ENTRANCE_LEAVE, null, 0, 0); + } + + static FlightrecMessage flatMsg(FlightrecMessageType type) { + return new FlightrecMessage(type, EntranceKind.ENTRANCE_UNUSED, null, 0, 0); + } + + @Test + void 'decode 2 flat messages'() { + def messages = [flatMsg(flatMsgType1), flatMsg(flatMsgType2)] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 2 + assert rootNode.children[0].msg == messages[0]; + assert rootNode.children[1].msg == messages[1]; + + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + } + + @Test + void 'decode 2 empty hierarchy msgs'() { + def messages = [ + enterMsg(hierarchyMsgType1), leaveMsg(hierarchyMsgType1), + enterMsg(hierarchyMsgType2), leaveMsg(hierarchyMsgType2) + ] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 2 + + rootNode.children[0].enterMsg == messages[0] + rootNode.children[0].leaveMsg == messages[1] + assert rootNode.children[0].children.empty + + rootNode.children[1].enterMsg == messages[2] + rootNode.children[1].leaveMsg == messages[3] + assert rootNode.children[1].children.empty + + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + } + + @Test + void 'decode 2 hierarchy messages with flat payload'() { + def messages = [ + enterMsg(hierarchyMsgType1), flatMsg(flatMsgType1), leaveMsg(hierarchyMsgType1), + enterMsg(hierarchyMsgType2), flatMsg(flatMsgType2), leaveMsg(hierarchyMsgType2) + ] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 2 + + rootNode.children[0].enterMsg == messages[0] + rootNode.children[0].leaveMsg == messages[2] + assert rootNode.children[0].children.size() == 1 + assert rootNode.children[0].children[0].msg == messages[1] + + rootNode.children[1].enterMsg == messages[3] + rootNode.children[1].leaveMsg == messages[5] + assert rootNode.children[1].children.size() == 1 + assert rootNode.children[1].children[0].msg == messages[4] + + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + } + + @Test + void 'decode hierarchical message with mixed payload'() { + def messages = [ + flatMsg(flatMsgType2), + enterMsg(hierarchyMsgType1), + flatMsg(flatMsgType1), + enterMsg(hierarchyMsgType2), + flatMsg(flatMsgType2), + leaveMsg(hierarchyMsgType2), + flatMsg(flatMsgType2), + leaveMsg(hierarchyMsgType1), + flatMsg(flatMsgType1) + ] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 3 + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + + assert rootNode.children[0].msg == messages[0] + assert rootNode.children[2].msg == messages[8] + + assert rootNode.children[1].enterMsg == messages[1] + assert rootNode.children[1].leaveMsg == messages[7] + assert rootNode.children[1].children.size() == 3 + + assert rootNode.children[1].children[0].msg == messages[2] + assert rootNode.children[1].children[2].msg == messages[6] + + assert rootNode.children[1].children[1].enterMsg == messages[3] + assert rootNode.children[1].children[1].leaveMsg == messages[5] + assert rootNode.children[1].children[1].children.size() == 1 + + assert rootNode.children[1].children[1].children[0].msg == messages[4] + } + + @Test + void 'decode hierarchical msg with flat payload and missing start'() { + def messages = [ + flatMsg(flatMsgType1), + leaveMsg(hierarchyMsgType1), + flatMsg(flatMsgType2) + ] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 2 + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + + assert rootNode.children[0].enterMsg == null + assert rootNode.children[0].leaveMsg == messages[1] + assert rootNode.children[0].children.size() == 1 + assert rootNode.children[0].children[0].msg == messages[0] + + assert rootNode.children[1].msg == messages[2] + } + + @Test + void 'decode empty hierarchical msg with missing start'() { + def messages = [ + leaveMsg(hierarchyMsgType1), + flatMsg(flatMsgType2) + ] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 2 + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + + assert rootNode.children[0].enterMsg == null + assert rootNode.children[0].leaveMsg == messages[0] + assert rootNode.children[0].children.empty + + assert rootNode.children[1].msg == messages[1] + } + + @Test + void 'decode hierarchical msg with flat payload and missing end'() { + def messages = [ + flatMsg(flatMsgType1), + enterMsg(hierarchyMsgType1), + flatMsg(flatMsgType2) + ] + def rootNode = FlightLogTreeBuilder.buildTree(messages) + + assert rootNode.children.size() == 2 + assert rootNode.enterMsg == null + assert rootNode.leaveMsg == null + + assert rootNode.children[0].msg == messages[0] + + assert rootNode.children[1].enterMsg == messages[1] + assert rootNode.children[1].leaveMsg == null + assert rootNode.children[1].children.size() == 1 + assert rootNode.children[1].children[0].msg == messages[2] + } +} diff --git a/publish.gradle b/publish.gradle index a27019b..c3782eb 100644 --- a/publish.gradle +++ b/publish.gradle @@ -65,10 +65,7 @@ task publishPrepareFiles { println flightRecJarTask.class.name File flightRecJarFile = flightRecJarTask.archivePath project.file('publish/publishRoot/flighrec').mkdirs() - copy { - from flightRecJarFile - into 'publish/publishRoot/flighrec' - } + GradleCppUtils.copyFile(flightRecJarFile, project.file('publish/publishRoot/flighrec/decoder.jar'), false) copy { from new File(project(':flightrec/decoder').projectDir, 'pub') into 'publish/publishRoot/flighrec'