Change ASCII conversion for hash and add tests

Hash was printed using snprintf(), but we can just write yet another hex
conversion utility!

Change-Id: I04f1992deaf5bf1b3e2751c8f07072f8ed6660e9
diff --git a/Android.mk b/Android.mk
index ce1a603..e353a4f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,38 +7,59 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+common_src_files := \
+	VolumeManager.cpp \
+	CommandListener.cpp \
+	VoldCommand.cpp \
+	NetlinkManager.cpp \
+	NetlinkHandler.cpp \
+	Volume.cpp \
+	DirectVolume.cpp \
+	logwrapper.c \
+	Process.cpp \
+	Fat.cpp \
+	Loop.cpp \
+	Devmapper.cpp \
+	ResponseCode.cpp \
+	Xwarp.cpp
+
+common_c_includes := \
+	$(KERNEL_HEADERS) \
+	external/openssl/include
+
+common_shared_libraries := \
+	libsysutils \
+	libcutils \
+	libdiskconfig \
+	libcrypto
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:=                                      \
-                  main.cpp                             \
-                  VolumeManager.cpp                    \
-                  CommandListener.cpp                  \
-                  VoldCommand.cpp                      \
-                  NetlinkManager.cpp                   \
-                  NetlinkHandler.cpp                   \
-                  Volume.cpp                           \
-                  DirectVolume.cpp                     \
-                  logwrapper.c                         \
-                  Process.cpp                          \
-                  Fat.cpp                              \
-                  Loop.cpp                             \
-                  Devmapper.cpp                        \
-                  ResponseCode.cpp                     \
-                  Xwarp.cpp
+LOCAL_MODULE := libvold
+
+LOCAL_SRC_FILES := $(common_src_files)
+
+LOCAL_C_INCLUDES := $(common_c_includes)
+
+LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
+
+LOCAL_MODULE_TAGS := eng tests
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
 
 LOCAL_MODULE:= vold
 
-LOCAL_C_INCLUDES :=                          \
-                    $(KERNEL_HEADERS)        \
-                    external/openssl/include
+LOCAL_SRC_FILES := \
+	main.cpp \
+	$(common_src_files)
+
+LOCAL_C_INCLUDES := $(common_c_includes)
 
 LOCAL_CFLAGS := 
 
-LOCAL_SHARED_LIBRARIES :=               \
-                          libsysutils   \
-                          libcutils     \
-                          libdiskconfig \
-                          libcrypto
+LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 8a70c37..4f803fc 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -63,26 +63,34 @@
     delete mActiveContainers;
 }
 
-#define MD5_ASCII_LENGTH ((MD5_DIGEST_LENGTH*2)+1)
-
 char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
+    static const char* digits = "0123456789abcdef";
+
     unsigned char sig[MD5_DIGEST_LENGTH];
 
-    if (len < MD5_ASCII_LENGTH) {
-        SLOGE("Target hash buffer size < %d bytes (%d)", MD5_ASCII_LENGTH, len);
+    if (buffer == NULL) {
+        SLOGE("Destination buffer is NULL");
+        errno = ESPIPE;
+        return NULL;
+    } else if (id == NULL) {
+        SLOGE("Source buffer is NULL");
+        errno = ESPIPE;
+        return NULL;
+    } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) {
+        SLOGE("Target hash buffer size < %d bytes (%d)",
+                MD5_ASCII_LENGTH_PLUS_NULL, len);
         errno = ESPIPE;
         return NULL;
     }
 
     MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig);
 
-    memset(buffer, 0, len);
-
+    char *p = buffer;
     for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
-        char tmp[3];
-        snprintf(tmp, 3, "%.02x", sig[i]);
-        strcat(buffer, tmp);
+        *p++ = digits[sig[i] >> 4];
+        *p++ = digits[sig[i] & 0x0F];
     }
+    *p = '\0';
 
     return buffer;
 }
diff --git a/VolumeManager.h b/VolumeManager.h
index 2ec9eb3..0693fd0 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -24,6 +24,9 @@
 
 #include "Volume.h"
 
+/* The length of an MD5 hash when encoded into ASCII hex characters */
+#define MD5_ASCII_LENGTH_PLUS_NULL ((MD5_DIGEST_LENGTH*2)+1)
+
 typedef android::List<char *> AsecIdCollection;
 
 class VolumeManager {
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..8ae4b5d
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,36 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+test_src_files := \
+	VolumeManager_test.cpp
+
+shared_libraries := \
+	liblog \
+	libstlport \
+	libcrypto
+
+static_libraries := \
+	libvold \
+	libgtest \
+	libgtest_main
+
+c_includes := \
+	external/openssl/include \
+	bionic \
+	bionic/libstdc++/include \
+	external/gtest/include \
+	external/stlport/stlport
+
+module_tags := eng tests
+
+$(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+    $(eval include $(BUILD_EXECUTABLE)) \
+)
diff --git a/tests/VolumeManager_test.cpp b/tests/VolumeManager_test.cpp
new file mode 100644
index 0000000..c0c1fa5
--- /dev/null
+++ b/tests/VolumeManager_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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 <errno.h>
+
+#define LOG_TAG "VolumeManager_test"
+#include <utils/Log.h>
+#include <openssl/md5.h>
+#include "../VolumeManager.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class VolumeManagerTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+    }
+
+    virtual void TearDown() {
+    }
+};
+
+TEST_F(VolumeManagerTest, AsecHashTests) {
+    char buffer[MD5_ASCII_LENGTH_PLUS_NULL];
+    char* dst = reinterpret_cast<char*>(&buffer);
+
+    const char* src1 = "";
+    const char* exp1 = "d41d8cd98f00b204e9800998ecf8427e";
+
+    EXPECT_TRUE(VolumeManager::asecHash(exp1, (char*)NULL, sizeof(buffer)) == NULL && errno == ESPIPE)
+            << "Should return NULL and set errno to ESPIPE when destination buffer is NULL";
+    EXPECT_TRUE(VolumeManager::asecHash(exp1, dst, 0) == NULL && errno == ESPIPE)
+            << "Should return NULL and set errno to ESPIPE when destination buffer length is 0";
+    EXPECT_TRUE(VolumeManager::asecHash((const char*)NULL, dst, sizeof(buffer)) == NULL && errno == ESPIPE)
+            << "Should return NULL and set errno to ESPIPE when source buffer is NULL";
+
+    EXPECT_FALSE(VolumeManager::asecHash(src1, dst, sizeof(buffer)) == NULL)
+            << "Should not return NULL on valid source, destination, and destination size";
+    EXPECT_STREQ(exp1, dst)
+            << "MD5 summed output should match";
+
+    const char* src2 = "android";
+    const char* exp2 = "c31b32364ce19ca8fcd150a417ecce58";
+    EXPECT_FALSE(VolumeManager::asecHash(src2, dst, sizeof(buffer)) == NULL)
+            << "Should not return NULL on valid source, destination, and destination size";
+    EXPECT_STREQ(exp2, dst)
+            << "MD5 summed output should match";
+}
+
+}