Merge "edify: Move the testcases to gtest."
diff --git a/edify/Android.mk b/edify/Android.mk
index b397045..d8058c1 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -1,24 +1,36 @@
 # Copyright 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
 LOCAL_PATH := $(call my-dir)
 
 edify_src_files := \
-	lexer.ll \
-	parser.yy \
-	expr.cpp
+    lexer.ll \
+    parser.yy \
+    expr.cpp
 
 #
-# Build the host-side command line tool
+# Build the host-side command line tool (host executable)
 #
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-		$(edify_src_files) \
-		main.cpp
+    $(edify_src_files) \
+    edify_parser.cpp
 
 LOCAL_CFLAGS := -Werror
 LOCAL_CPPFLAGS := -g -O0
-LOCAL_MODULE := edify
+LOCAL_MODULE := edify_parser
 LOCAL_YACCFLAGS := -v
 LOCAL_CPPFLAGS += -Wno-unused-parameter
 LOCAL_CPPFLAGS += -Wno-deprecated-register
@@ -29,7 +41,7 @@
 include $(BUILD_HOST_EXECUTABLE)
 
 #
-# Build the device-side library
+# Build the device-side library (static library)
 #
 include $(CLEAR_VARS)
 
diff --git a/edify/edify_parser.cpp b/edify/edify_parser.cpp
new file mode 100644
index 0000000..16ad2ab
--- /dev/null
+++ b/edify/edify_parser.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This is a host-side tool for validating a given edify script file.
+ *
+ * We used to have edify test cases here, which have been moved to
+ * tests/component/edify_test.cpp.
+ *
+ * Caveat: It doesn't recognize functions defined through updater, which
+ * makes the tool less useful. We should either extend the tool or remove it.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "expr.h"
+#include "parser.h"
+
+void ExprDump(int depth, Expr* n, char* script) {
+    printf("%*s", depth*2, "");
+    char temp = script[n->end];
+    script[n->end] = '\0';
+    printf("%s %p (%d-%d) \"%s\"\n",
+           n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
+           script+n->start);
+    script[n->end] = temp;
+    for (int i = 0; i < n->argc; ++i) {
+        ExprDump(depth+1, n->argv[i], script);
+    }
+}
+
+int main(int argc, char** argv) {
+    RegisterBuiltins();
+    FinishRegistration();
+
+    if (argc != 2) {
+        printf("Usage: %s <edify script>\n", argv[0]);
+        return 1;
+    }
+
+    FILE* f = fopen(argv[1], "r");
+    if (f == NULL) {
+        printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
+        return 1;
+    }
+    char buffer[8192];
+    int size = fread(buffer, 1, 8191, f);
+    fclose(f);
+    buffer[size] = '\0';
+
+    Expr* root;
+    int error_count = 0;
+    int error = parse_string(buffer, &root, &error_count);
+    printf("parse returned %d; %d errors encountered\n", error, error_count);
+    if (error == 0 || error_count > 0) {
+
+        ExprDump(0, root, buffer);
+
+        State state;
+        state.cookie = NULL;
+        state.script = buffer;
+        state.errmsg = NULL;
+
+        char* result = Evaluate(&state, root);
+        if (result == NULL) {
+            printf("result was NULL, message is: %s\n",
+                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
+            free(state.errmsg);
+        } else {
+            printf("result is [%s]\n", result);
+        }
+    }
+    return 0;
+}
diff --git a/edify/main.cpp b/edify/main.cpp
deleted file mode 100644
index 6007a3d..0000000
--- a/edify/main.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include "expr.h"
-#include "parser.h"
-
-extern int yyparse(Expr** root, int* error_count);
-
-int expect(const char* expr_str, const char* expected, int* errors) {
-    Expr* e;
-    char* result;
-
-    printf(".");
-
-    int error_count = parse_string(expr_str, &e, &error_count);
-    if (error_count > 0) {
-        printf("error parsing \"%s\" (%d errors)\n",
-               expr_str, error_count);
-        ++*errors;
-        return 0;
-    }
-
-    State state;
-    state.cookie = NULL;
-    state.script = strdup(expr_str);
-    state.errmsg = NULL;
-
-    result = Evaluate(&state, e);
-    free(state.errmsg);
-    free(state.script);
-    if (result == NULL && expected != NULL) {
-        printf("error evaluating \"%s\"\n", expr_str);
-        ++*errors;
-        return 0;
-    }
-
-    if (result == NULL && expected == NULL) {
-        return 1;
-    }
-
-    if (strcmp(result, expected) != 0) {
-        printf("evaluating \"%s\": expected \"%s\", got \"%s\"\n",
-               expr_str, expected, result);
-        ++*errors;
-        free(result);
-        return 0;
-    }
-
-    free(result);
-    return 1;
-}
-
-int test() {
-    int errors = 0;
-
-    expect("a", "a", &errors);
-    expect("\"a\"", "a", &errors);
-    expect("\"\\x61\"", "a", &errors);
-    expect("# this is a comment\n"
-           "  a\n"
-           "   \n",
-           "a", &errors);
-
-
-    // sequence operator
-    expect("a; b; c", "c", &errors);
-
-    // string concat operator
-    expect("a + b", "ab", &errors);
-    expect("a + \n \"b\"", "ab", &errors);
-    expect("a + b +\nc\n", "abc", &errors);
-
-    // string concat function
-    expect("concat(a, b)", "ab", &errors);
-    expect("concat(a,\n \"b\")", "ab", &errors);
-    expect("concat(a + b,\nc,\"d\")", "abcd", &errors);
-    expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors);
-
-    // logical and
-    expect("a && b", "b", &errors);
-    expect("a && \"\"", "", &errors);
-    expect("\"\" && b", "", &errors);
-    expect("\"\" && \"\"", "", &errors);
-    expect("\"\" && abort()", "", &errors);   // test short-circuiting
-    expect("t && abort()", NULL, &errors);
-
-    // logical or
-    expect("a || b", "a", &errors);
-    expect("a || \"\"", "a", &errors);
-    expect("\"\" || b", "b", &errors);
-    expect("\"\" || \"\"", "", &errors);
-    expect("a || abort()", "a", &errors);     // test short-circuiting
-    expect("\"\" || abort()", NULL, &errors);
-
-    // logical not
-    expect("!a", "", &errors);
-    expect("! \"\"", "t", &errors);
-    expect("!!a", "t", &errors);
-
-    // precedence
-    expect("\"\" == \"\" && b", "b", &errors);
-    expect("a + b == ab", "t", &errors);
-    expect("ab == a + b", "t", &errors);
-    expect("a + (b == ab)", "a", &errors);
-    expect("(ab == a) + b", "b", &errors);
-
-    // substring function
-    expect("is_substring(cad, abracadabra)", "t", &errors);
-    expect("is_substring(abrac, abracadabra)", "t", &errors);
-    expect("is_substring(dabra, abracadabra)", "t", &errors);
-    expect("is_substring(cad, abracxadabra)", "", &errors);
-    expect("is_substring(abrac, axbracadabra)", "", &errors);
-    expect("is_substring(dabra, abracadabrxa)", "", &errors);
-
-    // ifelse function
-    expect("ifelse(t, yes, no)", "yes", &errors);
-    expect("ifelse(!t, yes, no)", "no", &errors);
-    expect("ifelse(t, yes, abort())", "yes", &errors);
-    expect("ifelse(!t, abort(), no)", "no", &errors);
-
-    // if "statements"
-    expect("if t then yes else no endif", "yes", &errors);
-    expect("if \"\" then yes else no endif", "no", &errors);
-    expect("if \"\" then yes endif", "", &errors);
-    expect("if \"\"; t then yes endif", "yes", &errors);
-
-    // numeric comparisons
-    expect("less_than_int(3, 14)", "t", &errors);
-    expect("less_than_int(14, 3)", "", &errors);
-    expect("less_than_int(x, 3)", "", &errors);
-    expect("less_than_int(3, x)", "", &errors);
-    expect("greater_than_int(3, 14)", "", &errors);
-    expect("greater_than_int(14, 3)", "t", &errors);
-    expect("greater_than_int(x, 3)", "", &errors);
-    expect("greater_than_int(3, x)", "", &errors);
-
-    // big string
-    expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str(), &errors);
-
-    printf("\n");
-
-    return errors;
-}
-
-void ExprDump(int depth, Expr* n, char* script) {
-    printf("%*s", depth*2, "");
-    char temp = script[n->end];
-    script[n->end] = '\0';
-    printf("%s %p (%d-%d) \"%s\"\n",
-           n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
-           script+n->start);
-    script[n->end] = temp;
-    int i;
-    for (i = 0; i < n->argc; ++i) {
-        ExprDump(depth+1, n->argv[i], script);
-    }
-}
-
-int main(int argc, char** argv) {
-    RegisterBuiltins();
-    FinishRegistration();
-
-    if (argc == 1) {
-        return test() != 0;
-    }
-
-    FILE* f = fopen(argv[1], "r");
-    if (f == NULL) {
-        printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
-        return 1;
-    }
-    char buffer[8192];
-    int size = fread(buffer, 1, 8191, f);
-    fclose(f);
-    buffer[size] = '\0';
-
-    Expr* root;
-    int error_count = 0;
-    int error = parse_string(buffer, &root, &error_count);
-    printf("parse returned %d; %d errors encountered\n", error, error_count);
-    if (error == 0 || error_count > 0) {
-
-        ExprDump(0, root, buffer);
-
-        State state;
-        state.cookie = NULL;
-        state.script = buffer;
-        state.errmsg = NULL;
-
-        char* result = Evaluate(&state, root);
-        if (result == NULL) {
-            printf("result was NULL, message is: %s\n",
-                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
-            free(state.errmsg);
-        } else {
-            printf("result is [%s]\n", result);
-        }
-    }
-    return 0;
-}
diff --git a/tests/Android.mk b/tests/Android.mk
index 0754c7a..19cb809 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -41,11 +41,13 @@
 LOCAL_MODULE := recovery_component_test
 LOCAL_C_INCLUDES := bootable/recovery
 LOCAL_SRC_FILES := \
+    component/edify_test.cpp \
     component/verifier_test.cpp \
     component/applypatch_test.cpp
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_STATIC_LIBRARIES := \
     libapplypatch \
+    libedify \
     libotafault \
     libverifier \
     libcrypto_utils \
diff --git a/tests/component/edify_test.cpp b/tests/component/edify_test.cpp
new file mode 100644
index 0000000..ded8540
--- /dev/null
+++ b/tests/component/edify_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "edify/expr.h"
+
+static void expect(const char* expr_str, const char* expected) {
+    Expr* e;
+    int error_count;
+    EXPECT_EQ(parse_string(expr_str, &e, &error_count), 0);
+
+    State state;
+    state.cookie = nullptr;
+    state.errmsg = nullptr;
+    state.script = strdup(expr_str);
+
+    char* result = Evaluate(&state, e);
+
+    if (expected == nullptr) {
+        EXPECT_EQ(result, nullptr);
+    } else {
+        EXPECT_STREQ(result, expected);
+    }
+
+    free(state.errmsg);
+    free(state.script);
+    free(result);
+}
+
+class EdifyTest : public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+        RegisterBuiltins();
+        FinishRegistration();
+    }
+};
+
+TEST_F(EdifyTest, parsing) {
+    expect("a", "a");
+    expect("\"a\"", "a");
+    expect("\"\\x61\"", "a");
+    expect("# this is a comment\n"
+           "  a\n"
+           "   \n",
+           "a");
+}
+
+TEST_F(EdifyTest, sequence) {
+    // sequence operator
+    expect("a; b; c", "c");
+}
+
+TEST_F(EdifyTest, concat) {
+    // string concat operator
+    expect("a + b", "ab");
+    expect("a + \n \"b\"", "ab");
+    expect("a + b +\nc\n", "abc");
+
+    // string concat function
+    expect("concat(a, b)", "ab");
+    expect("concat(a,\n \"b\")", "ab");
+    expect("concat(a + b,\nc,\"d\")", "abcd");
+    expect("\"concat\"(a + b,\nc,\"d\")", "abcd");
+}
+
+TEST_F(EdifyTest, logical) {
+    // logical and
+    expect("a && b", "b");
+    expect("a && \"\"", "");
+    expect("\"\" && b", "");
+    expect("\"\" && \"\"", "");
+    expect("\"\" && abort()", "");   // test short-circuiting
+    expect("t && abort()", nullptr);
+
+    // logical or
+    expect("a || b", "a");
+    expect("a || \"\"", "a");
+    expect("\"\" || b", "b");
+    expect("\"\" || \"\"", "");
+    expect("a || abort()", "a");     // test short-circuiting
+    expect("\"\" || abort()", NULL);
+
+    // logical not
+    expect("!a", "");
+    expect("! \"\"", "t");
+    expect("!!a", "t");
+}
+
+TEST_F(EdifyTest, precedence) {
+    // precedence
+    expect("\"\" == \"\" && b", "b");
+    expect("a + b == ab", "t");
+    expect("ab == a + b", "t");
+    expect("a + (b == ab)", "a");
+    expect("(ab == a) + b", "b");
+}
+
+TEST_F(EdifyTest, substring) {
+    // substring function
+    expect("is_substring(cad, abracadabra)", "t");
+    expect("is_substring(abrac, abracadabra)", "t");
+    expect("is_substring(dabra, abracadabra)", "t");
+    expect("is_substring(cad, abracxadabra)", "");
+    expect("is_substring(abrac, axbracadabra)", "");
+    expect("is_substring(dabra, abracadabrxa)", "");
+}
+
+TEST_F(EdifyTest, ifelse) {
+    // ifelse function
+    expect("ifelse(t, yes, no)", "yes");
+    expect("ifelse(!t, yes, no)", "no");
+    expect("ifelse(t, yes, abort())", "yes");
+    expect("ifelse(!t, abort(), no)", "no");
+}
+
+TEST_F(EdifyTest, if_statement) {
+    // if "statements"
+    expect("if t then yes else no endif", "yes");
+    expect("if \"\" then yes else no endif", "no");
+    expect("if \"\" then yes endif", "");
+    expect("if \"\"; t then yes endif", "yes");
+}
+
+TEST_F(EdifyTest, comparison) {
+    // numeric comparisons
+    expect("less_than_int(3, 14)", "t");
+    expect("less_than_int(14, 3)", "");
+    expect("less_than_int(x, 3)", "");
+    expect("less_than_int(3, x)", "");
+    expect("greater_than_int(3, 14)", "");
+    expect("greater_than_int(14, 3)", "t");
+    expect("greater_than_int(x, 3)", "");
+    expect("greater_than_int(3, x)", "");
+}
+
+TEST_F(EdifyTest, big_string) {
+    // big string
+    expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str());
+}
+