Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonExporter changes #409

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Conversation

wborkofsky
Copy link

No description provided.

Copy link
Member

@bestchai bestchai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has all this new code been tested? I don't see any changes to tests as part of this pull request.

Copy link
Contributor

@ohmann ohmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of comments, sending because the code is getting a little messy for me to look through, so I'll wait for you to address this first round before I do a second round of comments. If you notice that one of my comments also applies to other areas of your code I didn't explicitly comment on, please make the relevant changes in those spots as well to prevent me from needing to make the same comments again next round. Also, please add tests as Ivan suggested (although I realize that my original JsonExporter code erroneously was untested as well). Thanks for doing this!

// EvBasedGraph evGraph = new EvBasedGraph(pGraph);
// System.out.println("evGraph:\n" + evGraph);
EvBasedGraph evGraph = new EvBasedGraph(pGraph);
System.out.println("evGraph:\n" + evGraph);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this for debugging and illustration purposes only. Remove this line.

List<Map<String, Integer>> edgesList = makeEdgesJSON(evGraph);
finalModelMap.put("edges", edgesList);

finalModelMap.put("displayables", displayablesList);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this up to where displayablesList is declared and assigned.


// Add invariants to final model map
List<Map<String, Object>> invariantList = makeInvariantsJSON(pGraph);
finalModelMap.put("invariants", invariantList);
List<Map<String, Object>> invariantsList = makeNewInvariantsJSON(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the method name have New in its name? Unless this means something, remove it.

private static List<Map<String, Integer>> makeNodesJSON(
EvBasedGraph evGraph) {

// List of Nodes to go into the JSON object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nodes -> nodes

// List of Nodes to go into the JSON object
List<Map<String, Integer>> nodeList = new LinkedList<Map<String, Integer>>();

// add the initial node first
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add -> Add

singleEventMap.put("eventType", evType.toString());
singleEventMap.put("logLine", event.getLineNum());
// Map of edges and their respective global ID's
static Map<EvBasedEdge, Integer> edgesIDMap = new LinkedHashMap<EvBasedEdge, Integer>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the same thing I mentioned above for nodesIDMap.

// Contains all edges used to avoid duplicates
Set<EvBasedEdge> usedEdges = new HashSet<EvBasedEdge>();

for (EvBasedNode node : allNodes) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just put evGraph.getNodes() directly within this foreach statement, since allNodes isn't used anywhere but here. Make the same change in all other methods that follow this same pattern.

// Get the edges for each node
Set<EvBasedEdge> allEdges = node.outEdges;

for (EvBasedEdge edge : allEdges) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for the allNodes foreach above.

edgesMap.put("destNodeID", nodesIDMap.get(edge.destNode));
String displayableValue = edge.eType.getETypeLabel() + " ["
+ edge.resMin + ", " + edge.resMax + "]";
if (edge.resMin == null && edge.resMax == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means displayableValue will be set twice for every edge for Synoptic models and still twice for a few edges touching initial or terminal in Perfume models. Instead, just assign displayableValue once based on whether this condition is true (so basically do an if/else instead of just an if).

// Add this event to this trace's list of events
singleTraceEventsList.add(singleEventMap);
// Map of displayables and their respective global ID's
static Map<String, Integer> displayablesIDMap = new LinkedHashMap<String, Integer>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above global maps.

// Map of nodes and their respective global ID's
private static Map<EvBasedNode, Integer> nodesIDMap;
// Map of edges and their respective global ID's
static Map<EvBasedEdge, Integer> edgesIDMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the rest of these global maps private, and initialize all of them to null.

List<Map<String, Integer>> nodeList = new LinkedList<>();

// Add the initial node first
Map<String, Integer> iniNodeMap = makeNode(evGraph,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ini -> init

@ohmann
Copy link
Contributor

ohmann commented Feb 23, 2017

You can manually run the whole Perfume algorithm to get a partition graph for your tests. Try this skeleton test class I made, and you can add your actual tests to it. Please don't actually use file i/o like my fake renameMeTest does. Change JsonExporter so that you can test it without actually outputting a file (just refactor it so that you aren't forced to generate the json and output a file, since this is currently all within the only entry point, exportJsonObject()).

package synoptic.tests.units;

import java.util.Arrays;

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;

/**
 * TODO
 */
public class JsonExporterTests {
    private PerfumeMain main;
    private PartitionGraph pGraph;

    @Before
    public void setUp() throws Exception {
        // Set up Perfume options
        PerfumeOptions perfOpts = new PerfumeOptions();
        perfOpts.regExps = Arrays.asList(
                new String[] { "(?<ip>.+), (?<TYPE>.+), (?<DTIME>.+)" });
        perfOpts.partitionRegExp = "\\k<ip>";
        perfOpts.logFilenames.add(
                "../traces/abstract/perfume-survey/browser-caching-traces.txt");

        // Create a Perfume instance
        AbstractMain.instance = null;
        main = new PerfumeMain(perfOpts.toAbstractOptions(),
                new DotExportFormatter());

        // Run Perfume
        pGraph = main.createInitialPartitionGraph();
        main.runSynoptic(pGraph);
    }

    /**
     * TODO
     */
    @Test
    public void renameMeTest() throws Exception {
        JsonExporter.exportJsonObject("/tmp/JsonExporterTest", pGraph);
    }
}

@@ -137,5 +137,4 @@ public void mainTest() throws Exception {
AbstractMain.instance = null;
SynopticMain.main(args);
}

}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hg revert this file

@@ -175,4 +175,4 @@ public void basicDistEventTypeTest() {
assertTrue(e1.compareTo(e2) == 0);
assertTrue(e2.compareTo(e1) == 0);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hg revert this file

private Object expectedJSON = null;
private JSONObject expectedJSONObject = null;
private JSONParser parser = null;
private String dispJString = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general (but almost always), global variables should be avoided when possible. Almost all of these should not be global and should be private, method-level variables. For example, main is only used within setUp(). The only reason a global variable should be used is if the state of an object needs to be shared between methods (not just the variable being re-initialized with the same name in different methods but actually the same object) and if there is no other convenient way to pass that object between the methods (e.g., parameters, return values). Please make these local variables instead of global.

new String[] { "(?<ip>.+), (?<TYPE>.+), (?<DTIME>.+)" });
perfOpts.partitionRegExp = "\\k<ip>";
perfOpts.logFilenames.add(
"./traces/abstract/perfume-survey/browser-caching-traces.txt");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed over email, different methods of running these unit tests result in different behaviors when using relative paths like this one (the current directory when running the tests is either $repo/synoptic/synoptic or $repo/synoptic/synoptic/bin). Therefore, this test will fail when run in some ways and pass in others.

Add a static method somewhere (perhaps SynopticTest) that resolves paths for files used by unit tests. Make the method something like getTestPath(String path) that takes a relative path like this--but probably omit the ./--and checks if the file as written works. If not, it checks if ../$pathAsWritten works. If neither work, it throws an exception. Call that new method from here to get the path for this txt file.

… JsonExporterTests, and added getTestPath method to SynopticTest.
Copy link
Contributor

@ohmann ohmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments. Also remove file abc

*/
@Test
public void displayableTest() throws Exception {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the commented-out test code below looks useful. You could verify that each of the expected displayable values (e.g., "cache-page [9,17]") is present, right? The latter could be done quickly by populating a set of all the displayable values in expected vs. in reality and then simply checking expectedSet.equals(actualSet) (but perhaps with better variable names). That lets you make java Set equality do all the work. You could do similar check for other lists that have meaningful string values, like invariantTypes and logStatements.

However, I totally understand your desire to avoid doing a very long and complex check that the entire graph of edges and nodes match up. :) I think my suggestions above hit a nice compromise, since they shouldn't be too difficult or long, but they still verify real expected values rather than only verifying that the produced JSON has the right lists that we expect and doesn't have nulls.

idCount++;
} else if (m.containsKey("id") && m.get("id") == null) {
res = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is redundancy here (the m.containsKey("id") check), plus you should fail immediately when you encounter a problem rather than using res. Basically:

if (m.containsKey("id")) {
    if (m.get("id") == null) {
        fail("Displayable contains null id");
    }
    idCount++;
}

Remove res entirely from this test and others, and change all of the conditionals to check this way. I'm not sure if you need to import anything or do the fail() call slightly differently, perhaps Assert.fail() instead, basically, use this.

*/
public static String getTestPath(String path) throws Exception {

// original path passed in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Capitalize to keep comments consistent with the rest of the codebase ("Original path passed in"). Same for other comments

// parent directory of the path passed in
File parentPath = new File("../" + path);
// removing parent directory (../) from path passed in
File parentRemovedPath = new File(path.substring(3));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure the path startsWith("../") or "..\" first. Also, there are more robust ways to do these kinds of path manipulations in general, like using Paths and Path, but I'm OK with these simple ways, since this is only used in our tests.

… corrected comments, and changed testPath method.
@ohmann
Copy link
Contributor

ohmann commented Jun 13, 2017

@wborkofsky I apologize that I forgot about this pull request. Did your changes after my March 29 comments address all of my comments, i.e., am I up to bat on this? I saw that at least some of the comments were addressed, but I saw that you didn't remove the abc file, and I didn't look closely enough to see if you did anything with my request to do a set comparison of some of the fields for actual vs. expected, e.g., expected edge displayables are {"cache-image", "retrieve-image"}, then ensure the actual JSON contains exactly those same edge displayables. (As I mentioned in the comment, it would be hard and annoying to ensure the expected/actual JSON exactly matches up because IDs will change, but this is sort of getting halfway there.)

If everything is addressed or you have responses to any comments you didn't address, please let me know, and I'll take another look!

@ohmann
Copy link
Contributor

ohmann commented Oct 12, 2017

@wborkofsky Just a quick ping on my question from above: "Did your changes after my March 29 comments address all of my comments, i.e., am I up to bat on this?"

@wborkofsky
Copy link
Author

wborkofsky commented Oct 19, 2017

@ohmann It appears that I did add in all the test code for set equality. For the file "abc", in my commit, it shows as all lines removed so I believe it should have been removed as well and it does not appear in the synoptic version on my computer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants