Merge "libandroidfw_tests: package test data correctly"
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index c4c14c9e..987096f 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -14,15 +14,27 @@
 
 // libandroidfw is partially built for the host (used by obbtool, aapt, and others)
 
-cc_library {
-    name: "libandroidfw",
-    host_supported: true,
+cc_defaults {
+    name: "libandroidfw_defaults",
     cflags: [
-        "-Wall",
         "-Werror",
-        "-Wunused",
         "-Wunreachable-code",
     ],
+    target: {
+        windows: {
+            // The Windows compiler warns incorrectly for value initialization with {}.
+            cppflags: ["-Wno-missing-field-initializers"],
+        },
+        host: {
+            cflags: ["-DSTATIC_ANDROIDFW_FOR_TOOLS"],
+        },
+    },
+}
+
+cc_library {
+    name: "libandroidfw",
+    defaults: ["libandroidfw_defaults"],
+    host_supported: true,
     srcs: [
         "ApkAssets.cpp",
         "Asset.cpp",
@@ -67,7 +79,6 @@
             },
         },
         host: {
-            cflags: ["-DSTATIC_ANDROIDFW_FOR_TOOLS"],
             shared: {
                 enabled: false,
             },
@@ -84,9 +95,82 @@
         },
         windows: {
             enabled: true,
-            cppflags: ["-Wno-missing-field-initializers"],  // The Windows compiler warns
-                                                            // incorrectly for value
-                                                            // initialization with {}.
         },
     },
 }
+
+common_test_libs = [
+    "libandroidfw",
+    "libbase",
+    "libcutils",
+    "libutils",
+    "libziparchive",
+]
+
+cc_test {
+    name: "libandroidfw_tests",
+    host_supported: true,
+    defaults: ["libandroidfw_defaults"],
+    cppflags: [
+        // This is to suppress warnings/errors from gtest
+        "-Wno-unnamed-type-template-args",
+    ],
+    srcs: [
+        // Helpers/infra for testing.
+        "tests/CommonHelpers.cpp",
+        "tests/TestHelpers.cpp",
+        "tests/TestMain.cpp",
+
+        // Actual tests.
+        "tests/ApkAssets_test.cpp",
+        "tests/AppAsLib_test.cpp",
+        "tests/Asset_test.cpp",
+        "tests/AssetManager2_test.cpp",
+        "tests/AttributeFinder_test.cpp",
+        "tests/AttributeResolution_test.cpp",
+        "tests/ByteBucketArray_test.cpp",
+        "tests/Config_test.cpp",
+        "tests/ConfigLocale_test.cpp",
+        "tests/Idmap_test.cpp",
+        "tests/LoadedArsc_test.cpp",
+        "tests/ResourceUtils_test.cpp",
+        "tests/ResTable_test.cpp",
+        "tests/Split_test.cpp",
+        "tests/StringPiece_test.cpp",
+        "tests/Theme_test.cpp",
+        "tests/TypeWrappers_test.cpp",
+        "tests/ZipUtils_test.cpp",
+    ],
+    target: {
+        android: {
+            srcs: [
+                "tests/BackupData_test.cpp",
+                "tests/ObbFile_test.cpp",
+            ],
+            shared_libs: common_test_libs + ["libui"],
+        },
+        host: {
+            static_libs: common_test_libs + ["liblog", "libz"],
+        },
+    },
+    data: ["tests/data/**/*.apk"],
+}
+
+cc_benchmark {
+    name: "libandroidfw_benchmarks",
+    defaults: ["libandroidfw_defaults"],
+    srcs: [
+        // Helpers/infra for benchmarking.
+        "tests/BenchMain.cpp",
+        "tests/BenchmarkHelpers.cpp",
+        "tests/CommonHelpers.cpp",
+
+        // Actual benchmarks.
+        "tests/AssetManager2_bench.cpp",
+        "tests/SparseEntry_bench.cpp",
+        "tests/Theme_bench.cpp",
+    ],
+    shared_libs: common_test_libs,
+    data: ["tests/data/**/*.apk"],
+}
+
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
deleted file mode 100644
index 68c51ef..0000000
--- a/libs/androidfw/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
deleted file mode 100644
index 921fd14..0000000
--- a/libs/androidfw/tests/Android.mk
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-# ==========================================================
-# Setup some common variables for the different build
-# targets here.
-# ==========================================================
-LOCAL_PATH:= $(call my-dir)
-
-testFiles := \
-    ApkAssets_test.cpp \
-    AppAsLib_test.cpp \
-    Asset_test.cpp \
-    AssetManager2_test.cpp \
-    AttributeFinder_test.cpp \
-    AttributeResolution_test.cpp \
-    ByteBucketArray_test.cpp \
-    Config_test.cpp \
-    ConfigLocale_test.cpp \
-    Idmap_test.cpp \
-    LoadedArsc_test.cpp \
-    ResourceUtils_test.cpp \
-    ResTable_test.cpp \
-    Split_test.cpp \
-    StringPiece_test.cpp \
-    TestHelpers.cpp \
-    TestMain.cpp \
-    Theme_test.cpp \
-    TypeWrappers_test.cpp \
-    ZipUtils_test.cpp
-
-benchmarkFiles := \
-    AssetManager2_bench.cpp \
-    BenchMain.cpp \
-    BenchmarkHelpers.cpp \
-    SparseEntry_bench.cpp \
-    TestHelpers.cpp \
-    Theme_bench.cpp
-
-androidfw_test_cflags := \
-    -Wall \
-    -Werror \
-    -Wunused \
-    -Wunreachable-code \
-    -Wno-missing-field-initializers
-
-# gtest is broken.
-androidfw_test_cflags += -Wno-unnamed-type-template-args
-
-# ==========================================================
-# Build the host tests: libandroidfw_tests
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libandroidfw_tests
-LOCAL_CFLAGS := $(androidfw_test_cflags)
-LOCAL_SRC_FILES := $(testFiles)
-LOCAL_STATIC_LIBRARIES := \
-    libandroidfw \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libz \
-    libziparchive
-LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
-
-include $(BUILD_HOST_NATIVE_TEST)
-
-# ==========================================================
-# Build the device tests: libandroidfw_tests
-# ==========================================================
-ifneq ($(SDK_ONLY),true)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libandroidfw_tests
-LOCAL_CFLAGS := $(androidfw_test_cflags)
-LOCAL_SRC_FILES := $(testFiles) \
-    BackupData_test.cpp \
-    ObbFile_test.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroidfw \
-    libbase \
-    libcutils \
-    libutils \
-    libui \
-    libziparchive 
-LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
-
-include $(BUILD_NATIVE_TEST)
-
-# ==========================================================
-# Build the device benchmarks: libandroidfw_benchmarks
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libandroidfw_benchmarks
-LOCAL_CFLAGS := $(androidfw_test_cflags)
-LOCAL_SRC_FILES := $(benchmarkFiles)
-LOCAL_STATIC_LIBRARIES := \
-    libgoogle-benchmark
-LOCAL_SHARED_LIBRARIES := \
-    libandroidfw \
-    libbase \
-    libcutils \
-    libutils \
-    libziparchive
-LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
-
-include $(BUILD_NATIVE_TEST)
-endif # Not SDK_ONLY
-
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 67de741..99a07a5 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -23,7 +23,6 @@
 #include "androidfw/ResourceTypes.h"
 
 #include "BenchmarkHelpers.h"
