diff --git a/expected-perfume-model.json b/expected-perfume-model.json new file mode 100644 index 00000000..9df1b473 --- /dev/null +++ b/expected-perfume-model.json @@ -0,0 +1,1153 @@ +{ + "displayables": [ + { + "id": 0, + "displayableValue": "retrieve-image [121, 170]" + }, + { + "id": 1, + "displayableValue": "cache-image [21, 70]" + }, + { + "id": 2, + "displayableValue": "cache-page [9, 17]" + }, + { + "id": 3, + "displayableValue": "quit" + }, + { + "id": 4, + "displayableValue": "retrieve-image [22, 68]" + }, + { + "id": 5, + "displayableValue": "INITIAL" + }, + { + "id": 6, + "displayableValue": "TERMINAL" + }, + { + "id": 7, + "displayableValue": "retrieve-page [109, 119]" + }, + { + "id": 8, + "displayableValue": "retrieve-page [9, 17]" + }, + { + "id": 9, + "displayableValue": "cache-image [22, 68]" + } + ], + "nodes": [ + { + "id": 10, + "displayableIDs: ": 5 + }, + { + "id": 11 + }, + { + "id": 12 + }, + { + "id": 13 + }, + { + "id": 14 + }, + { + "id": 15 + }, + { + "id": 16 + }, + { + "id": 17 + }, + { + "id": 18, + "displayablesIDs: ": 6 + } + ], + "edges": [ + { + "id": 19, + "srcNodeID": 11, + "destNodeID": 14, + "displayablesIDs": 0 + }, + { + "id": 20, + "srcNodeID": 12, + "destNodeID": 11, + "displayablesIDs": 1 + }, + { + "id": 21, + "srcNodeID": 13, + "destNodeID": 16, + "displayablesIDs": 2 + }, + { + "id": 22, + "srcNodeID": 14, + "destNodeID": 18, + "displayablesIDs": 3 + }, + { + "id": 23, + "srcNodeID": 15, + "destNodeID": 14, + "displayablesIDs": 4 + }, + { + "id": 24, + "srcNodeID": 10, + "destNodeID": 17 + }, + { + "id": 25, + "srcNodeID": 10, + "destNodeID": 13 + }, + { + "id": 26, + "srcNodeID": 16, + "destNodeID": 17, + "displayablesIDs": 7 + }, + { + "id": 27, + "srcNodeID": 16, + "destNodeID": 12, + "displayablesIDs": 8 + }, + { + "id": 28, + "srcNodeID": 17, + "destNodeID": 15, + "displayablesIDs": 9 + } + ], + "eventTypes": [ + { + "id": 29, + "label": "retrieve-image" + }, + { + "id": 30, + "label": "cache-image" + }, + { + "id": 31, + "label": "cache-page" + }, + { + "id": 32, + "label": "quit" + }, + { + "id": 33, + "label": "retrieve-page" + } + ], + "events": [ + { + "id": 34, + "traceID": 0, + "traceIndex": 0, + "eventTypeID": 31, + "timestamp": 0 + }, + { + "id": 35, + "traceID": 0, + "traceIndex": 1, + "eventTypeID": 33, + "timestamp": 9 + }, + { + "id": 36, + "traceID": 0, + "traceIndex": 2, + "eventTypeID": 30, + "timestamp": 18 + }, + { + "id": 37, + "traceID": 0, + "traceIndex": 3, + "eventTypeID": 29, + "timestamp": 39 + }, + { + "id": 38, + "traceID": 0, + "traceIndex": 4, + "eventTypeID": 32, + "timestamp": 160 + }, + { + "id": 39, + "traceID": 1, + "traceIndex": 0, + "eventTypeID": 31, + "timestamp": 0 + }, + { + "id": 40, + "traceID": 1, + "traceIndex": 1, + "eventTypeID": 33, + "timestamp": 17 + }, + { + "id": 41, + "traceID": 1, + "traceIndex": 2, + "eventTypeID": 30, + "timestamp": 34 + }, + { + "id": 42, + "traceID": 1, + "traceIndex": 3, + "eventTypeID": 29, + "timestamp": 104 + }, + { + "id": 43, + "traceID": 1, + "traceIndex": 4, + "eventTypeID": 32, + "timestamp": 274 + }, + { + "id": 44, + "traceID": 2, + "traceIndex": 0, + "eventTypeID": 31, + "timestamp": 0 + }, + { + "id": 45, + "traceID": 2, + "traceIndex": 1, + "eventTypeID": 33, + "timestamp": 9 + }, + { + "id": 46, + "traceID": 2, + "traceIndex": 2, + "eventTypeID": 30, + "timestamp": 118 + }, + { + "id": 47, + "traceID": 2, + "traceIndex": 3, + "eventTypeID": 29, + "timestamp": 140 + }, + { + "id": 48, + "traceID": 2, + "traceIndex": 4, + "eventTypeID": 32, + "timestamp": 162 + }, + { + "id": 49, + "traceID": 3, + "traceIndex": 0, + "eventTypeID": 31, + "timestamp": 0 + }, + { + "id": 50, + "traceID": 3, + "traceIndex": 1, + "eventTypeID": 33, + "timestamp": 17 + }, + { + "id": 51, + "traceID": 3, + "traceIndex": 2, + "eventTypeID": 30, + "timestamp": 136 + }, + { + "id": 52, + "traceID": 3, + "traceIndex": 3, + "eventTypeID": 29, + "timestamp": 204 + }, + { + "id": 53, + "traceID": 3, + "traceIndex": 4, + "eventTypeID": 32, + "timestamp": 272 + }, + { + "id": 54, + "traceID": 4, + "traceIndex": 0, + "eventTypeID": 30, + "timestamp": 0 + }, + { + "id": 55, + "traceID": 4, + "traceIndex": 1, + "eventTypeID": 29, + "timestamp": 27 + }, + { + "id": 56, + "traceID": 4, + "traceIndex": 2, + "eventTypeID": 32, + "timestamp": 54 + }, + { + "id": 57, + "traceID": 5, + "traceIndex": 0, + "eventTypeID": 30, + "timestamp": 0 + }, + { + "id": 58, + "traceID": 5, + "traceIndex": 1, + "eventTypeID": 29, + "timestamp": 62 + }, + { + "id": 59, + "traceID": 5, + "traceIndex": 2, + "eventTypeID": 32, + "timestamp": 124 + } + ], + "invariantTypes": [ + { + "id": 60, + "type": "NeverFollowedBy", + "shortName": "↛" + }, + { + "id": 61, + "type": "AlwaysFollowedBy", + "shortName": "→" + }, + { + "id": 62, + "type": "AlwaysPrecedes", + "shortName": "←" + } + ], + "invariants": [ + { + "id": 63, + "invariantTypeID": 60, + "predicates": [ + 31 + ] + }, + { + "id": 64, + "invariantTypeID": 61, + "predicates": [ + 31, + 33 + ], + "resourceBounds": { + "type": "lower", + "bound": "9" + } + }, + { + "id": 65, + "invariantTypeID": 61, + "predicates": [ + 31, + 33 + ], + "resourceBounds": { + "type": "upper", + "bound": "17" + } + }, + { + "id": 66, + "invariantTypeID": 62, + "predicates": [ + 31, + 33 + ], + "resourceBounds": { + "type": "lower", + "bound": "9" + } + }, + { + "id": 67, + "invariantTypeID": 62, + "predicates": [ + 31, + 33 + ], + "resourceBounds": { + "type": "upper", + "bound": "17" + } + }, + { + "id": 68, + "invariantTypeID": 61, + "predicates": [ + 31, + 30 + ], + "resourceBounds": { + "type": "lower", + "bound": "18" + } + }, + { + "id": 69, + "invariantTypeID": 61, + "predicates": [ + 31, + 30 + ], + "resourceBounds": { + "type": "upper", + "bound": "136" + } + }, + { + "id": 70, + "invariantTypeID": 61, + "predicates": [ + 31, + 29 + ], + "resourceBounds": { + "type": "lower", + "bound": "39" + } + }, + { + "id": 71, + "invariantTypeID": 61, + "predicates": [ + 31, + 29 + ], + "resourceBounds": { + "type": "upper", + "bound": "204" + } + }, + { + "id": 72, + "invariantTypeID": 61, + "predicates": [ + 31, + 32 + ], + "resourceBounds": { + "type": "lower", + "bound": "160" + } + }, + { + "id": 73, + "invariantTypeID": 61, + "predicates": [ + 31, + 32 + ], + "resourceBounds": { + "type": "upper", + "bound": "274" + } + }, + { + "id": 74, + "invariantTypeID": 60, + "predicates": [ + 33, + 31 + ] + }, + { + "id": 75, + "invariantTypeID": 60, + "predicates": [ + 33 + ] + }, + { + "id": 76, + "invariantTypeID": 61, + "predicates": [ + 33, + 30 + ], + "resourceBounds": { + "type": "lower", + "bound": "9" + } + }, + { + "id": 77, + "invariantTypeID": 61, + "predicates": [ + 33, + 30 + ], + "resourceBounds": { + "type": "upper", + "bound": "119" + } + }, + { + "id": 78, + "invariantTypeID": 61, + "predicates": [ + 33, + 29 + ], + "resourceBounds": { + "type": "lower", + "bound": "30" + } + }, + { + "id": 79, + "invariantTypeID": 61, + "predicates": [ + 33, + 29 + ], + "resourceBounds": { + "type": "upper", + "bound": "187" + } + }, + { + "id": 80, + "invariantTypeID": 61, + "predicates": [ + 33, + 32 + ], + "resourceBounds": { + "type": "lower", + "bound": "151" + } + }, + { + "id": 81, + "invariantTypeID": 61, + "predicates": [ + 33, + 32 + ], + "resourceBounds": { + "type": "upper", + "bound": "257" + } + }, + { + "id": 82, + "invariantTypeID": 60, + "predicates": [ + 30, + 31 + ] + }, + { + "id": 83, + "invariantTypeID": 60, + "predicates": [ + 30, + 33 + ] + }, + { + "id": 84, + "invariantTypeID": 60, + "predicates": [ + 30 + ] + }, + { + "id": 85, + "invariantTypeID": 61, + "predicates": [ + 30, + 29 + ], + "resourceBounds": { + "type": "lower", + "bound": "21" + } + }, + { + "id": 86, + "invariantTypeID": 61, + "predicates": [ + 30, + 29 + ], + "resourceBounds": { + "type": "upper", + "bound": "70" + } + }, + { + "id": 87, + "invariantTypeID": 62, + "predicates": [ + 30, + 29 + ], + "resourceBounds": { + "type": "lower", + "bound": "21" + } + }, + { + "id": 88, + "invariantTypeID": 62, + "predicates": [ + 30, + 29 + ], + "resourceBounds": { + "type": "upper", + "bound": "70" + } + }, + { + "id": 89, + "invariantTypeID": 61, + "predicates": [ + 30, + 32 + ], + "resourceBounds": { + "type": "lower", + "bound": "44" + } + }, + { + "id": 90, + "invariantTypeID": 61, + "predicates": [ + 30, + 32 + ], + "resourceBounds": { + "type": "upper", + "bound": "240" + } + }, + { + "id": 91, + "invariantTypeID": 62, + "predicates": [ + 30, + 32 + ], + "resourceBounds": { + "type": "lower", + "bound": "44" + } + }, + { + "id": 92, + "invariantTypeID": 62, + "predicates": [ + 30, + 32 + ], + "resourceBounds": { + "type": "upper", + "bound": "240" + } + }, + { + "id": 93, + "invariantTypeID": 60, + "predicates": [ + 29, + 31 + ] + }, + { + "id": 94, + "invariantTypeID": 60, + "predicates": [ + 29, + 33 + ] + }, + { + "id": 95, + "invariantTypeID": 60, + "predicates": [ + 29, + 30 + ] + }, + { + "id": 96, + "invariantTypeID": 60, + "predicates": [ + 29 + ] + }, + { + "id": 97, + "invariantTypeID": 61, + "predicates": [ + 29, + 32 + ], + "resourceBounds": { + "type": "lower", + "bound": "22" + } + }, + { + "id": 98, + "invariantTypeID": 61, + "predicates": [ + 29, + 32 + ], + "resourceBounds": { + "type": "upper", + "bound": "170" + } + }, + { + "id": 99, + "invariantTypeID": 62, + "predicates": [ + 29, + 32 + ], + "resourceBounds": { + "type": "lower", + "bound": "22" + } + }, + { + "id": 100, + "invariantTypeID": 62, + "predicates": [ + 29, + 32 + ], + "resourceBounds": { + "type": "upper", + "bound": "170" + } + }, + { + "id": 101, + "invariantTypeID": 60, + "predicates": [ + 32, + 31 + ] + }, + { + "id": 102, + "invariantTypeID": 60, + "predicates": [ + 32, + 33 + ] + }, + { + "id": 103, + "invariantTypeID": 60, + "predicates": [ + 32, + 30 + ] + }, + { + "id": 104, + "invariantTypeID": 60, + "predicates": [ + 32, + 29 + ] + }, + { + "id": 105, + "invariantTypeID": 60, + "predicates": [ + 32 + ] + } + ], + "logStatements": [ + { + "id": 106, + "text": "164.163.76.74, retrieve-image, 39", + "logPosition": 4 + }, + { + "id": 107, + "text": "237.250.28.190, retrieve-image, 104", + "logPosition": 9 + }, + { + "id": 108, + "text": "237.250.28.190, cache-image, 34", + "logPosition": 8 + }, + { + "id": 109, + "text": "164.163.76.74, cache-image, 18", + "logPosition": 3 + }, + { + "id": 110, + "text": "164.163.76.74, cache-page, 0", + "logPosition": 1 + }, + { + "id": 111, + "text": "237.250.28.190, cache-page, 0", + "logPosition": 6 + }, + { + "id": 112, + "text": "130.78.242.94, cache-page, 0", + "logPosition": 11 + }, + { + "id": 113, + "text": "177.176.181.25, cache-page, 0", + "logPosition": 16 + }, + { + "id": 114, + "text": "164.163.76.74, quit, 160", + "logPosition": 5 + }, + { + "id": 115, + "text": "237.250.28.190, quit, 274", + "logPosition": 10 + }, + { + "id": 116, + "text": "130.78.242.94, quit, 162", + "logPosition": 15 + }, + { + "id": 117, + "text": "177.176.181.25, quit, 272", + "logPosition": 20 + }, + { + "id": 118, + "text": "195.88.181.89, quit, 54", + "logPosition": 23 + }, + { + "id": 119, + "text": "153.98.187.29, quit, 124", + "logPosition": 26 + }, + { + "id": 120, + "text": "130.78.242.94, retrieve-image, 140", + "logPosition": 14 + }, + { + "id": 121, + "text": "153.98.187.29, retrieve-image, 62", + "logPosition": 25 + }, + { + "id": 122, + "text": "195.88.181.89, retrieve-image, 27", + "logPosition": 22 + }, + { + "id": 123, + "text": "177.176.181.25, retrieve-image, 204", + "logPosition": 19 + }, + { + "id": 124, + "text": "177.176.181.25, retrieve-page, 17", + "logPosition": 17 + }, + { + "id": 125, + "text": "164.163.76.74, retrieve-page, 9", + "logPosition": 2 + }, + { + "id": 126, + "text": "237.250.28.190, retrieve-page, 17", + "logPosition": 7 + }, + { + "id": 127, + "text": "130.78.242.94, retrieve-page, 9", + "logPosition": 12 + }, + { + "id": 128, + "text": "195.88.181.89, cache-image, 0", + "logPosition": 21 + }, + { + "id": 129, + "text": "153.98.187.29, cache-image, 0", + "logPosition": 24 + }, + { + "id": 130, + "text": "130.78.242.94, cache-image, 118", + "logPosition": 13 + }, + { + "id": 131, + "text": "177.176.181.25, cache-image, 136", + "logPosition": 18 + } + ], + "links": [ + { + "id1": 37, + "id2": 106 + }, + { + "id1": 19, + "id2": 106 + }, + { + "id1": 42, + "id2": 107 + }, + { + "id1": 19, + "id2": 107 + }, + { + "id1": 41, + "id2": 108 + }, + { + "id1": 20, + "id2": 108 + }, + { + "id1": 36, + "id2": 109 + }, + { + "id1": 20, + "id2": 109 + }, + { + "id1": 34, + "id2": 110 + }, + { + "id1": 21, + "id2": 110 + }, + { + "id1": 39, + "id2": 111 + }, + { + "id1": 21, + "id2": 111 + }, + { + "id1": 44, + "id2": 112 + }, + { + "id1": 21, + "id2": 112 + }, + { + "id1": 49, + "id2": 113 + }, + { + "id1": 21, + "id2": 113 + }, + { + "id1": 38, + "id2": 114 + }, + { + "id1": 22, + "id2": 114 + }, + { + "id1": 43, + "id2": 115 + }, + { + "id1": 22, + "id2": 115 + }, + { + "id1": 48, + "id2": 116 + }, + { + "id1": 22, + "id2": 116 + }, + { + "id1": 53, + "id2": 117 + }, + { + "id1": 22, + "id2": 117 + }, + { + "id1": 56, + "id2": 118 + }, + { + "id1": 22, + "id2": 118 + }, + { + "id1": 59, + "id2": 119 + }, + { + "id1": 22, + "id2": 119 + }, + { + "id1": 47, + "id2": 120 + }, + { + "id1": 23, + "id2": 120 + }, + { + "id1": 58, + "id2": 121 + }, + { + "id1": 23, + "id2": 121 + }, + { + "id1": 55, + "id2": 122 + }, + { + "id1": 23, + "id2": 122 + }, + { + "id1": 52, + "id2": 123 + }, + { + "id1": 23, + "id2": 123 + }, + { + "id1": 50, + "id2": 124 + }, + { + "id1": 26, + "id2": 124 + }, + { + "id1": 35, + "id2": 125 + }, + { + "id1": 26, + "id2": 125 + }, + { + "id1": 40, + "id2": 126 + }, + { + "id1": 26, + "id2": 126 + }, + { + "id1": 45, + "id2": 127 + }, + { + "id1": 26, + "id2": 127 + }, + { + "id1": 54, + "id2": 128 + }, + { + "id1": 28, + "id2": 128 + }, + { + "id1": 57, + "id2": 129 + }, + { + "id1": 28, + "id2": 129 + }, + { + "id1": 46, + "id2": 130 + }, + { + "id1": 28, + "id2": 130 + }, + { + "id1": 51, + "id2": 131 + }, + { + "id1": 28, + "id2": 131 + } + ] +} \ No newline at end of file diff --git a/synoptic/src/synoptic/model/export/JsonExporter.java b/synoptic/src/synoptic/model/export/JsonExporter.java index 9e8ddf47..ca39c331 100644 --- a/synoptic/src/synoptic/model/export/JsonExporter.java +++ b/synoptic/src/synoptic/model/export/JsonExporter.java @@ -3,12 +3,13 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.List; import org.json.simple.JSONValue; @@ -16,9 +17,11 @@ import synoptic.invariants.TemporalInvariantSet; import synoptic.invariants.constraints.TempConstrainedInvariant; import synoptic.model.EventNode; -import synoptic.model.Partition; import synoptic.model.PartitionGraph; import synoptic.model.event.EventType; +import synoptic.model.export.types.EvBasedEdge; +import synoptic.model.export.types.EvBasedGraph; +import synoptic.model.export.types.EvBasedNode; import synoptic.model.interfaces.IGraph; import synoptic.model.interfaces.INode; @@ -29,25 +32,24 @@ */ public class JsonExporter { - /** - * Each event mapped to its relevant JSON information, the trace ID and its - * index within the trace - */ - private static Map eventMap = new HashMap(); - - /** - * Simple pair of a trace ID and an event index within the trace to uniquely - * identify a specific event instance/node - */ - private static class EventInstance { - public int traceID; - public int eventIndexWithinTrace; - - public EventInstance(int traceID, int eventIndexWithinTrace) { - this.traceID = traceID; - this.eventIndexWithinTrace = eventIndexWithinTrace; - } - } + // ID variable for the entire class + private static int globalID = 0; + private static final String initial = "INITIAL"; + private static final String terminal = "TERMINAL"; + // Map of nodes and their respective global ID's + private static Map nodesIDMap = null; + // Map of edges and their respective global ID's + private static Map edgesIDMap = null; + // Map of displayables and their respective global ID's + private static Map displayablesIDMap = null; + // Map of event types and their respective global ID's + private static Map eventTypesIDMap = null; + // Map of events and their respective global ID's + private static Map eventsIDMap = null; + // Map of events and their respective global ID's + private static Map invariantTypesIDMap = null; + // Map of log statements and their respective global ID's + private static Map logStatementsIDMap = null; /** * Export the JSON object representation of the partition graph pGraph to @@ -66,22 +68,48 @@ public static > void exportJsonObject( assert graph instanceof PartitionGraph; PartitionGraph pGraph = (PartitionGraph) graph; - // EvBasedGraph evGraph = new EvBasedGraph(pGraph); - // System.out.println("evGraph:\n" + evGraph); + EvBasedGraph evGraph = new EvBasedGraph(pGraph); + + Map finalModelMap = new LinkedHashMap<>(); + + // Add displayables to final model map + List> displayablesList = makeDisplayablesJSON( + evGraph); + finalModelMap.put("displayables", displayablesList); - Map finalModelMap = new LinkedHashMap(); + // Add nodes to the final model map + List> nodesList = makeNodesJSON(evGraph); + finalModelMap.put("nodes", nodesList); - // Add log to final model map - List> logListOfTraces = makeLogJSON(pGraph); - finalModelMap.put("log", logListOfTraces); + // Add edges to final model map + List> edgesList = makeEdgesJSON(evGraph); + finalModelMap.put("edges", edgesList); - // Add partitions to final model map - List> partitionList = makePartitionsJSON(pGraph); - finalModelMap.put("partitions", partitionList); + // Add event types to final model map + List> eventTypesList = makeEventTypesJSON(evGraph); + finalModelMap.put("eventTypes", eventTypesList); + + // Add events to final model map + List> eventsList = makeEventsJSON(evGraph); + finalModelMap.put("events", eventsList); + + // Add invariant types to final model map + List> invariantTypesList = makeInvariantTypesJSON( + pGraph); + finalModelMap.put("invariantTypes", invariantTypesList); // Add invariants to final model map - List> invariantList = makeInvariantsJSON(pGraph); - finalModelMap.put("invariants", invariantList); + List> invariantsList = makeInvariantsJSON(pGraph); + finalModelMap.put("invariants", invariantsList); + + // Add log statements to final model map + List> logStatementsList = makeLogStatementsJSON( + evGraph); + finalModelMap.put("logStatements", logStatementsList); + + // Add links to final model map + List> linksList = makeLinksJSON(evGraph); + finalModelMap.put("links", linksList); // Output the final model map as a JSON object try { @@ -96,193 +124,573 @@ public static > void exportJsonObject( } } + + public static > void exportJsonObject(IGraph graph) { + + // The graph must be a partition graph + assert graph instanceof PartitionGraph; + PartitionGraph pGraph = (PartitionGraph) graph; + + EvBasedGraph evGraph = new EvBasedGraph(pGraph); + + Map finalModelMap = new LinkedHashMap<>(); + + // Add displayables to final model map + List> displayablesList = makeDisplayablesJSON( + evGraph); + finalModelMap.put("displayables", displayablesList); + + // Add nodes to the final model map + List> nodesList = makeNodesJSON(evGraph); + finalModelMap.put("nodes", nodesList); + + // Add edges to final model map + List> edgesList = makeEdgesJSON(evGraph); + finalModelMap.put("edges", edgesList); + + // Add event types to final model map + List> eventTypesList = makeEventTypesJSON(evGraph); + finalModelMap.put("eventTypes", eventTypesList); + + // Add events to final model map + List> eventsList = makeEventsJSON(evGraph); + finalModelMap.put("events", eventsList); + + // Add invariant types to final model map + List> invariantTypesList = makeInvariantTypesJSON( + pGraph); + finalModelMap.put("invariantTypes", invariantTypesList); + + // Add invariants to final model map + List> invariantsList = makeInvariantsJSON(pGraph); + finalModelMap.put("invariants", invariantsList); + + // Add log statements to final model map + List> logStatementsList = makeLogStatementsJSON( + evGraph); + finalModelMap.put("logStatements", logStatementsList); + + // Add links to final model map + List> linksList = makeLinksJSON(evGraph); + finalModelMap.put("links", linksList); + + } + /** - * Creates the 'log' of the JSON object: a list of traces within the log of - * this partition graph + * Creates the 'nodes' of the JSON object: a list of nodes within this + * EvBasedGraph * - * @param pGraph - * The partition graph whose log we're outputting + * @param evGraph + * The EvBasedGraph whose nodes we're outputting */ - private static List> makeLogJSON( - PartitionGraph pGraph) { - // The log (list of traces) to go into the JSON object - List> logListOfTraces = new LinkedList>(); - - // Get all partitions in the partition graph - Set allPartitions = pGraph.getNodes(); - - // Get the INITIAL partition, which will be used to retrieve all traces - // and their events - Partition initialPart = null; - for (Partition part : allPartitions) { - if (part.isInitial()) { - initialPart = part; - break; - } + + public static List> makeNodesJSON( + EvBasedGraph evGraph) { + + nodesIDMap = new LinkedHashMap<>(); + + assert (displayablesIDMap != null) : "displayablesIDMap is null"; + + // List of nodes to go into the JSON object + List> nodeList = new LinkedList<>(); + + // Add the initial node first + Map initNodeMap = makeNode(evGraph, evGraph.getInitialNode()); + nodeList.add(initNodeMap); + + // Get all the nodes in the evGraph + for (EvBasedNode node : evGraph.getNodes()) { + + if (node != evGraph.getInitialNode() && node != evGraph.getTerminalNode()) { + + Map nodeMap = makeNode(evGraph, node); + nodeList.add(nodeMap); + } } - // There must have been an INITIAL partition found - assert initialPart != null; - if (initialPart == null) { - return null; + // Add the terminal node last + Map termNodeMap = makeNode(evGraph, + evGraph.getTerminalNode()); + nodeList.add(termNodeMap); + + return nodeList; + } + + /** + * Processes nodes for makeNodesJSON by adding their ID and displayable + * value if applicable + * + * @param evGraph + * The EvBasedGraph whose nodes we're processing + * @param node + * The particular node being processed + */ + public static Map makeNode(EvBasedGraph evGraph, + EvBasedNode node) { + Map nodeMap = new LinkedHashMap<>(); + nodeMap.put("id", globalID); + nodesIDMap.put(node, globalID); + if (node == evGraph.getInitialNode()) { + List displayableList = new LinkedList<>(); + displayableList.add(displayablesIDMap.get(initial)); + nodeMap.put("displayableIDs", displayableList); + } + if (node == evGraph.getTerminalNode()) { + List displayableList = new LinkedList<>(); + displayableList.add(displayablesIDMap.get(terminal)); + nodeMap.put("displayableIDs", displayableList); } + globalID++; + return nodeMap; + } - // Follow all traces and store them in the log list of traces - int traceID = 0; - for (EventNode startingEvent : initialPart.getEventNodes().iterator() - .next().getAllSuccessors()) { - // One trace, contains the trace number and a list of events - Map singleTraceMap = new LinkedHashMap(); - // List of events - List> singleTraceEventsList = new LinkedList>(); - - int eventIndexWithinTrace = 0; - for (EventNode event = startingEvent; !event - .isTerminal(); event = event.getAllSuccessors().iterator() - .next()) { - // One event, contains event index, event type, and timestamp - Map singleEventMap = new LinkedHashMap(); + /** + * Creates the 'edges' of the JSON object: a list of edges within this + * EvBasedGraph + * + * @param evGraph + * The EvBasedGraph whose edges we're outputting + */ + public static List> makeEdgesJSON( + EvBasedGraph evGraph) { - // Populate this event's index within the trace, its type, and - // the line in the log where it was found - singleEventMap.put("eventIndex", eventIndexWithinTrace); - EventType evType = event.getEType(); - singleEventMap.put("eventType", evType.toString()); - singleEventMap.put("logLine", event.getLineNum()); + edgesIDMap = new LinkedHashMap<>(); - // Populate this event's time if it's not INITIAL or TERMINAL - if (!evType.isSpecialEventType()) { - singleEventMap.put("timestamp", event.getTime()); + assert (nodesIDMap != null + && displayablesIDMap != null) : "Either one or both of nodesIDMap and displayablesIDMap is null"; + + // List of edges and info to go into the JSON Object + List> edgesList = new LinkedList<>(); + + // Contains all edges used to avoid duplicates + Set usedEdges = new HashSet<>(); + + for (EvBasedNode node : evGraph.getNodes()) { + + for (EvBasedEdge edge : node.outEdges) { + + Map edgesMap = new LinkedHashMap<>(); + + // Check to see if it is a duplicate + if (!usedEdges.contains(edge)) { + + edgesMap.put("id", globalID); + edgesMap.put("srcNodeID", nodesIDMap.get(edge.srcNode)); + edgesMap.put("destNodeID", nodesIDMap.get(edge.destNode)); + + if (edge.eType.getETypeLabel() != initial + && edge.eType.getETypeLabel() != terminal) { + + String displayableValue; + + if (edge.resMin == null && edge.resMax == null) { + displayableValue = edge.eType.getETypeLabel(); + } else { + displayableValue = edge.eType.getETypeLabel() + " [" + + edge.resMin + ", " + edge.resMax + "]"; + } + List displayableList = new LinkedList<>(); + displayableList.add(displayablesIDMap.get(displayableValue)); + edgesMap.put("displayableIDs", displayableList); + } + + edgesIDMap.put(edge, globalID); + edgesList.add(edgesMap); + usedEdges.add(edge); + + globalID++; } + } + } + return edgesList; + } + + /** + * Creates the 'displayables' of the JSON object: a list of displayables + * within this EvBasedGraph + * + * @param evGraph + * The EvBasedGraph whose displayables we're outputting + */ + public static List> makeDisplayablesJSON( + EvBasedGraph evGraph) { + + displayablesIDMap = new LinkedHashMap<>(); + + // List of edges and info to go into the JSON Object + List> displayablesList = new LinkedList<>(); + + // Contains all the edges to avoid duplicates + Set usedEdges = new HashSet<>(); + + for (EvBasedNode node : evGraph.getNodes()) { + + for (EvBasedEdge edge : node.outEdges) { - // Add this event to this trace's list of events - singleTraceEventsList.add(singleEventMap); + Map displayablesMap = new LinkedHashMap<>(); - // Record this event's event instance information to ease the - // creation of the partition part of the JSON later - eventMap.put(event, - new EventInstance(traceID, eventIndexWithinTrace++)); + // Check for duplicate + if (!usedEdges.contains(edge)) { + + // Make the string to go into displayableValue + String displayableValue = edge.eType.getETypeLabel() + " [" + + edge.resMin + ", " + edge.resMax + "]"; + if (edge.resMin == null && edge.resMax == null) { + if (displayablesIDMap.containsKey(initial) + && edge.eType.getETypeLabel() == initial) { + displayableValue = terminal; + } else { + displayableValue = edge.eType.getETypeLabel(); + } + } + + displayablesMap.put("id", globalID); + displayablesMap.put("displayableValue", displayableValue); + displayablesIDMap.put(displayableValue, globalID); + displayablesList.add(displayablesMap); + usedEdges.add(edge); + globalID++; + } } + } + + if(!displayablesIDMap.containsKey(terminal)) { + Map displayablesMap = new LinkedHashMap<>(); + displayablesMap.put("id", globalID); + displayablesMap.put("displayableValue", terminal); + displayablesIDMap.put(terminal, globalID); + displayablesList.add(displayablesMap); + globalID++; + } + + return displayablesList; + } + + /** + * Creates the 'eventTypes' of the JSON object: a list of event types within + * this EvBasedGraph + * + * @param evGraph + * The EvBasedGraph whose event types we're outputting + */ + public static List> makeEventTypesJSON( + EvBasedGraph evGraph) { + + eventTypesIDMap = new LinkedHashMap<>(); + + // The list of event types to go into the JSON object + List> eventTypesList = new LinkedList<>(); - // Populate the single trace - singleTraceMap.put("traceID", traceID++); - singleTraceMap.put("events", singleTraceEventsList); + for (EvBasedNode node : evGraph.getNodes()) { - // Put the trace into the log's list of traces - logListOfTraces.add(singleTraceMap); + for (EvBasedEdge edge : node.outEdges) { + + Map eventTypesMap = new LinkedHashMap<>(); + + if (!eventTypesIDMap.containsKey(edge.eType.toString()) + && !edge.eType.isSpecialEventType()) { + eventTypesMap.put("id", globalID); + eventTypesMap.put("label", edge.eType.toString()); + eventTypesList.add(eventTypesMap); + eventTypesIDMap.put(edge.eType.toString(), globalID); + globalID++; + } + } } - return logListOfTraces; + return eventTypesList; } /** - * Creates the 'partitions' of the JSON object: a list of partitions within - * this partition graph + * Creates the 'events' of the JSON object: a list of events within this + * EvBasedGraph * - * @param pGraph - * The partition graph whose partitions we're outputting + * @param evGraph + * The EvBasedGraph whose events we're outputting */ - private static List> makePartitionsJSON( - PartitionGraph pGraph) { - // The list of partitions to go into the JSON object - List> partitionsList = new LinkedList>(); + public static List> makeEventsJSON( + EvBasedGraph evGraph) { - // Get all partitions in the partition graph - Set allPartitions = pGraph.getNodes(); + eventsIDMap = new LinkedHashMap<>(); - PartitionLoop: - for (Partition partition : allPartitions) { - // One partition, contains event type and list of events - Map singlePartitionMap = new LinkedHashMap(); + assert (eventTypesIDMap != null) : "eventTypesIDMap is null"; - // This partition's list of events it contains - List> singlePartitionEventList = new LinkedList>(); + // The events to go into the JSON object + List> eventsList = new LinkedList<>(); - // Populate this partition's event type - EventType evType = partition.getEType(); - singlePartitionMap.put("eventType", evType.toString()); + EvBasedNode initialNode = evGraph.getInitialNode(); + Set iniOutEdges = initialNode.outEdges; + // Set iniOutEvents = iniOutEdges.events; - for (EventNode event : partition.getEventNodes()) { - // One event, contains trace ID and index within the trace - Map singleEventMap = new LinkedHashMap(); + int traceID = 0; + for (EventNode startingEvent : iniOutEdges.iterator().next().events + .iterator().next().getAllSuccessors()) { + int traceIndex = 0; - // Get the event instance info required to identify this event - // within the JSON object - EventInstance evInstance = eventMap.get(event); + for (EventNode event = startingEvent; !event + .isTerminal(); event = event.getAllSuccessors().iterator() + .next()) { - if (evType.isSpecialEventType()) { - continue PartitionLoop; - } + Map singleEventMap = new LinkedHashMap<>(); - // Populate this event's trace ID and index within the trace - singleEventMap.put("traceID", evInstance.traceID); - singleEventMap.put("eventIndex", - evInstance.eventIndexWithinTrace); + singleEventMap.put("id", globalID); + singleEventMap.put("traceID", traceID); + singleEventMap.put("traceIndex", traceIndex); + EventType evType = event.getEType(); + singleEventMap.put("eventTypeID", + eventTypesIDMap.get(evType.toString())); + eventsIDMap.put(event, globalID); - // Put the event into the partition's list of events - singlePartitionEventList.add(singleEventMap); + // Populate this event's time if it's not INITIAL or TERMINAL + // if (!evType.isSpecialEventType()) { + // singleEventMap.put("timestamp", event.getTime()); + // } + eventsList.add(singleEventMap); + // eventMap.put(event, new EventInstance(traceID, + // traceIndex++)); + traceIndex++; + globalID++; } - // Store this partition's list of events - singlePartitionMap.put("events", singlePartitionEventList); - - // Put the partition into the full list of partitions - partitionsList.add(singlePartitionMap); + traceID++; } - - return partitionsList; + return eventsList; } /** - * Creates the 'invariants' of the JSON object: a list of the invariants - * used to construct the partition graph + * Creates the 'invariantTypes' of the JSON object: a list of invariant + * types within this EvBasedGraph * - * @param pGraph - * The partition graph made using the invariants we're outputting + * @param evGraph + * The EvBasedGraph whose invariant types we're outputting */ - private static List> makeInvariantsJSON( + public static List> makeInvariantTypesJSON( PartitionGraph pGraph) { - // The list of invariants to go into the JSON object - List> invariantsList = new LinkedList>(); + + invariantTypesIDMap = new LinkedHashMap<>(); + + // The list of invariant types to go into the JSON object + List> invariantTypesList = new LinkedList<>(); // Get all invariants in the partition graph TemporalInvariantSet allInvariants = pGraph.getInvariants(); for (ITemporalInvariant inv : allInvariants) { - // One invariant, contains type, predicates, constraint, and bounds - Map singleInvariantMap = new LinkedHashMap(); + // One invariant contains type, predicates, constraint, and bounds + Map invariantMap = new LinkedHashMap<>(); + + // To avoid duplicates, check if an invariant with the same name has + // already been mapped + if (!invariantTypesIDMap.containsKey(inv.getLongName())) { + // Store the invariant type ID + invariantMap.put("id", globalID); + // Store the invariant type + invariantMap.put("type", inv.getLongName()); + // Store the invariant type short name + if (inv.getLongName() == "AlwaysPrecedes") { + invariantMap.put("shortName", "←"); + } + if (inv.getLongName() == "AlwaysFollowedBy") { + invariantMap.put("shortName", "→"); + } + if (inv.getLongName() == "NeverFollowedBy") { + invariantMap.put("shortName", "↛"); + } + if (inv.getLongName() == "InterruptedBy") { + invariantMap.put("shortName", "⇻"); + } + // Add it to invariant type map for global usage + invariantTypesIDMap.put(inv.getLongName(), globalID); + // Put the invariant map into the list of invariant types + invariantTypesList.add(invariantMap); + // Increment global ID + globalID++; + } + } + return invariantTypesList; + } - // Store the invariant type - singleInvariantMap.put("invariantType", inv.getLongName()); + /** + * Creates the 'invariants' of the JSON object: a list of invariants within + * this EvBasedGraph + * + * @param evGraph + * The EvBasedGraph whose invariants we're outputting + */ + public static List> makeInvariantsJSON( + PartitionGraph pGraph) { - // Get invariant predicates - List predicateList = new LinkedList(); - for (EventType evType : inv.getPredicates()) { - predicateList.add(evType.toString()); - } + assert (invariantTypesIDMap != null) : "invariantTypesIDMap is null"; + boolean skip = false; - // Handle case where both predicates are identical [i.e., only one - // object in the predicate set returned by - // ITemporalInvariant.getPredicates()]. This WILL BREAK if any - // 3-predicate invariants are introduced. - if (predicateList.size() == 1) { - predicateList.add(predicateList.get(0)); - } - singleInvariantMap.put("predicates", predicateList); + // The list of invariants to go into the JSON object + List> invariantsList = new LinkedList<>(); + List> resourceBoundsArray = new LinkedList<>(); + Map resourceBoundsMap = new LinkedHashMap<>(); - if (inv instanceof TempConstrainedInvariant) { - TempConstrainedInvariant constInv = (TempConstrainedInvariant) inv; - // Store the constraints with bounds - List constraintBoundList = new LinkedList(); - constraintBoundList.add(constInv.getConstraint().toString()); - singleInvariantMap.put("constraints", constraintBoundList); + // Get all invariants in the partition graph + TemporalInvariantSet allInvariants = pGraph.getInvariants(); + // One invariant, contains type, predicates, constraint, and bounds + Map invariantMap = new LinkedHashMap<>(); + for (ITemporalInvariant inv : allInvariants) { + + + if(skip == false) { + resourceBoundsMap = new LinkedHashMap<>(); + invariantMap = new LinkedHashMap<>(); + // Store the invariant ID + invariantMap.put("id", globalID); + // Get the invariant type ID and store it + invariantMap.put("invariantTypeID", + invariantTypesIDMap.get(inv.getLongName())); + // Get invariant predicates + List predicateList = new LinkedList<>(); + for (EventType evType : inv.getPredicates()) { + predicateList.add(eventTypesIDMap.get(evType.toString())); + } + invariantMap.put("predicates", predicateList); + if (inv instanceof TempConstrainedInvariant) { + TempConstrainedInvariant constInv = (TempConstrainedInvariant) inv; + resourceBoundsArray = new LinkedList<>(); + String constraint = constInv.getConstraint().toString(); + // Extract the bound type and number from the constraint string + if (constraint.contains("lowerbound = ")) { + String boundType = "lower"; + resourceBoundsMap.put("type", boundType); + constraint = constraint.replaceAll("[^\\.0123456789]", ""); + resourceBoundsMap.put("bound", constraint); + resourceBoundsArray.add(resourceBoundsMap); + // invariantMap.put("resourceBounds", resourceBoundsArray); + skip = true; + //invariantsList.add(invariantMap); + + } + } + else { + invariantsList.add(invariantMap); + globalID++; + } + } + if(skip == true) { + TempConstrainedInvariant constInv = (TempConstrainedInvariant) inv; + String constraint = constInv.getConstraint().toString(); + resourceBoundsMap = new LinkedHashMap<>(); + if (constraint.contains("upperbound = ")) { + String boundType = "upper"; + resourceBoundsMap.put("type", boundType); + constraint = constraint.replaceAll("[^\\.0123456789]", ""); + resourceBoundsMap.put("bound", constraint); + resourceBoundsArray.add(resourceBoundsMap); + invariantMap.put("resourceBounds", resourceBoundsArray); + skip = false; + globalID++; + invariantsList.add(invariantMap); + + } } + // Put the invariant map into the list of invariants - invariantsList.add(singleInvariantMap); + // invariantsList.add(invariantMap); } return invariantsList; } -} + + /** + * Creates the 'logStatements' of the JSON object: a list of log statements + * within this EvBasedGraph + * + * @param evGraph + * The EvBasedGraph whose logStatements we're outputting + */ + + public static List> makeLogStatementsJSON( + EvBasedGraph evGraph) { + + logStatementsIDMap = new LinkedHashMap<>(); + + // The log statements to go into the JSON Object + List> logStatementsList = new LinkedList<>(); + + // Contains all edges used to avoid duplicates + Set usedEvents = new HashSet<>(); + + for (EvBasedNode node : evGraph.getNodes()) { + + for (EvBasedEdge edge : node.outEdges) { + + for (EventNode event : edge.events) { + + Map logStatementsMap = new LinkedHashMap<>(); + if (!usedEvents.contains(event)) { + if (event.getLine() != null) { + + logStatementsMap.put("id", globalID); + logStatementsMap.put("text", event.getLine()); + logStatementsMap.put("logPosition", + event.getLineNum()); + + logStatementsList.add(logStatementsMap); + logStatementsIDMap.put(event.getLine(), globalID); + usedEvents.add(event); + globalID++; + } + } + } + } + } + + return logStatementsList; + } + + /** + * Creates the 'nodes' of the JSON object: a list of nodes within this + * EvBasedGraph + * + * @param evGraph + * The EvBasedGraph whose nodes we're outputting + */ + public static List> makeLinksJSON( + EvBasedGraph evGraph) { + + assert (eventsIDMap != null && logStatementsIDMap != null + && edgesIDMap != null) : "Either one, two or all three of eventsIDMap, logStatementsIDMap, or edgesIDMap is null"; + + List> linksList = new LinkedList<>(); + + // Contains all edges used to avoid duplicates + Set usedEvents = new HashSet<>(); + + for (EvBasedNode node : evGraph.getNodes()) { + + for (EvBasedEdge edge : node.outEdges) { + + for (EventNode event : edge.events) { + + if (!usedEvents.contains(event)) { + if (logStatementsIDMap.get(event.getLine()) != null) { + Map eventLinksMap = new LinkedHashMap<>(); + eventLinksMap.put("id1", eventsIDMap.get(event)); + eventLinksMap.put("id2", + logStatementsIDMap.get(event.getLine())); + linksList.add(eventLinksMap); + usedEvents.add(event); + Map edgeLinksMap = new LinkedHashMap<>(); + edgeLinksMap.put("id1", edgesIDMap.get(edge)); + edgeLinksMap.put("id2", + logStatementsIDMap.get(event.getLine())); + linksList.add(edgeLinksMap); + } + } + } + + } + } + + return linksList; + + } + +} \ No newline at end of file diff --git a/synoptic/src/synoptic/tests/SynopticTest.java b/synoptic/src/synoptic/tests/SynopticTest.java index 6e9a36a7..2d5bc28b 100644 --- a/synoptic/src/synoptic/tests/SynopticTest.java +++ b/synoptic/src/synoptic/tests/SynopticTest.java @@ -1,14 +1,13 @@ package synoptic.tests; import java.io.File; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.rules.TestName; -import junit.framework.Assert; - import synoptic.invariants.ITemporalInvariant; import synoptic.invariants.InterruptedByInvariant; import synoptic.invariants.TemporalInvariantSet; @@ -26,6 +25,8 @@ import synoptic.model.event.StringEventType; import synoptic.util.InternalSynopticException; +import junit.framework.Assert; + /** * Base class for all Synoptic project tests. Performs common set-up and * tear-down tasks, and defines methods used by multiple tests. @@ -57,6 +58,35 @@ public void setUp() throws ParseException { super.setUp(); } + /* + * Due to differences between command line and IDE usage, checks the path + * used and adjusts it accordingly. + */ + public static String getTestPath(String path) throws Exception { + + // The Original path passed in. + File originalPath = new File(path); + if (originalPath.exists()) { + return path; + } + // Removing the parent directory of the path passed in if it exists. + else if (path.startsWith("../") | path.startsWith("..\\")) { + File parentRemovedPath = new File(path.substring(3)); + if (parentRemovedPath.exists()) { + return (path.substring(3)); + } + } + // The parent directory of the path passed in. + else if (!(path.startsWith("../") | path.startsWith("..\\"))) { + File parentPath = new File(("../" + path)); + if (parentPath.exists()) { + return ("../" + path); + } + } + // If none of the paths work, throw an error. + throw new FileNotFoundException("Invalid path or file is missing."); + } + // ////////////////////////////////////////////// // Common routines to simplify testing. // ////////////////////////////////////////////// @@ -85,7 +115,8 @@ public List stringsToStringEventTypes(String[] types) { * array of host ids into a list of DistEventType objects. Does not handle * INITIAL or TERMINAL events types. */ - public List stringsToDistEventTypes(String[] types, String[] pids) { + public List stringsToDistEventTypes(String[] types, + String[] pids) { Assert.assertTrue(types.length == pids.length); ArrayList ret = new ArrayList(types.length); for (int i = 0; i < types.length; i++) { @@ -170,8 +201,8 @@ public static String concatinateWithNewlines(String[] events) { } public ArrayList parseLogEvents(String[] events, - TraceParser parser) throws InternalSynopticException, - ParseException { + TraceParser parser) + throws InternalSynopticException, ParseException { String traceStr = concatinateWithNewlines(events); ArrayList parsedEvents = parser.parseTraceString(traceStr, testName.getMethodName(), -1); @@ -187,7 +218,8 @@ public ArrayList parseLogEvents(String[] events, * @throws ParseException * @throws InternalSynopticException */ - public TraceGraph genChainsTraceGraph(String[] events, TraceParser parser) + public TraceGraph genChainsTraceGraph(String[] events, + TraceParser parser) throws ParseException, InternalSynopticException { ArrayList parsedEvents = parseLogEvents(events, parser); return parser.generateDefaultOrderRelation(parsedEvents); @@ -235,8 +267,8 @@ public PartitionGraph genInitialPartitionGraph(String[] events, boolean multipleRelations) throws Exception { ChainsTraceGraph inputGraph = (ChainsTraceGraph) genChainsTraceGraph( events, parser); - return new PartitionGraph(inputGraph, true, miner.computeInvariants( - inputGraph, multipleRelations, false)); + return new PartitionGraph(inputGraph, true, + miner.computeInvariants(inputGraph, multipleRelations, false)); } /** diff --git a/synoptic/src/synoptic/tests/units/JsonExporterTests.java b/synoptic/src/synoptic/tests/units/JsonExporterTests.java new file mode 100644 index 00000000..7007a1e0 --- /dev/null +++ b/synoptic/src/synoptic/tests/units/JsonExporterTests.java @@ -0,0 +1,504 @@ +package synoptic.tests.units; + +import static org.junit.Assert.assertTrue; + +import java.io.FileReader; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import synoptic.main.AbstractMain; +import synoptic.main.PerfumeMain; +import synoptic.main.options.PerfumeOptions; +import synoptic.model.PartitionGraph; +import synoptic.model.export.DotExportFormatter; +import synoptic.model.export.JsonExporter; +import synoptic.model.export.types.EvBasedGraph; +import synoptic.tests.SynopticTest; + +public class JsonExporterTests { + + private PartitionGraph pGraph = null; + + @Before + public void setUp() throws Exception { + PerfumeMain main = null; + // Set up Perfume options + PerfumeOptions perfOpts = new PerfumeOptions(); + perfOpts.regExps = Arrays.asList( + new String[] { "(?.+), (?.+), (?.+)" }); + perfOpts.partitionRegExp = "\\k"; + String pathToLogFileName = SynopticTest.getTestPath( + "traces/abstract/perfume-survey/browser-caching-traces.txt"); + perfOpts.logFilenames.add(pathToLogFileName); + + // Create a Perfume instance + AbstractMain.instance = null; + main = new PerfumeMain(perfOpts.toAbstractOptions(), + new DotExportFormatter()); + + // Run Perfume + pGraph = main.createInitialPartitionGraph(); + main.runSynoptic(pGraph); + } + + /** + * Creates an evBasedGraph for testing purposes + */ + public EvBasedGraph createEvGraph() { + JsonExporter.exportJsonObject(pGraph); + return new EvBasedGraph(pGraph); + } + + /** + * Parses and creates a JSONArray from expected-perfume-model.json for a + * specific field + * + * @param field + * The field for which the JSONArray will be created + */ + public JSONArray parseJSONArray(String field) throws Exception { + + JSONParser parser = new JSONParser(); + Object expectedJSON = parser + .parse(new FileReader("expected-perfume-model.json")); + JSONObject expectedJSONObject = (JSONObject) expectedJSON; + + return (JSONArray) expectedJSONObject.get(field); + } + + /* + * Tests for the makeDisplayablesJSON method - Checks that each displayable + * has an ID and displayableValue and that these values are not null + */ + @Test + public void displayableTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> generatedDisplayablesList = JsonExporter + .makeDisplayablesJSON(evGraph); + Set generatedDisplayables = new HashSet<>(); + int idCount = 0; + int dVCount = 0; + + for (Map genDisplayable : generatedDisplayablesList) { + + if (genDisplayable.get("displayableValue") != null) { + generatedDisplayables + .add(genDisplayable.get("displayableValue").toString()); + } + + if (genDisplayable.containsKey("id")) { + if (genDisplayable.get("id") == null) { + Assert.fail("Displayable contains null id."); + } + idCount++; + } + if (genDisplayable.containsKey("displayableValue")) { + if (genDisplayable.get("displayableValue") == null) { + Assert.fail("DisplayableValue contains null value."); + } + dVCount++; + } + } + + JSONArray displayables = parseJSONArray("displayables"); + Set expectedDisplayables = new HashSet<>(); + + for (Object expDisplayable : displayables) { + + JSONObject dispJSONObj = (JSONObject) expDisplayable; + String displayableValue = dispJSONObj.get("displayableValue") + .toString(); + expectedDisplayables.add(displayableValue); + } + + assertTrue(expectedDisplayables.equals(generatedDisplayables)); + assertTrue(idCount == generatedDisplayablesList.size()); + assertTrue(idCount == dVCount); + } + + /* + * Tests for the makeNodesJSON method - Checks that each node has an + * associated ID and its ID is not null, if the node has an associated + * displayableID, check that it is not null + */ + @Test + public void nodesTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> generatedNodesList = JsonExporter + .makeNodesJSON(evGraph); + int idCount = 0; + + for (Map genNode : generatedNodesList) { + + if (genNode.containsKey("id")) { + if (genNode.get("id") == null) { + Assert.fail("Node contains null id."); + } + idCount++; + } + if (genNode.containsKey("displayableIDs") + && genNode.get("displayableIDs") == null) { + Assert.fail("DisplayableIDs is null."); + } + } + + assertTrue(idCount == generatedNodesList.size()); + } + + /* + * Tests for the makeEdgesJSON method - Checks that each edge has an + * associated ID, source node ID, destination node ID and these values are + * not null, if the edge has an associated displayableID, check that it is + * not null + */ + @Test + public void edgesTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> t = JsonExporter.makeNodesJSON(evGraph); + List> generatedEdgesList = JsonExporter + .makeEdgesJSON(evGraph); + int idCount = 0; + int srcNodeCount = 0; + int destNodeCount = 0; + + for (Map genEdge : generatedEdgesList) { + + if (genEdge.containsKey("id")) { + if (genEdge.get("id") == null) { + Assert.fail("Edge contains null id."); + } + idCount++; + } + if (genEdge.containsKey("srcNodeID")) { + if (genEdge.get("srcNodeID") == null) { + Assert.fail("Edge contains null srcNodeID."); + } + srcNodeCount++; + } + if (genEdge.containsKey("destNodeID")) { + if (genEdge.get("destNodeID") == null) { + Assert.fail("Edge contains null destNodeID."); + } + destNodeCount++; + } + if (genEdge.containsKey("displayableIDs") + && genEdge.get("displayableIDs") == null) { + Assert.fail("DisplayableIDs is null."); + } + } + + assertTrue(idCount == srcNodeCount); + assertTrue(idCount == destNodeCount); + assertTrue(idCount == generatedEdgesList.size()); + } + + /* + * Tests for the makeEventTypesJSON method - Checks that each eventType has + * an associated ID and label and that these values are not null + */ + @Test + public void eventTypesTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> generatedEventTypesList = JsonExporter + .makeEventTypesJSON(evGraph); + Set generatedEventTypes = new HashSet<>(); + int idCount = 0; + int labelCount = 0; + + for (Map genEventType : generatedEventTypesList) { + + if (genEventType.get("label") != null) { + generatedEventTypes.add(genEventType.get("label").toString()); + } + if (genEventType.containsKey("id")) { + if (genEventType.get("id") == null) { + Assert.fail("EventTypes contains null id."); + } + idCount++; + } + if (genEventType.containsKey("label")) { + if (genEventType.get("label") == null) { + Assert.fail("EventTypes contains null label."); + } + labelCount++; + } + } + + JSONArray eventTypes = parseJSONArray("eventTypes"); + Set expectedEventTypes = new HashSet<>(); + + for (Object expEventType : eventTypes) { + + JSONObject eventTypeJSONObj = (JSONObject) expEventType; + String label = eventTypeJSONObj.get("label").toString(); + expectedEventTypes.add(label); + } + + assertTrue(expectedEventTypes.equals(generatedEventTypes)); + assertTrue(idCount == generatedEventTypesList.size()); + assertTrue(idCount == labelCount); + } + + /* + * Tests for the makeEventsJSON method - Checks that each event has an + * associated ID, traceID, traceIndex, eventTypeID and timestamp and these + * values are not null + */ + @Test + public void eventsTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> generatedEventsList = JsonExporter + .makeEventsJSON(evGraph); + int idCount = 0; + int traceIDCount = 0; + int traceIndexCount = 0; + int eventTypeIDCount = 0; + int timestampCount = 0; + + for (Map genEvent : generatedEventsList) { + + if (genEvent.containsKey("id")) { + if (genEvent.get("id") == null) { + Assert.fail("Events contains null id."); + } + idCount++; + } + if (genEvent.containsKey("traceID")) { + if (genEvent.get("traceID") == null) { + Assert.fail("Events contains null traceID."); + } + traceIDCount++; + } + if (genEvent.containsKey("traceIndex")) { + if (genEvent.get("traceIndex") == null) { + Assert.fail("Events contains null traceIndex."); + } + traceIndexCount++; + } + if (genEvent.containsKey("eventTypeID")) { + if (genEvent.get("eventTypeID") == null) { + Assert.fail("Events contains null eventTypeID."); + } + eventTypeIDCount++; + } + if (genEvent.containsKey("timestamp")) { + if (genEvent.get("timestamp") == null) { + Assert.fail("Events contains null timestamp."); + } + timestampCount++; + } + } + + assertTrue(idCount == generatedEventsList.size()); + assertTrue(idCount == traceIDCount); + assertTrue(idCount == traceIndexCount); + assertTrue(idCount == eventTypeIDCount); + assertTrue(idCount == timestampCount); + } + + /* + * Tests for the makeInvariantTypesJSON method - Checks that each + * invariantType has an associated ID, type, and short name and that these + * values are not null + */ + @Test + public void invariantTypesTest() throws Exception { + + List> generatedInvariantTypesList = JsonExporter + .makeInvariantTypesJSON(pGraph); + Set generatedInvariantTypes = new HashSet<>(); + int idCount = 0; + int typeCount = 0; + int shortNameCount = 0; + + for (Map genInvariantType : generatedInvariantTypesList) { + + if (genInvariantType.get("type") != null) { + generatedInvariantTypes + .add(genInvariantType.get("type").toString()); + } + if (genInvariantType.containsKey("id")) { + if (genInvariantType.get("id") == null) { + Assert.fail("InvariantTypes contains a null id."); + } + idCount++; + } + if (genInvariantType.containsKey("type")) { + if (genInvariantType.get("type") == null) { + Assert.fail("InvariantTypes contains a null type."); + } + typeCount++; + } + if (genInvariantType.containsKey("shortName")) { + if (genInvariantType.get("shortName") == null) { + Assert.fail("InvariantTypes contains a null shortName."); + } + shortNameCount++; + } + } + + JSONArray invariantTypes = parseJSONArray("invariantTypes"); + Set expectedInvariantTypes = new HashSet<>(); + + for (Object expInvariantType : invariantTypes) { + + JSONObject invariantTypeJSONObj = (JSONObject) expInvariantType; + String type = invariantTypeJSONObj.get("type").toString(); + expectedInvariantTypes.add(type); + } + + assertTrue(expectedInvariantTypes.equals(generatedInvariantTypes)); + assertTrue(idCount == generatedInvariantTypesList.size()); + assertTrue(idCount == typeCount); + assertTrue(idCount == shortNameCount); + } + + /* + * Tests for the makeInvariantsJSON method Checks - that each invariant has + * an associated ID and invariantTypeID and that these values are not null, + * if there is an associated predicate or resourceBoound, check that there + * values are not null + */ + @Test + public void invariantsTest() throws Exception { + + List> generatedInvariantsList = JsonExporter + .makeInvariantsJSON(pGraph); + int idCount = 0; + int invariantTypeIDCount = 0; + + for (Map genInvariant : generatedInvariantsList) { + + if (genInvariant.containsKey("id")) { + if (genInvariant.get("id") == null) { + Assert.fail("Invariants contains a null id."); + } + idCount++; + } + if (genInvariant.containsKey("invariantTypeID")) { + if (genInvariant.get("invariantTypeID") == null) { + Assert.fail("Invariants contains a null invariantTypeID."); + } + invariantTypeIDCount++; + } + if (genInvariant.containsKey("predicates") + && genInvariant.get("predicates") == null) { + Assert.fail("Invariants contains null predicates."); + } + if (genInvariant.containsKey("resourceBounds") + && genInvariant.get("resourceBounds") == null) { + Assert.fail("Invariants contains null resourceBounds."); + } + } + + assertTrue(idCount == generatedInvariantsList.size()); + assertTrue(idCount == invariantTypeIDCount); + } + + /* + * Tests for the makeLogStatementsJSON method - Checks that each + * logStatement has an associated ID, text, and log position and that these + * values are not null + */ + @Test + public void logStatementsTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> generatedLogStatementsList = JsonExporter + .makeLogStatementsJSON(evGraph); + Set generatedLogStatements = new HashSet<>(); + int idCount = 0; + int textCount = 0; + int logPositionCount = 0; + + for (Map genLogStatement : generatedLogStatementsList) { + + if (genLogStatement.get("text") != null) { + generatedLogStatements + .add(genLogStatement.get("text").toString()); + } + if (genLogStatement.containsKey("id")) { + if (genLogStatement.get("id") == null) { + Assert.fail("LogStatements contains a null id."); + } + idCount++; + } + if (genLogStatement.containsKey("text")) { + if (genLogStatement.get("text") == null) { + Assert.fail("LogStatements contains a null text."); + } + textCount++; + } + if (genLogStatement.containsKey("logPosition")) { + if (genLogStatement.get("logPosition") == null) { + Assert.fail("LogStatements contains a null logPosition."); + } + logPositionCount++; + } + } + + JSONArray logStatements = parseJSONArray("logStatements"); + Set expectedLogStatements = new HashSet<>(); + + for (Object expLogStatement : logStatements) { + JSONObject logStatementJSONObj = (JSONObject) expLogStatement; + String type = logStatementJSONObj.get("text").toString(); + expectedLogStatements.add(type); + } + + assertTrue(expectedLogStatements.equals(generatedLogStatements)); + assertTrue(idCount == generatedLogStatementsList.size()); + assertTrue(idCount == textCount); + assertTrue(idCount == logPositionCount); + } + + /* + * Tests for the makeLinksJSON method - Checks that each link has an + * associated id1 and id2 and that these values are not null + */ + @Test + public void linksTest() throws Exception { + + EvBasedGraph evGraph = createEvGraph(); + List> generatedEdgesList = JsonExporter + .makeEdgesJSON(evGraph); + List> generatedLinksList = JsonExporter + .makeLinksJSON(evGraph); + int idCount1 = 0; + int idCount2 = 0; + + for (Map genLink : generatedLinksList) { + + if (genLink.containsKey("id1")) { + if (genLink.get("id1") == null) { + Assert.fail("Links contains a null id1."); + } + idCount1++; + } + if (genLink.containsKey("id2")) { + if (genLink.get("id2") == null) { + Assert.fail("Links contains a null id2."); + } + idCount2++; + } + } + + assertTrue(idCount1 == generatedLinksList.size()); + assertTrue(idCount1 == idCount2); + } +} \ No newline at end of file