Add ParsedOptions test (and migrate RuntimeTest to CommonTest)

Change-Id: Ic338520dfaca2228260e546ed1522c37b83f42ac
diff --git a/src/class_linker.h b/src/class_linker.h
index b2b4c95..34ad9d7 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -203,7 +203,7 @@
 
   bool init_done_;
 
-  friend class RuntimeTest;
+  friend class CommonTest;
   FRIEND_TEST(DexCacheTest, Open);
   friend class ObjectTest;
   FRIEND_TEST(ObjectTest, AllocObjectArray);
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 3b70aa9..7e93e0d 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -9,7 +9,7 @@
 
 namespace art {
 
-class ClassLinkerTest : public RuntimeTest {
+class ClassLinkerTest : public CommonTest {
  protected:
   void AssertNonExistantClass(const StringPiece& descriptor) {
     EXPECT_TRUE(class_linker_->FindSystemClass(descriptor) == NULL);
diff --git a/src/common_test.h b/src/common_test.h
index bfc58f6..14a68a2 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -319,7 +319,7 @@
   int fd_;
 };
 
-class RuntimeTest : public testing::Test {
+class CommonTest : public testing::Test {
  protected:
   virtual void SetUp() {
     is_host_ = getenv("ANDROID_BUILD_TOP") != NULL;
@@ -416,7 +416,7 @@
   bool is_host_;
   scoped_ptr_malloc<char> android_data_;
   std::string art_cache_;
-  scoped_ptr<DexFile> java_lang_dex_file_;
+  scoped_ptr<const DexFile> java_lang_dex_file_;
   scoped_ptr<Runtime> runtime_;
   ClassLinker* class_linker_;
 };
diff --git a/src/dex_cache_test.cc b/src/dex_cache_test.cc
index c439311..cb682a0 100644
--- a/src/dex_cache_test.cc
+++ b/src/dex_cache_test.cc
@@ -12,7 +12,7 @@
 
 namespace art {
 
-class DexCacheTest : public RuntimeTest {};
+class DexCacheTest : public CommonTest {};
 
 TEST_F(DexCacheTest, Open) {
 
diff --git a/src/exception_test.cc b/src/exception_test.cc
index ae1546f..e2f97c0 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -60,7 +60,7 @@
   "AQAAABgCAAABEAAAAQAAADgCAAACIAAADQAAAD4CAAADIAAABAAAAN4CAAAEIAAAAQAAAAEDAAAA"
   "IAAAAgAAAAkDAAAAEAAAAQAAACgDAAA=";
 
-class ExceptionTest : public RuntimeTest {
+class ExceptionTest : public CommonTest {
 };
 
 TEST_F(ExceptionTest, MyClass_F_G) {
diff --git a/src/file_test.cc b/src/file_test.cc
index 4afc834..a836d83 100644
--- a/src/file_test.cc
+++ b/src/file_test.cc
@@ -9,7 +9,7 @@
 
 namespace art {
 
-class FileTest : public RuntimeTest {};
+class FileTest : public CommonTest {};
 
 TEST_F(FileTest, Read) {
   std::string filename = GetLibCoreDexFileName();
diff --git a/src/image_test.cc b/src/image_test.cc
index c3662cd..55984f6 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -7,7 +7,7 @@
 
 namespace art {
 
-class ImageTest : public RuntimeTest {};
+class ImageTest : public CommonTest {};
 
 TEST_F(ImageTest, WriteRead) {
   scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
diff --git a/src/indirect_reference_table_test.cc b/src/indirect_reference_table_test.cc
index 97e3a70..352ebd5 100644
--- a/src/indirect_reference_table_test.cc
+++ b/src/indirect_reference_table_test.cc
@@ -22,7 +22,7 @@
 
 namespace art {
 
-class IndirectReferenceTableTest : public RuntimeTest {
+class IndirectReferenceTableTest : public CommonTest {
 };
 
 TEST_F(IndirectReferenceTableTest, BasicTest) {
diff --git a/src/intern_table_test.cc b/src/intern_table_test.cc
index daae841..e6bb611 100644
--- a/src/intern_table_test.cc
+++ b/src/intern_table_test.cc
@@ -9,7 +9,7 @@
 
 namespace art {
 
-class InternTableTest : public RuntimeTest {};
+class InternTableTest : public CommonTest {};
 
 TEST_F(InternTableTest, Intern) {
   InternTable intern_table;
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 59b9004..d4df48a 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -15,10 +15,10 @@
 
 namespace art {
 
-class JniCompilerTest : public RuntimeTest {
+class JniCompilerTest : public CommonTest {
  protected:
   virtual void SetUp() {
-    RuntimeTest::SetUp();
+    CommonTest::SetUp();
     // Create thunk code that performs the native to managed transition
     thunk_code_.reset(MemMap::Map(kPageSize,
                                   PROT_READ | PROT_WRITE | PROT_EXEC,
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 04b3225..4fa60c5 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -10,10 +10,10 @@
 
 namespace art {
 
-class JniInternalTest : public RuntimeTest {
+class JniInternalTest : public CommonTest {
  protected:
   virtual void SetUp() {
-    RuntimeTest::SetUp();
+    CommonTest::SetUp();
     env_ = Thread::Current()->GetJniEnv();
   }
   JNIEnv* env_;
diff --git a/src/object_test.cc b/src/object_test.cc
index e11cf07..8f9bed6 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -13,7 +13,7 @@
 
 namespace art {
 
-class ObjectTest : public RuntimeTest {
+class ObjectTest : public CommonTest {
  protected:
   void AssertString(size_t length,
                     const char* utf8_in,
diff --git a/src/reference_table_test.cc b/src/reference_table_test.cc
index c61e3c9..4728cb0 100644
--- a/src/reference_table_test.cc
+++ b/src/reference_table_test.cc
@@ -8,7 +8,7 @@
 
 namespace art {
 
-class ReferenceTableTest : public RuntimeTest {
+class ReferenceTableTest : public CommonTest {
 };
 
 TEST_F(ReferenceTableTest, Basics) {
diff --git a/src/runtime.cc b/src/runtime.cc
index 11eb9bd..de76bb3 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -88,7 +88,6 @@
 // [gG] gigabytes.
 //
 // "s" should point just past the "-Xm?" part of the string.
-// "min" specifies the lowest acceptable value described by "s".
 // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
 // of 1024.
 //
@@ -111,35 +110,35 @@
       // there should be exactly one more character
       // that specifies a multiplier.
       if (*s2 != '\0') {
-          // The remainder of the string is either a single multiplier
-          // character, or nothing to indicate that the value is in
-          // bytes.
-          char c = *s2++;
-          if (*s2 == '\0') {
-            size_t mul;
-            if (c == '\0') {
-              mul = 1;
-            } else if (c == 'k' || c == 'K') {
-              mul = 1024;
-            } else if (c == 'm' || c == 'M') {
-              mul = 1024 * 1024;
-            } else if (c == 'g' || c == 'G') {
-              mul = 1024 * 1024 * 1024;
-            } else {
-              // Unknown multiplier character.
-              return 0;
-            }
-
-            if (val <= std::numeric_limits<size_t>::max() / mul) {
-              val *= mul;
-            } else {
-              // Clamp to a multiple of 1024.
-              val = std::numeric_limits<size_t>::max() & ~(1024-1);
-            }
+        // The remainder of the string is either a single multiplier
+        // character, or nothing to indicate that the value is in
+        // bytes.
+        char c = *s2++;
+        if (*s2 == '\0') {
+          size_t mul;
+          if (c == '\0') {
+            mul = 1;
+          } else if (c == 'k' || c == 'K') {
+            mul = KB;
+          } else if (c == 'm' || c == 'M') {
+            mul = MB;
+          } else if (c == 'g' || c == 'G') {
+            mul = GB;
           } else {
-            // There's more than one character after the numeric part.
+            // Unknown multiplier character.
             return 0;
           }
+
+          if (val <= std::numeric_limits<size_t>::max() / mul) {
+            val *= mul;
+          } else {
+            // Clamp to a multiple of 1024.
+            val = std::numeric_limits<size_t>::max() & ~(1024-1);
+          }
+        } else {
+          // There's more than one character after the numeric part.
+          return 0;
+        }
       }
       // The man page says that a -Xm value must be a multiple of 1024.
       if (val % div == 0) {
@@ -200,6 +199,8 @@
 #endif
   parsed->heap_initial_size_ = Heap::kInitialSize;
   parsed->heap_maximum_size_ = Heap::kMaximumSize;
+  parsed->stack_size_ = Thread::kDefaultStackSize;
+
   parsed->hook_vfprintf_ = vfprintf;
   parsed->hook_exit_ = exit;
   parsed->hook_abort_ = abort;
@@ -209,15 +210,54 @@
     if (option.starts_with("-Xbootclasspath:")) {
       boot_class_path = option.substr(strlen("-Xbootclasspath:")).data();
     } else if (option == "bootclasspath") {
-      parsed->boot_class_path_ = *reinterpret_cast<const std::vector<DexFile*>*>(options[i].second);
+      const void* dex_vector = options[i].second;
+      const std::vector<DexFile*>* v = reinterpret_cast<const std::vector<DexFile*>*>(dex_vector);
+      if (v == NULL) {
+        if (ignore_unrecognized) {
+          continue;
+        }
+        // TODO: usage
+        LOG(FATAL) << "Could not parse " << option;
+        return NULL;
+      }
+      parsed->boot_class_path_ = *v;
     } else if (option.starts_with("-Xbootimage:")) {
       parsed->boot_image_ = option.substr(strlen("-Xbootimage:")).data();
     } else if (option.starts_with("-Xcheck:jni")) {
       parsed->check_jni_ = true;
     } else if (option.starts_with("-Xms")) {
-      parsed->heap_initial_size_ = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024);
+      size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024);
+      if (size == 0) {
+        if (ignore_unrecognized) {
+          continue;
+        }
+        // TODO usage
+        LOG(FATAL) << "Could not parse " << option;
+        return NULL;
+      }
+      parsed->heap_initial_size_ = size;
     } else if (option.starts_with("-Xmx")) {
-      parsed->heap_maximum_size_ = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024);
+      size_t size = ParseMemoryOption(option.substr(strlen("-Xmx")).data(), 1024);
+      if (size == 0) {
+        if (ignore_unrecognized) {
+          continue;
+        }
+        // TODO usage
+        LOG(FATAL) << "Could not parse " << option;
+        return NULL;
+      }
+      parsed->heap_maximum_size_ = size;
+    } else if (option.starts_with("-Xss")) {
+      size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).data(), 1);
+      if (size == 0) {
+        if (ignore_unrecognized) {
+          continue;
+        }
+        // TODO usage
+        LOG(FATAL) << "Could not parse " << option;
+        return NULL;
+      }
+      parsed->stack_size_ = size;
     } else if (option.starts_with("-D")) {
       parsed->properties_.push_back(option.substr(strlen("-D")).data());
     } else if (option.starts_with("-verbose:")) {
diff --git a/src/runtime.h b/src/runtime.h
index 11c6f78..0aa0cb5 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -37,6 +37,7 @@
     bool check_jni_;
     size_t heap_initial_size_;
     size_t heap_maximum_size_;
+    size_t stack_size_;
     jint (*hook_vfprintf_)(FILE* stream, const char* format, va_list ap);
     void (*hook_exit_)(jint status);
     void (*hook_abort_)();
diff --git a/src/runtime_test.cc b/src/runtime_test.cc
index 3aa257d..6fc14b8 100644
--- a/src/runtime_test.cc
+++ b/src/runtime_test.cc
@@ -1,16 +1,14 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
 #include "runtime.h"
-
-#include "gtest/gtest.h"
+#include "common_test.h"
 
 namespace art {
 void ParseClassPath(const char* class_path, std::vector<std::string>& vec);
-}
 
-namespace {
+class RuntimeTest : public CommonTest {};
 
-TEST(RuntimeTest, ParseClassPath) {
+TEST_F(RuntimeTest, ParseClassPath) {
   std::vector<std::string> vec;
 
   art::ParseClassPath("", vec);
@@ -66,4 +64,48 @@
   vec.clear();
 }
 
-}  // namespace
+TEST_F(RuntimeTest, ParsedOptions) {
+  void* test_vfprintf = reinterpret_cast<void*>(0xa);
+  void* test_abort = reinterpret_cast<void*>(0xb);
+  void* test_exit = reinterpret_cast<void*>(0xc);
+  void* null = reinterpret_cast<void*>(NULL);
+  scoped_ptr<const DexFile> java_lang_dex_file(GetLibCoreDex());
+  std::vector<const DexFile*> boot_class_path;
+  boot_class_path.push_back(java_lang_dex_file_.get());
+
+  Runtime::Options options;
+  options.push_back(std::make_pair("-Xbootclasspath:class_path_foo:class_path_bar", null));
+  options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+  options.push_back(std::make_pair("-Xbootimage:boot_image", null));
+  options.push_back(std::make_pair("-Xcheck:jni", null));
+  options.push_back(std::make_pair("-Xms2048", null));
+  options.push_back(std::make_pair("-Xmx4k", null));
+  options.push_back(std::make_pair("-Xss1m", null));
+  options.push_back(std::make_pair("-Dfoo=bar", null));
+  options.push_back(std::make_pair("-Dbaz=qux", null));
+  options.push_back(std::make_pair("-verbose:gc,class,jni", null));
+  options.push_back(std::make_pair("vfprintf", test_vfprintf));
+  options.push_back(std::make_pair("abort", test_abort));
+  options.push_back(std::make_pair("exit", test_exit));
+  scoped_ptr<Runtime::ParsedOptions> parsed(Runtime::ParsedOptions::Create(options, false));
+  ASSERT_TRUE(parsed != NULL);
+
+  EXPECT_EQ(1U, parsed->boot_class_path_.size());  // bootclasspath overrides -Xbootclasspath
+  EXPECT_STREQ("boot_image", parsed->boot_image_);
+  EXPECT_EQ(true, parsed->check_jni_);
+  EXPECT_EQ(2048U, parsed->heap_initial_size_);
+  EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
+  EXPECT_EQ(1 * MB, parsed->stack_size_);
+  EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_);
+  EXPECT_TRUE(test_exit == parsed->hook_exit_);
+  EXPECT_TRUE(test_abort == parsed->hook_abort_);
+  ASSERT_EQ(3U, parsed->verbose_.size());
+  EXPECT_TRUE(parsed->verbose_.find("gc") != parsed->verbose_.end());
+  EXPECT_TRUE(parsed->verbose_.find("class") != parsed->verbose_.end());
+  EXPECT_TRUE(parsed->verbose_.find("jni") != parsed->verbose_.end());
+  ASSERT_EQ(2U, parsed->properties_.size());
+  EXPECT_EQ("foo=bar", parsed->properties_[0]);
+  EXPECT_EQ("baz=qux", parsed->properties_[1]);
+}
+
+}  // namespace art
diff --git a/src/thread.cc b/src/thread.cc
index edbc770..71cd877 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -56,7 +56,7 @@
 Thread* Thread::Create(size_t stack_size) {
   int prot = PROT_READ | PROT_WRITE;
   // TODO: require the stack size to be page aligned?
-  size_t length = RoundUp(stack_size, 0x1000);
+  size_t length = RoundUp(stack_size, kPageSize);
   void* stack_limit = mmap(NULL, length, prot, MAP_PRIVATE, -1, 0);
   if (stack_limit == MAP_FAILED) {
     LOG(FATAL) << "mmap";
diff --git a/src/utils_test.cc b/src/utils_test.cc
index 1b7315f..dd411c2 100644
--- a/src/utils_test.cc
+++ b/src/utils_test.cc
@@ -8,7 +8,7 @@
 
 namespace art {
 
-class UtilsTest : public RuntimeTest {
+class UtilsTest : public CommonTest {
 };
 
 TEST(PrettyDescriptorTest, ArrayReferences) {
diff --git a/src/zip_archive_test.cc b/src/zip_archive_test.cc
index 74b3795..19f4ee0 100644
--- a/src/zip_archive_test.cc
+++ b/src/zip_archive_test.cc
@@ -11,7 +11,7 @@
 
 namespace art {
 
-class ZipArchiveTest : public RuntimeTest {};
+class ZipArchiveTest : public CommonTest {};
 
 TEST_F(ZipArchiveTest, FindAndExtract) {
   scoped_ptr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName()));