-#include "TestHelpers.h"
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
 #include "data/styles/R.h"
diff --git a/libs/androidfw/tests/BenchMain.cpp b/libs/androidfw/tests/BenchMain.cpp
index 105c5f9..58fc54a 100644
--- a/libs/androidfw/tests/BenchMain.cpp
+++ b/libs/androidfw/tests/BenchMain.cpp
@@ -18,7 +18,7 @@
 
 #include "benchmark/benchmark.h"
 
-#include "TestHelpers.h"
+#include "BenchmarkHelpers.h"
 
 int main(int argc, char** argv) {
   ::benchmark::Initialize(&argc, argv);
diff --git a/libs/androidfw/tests/BenchmarkHelpers.h b/libs/androidfw/tests/BenchmarkHelpers.h
index fc36664..0bb96b5 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.h
+++ b/libs/androidfw/tests/BenchmarkHelpers.h
@@ -14,21 +14,22 @@
  * limitations under the License.
  */
 
-#ifndef TESTS_BENCHMARKHELPERS_H_
-#define TESTS_BENCHMARKHELPERS_H_
+#ifndef ANDROIDFW_TESTS_BENCHMARKHELPERS_H
+#define ANDROIDFW_TESTS_BENCHMARKHELPERS_H
 
 #include <string>
 #include <vector>
 
+#include "androidfw/ResourceTypes.h"
 #include "benchmark/benchmark.h"
 
-#include "androidfw/ResourceTypes.h"
+#include "CommonHelpers.h"
 
 namespace android {
 
 void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTable_config* config,
-                             uint32_t resid, benchmark::State& state);
+                             uint32_t resid, ::benchmark::State& state);
 
 }  // namespace android
 
