Merge "hnvram: Added option to choose target file + tests"
diff --git a/hnvram/Makefile b/hnvram/Makefile
index 2790f4d..badf27a 100644
--- a/hnvram/Makefile
+++ b/hnvram/Makefile
@@ -11,7 +11,7 @@
 BINDIR=$(DESTDIR)$(PREFIX)/bin
 
 HUMAX_UPGRADE_DIR ?= ../../../humax/misc/libupgrade
-CFLAGS += -g -Os -I$(HUMAX_UPGRADE_DIR) $(EXTRACFLAGS)
+CFLAGS += -g -Os -I$(HUMAX_UPGRADE_DIR) -I$(HUMAX_UPGRADE_DIR)/test $(EXTRACFLAGS)
 LDFLAGS += -L$(HUMAX_UPGRADE_DIR) $(EXTRALDFLAGS)
 
 all: hnvram
@@ -22,12 +22,16 @@
 	$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS) -lhmxupgrade
 
 unit_test: test
-test: hnvram_test
-	./hnvram_test
+test: clean hnvram_unit_test hnvram_integration_test
+	./hnvram_unit_test
+	./hnvram_integration_test
 
-hnvram_test: hnvram_test.cc hnvram_main.c $(INCS)
+hnvram_unit_test: hnvram_test.cc hnvram_main.c $(INCS)
 	$(CPP) $(CFLAGS) hnvram_test.cc -o $@ $(LDFLAGS) -lgtest -lpthread
 
+hnvram_integration_test: hnvram_integration_test.cc
+	$(CPP) $(CFLAGS) hnvram_integration_test.cc -o $@ $(LDFLAGS) -lgtest -lpthread
+
 install:
 	mkdir -p $(BINDIR)
 	cp hnvram $(BINDIR)/hnvram_binary
@@ -36,4 +40,4 @@
 	@echo "No libs to install."
 
 clean:
