changeset 61:fac8e7fcc8b0

Simple test framework - capable of parsing Hg cmdline output to compare with Java result
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 18 Jan 2011 18:32:49 +0100
parents 613c936d74e4
children 25819103de17
files .classpath design.txt test/com/tmate/hgkit/ExecHelper.java test/com/tmate/hgkit/OutputParser.java test/com/tmate/hgkit/StatusOutputParser.java test/com/tmate/hgkit/TestStatus.java
diffstat 6 files changed, 275 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.classpath	Tue Jan 18 00:30:41 2011 +0100
+++ b/.classpath	Tue Jan 18 18:32:49 2011 +0100
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="test"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
--- a/design.txt	Tue Jan 18 00:30:41 2011 +0100
+++ b/design.txt	Tue Jan 18 18:32:49 2011 +0100
@@ -65,4 +65,6 @@
 <<<<<
 
 Tests:
-DataAccess - readBytes(length > memBufferSize, length*2 > memBufferSize) - to check impl is capable to read huge chunks of data, regardless of own buffer size
\ No newline at end of file
+DataAccess - readBytes(length > memBufferSize, length*2 > memBufferSize) - to check impl is capable to read huge chunks of data, regardless of own buffer size
+
+ExecHelper('cmd', OutputParser()).run(). StatusOutputParser, LogOutputParser extends OutputParser. construct java result similar to that of cmd, compare results
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/tmate/hgkit/ExecHelper.java	Tue Jan 18 18:32:49 2011 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Artem Tikhomirov 
+ */
+package com.tmate.hgkit;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.CharBuffer;
+import java.util.LinkedList;
+
+/**
+ *
+ * @author artem
+ */
+public class ExecHelper {
+
+	private final OutputParser parser;
+	private final File dir;
+
+	public ExecHelper(OutputParser outParser, File workingDir) {
+		parser = outParser;
+		dir = workingDir;
+	}
+
+	public void run(String... cmd) throws IOException, InterruptedException {
+		Process p = new ProcessBuilder(cmd).directory(dir).redirectErrorStream(true).start();
+//		Process p = Runtime.getRuntime().exec(cmd, null, dir);
+		InputStreamReader stdOut = new InputStreamReader(p.getInputStream());
+		LinkedList<CharBuffer> l = new LinkedList<CharBuffer>();
+		int r = -1;
+		CharBuffer b = null;
+		do {
+			if (b == null || b.remaining() < b.capacity() / 3) {
+				b = CharBuffer.allocate(512);
+				l.add(b);
+			}
+			r = stdOut.read(b);
+		} while (r != -1);
+		int total = 0;
+		for (CharBuffer cb : l) {
+			total += cb.position();
+			cb.flip();
+		}
+		CharBuffer res = CharBuffer.allocate(total);
+		for (CharBuffer cb : l) {
+			res.put(cb);
+		}
+		res.flip();
+		p.waitFor();
+		parser.parse(res);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/tmate/hgkit/OutputParser.java	Tue Jan 18 18:32:49 2011 +0100
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2011 Artem Tikhomirov 
+ */
+package com.tmate.hgkit;
+
+/**
+ *
+ * @author artem
+ */
+public interface OutputParser {
+
+	public void parse(CharSequence seq);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/tmate/hgkit/StatusOutputParser.java	Tue Jan 18 18:32:49 2011 +0100
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2011 Artem Tikhomirov 
+ */
+package com.tmate.hgkit;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @author artem
+ */
+public class StatusOutputParser implements OutputParser {
+
+	private final Pattern pattern;
+	private List<String> modified, added, removed, clean, missing, unknown, ignored;
+	private Map<String, String> copied;
+	private final boolean winPathSeparator;
+
+	public StatusOutputParser() {
+//		pattern = Pattern.compile("^([MAR?IC! ]) ([\\w \\.-/\\\\]+)$", Pattern.MULTILINE);
+		pattern = Pattern.compile("^([MAR?IC! ]) (.+)$", Pattern.MULTILINE);
+		winPathSeparator = File.separatorChar == '\\';
+	}
+
+	public void reset() {
+		modified = added = removed = clean = missing = unknown = ignored = null;
+		copied = null;
+	}
+
+	public void parse(CharSequence seq) {
+		Matcher m = pattern.matcher(seq);
+		while (m.find()) {
+			String fname = m.group(2);
+			switch ((int) m.group(1).charAt(0)) {
+			case (int) 'M' : {
+				modified = doAdd(modified, fname);
+				break;
+			}
+			case (int) 'A' : {
+				added = doAdd(added, fname);
+				break;
+			}
+			case (int) 'R' : {
+				removed = doAdd(removed, fname);
+				break;
+			}
+			case (int) '?' : {
+				unknown = doAdd(unknown, fname);
+				break;
+			}
+			case (int) 'I' : {
+				ignored = doAdd(ignored, fname);
+				break;
+			}
+			case (int) 'C' : {
+				clean = doAdd(clean, fname);
+				break;
+			}
+			case (int) '!' : {
+				missing = doAdd(missing, fname);
+				break;
+			}
+			case (int) ' ' : {
+				if (copied == null) {
+					copied = new TreeMap<String, String>();
+				}
+				// last added is copy destination
+				// to get or to remove it - depends on what StatusCollector does in this case
+				copied.put(fname, added.get(added.size() - 1));
+				break;
+			}
+			}
+		}
+	}
+
+	// 
+	public List<String> getModified() {
+		return proper(modified);
+	}
+
+	public List<String> getAdded() {
+		return proper(added);
+	}
+
+	public List<String> getRemoved() {
+		return proper(removed);
+	}
+
+	public Map<String,String> getCopied() {
+		if (copied == null) {
+			return Collections.emptyMap();
+		}
+		return Collections.unmodifiableMap(copied);
+	}
+
+	public List<String> getClean() {
+		return proper(clean);
+	}
+
+	public List<String> getMissing() {
+		return proper(missing);
+	}
+
+	public List<String> getUnknown() {
+		return proper(unknown);
+	}
+
+	public List<String> getIgnored() {
+		return proper(ignored);
+	}
+	
+	private List<String> proper(List<String> l) {
+		if (l == null) {
+			return Collections.emptyList();
+		}
+		return Collections.unmodifiableList(l);
+	}
+
+	private List<String> doAdd(List<String> l, String s) {
+		if (l == null) {
+			l = new LinkedList<String>();
+		}
+		if (winPathSeparator) {
+			// Java impl always give slashed path, while Hg uses local, os-specific convention
+			s = s.replace('\\', '/'); 
+		}
+		l.add(s);
+		return l;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/tmate/hgkit/TestStatus.java	Tue Jan 18 18:32:49 2011 +0100
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2011 Artem Tikhomirov 
+ */
+package com.tmate.hgkit;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.tmate.hgkit.fs.FileWalker;
+import com.tmate.hgkit.fs.RepositoryLookup;
+import com.tmate.hgkit.ll.HgRepository;
+import com.tmate.hgkit.ll.StatusCollector;
+import com.tmate.hgkit.ll.WorkingCopyStatusCollector;
+
+/**
+ *
+ * @author artem
+ */
+public class TestStatus {
+
+	public static void main(String[] args) throws Exception {
+		final StatusOutputParser statusParser = new StatusOutputParser();
+		ExecHelper eh = new ExecHelper(statusParser, null);
+		eh.run("hg", "status", "-A");
+		// run java equivalent
+		HgRepository repo = new RepositoryLookup().detectFromWorkingDir();
+		final WorkingCopyStatusCollector wcc = new WorkingCopyStatusCollector(repo, new FileWalker(new File(System.getProperty("user.dir"))));
+		StatusCollector.Record r = wcc.status(HgRepository.TIP);
+		// compare result
+		reportNotEqual("MODIFIED", r.getModified(), statusParser.getModified());
+		reportNotEqual("ADDED", r.getAdded(), statusParser.getAdded());
+		reportNotEqual("REMOVED", r.getRemoved(), statusParser.getRemoved());
+		reportNotEqual("CLEAN", r.getClean(), statusParser.getClean());
+		reportNotEqual("IGNORED", r.getIgnored(), statusParser.getIgnored());
+		reportNotEqual("MISSING", r.getMissing(), statusParser.getMissing());
+		reportNotEqual("UNKNOWN", r.getUnknown(), statusParser.getUnknown());
+		// TODO compare equals
+	}
+	
+	private static <T> void reportNotEqual(String what, Collection<T> l1, Collection<T> l2) {
+		List<T> diff = difference(l1, l2);
+		System.out.print(what);
+		if (!diff.isEmpty()) {
+			System.out.print(" are NOT the same: ");
+			for (T t : diff) {
+				System.out.print(t);
+				System.out.print(", ");
+			}
+			System.out.println();
+		} else {
+			System.out.println(" are the same");
+		}
+	}
+
+	private static <T> List<T> difference(Collection<T> l1, Collection<T> l2) {
+		LinkedList<T> result = new LinkedList<T>(l2);
+		for (T t : l1) {
+			if (l2.contains(t)) {
+				result.remove(t);
+			} else {
+				result.add(t);
+			}
+		}
+		return result;
+	}
+}