-#endif /* TESTS_BENCHMARKHELPERS_H_ */
+#endif  // ANDROIDFW_TESTS_BENCHMARKHELPERS_H
diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp
new file mode 100644
index 0000000..faa5350
--- /dev/null
+++ b/libs/androidfw/tests/CommonHelpers.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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 "CommonHelpers.h"
+
+#include <iostream>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/strings.h"
+
+namespace android {
+
+static std::string sTestDataPath;
+
+void InitializeTest(int* argc, char** argv) {
+  // Set the default test data path to be the executable path directory + data.
+  SetTestDataPath(base::GetExecutableDirectory() + "/tests/data");
+
+  for (int i = 1; i < *argc; i++) {
+    const std::string arg = argv[i];
+    if (base::StartsWith(arg, "--testdata=")) {
+      SetTestDataPath(arg.substr(strlen("--testdata=")));
+      for (int j = i; j != *argc; j++) {
+        argv[j] = argv[j + 1];
+      }
+      --(*argc);
+      --i;
+    } else if (arg == "-h" || arg == "--help") {
+      std::cerr << "\nAdditional options specific to this test:\n"
+                   "  --testdata=[PATH]\n"
+                   "      Specify the location of test data used within the tests.\n";
+      exit(1);
+    }
+  }
+}
+
+void SetTestDataPath(const std::string& path) {
+  sTestDataPath = path;
+}
+
+const std::string& GetTestDataPath() {
+  CHECK(!sTestDataPath.empty()) << "no test data path set.";
+  return sTestDataPath;
+}
+
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
+  String8 str = pool->string8ObjectAt(idx);
+  return std::string(str.string(), str.length());
+}
+
+}  // namespace android
diff --git a/libs/androidfw/tests/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h
new file mode 100644
index 0000000..c160fbb
--- /dev/null
+++ b/libs/androidfw/tests/CommonHelpers.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ANDROIDFW_TEST_COMMON_HELPERS_H
+#define ANDROIDFW_TEST_COMMON_HELPERS_H
+
+#include <ostream>
+#include <string>
+
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
+
+namespace android {
+
+void InitializeTest(int* argc, char** argv);
+
+enum { MAY_NOT_BE_BAG = false };
+
+void SetTestDataPath(const std::string& path);
+
+const std::string& GetTestDataPath();
+
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx);
+
+static inline bool operator==(const ResTable_config& a, const ResTable_config& b) {
+  return a.compare(b) == 0;
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const String8& str) {
+  return out << str.string();
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const String16& str) {
+  return out << String8(str).string();
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) {
+  return out << c.toString();
+}
+
+}  // namespace android
+
+#endif  // ANDROIDFW_TEST_COMMON_HELPERS_H
diff --git a/libs/androidfw/tests/SparseEntry_bench.cpp b/libs/androidfw/tests/SparseEntry_bench.cpp
index 1ebf7ce..d6dc07d 100644
--- a/libs/androidfw/tests/SparseEntry_bench.cpp
+++ b/libs/androidfw/tests/SparseEntry_bench.cpp
@@ -18,7 +18,6 @@
 #include "androidfw/ResourceTypes.h"
 
 #include "BenchmarkHelpers.h"
-#include "TestHelpers.h"
 #include "data/sparse/R.h"
 
 namespace sparse = com::android::sparse;
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 1e763a5..9e320a2 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -16,67 +16,22 @@
 
 #include "TestHelpers.h"
 