-	rm -f hnvram hnvram_test *.o
+	rm -f hnvram hnvram_unit_test hnvram_integration_test *.o
diff --git a/hnvram/hnvram_integration_test.cc b/hnvram/hnvram_integration_test.cc
new file mode 100644
index 0000000..b7ceda1
--- /dev/null
+++ b/hnvram/hnvram_integration_test.cc
@@ -0,0 +1,205 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+// Author: germuth@google.com (Aaron Germuth)
+
+// Tests external methods of hnvram_main end-to-end. No stubbing of lower-level
+// methods (Black box testing)
+
+#include <stdio.h>
+#include "gtest/gtest.h"
+#include "hmx_upgrade_nvram.h"
+
+#define TEST_MAIN
+#include "hnvram_main.c"
+#include "hmx_test_base.cc"
+#include "hmx_upgrade_nvram.c"
+#include "hmx_upgrade_flash.c"
+
+// Test constants
+const char* name = "NEW_VAR";
+const char* val = "ABCDEF";
+const char* val2 = "ZZZZZZZZZ";
+const int valLen = 6;
+const int valLen2 = 9;
+
+const char* fieldName = "MAC_ADDR_BT";
+const char* fieldVal = "\x01\x02\x03\x04\x05\x06";
+const char* fieldValStr = "01:02:03:04:05:06";
+const char* fieldVal2 = "12:34:56:78:0a:bc";
+const int fieldValLen = 6;
+
+// Test parameters
+const HMX_NVRAM_PARTITION_E partitions[] =
+  {HMX_NVRAM_PARTITION_RO, HMX_NVRAM_PARTITION_RW};
+HMX_NVRAM_PARTITION_E part;
+
+class HnvramIntegrationTest : public HnvramTest,
+  public ::testing::WithParamInterface<HMX_NVRAM_PARTITION_E> {
+  public:
+    HnvramIntegrationTest() {}
+    virtual ~HnvramIntegrationTest() {}
+
+    virtual void SetUp() {
+      part = GetParam();
+
+      libupgrade_verbose = 0;
+      can_add_flag = 0;
+
+      HnvramTest::SetUp();
+
+      HMX_NVRAM_Init(hnvramFileName);
+    }
+
+    virtual void TearDown() {
+      part = HMX_NVRAM_PARTITION_UNSPECIFIED;
+
+      // clear dlists
+      drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RO, (unsigned char*)fieldName);
+      drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RW, (unsigned char*)name);
+      drv_NVRAM_Delete(HMX_NVRAM_PARTITION_RO, (unsigned char*)name);
+
+      HnvramTest::TearDown();
+    }
+};
+
+TEST_P(HnvramIntegrationTest, TestWriteNvramNew) {
+  // Should fail without can_add
+  EXPECT_EQ(-1, write_nvram_new(name, val, part));
+
+  // Should fail to parse
+  can_add_flag = 1;
+  char valLarge[NVRAM_MAX_DATA + 1];
+  memset(valLarge, 1, sizeof(valLarge));
+  EXPECT_EQ(-2, write_nvram_new(name, valLarge, part));
+
+  // Should fail cleanly with bad partition
+  HMX_NVRAM_Init("/tmp/");
+  EXPECT_EQ(-3, write_nvram_new(name, val, part));
+
+  // Read back writes
+  HMX_NVRAM_Init(hnvramFileName);
+  unsigned char read[255];
+  unsigned int readLen = 0;
+  EXPECT_EQ(0, write_nvram_new(name, val, part));
+  EXPECT_EQ(DRV_OK,
+            HMX_NVRAM_Read(part, (unsigned char*)name, 0,
+                           read, sizeof(read), &readLen));
+  EXPECT_EQ(0, memcmp(val, read, valLen));
+  EXPECT_EQ(valLen, readLen);
+}
+
+TEST_P(HnvramIntegrationTest, TestWriteNvram) {
+  // Should fail with large val
+  char valLarge[NVRAM_MAX_DATA + 1];
+  memset(valLarge, 1, sizeof(valLarge));
+  EXPECT_EQ(-1, write_nvram(name, valLarge, part));
+
+  // Failure to parse
+  EXPECT_EQ(-2, write_nvram(fieldName, "not-proper-mac-addr", part));
+
+  // Variable doesn't already exist
+  EXPECT_EQ(-3, write_nvram(name, val, part));
+
+  // Variable exists in wrong partition
+  can_add_flag = 1;
+  EXPECT_EQ(0, write_nvram_new(name, val, part));
+  EXPECT_EQ(-4, write_nvram(name, val, HMX_NVRAM_PARTITION_W_RAWFS));
+
+  // Fail cleanly from lower-level write
+  HMX_NVRAM_Init("/tmp/");
+  EXPECT_EQ(-5, write_nvram(name, val, part));
+  HMX_NVRAM_Init(hnvramFileName);
+
+  // Try to specify partition with a field variable
+  EXPECT_EQ(0, write_nvram_new(fieldName, fieldVal, part));
+  HMX_NVRAM_Init(hnvramFileName);
+  EXPECT_EQ(-6, write_nvram(fieldName, fieldVal2, part));
+
+  // Failure from lower-level write w/field
+  HMX_NVRAM_Init("/tmp/");
+  char out[255];
+  EXPECT_EQ(-7, write_nvram(fieldName, fieldValStr,
+                        HMX_NVRAM_PARTITION_UNSPECIFIED));
+  HMX_NVRAM_Init(hnvramFileName);
+
+  // Read back val after changing val
+  EXPECT_EQ(0, write_nvram(name, val2, part));
+  unsigned char read[255];
+  unsigned int readLen = 0;
+  EXPECT_EQ(DRV_OK, HMX_NVRAM_Read(part, (unsigned char*)name, 0,
+                           read, sizeof(read), &readLen));
+  EXPECT_EQ(0, memcmp(read, val2, readLen));
+  EXPECT_EQ(readLen, valLen2);
+}
+
+TEST_P(HnvramIntegrationTest, TestClearNvram) {
+  // Delete non-existing variable
+  HMX_NVRAM_Init(hnvramFileName);
+  EXPECT_EQ(DRV_OK, clear_nvram(name));
+
+  can_add_flag = 1;
+  EXPECT_EQ(0, write_nvram_new(name, val, part));
+
+  // No hnvram partition
+  HMX_NVRAM_Init("/tmp/");
+  EXPECT_EQ(DRV_ERR, clear_nvram(name));
+
+  // Delete Existing
+  HMX_NVRAM_Init(hnvramFileName);
+  EXPECT_EQ(DRV_OK, clear_nvram(name));
+}
+
+TEST_P(HnvramIntegrationTest, TestReadNvram) {
+  char readR[255];
+  memset(readR, 56, 30);
+  HMX_NVRAM_PARTITION_E part_used;
+  EXPECT_TRUE(NULL == read_nvram(fieldName, readR, sizeof(readR), 0, &part_used));
+
+  // No variable to find
+  EXPECT_EQ(NULL, read_nvram(name, readR, sizeof(readR), 0, &part_used));
+
+  // Find field
+  can_add_flag = 1;
+  EXPECT_EQ(0, write_nvram_new(fieldName, fieldVal, HMX_NVRAM_PARTITION_RO));
+  EXPECT_FALSE(NULL == read_nvram(fieldName, readR, sizeof(readR), 1, &part_used));
+  EXPECT_EQ(0, memcmp(readR, fieldValStr, 18));
+  EXPECT_EQ(part_used, HMX_NVRAM_PARTITION_RO);
+
+  // Find variable
+  EXPECT_EQ(0, write_nvram_new(name, val, part));
+  EXPECT_FALSE(NULL == read_nvram(name, readR, sizeof(readR), 1, &part_used));
+  EXPECT_EQ(0, memcmp(readR, val, valLen));
+  EXPECT_EQ(part_used, part);
+}
+
+TEST_P(HnvramIntegrationTest, TestInitNvram) {
+  char readR[255];
+  HMX_NVRAM_PARTITION_E part_used;
+
+  // Set envvar to bad file
+  EXPECT_EQ(0, setenv("HNVRAM_LOCATION", "/tmp/", 1));
+  EXPECT_EQ(DRV_OK, init_nvram());
+
+  // Should fail to read
+  EXPECT_TRUE(NULL == read_nvram(name, readR, sizeof(readR), 1, &part_used));
+
+  // Set envvar to proper, empty file
+  EXPECT_EQ(0, setenv("HNVRAM_LOCATION", hnvramFileName, 1));
+  EXPECT_EQ(DRV_OK, init_nvram());
+
+  // Write and read it back
+  can_add_flag = 1;
+  EXPECT_EQ(0, write_nvram_new(name, val, part));
+
+  EXPECT_FALSE(NULL == read_nvram(name, readR, sizeof(readR), 1, &part_used));
+  EXPECT_EQ(0, memcmp(readR, val, valLen));
+  EXPECT_EQ(part_used, part);
+}
+
+INSTANTIATE_TEST_CASE_P(TryAllPartitions, HnvramIntegrationTest,
+                        ::testing::ValuesIn(partitions));
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::testing::AddGlobalTestEnvironment(new HnvramEnvironment);
+  return RUN_ALL_TESTS();
+}
diff --git a/hnvram/hnvram_main.c b/hnvram/hnvram_main.c
index a406caa..4ba0cde 100644
--- a/hnvram/hnvram_main.c
+++ b/hnvram/hnvram_main.c
@@ -42,6 +42,8 @@
   printf("\t-n : toggles whether -w can create new variables. Default is off\n");
   printf("\t-p [RW|RO] : toggles what partition new writes (-n) used. Default is RW\n");
   printf("\t-k VARNAME : delete existing key/value pair from NVRAM.\n");
