build: meson support for unit tests

Add support for building and running existing unit tests
under meson.

Fix a few test case failures that might surface from running
as non-root or without a full OpenBMC set of applications.

Fixes: openbmc/phosphor-logging#11

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I588c0b7d8dcdc60b9a03fee0b5d373d0023b0530
diff --git a/config.h.meson b/config.h.meson
index 8618a4b..e24765b 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -10,6 +10,8 @@
 
 #define BMC_VERSION_FILE "/etc/os-release"
 #define BUSNAME_LOGGING "xyz.openbmc_project.Logging"
+#define BUSPATH_REMOTE_LOGGING_CONFIG                                          \
+    "/xyz/openbmc_project/logging/config/remote"
 #define CALLOUT_FWD_ASSOCIATION "callout"
 #define CALLOUT_REV_ASSOCIATION "fault"
 #define INVENTORY_ROOT "/xyz/openbmc_project/inventory"
@@ -20,7 +22,11 @@
 #define SYSTEMD_INTERFACE "org.freedesktop.systemd1.Manager"
 #define SYSTEMD_PATH "/org/freedesktop/systemd1"
 
-#define ERRLOG_PERSIST_PATH "/var/lib/phosphor-logging/errors" // TODO: need /tmp/errors in some cases?
+#ifdef TESTCASE
+#define ERRLOG_PERSIST_PATH "/tmp/errors"
+#else
+#define ERRLOG_PERSIST_PATH "/var/lib/phosphor-logging/errors"
+#endif
 
 static constexpr size_t ERROR_CAP = @error_cap@;
 static constexpr size_t ERROR_INFO_CAP = @error_info_cap@;
diff --git a/extensions.cpp b/extensions.cpp
index ffeb059..fa68789 100644
--- a/extensions.cpp
+++ b/extensions.cpp
@@ -1,5 +1,9 @@
 #include "extensions.hpp"
 
+// The 'extensions_test' testcase needs to define these itself.
+// Skip over the definition to avoid duplicate symbol definitions.
+#ifndef TESTCASE_extensions_test
+
 namespace phosphor
 {
 namespace logging
@@ -14,3 +18,5 @@
 
 } // namespace logging
 } // namespace phosphor
+
+#endif
diff --git a/log_manager.cpp b/log_manager.cpp
index 90cf1d4..83ad168 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -81,9 +81,16 @@
     return entryId;
 }
 
-void Manager::_commit(uint64_t transactionId, std::string&& errMsg,
-                      Entry::Level errLvl)
+void Manager::_commit(uint64_t transactionId [[maybe_unused]],
+                      std::string&& errMsg, Entry::Level errLvl)
 {
+    std::vector<std::string> additionalData{};
+
+    // When running as a test-case, the system may have a LOT of journal
+    // data and we may not have permissions to do some of the journal sync
+    // operations.  Just skip over them.
+#ifndef TESTCASE
+
     constexpr const auto transactionIdVar = "TRANSACTION_ID";
     // Length of 'TRANSACTION_ID' string.
     constexpr const auto transactionIdVarSize = std::strlen(transactionIdVar);
@@ -114,8 +121,6 @@
     // Add _PID field information in AdditionalData.
     metalist.insert("_PID");
 
-    std::vector<std::string> additionalData;
-
     // Read the journal from the end to get the most recent entry first.
     // The result from the sd_journal_get_data() is of the form VARIABLE=value.
     SD_JOURNAL_FOREACH_BACKWARDS(j)
@@ -186,6 +191,7 @@
 
     sd_journal_close(j);
 
+#endif
     createEntry(errMsg, errLvl, additionalData);
 }
 
@@ -251,6 +257,12 @@
 
 bool Manager::isQuiesceOnErrorEnabled()
 {
+    // When running under tests, the Logging.Settings service will not be
+    // present.  Assume false.
+#ifdef TESTCASE
+    return false;
+#endif
+
     std::variant<bool> property;
 
     auto method = this->busLog.new_method_call(
@@ -675,6 +687,7 @@
                 log<level::ERR>("Failed to kill journal service");
                 break;
             }
+
             continue;
         }
 
