Merge "DictionaryWriter to abstract binary dictionary writing."
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 2b8dbbc..6e1b80e 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -201,7 +201,7 @@
 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict,
         jintArray word) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
-    if (!dictionary) return 0;
+    if (!dictionary) return NOT_A_PROBABILITY;
     const jsize wordLength = env->GetArrayLength(word);
     int codePoints[wordLength];
     env->GetIntArrayRegion(word, 0, wordLength, codePoints);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
index cca81a0..87acafe 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
@@ -51,7 +51,7 @@
 @LargeTest
 public class BinaryDictIOTests extends AndroidTestCase {
     private static final String TAG = BinaryDictIOTests.class.getSimpleName();
-    private static final int MAX_UNIGRAMS = 100;
+    private static final int DEFAULT_MAX_UNIGRAMS = 100;
     private static final int UNIGRAM_FREQ = 10;
     private static final int BIGRAM_FREQ = 50;
     private static final int TOLERANCE_OF_BIGRAM_FREQ = 5;
@@ -73,13 +73,15 @@
             new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */);
 
     public BinaryDictIOTests() {
-        super();
+        this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS);
+    }
 
-        final long time = System.currentTimeMillis();
-        Log.e(TAG, "Testing dictionary: seed is " + time);
-        final Random random = new Random(time);
+    public BinaryDictIOTests(final long seed, final int maxUnigrams) {
+        super();
+        Log.e(TAG, "Testing dictionary: seed is " + seed);
+        final Random random = new Random(seed);
         sWords.clear();
-        generateWords(MAX_UNIGRAMS, random);
+        generateWords(maxUnigrams, random);
 
         for (int i = 0; i < sWords.size(); ++i) {
             sChainBigrams.put(i, new ArrayList<Integer>());
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java
index 7b311c3..cacee52 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java
@@ -72,15 +72,21 @@
         return command;
     }
 
-    private void execute(final String[] arguments) {
+    /**
+     * Executes the specified command with the specified arguments.
+     * @param arguments the arguments passed to dicttool.
+     * @return 0 for success, an error code otherwise (always 1 at the moment)
+     */
+    private int execute(final String[] arguments) {
         final Command command = getCommand(arguments);
         try {
             command.run();
+            return 0;
         } catch (Exception e) {
             System.out.println("Exception while processing command "
                     + command.getClass().getSimpleName() + " : " + e);
             e.printStackTrace();
-            return;
+            return 1;
         }
     }
 
@@ -89,6 +95,7 @@
             help();
             return;
         }
-        new Dicttool().execute(arguments);
+        // Exit with the success/error code from #execute() as status.
+        System.exit(new Dicttool().execute(arguments));
     }
 }
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
index df5ea35..972b6e7 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
@@ -16,10 +16,12 @@
 
 package com.android.inputmethod.latin.dicttool;
 
+import com.android.inputmethod.latin.makedict.BinaryDictIOTests;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtilsTests;
 import com.android.inputmethod.latin.makedict.BinaryDictInputOutputTest;
 import com.android.inputmethod.latin.makedict.FusionDictionaryTest;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -36,7 +38,8 @@
         BinaryDictOffdeviceUtilsTests.class,
         FusionDictionaryTest.class,
         BinaryDictInputOutputTest.class,
-        BinaryDictIOUtilsTests.class
+        BinaryDictIOUtilsTests.class,
+        BinaryDictIOTests.class
     };
     private ArrayList<Method> mAllTestMethods = new ArrayList<Method>();
     private ArrayList<String> mUsedTestMethods = new ArrayList<String>();
@@ -86,12 +89,19 @@
         for (final Method m : mAllTestMethods) {
             final Class<?> declaringClass = m.getDeclaringClass();
             if (!mUsedTestMethods.isEmpty() && !mUsedTestMethods.contains(m.getName())) continue;
-            final Object instance;
-            if (BinaryDictIOUtilsTests.class == declaringClass) {
-                instance = new BinaryDictIOUtilsTests(mSeed, mMaxUnigrams);
-            } else {
-                instance = declaringClass.newInstance();
+            // Some of the test classes expose a two-argument constructor, taking a long as a
+            // seed for Random, and an int for a vocabulary size to test the dictionary with. They
+            // correspond respectively to the -s and -m numerical arguments to this command, which
+            // are stored in mSeed and mMaxUnigrams. If the two-arguments constructor is present,
+            // then invoke it; otherwise, invoke the default constructor.
+            Constructor<?> twoArgsConstructor = null;
+            try {
+                twoArgsConstructor = declaringClass.getDeclaredConstructor(Long.TYPE, Integer.TYPE);
+            } catch (NoSuchMethodException e) {
+                // No constructor with two args
             }
+            final Object instance = null == twoArgsConstructor ? declaringClass.newInstance()
+                    : twoArgsConstructor.newInstance(mSeed, mMaxUnigrams);
             m.invoke(instance);
         }
     }