+  printf("\t Set environment variable: $HNVRAM_LOCATION to change where read/writes are performed.");
+  printf("\t By default hnvram uses '/dev/mtd/hnvram'\n");
 }
 
 // Format of data in the NVRAM
@@ -369,7 +371,7 @@
   return NULL;
 }
 
-DRV_Error clear_nvram(char* optarg) {
+DRV_Error clear_nvram(const char* optarg) {
   DRV_Error err1 = HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_RW,
                                     (unsigned char*)optarg);
   DRV_Error err2 = HMX_NVRAM_Remove(HMX_NVRAM_PARTITION_RO,
@@ -387,7 +389,8 @@
 }
 
 
-int write_nvram(char* name, char* value, HMX_NVRAM_PARTITION_E desired_part) {
+int write_nvram(const char* name, const char* value,
+                HMX_NVRAM_PARTITION_E desired_part) {
   const hnvram_field_t* field = get_nvram_field(name);
   int is_field = (field != NULL);
 
@@ -400,8 +403,8 @@
 
   if (strlen(value) > NVRAM_MAX_DATA) {
     fprintf(stderr, "Value length %d exceeds maximum data size of %d\n",
-      strlen(value), NVRAM_MAX_DATA);
-    return -2;
+      (int)strlen(value), NVRAM_MAX_DATA);
+    return -1;
   }
 
   unsigned char nvram_value[NVRAM_MAX_DATA];
@@ -444,18 +447,18 @@
 }
 
 // Adds new variable to HNVRAM in desired_partition as STRING
-int write_nvram_new(char* name, char* value,
+int write_nvram_new(const char* name, const char* value,
                     HMX_NVRAM_PARTITION_E desired_part) {
+  if (!can_add_flag) {
+    fprintf(stderr, "Key not found in NVRAM. Add -n to allow creation %s\n",
+            name);
+    return -1;
+  }
+
   char tmp[NVRAM_MAX_DATA] = {0};
   unsigned char nvram_value[NVRAM_MAX_DATA];
   unsigned int nvram_len = sizeof(nvram_value);
   if (parse_nvram(HNVRAM_STRING, value, nvram_value, &nvram_len) == NULL) {
-    return -1;
-  }
-
-  if (!can_add_flag) {
-    fprintf(stderr, "Key not found in NVRAM. Add -n to allow creation %s\n",
-            name);
     return -2;
   }
 
@@ -472,13 +475,19 @@
   return 0;
 }
 
+int init_nvram() {
+  const char* location = secure_getenv("HNVRAM_LOCATION");
+  return (int)HMX_NVRAM_Init(location);
+}
+
 int hnvram_main(int argc, char* const argv[]) {
   DRV_Error err;
 
   libupgrade_verbose = 0;
 
-  if ((err = HMX_NVRAM_Init()) != DRV_OK) {
-    fprintf(stderr, "NVRAM Init failed: %d\n", err);
+  int ret = init_nvram();
+  if (ret != 0) {
+    fprintf(stderr, "NVRAM Init failed: %d\n", ret);
     exit(1);
   }
 
@@ -524,13 +533,13 @@
           char* value = equal + 1;
 
           int ret = write_nvram(name, value, desired_part);
-          if (ret == -3 && can_add_flag) {
-            // key not found, and we are authorized to add a new one
+          if (ret == -3) {
+            // key not found, try to add a new one
             ret = write_nvram_new(name, value, desired_part);
           }
 
           if (ret != 0) {
-            fprintf(stderr, "Unable to write %s\n", duparg);
+            fprintf(stderr, "Err %d: Unable to write %s\n", ret, duparg);
             free(duparg);
             exit(1);
           }
diff --git a/hnvram/hnvram_test.cc b/hnvram/hnvram_test.cc
index 643a436..dbc05a1 100644
--- a/hnvram/hnvram_test.cc
+++ b/hnvram/hnvram_test.cc
@@ -85,7 +85,7 @@
   return HMX_NVRAM_SetField_Return;
 }
 
-DRV_Error HMX_NVRAM_Init(void) {
+DRV_Error HMX_NVRAM_Init(const char* target_mtd) {
   return DRV_OK;
 }