diff --git a/meson.build b/meson.build
index bc21ff7..33d0348 100644
--- a/meson.build
+++ b/meson.build
@@ -148,18 +148,23 @@
 )
 
 # Generate daemon.
-executable('phosphor-log-manager',
+log_manager_sources = [
     generated_sources,
     elog_lookup_gen,
     elog_process_gen,
-    'elog_entry.cpp',
-    'elog_meta.cpp',
-    'elog_serialize.cpp',
-    'extensions.cpp',
-    'log_manager.cpp',
+    files(
+        'elog_entry.cpp',
+        'elog_meta.cpp',
+        'elog_serialize.cpp',
+        'extensions.cpp',
+        'log_manager.cpp',
+        'sdjournal.cpp',
+        'util.cpp',
+    )
+]
+executable('phosphor-log-manager',
+    log_manager_sources,
     'log_manager_main.cpp',
-    'sdjournal.cpp',
-    'util.cpp',
     include_directories: include_directories('gen'),
     link_with: libphosphor_logging,
     dependencies: [
@@ -192,3 +197,7 @@
     ],
     install: true,
 )
+
+if not get_option('tests').disabled()
+    subdir('test')
+endif
diff --git a/meson_options.txt b/meson_options.txt
index 5f3bd2f..de37d35 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,3 +1,5 @@
+option('tests', type: 'feature', description: 'Build tests')
+
 option('yamldir', type: 'string', description: 'Path to YAML')
 option(
     'callout_yaml',
diff --git a/subprojects/googletest.wrap b/subprojects/googletest.wrap
new file mode 100644
index 0000000..56da9ef
--- /dev/null
+++ b/subprojects/googletest.wrap
@@ -0,0 +1,3 @@
+[wrap-git]
+url = https://github.com/google/googletest
+revision = HEAD
diff --git a/test/meson.build b/test/meson.build
new file mode 100644
index 0000000..19c81f2
--- /dev/null
+++ b/test/meson.build
@@ -0,0 +1,58 @@
+gtest_dep = dependency('gtest', main: true, disabler: true, required: false)
+gmock_dep = dependency('gmock', disabler: true, required: false)
+if not gtest_dep.found() or not gmock_dep.found()
+    gtest_proj = import('cmake').subproject('googletest', required: false)
+    if gtest_proj.found()
+        gtest_dep = declare_dependency(
+            dependencies: [
+                dependency('threads'),
+                gtest_proj.dependency('gtest'),
+                gtest_proj.dependency('gtest_main'),
+            ]
+        )
+        gmock_dep = gtest_proj.dependency('gmock')
+  else
+        assert(
+            not get_option('tests').enabled(),
+            'Googletest is required if tests are enabled'
+        )
+  endif
+endif
+
+tests = [
+    'elog_quiesce_test',
+    'elog_update_ts_test',
+    'extensions_test',
+    'remote_logging_test_address',
+    'remote_logging_test_config',
+    'remote_logging_test_port',
+    'sdtest',
+    'serialization_test_path',
+    'serialization_test_properties',
+    'elog_errorwrap_test',
+]
+foreach t : tests
+    test(
+        'test_' + t.underscorify(),
+        executable(
+            'test-' + t.underscorify(),
+            t + '.cpp',
+            log_manager_sources,
+            '../phosphor-rsyslog-config/server-conf.cpp',
+            link_with: libphosphor_logging,
+            cpp_args: [
+                '-DTESTCASE=' + t.underscorify(),
+                '-DTESTCASE_' + t.underscorify(),
+            ],
+            dependencies: [
+                cereal_dep,
+                gmock_dep,
+                gtest_dep,
+                pdi_dep,
+                sdbusplus_dep,
+                sdeventplus_dep,
+            ],
+            include_directories: include_directories('..', '../gen'),
+        )
+    )
+endforeach