-#include <libgen.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-
-#include "android-base/file.h"
-#include "android-base/logging.h"
-#include "android-base/strings.h"
 #include "ziparchive/zip_archive.h"
 
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
 namespace android {
 
-static std::string sTestDataPath;
-
-// Extract the directory of the current executable path.
-static std::string GetExecutableDir() {
-  const std::string path = base::GetExecutablePath();
-  std::unique_ptr<char, decltype(&std::free)> mutable_path = {strdup(path.c_str()), std::free};
-  std::string executable_dir = dirname(mutable_path.get());
-  return executable_dir;
-}
-
-void InitializeTest(int* argc, char** argv) {
-  // Set the default test data path to be the executable path directory.
-  SetTestDataPath(GetExecutableDir());
-
-  for (int i = 1; i < *argc; i++) {
-    const std::string arg = argv[i];
-    if (base::StartsWith(arg, "--testdata=")) {
-      SetTestDataPath(arg.substr(strlen("--testdata=")));
-      for (int j = i; j != *argc; j++) {
-        argv[j] = argv[j + 1];
-      }
-      --(*argc);
-      --i;
-    } else if (arg == "-h" || arg == "--help") {
-      std::cerr << "\nAdditional options specific to this test:\n"
-                   "  --testdata=[PATH]\n"
-                   "      Specify the location of test data used within the tests.\n";
-      exit(1);
-    }
-  }
-}
-
-void SetTestDataPath(const std::string& path) { sTestDataPath = path; }
-
-const std::string& GetTestDataPath() {
-  CHECK(!sTestDataPath.empty()) << "no test data path set.";
-  return sTestDataPath;
-}
-
-::testing::AssertionResult ReadFileFromZipToString(const std::string& zip_path,
-                                                   const std::string& file,
-                                                   std::string* out_contents) {
+AssertionResult ReadFileFromZipToString(const std::string& zip_path, const std::string& file,
+                                        std::string* out_contents) {
   out_contents->clear();
   ::ZipArchiveHandle handle;
   int32_t result = OpenArchive(zip_path.c_str(), &handle);
   if (result != 0) {
-    return ::testing::AssertionFailure() << "Failed to open zip '" << zip_path
-                                         << "': " << ::ErrorCodeString(result);
+    return AssertionFailure() << "Failed to open zip '" << zip_path
+                              << "': " << ::ErrorCodeString(result);
   }
 
   ::ZipString name(file.c_str());
@@ -84,8 +39,8 @@
   result = ::FindEntry(handle, name, &entry);
   if (result != 0) {
     ::CloseArchive(handle);
-    return ::testing::AssertionFailure() << "Could not find file '" << file << "' in zip '"
-                                         << zip_path << "' : " << ::ErrorCodeString(result);
+    return AssertionFailure() << "Could not find file '" << file << "' in zip '" << zip_path
+                              << "' : " << ::ErrorCodeString(result);
   }
 
   out_contents->resize(entry.uncompressed_length);
@@ -94,41 +49,36 @@
       out_contents->size());
   if (result != 0) {
     ::CloseArchive(handle);
-    return ::testing::AssertionFailure() << "Failed to extract file '" << file << "' from zip '"
-                                         << zip_path << "': " << ::ErrorCodeString(result);
+    return AssertionFailure() << "Failed to extract file '" << file << "' from zip '" << zip_path
+                              << "': " << ::ErrorCodeString(result);
   }
 
   ::CloseArchive(handle);
-  return ::testing::AssertionSuccess();
+  return AssertionSuccess();
 }
 
-::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
-                                         const char* expected_str) {
+AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
+                              const char* expected_str) {
   Res_value val;
   ssize_t block = table.getResource(resource_id, &val, MAY_NOT_BE_BAG);
   if (block < 0) {
-    return ::testing::AssertionFailure() << "could not find resource";
+    return AssertionFailure() << "could not find resource";
   }
 
   if (val.dataType != Res_value::TYPE_STRING) {
-    return ::testing::AssertionFailure() << "resource is not a string";
+    return AssertionFailure() << "resource is not a string";
   }
 
   const ResStringPool* pool = table.getTableStringBlock(block);
   if (pool == NULL) {
-    return ::testing::AssertionFailure() << "table has no string pool for block " << block;
+    return AssertionFailure() << "table has no string pool for block " << block;
   }
 
   const String8 actual_str = pool->string8ObjectAt(val.data);
   if (String8(expected_str) != actual_str) {
-    return ::testing::AssertionFailure() << actual_str.string();
+    return AssertionFailure() << actual_str.string();
   }
-  return ::testing::AssertionSuccess() << actual_str.string();
-}
-
-std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
-  String8 str = pool->string8ObjectAt(idx);
-  return std::string(str.string(), str.length());
+  return AssertionSuccess() << actual_str.string();
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ec78b2a..43a9955 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -14,53 +14,25 @@
  * limitations under the License.
  */
 
-#ifndef TEST_HELPERS_H_
-#define TEST_HELPERS_H_
+#ifndef ANDROIDFW_TEST_TESTHELPERS_H
+#define ANDROIDFW_TEST_TESTHELPERS_H
 
-#include <ostream>
 #include <string>
-#include <vector>
 
 #include "androidfw/ResourceTypes.h"
 #include "gtest/gtest.h"
-#include "utils/String16.h"
-#include "utils/String8.h"
 
-static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
-  return out << str.string();
-}
-
-static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
-  return out << android::String8(str).string();
-}
+#include "CommonHelpers.h"
 
 namespace android {
 
-void InitializeTest(int* argc, char** argv);
-
-enum { MAY_NOT_BE_BAG = false };
-
-void SetTestDataPath(const std::string& path);
-
-const std::string& GetTestDataPath();
-
 ::testing::AssertionResult ReadFileFromZipToString(const std::string& zip_path,
                                                    const std::string& file,
                                                    std::string* out_contents);
 
-static inline bool operator==(const ResTable_config& a, const ResTable_config& b) {
-  return a.compare(b) == 0;
-}
-
-static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) {
-  return out << c.toString().string();
-}
-
 ::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
                                          const char* expected_str);
 
-std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx);
-
 }  // namespace android
 
-#endif  // TEST_HELPERS_H_
+#endif  // ANDROIDFW_TEST_TESTHELPERS_H