Revised GPIO module interface + GPIO discovery logic

* The GpioHandler class is no longer a static singleton, rather an
  ordinary object with a dynamic guard against multiple instances. This
  makes testing/mocking a lot easier and simplifies implementation.

* It uses a basic, mockable udev interface; the module comes with
  complete unit testing of the discovery mechanism.

* Corresponding changes to user classes, including UpdateAttempter and
  UpdateCheckScheduler.

Note that the implementation of the test mode signaling protocol is
currently a no-op, always returning false, and hence has no effect on
the update process yet. This mechanism will be implemented in a later
CL.

BUG=chromium-os:25397
TEST=Builds and passes unit tests (including new ones)

Change-Id: I2f6254db6799ff5ef8616314890833f6e3269ff6
Reviewed-on: https://gerrit.chromium.org/gerrit/22869
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/gpio_mock_udev_interface.cc b/gpio_mock_udev_interface.cc
new file mode 100644
index 0000000..22c7b7f
--- /dev/null
+++ b/gpio_mock_udev_interface.cc
@@ -0,0 +1,371 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/gpio_mock_udev_interface.h"
+
+#include <string>
+
+#include <base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "gpio_handler_unittest.h"
+
+namespace chromeos_update_engine {
+
+const char* StandardGpioMockUdevInterface::kUdevGpioSubsystem = "gpio";
+const char* StandardGpioMockUdevInterface::kUdevGpioChipSysname = "gpiochip*";
+
+const StandardGpioMockUdevInterface::GpioDescriptor
+    StandardGpioMockUdevInterface::gpio_descriptors_[kMockGpioIdMax] = {
+  { "ID_GPIO_DUTFLAGA", MOCK_DUTFLAGA_GPIO_ID },
+  { "ID_GPIO_DUTFLAGB", MOCK_DUTFLAGB_GPIO_ID },
+};
+
+const char* StandardGpioMockUdevInterface::enum_gpio_chip_dev_list_[] = {
+  "gpiochip" MOCK_GPIO_CHIP_ID,
+  NULL
+};
+const char* StandardGpioMockUdevInterface::enum_dutflaga_gpio_dev_list_[] = {
+  "gpio" MOCK_DUTFLAGA_GPIO_ID,
+  NULL
+};
+const char* StandardGpioMockUdevInterface::enum_dutflagb_gpio_dev_list_[] = {
+  "gpio" MOCK_DUTFLAGB_GPIO_ID,
+  NULL
+};
+
+StandardGpioMockUdevInterface::StandardGpioMockUdevInterface()
+    : udev_id_(0), udev_enum_id_(0), num_discovered_(0) {
+  std::fill_n(used_udev_ids_, MAX_UDEV_OBJECTS, false);
+  std::fill_n(used_udev_enum_ids_, MAX_UDEV_ENUM_OBJECTS, false);
+
+  gpio_chip_dev_ = (MockDevice) {
+    MOCK_SYSFS_PREFIX "/gpiochip" MOCK_GPIO_CHIP_ID,
+    gpio_descriptors_, kMockGpioIdMax, false, false
+  };
+  dutflaga_gpio_dev_ = (MockDevice) {
+    MOCK_SYSFS_PREFIX "/gpio" MOCK_DUTFLAGA_GPIO_ID, NULL, 0, true, false
+  };
+  dutflagb_gpio_dev_ = (MockDevice) {
+    MOCK_SYSFS_PREFIX "/gpio" MOCK_DUTFLAGB_GPIO_ID, NULL, 0, true, false
+  };
+}
+
+// Device lists are simply null-terminated arrays of strings.
+const char* StandardGpioMockUdevInterface::ListEntryGetName(
+    struct udev_list_entry* list_entry) {
+  const char** mock_list_entry = reinterpret_cast<const char**>(list_entry);
+  EXPECT_TRUE(mock_list_entry && *mock_list_entry);
+  if (!mock_list_entry)
+    return NULL;
+  // The mock list entry is the name string itself...
+  return *mock_list_entry;
+}
+
+struct udev_list_entry* StandardGpioMockUdevInterface::ListEntryGetNext(
+    struct udev_list_entry* list_entry) {
+  char** mock_list_entry = reinterpret_cast<char**>(list_entry);
+  EXPECT_TRUE(mock_list_entry && *mock_list_entry);
+  if (!mock_list_entry)
+    return NULL;
+  // Advance to the next element in the array.
+  mock_list_entry++;
+  return (*mock_list_entry ?
+          reinterpret_cast<struct udev_list_entry*>(mock_list_entry) : NULL);
+}
+
+struct udev_device* StandardGpioMockUdevInterface::DeviceNewFromSyspath(
+    struct udev* udev, const char* syspath) {
+  const size_t udev_id = UdevToId(udev);
+  if (udev_id >= MAX_UDEV_OBJECTS)
+    return NULL;
+  MockDevice* mock_dev;
+
+  // Generate the desired mock device based on the provided syspath.
+  if (!strcmp(syspath, enum_gpio_chip_dev_list_[0])) {
+    mock_dev = const_cast<MockDevice*>(&gpio_chip_dev_);
+  } else if (!strcmp(syspath, enum_dutflaga_gpio_dev_list_[0])) {
+    mock_dev = const_cast<MockDevice*>(&dutflaga_gpio_dev_);
+  } else if (!strcmp(syspath, enum_dutflagb_gpio_dev_list_[0])) {
+    mock_dev = const_cast<MockDevice*>(&dutflagb_gpio_dev_);
+  } else {
+    ADD_FAILURE();
+    return NULL;
+  }
+
+  EXPECT_FALSE(mock_dev->is_used);
+  if (mock_dev->is_used)
+    return NULL;
+  mock_dev->is_used = true;
+
+  return reinterpret_cast<struct udev_device*>(mock_dev);
+}
+
+const char* StandardGpioMockUdevInterface::DeviceGetPropertyValue(
+    struct udev_device* udev_device, const char* key) {
+  const MockDevice* mock_dev = UdevDeviceToMock(udev_device);
+  EXPECT_TRUE(mock_dev->properties);
+  if (!mock_dev->properties)
+    return NULL;
+  for (size_t i = 0; i < mock_dev->num_properties; i++)
+    if (!strcmp(key, mock_dev->properties[i].property))
+      return mock_dev->properties[i].value;
+  return NULL;
+}
+
+const char* StandardGpioMockUdevInterface::DeviceGetSyspath(
+    struct udev_device* udev_device) {
+  const MockDevice* mock_dev = UdevDeviceToMock(udev_device);
+  if (mock_dev->is_gpio)
+    num_discovered_++;
+  return mock_dev->syspath;
+}
+
+void StandardGpioMockUdevInterface::DeviceUnref(
+    struct udev_device* udev_device) {
+  MockDevice* mock_dev = UdevDeviceToMock(udev_device);
+  mock_dev->is_used = false;
+}
+
+struct udev_enumerate* StandardGpioMockUdevInterface::EnumerateNew(
+    struct udev* udev) {
+  const size_t udev_id = UdevToId(udev);
+  if (udev_id >= MAX_UDEV_OBJECTS)
+    return NULL;
+  // A new enumeration "pointer" is cast for an integer identifier.
+  EXPECT_LT(udev_enum_id_, MAX_UDEV_ENUM_OBJECTS);
+  if (udev_enum_id_ >= MAX_UDEV_ENUM_OBJECTS)
+    return NULL;
+  used_udev_enum_ids_[udev_enum_id_] = true;
+  return reinterpret_cast<struct udev_enumerate*>(++udev_enum_id_);
+}
+
+int StandardGpioMockUdevInterface::EnumerateAddMatchSubsystem(
+    struct udev_enumerate* udev_enum, const char* subsystem) {
+  const size_t udev_enum_id = UdevEnumToId(udev_enum);
+  if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+    return -1;
+
+  // Ensure client is correctly requesting the GPIO subsystem.
+  EXPECT_STREQ(subsystem, kUdevGpioSubsystem);
+  if (strcmp(subsystem, kUdevGpioSubsystem))
+    return -1;
+
+  return 0;
+}
+
+int StandardGpioMockUdevInterface::EnumerateAddMatchSysname(
+    struct udev_enumerate* udev_enum, const char* sysname) {
+  const size_t udev_enum_id = UdevEnumToId(udev_enum);
+  if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+    return -1;
+
+  // Ensure client is requesting the correct sysnamem, depending on the correct
+  // phase in the detection process.
+  switch (udev_enum_id) {
+    case 0:  // looking for a GPIO chip
+      EXPECT_STREQ(sysname, kUdevGpioChipSysname);
+      if (strcmp(sysname, kUdevGpioChipSysname))
+        return -1;
+      break;
+    case 1:  // looking for dut_flaga/b
+    case 2: {
+      const int gpio_id = udev_enum_id - 1;
+      const std::string gpio_pattern =
+          StringPrintf("*%s", gpio_descriptors_[gpio_id].value);
+      EXPECT_STREQ(sysname, gpio_pattern.c_str());
+      if (strcmp(sysname, gpio_pattern.c_str()))
+        return -1;
+      break;
+    }
+    default:
+      ADD_FAILURE();  // can't get here
+      return -1;
+  }
+
+  return 0;
+}
+
+int StandardGpioMockUdevInterface::EnumerateScanDevices(
+    struct udev_enumerate* udev_enum) {
+  const size_t udev_enum_id = UdevEnumToId(udev_enum);
+  if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+    return -1;
+  return 0;  // nothing to do, really
+}
+
+struct udev_list_entry* StandardGpioMockUdevInterface::EnumerateGetListEntry(
+    struct udev_enumerate* udev_enum) {
+  const size_t udev_enum_id = UdevEnumToId(udev_enum);
+  if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+    return NULL;
+
+  // Return a list of devices corresponding to the provided enumeration.
+  switch (udev_enum_id) {
+    case 0:
+      return reinterpret_cast<struct udev_list_entry*>(
+          enum_gpio_chip_dev_list_);
+    case 1:
+      return reinterpret_cast<struct udev_list_entry*>(
+          enum_dutflaga_gpio_dev_list_);
+    case 2:
+      return reinterpret_cast<struct udev_list_entry*>(
+          enum_dutflagb_gpio_dev_list_);
+  }
+
+  ADD_FAILURE();  // can't get here
+  return NULL;
+}
+
+void StandardGpioMockUdevInterface::EnumerateUnref(
+    struct udev_enumerate* udev_enum) {
+  const size_t udev_enum_id = UdevEnumToId(udev_enum);
+  if (udev_enum_id >= MAX_UDEV_ENUM_OBJECTS)
+    return;
+  used_udev_enum_ids_[udev_enum_id] = false;  // make sure it's freed just once
+}
+
+struct udev* StandardGpioMockUdevInterface::New() {
+  // The returned "pointer" is cast from an integer identifier.
+  EXPECT_LT(udev_id_, MAX_UDEV_OBJECTS);
+  if (udev_id_ >= MAX_UDEV_OBJECTS)
+    return NULL;
+  used_udev_ids_[udev_id_] = true;
+  return reinterpret_cast<struct udev*>(++udev_id_);
+}
+
+void StandardGpioMockUdevInterface::Unref(struct udev* udev) {
+  // Convert to object identifier, ensuring the object has been "allocated".
+  const size_t udev_id = UdevToId(udev);
+  if (udev_id >= MAX_UDEV_OBJECTS)
+    return;
+  used_udev_ids_[udev_id] = false;  // make sure it's freed just once
+}
+
+void StandardGpioMockUdevInterface::ExpectAllResourcesDeallocated() const {
+  // Make sure that all handles were released.
+  for (size_t i = 0; i < MAX_UDEV_OBJECTS; i++)
+    EXPECT_FALSE(used_udev_ids_[i]);
+  for (size_t i = 0; i < MAX_UDEV_ENUM_OBJECTS; i++)
+    EXPECT_FALSE(used_udev_enum_ids_[i]);
+  EXPECT_FALSE(gpio_chip_dev_.is_used);
+  EXPECT_FALSE(dutflaga_gpio_dev_.is_used);
+  EXPECT_FALSE(dutflagb_gpio_dev_.is_used);
+}
+
+void StandardGpioMockUdevInterface::ExpectDiscoverySuccess() const {
+  EXPECT_EQ(num_discovered_, kMockGpioIdMax);
+}
+
+void StandardGpioMockUdevInterface::ExpectDiscoveryFail() const {
+  EXPECT_LT(num_discovered_, kMockGpioIdMax);
+}
+
+StandardGpioMockUdevInterface::MockDevice*
+StandardGpioMockUdevInterface::UdevDeviceToMock(
+    struct udev_device* udev_dev) const {
+  EXPECT_TRUE(udev_dev);
+  if (!udev_dev)
+    return NULL;
+  MockDevice* mock_dev = reinterpret_cast<MockDevice*>(udev_dev);
+  EXPECT_TRUE(mock_dev->is_used);
+  if (!mock_dev->is_used)
+    return NULL;
+  return mock_dev;
+}
+
+size_t StandardGpioMockUdevInterface::UdevEnumToId(
+    struct udev_enumerate* udev_enum) const {
+  EXPECT_TRUE(udev_enum);
+  size_t udev_enum_id = reinterpret_cast<size_t>(udev_enum) - 1;
+  EXPECT_LT(udev_enum_id, MAX_UDEV_ENUM_OBJECTS);
+  EXPECT_TRUE(used_udev_enum_ids_[udev_enum_id]);
+  if (!(udev_enum && udev_enum_id < MAX_UDEV_ENUM_OBJECTS &&
+        used_udev_enum_ids_[udev_enum_id]))
+    return MAX_UDEV_ENUM_OBJECTS;
+  return udev_enum_id;
+}
+
+size_t StandardGpioMockUdevInterface::UdevToId(struct udev* udev) const {
+  EXPECT_TRUE(udev);
+  size_t udev_id = reinterpret_cast<size_t>(udev) - 1;
+  EXPECT_LT(udev_id, MAX_UDEV_OBJECTS);
+  EXPECT_TRUE(used_udev_ids_[udev_id]);
+  if (!(udev && udev_id < MAX_UDEV_OBJECTS && used_udev_ids_[udev_id]))
+    return MAX_UDEV_OBJECTS;
+  return udev_id;
+}
+
+#define MOCK_GPIO_CHIP1_ID     "200"
+#define MOCK_GPIO_CHIP2_ID     "201"
+
+const char* MultiChipGpioMockUdevInterface::enum_gpio_chip_dev_list_[] = {
+  "gpiochip" MOCK_GPIO_CHIP1_ID,
+  "gpiochip" MOCK_GPIO_CHIP2_ID,
+  NULL
+};
+
+MultiChipGpioMockUdevInterface::MultiChipGpioMockUdevInterface() {
+  gpio_chip1_dev_ = (MockDevice) {
+    MOCK_SYSFS_PREFIX "/gpiochip" MOCK_GPIO_CHIP1_ID,
+    gpio_descriptors_, kMockGpioIdMax, false, false
+  };
+  gpio_chip2_dev_ = (MockDevice) {
+    MOCK_SYSFS_PREFIX "/gpiochip" MOCK_GPIO_CHIP2_ID,
+    gpio_descriptors_, kMockGpioIdMax, false, false
+  };
+}
+
+struct udev_device* MultiChipGpioMockUdevInterface::DeviceNewFromSyspath(
+    struct udev* udev, const char* syspath) {
+  const size_t udev_id = UdevToId(udev);
+  if (udev_id >= MAX_UDEV_OBJECTS)
+    return NULL;
+  MockDevice* mock_dev;
+
+  // Generate the desired mock device based on the provided syspath.
+  if (!strcmp(syspath, enum_gpio_chip_dev_list_[0])) {
+    mock_dev = const_cast<MockDevice*>(&gpio_chip1_dev_);
+  } else if (!strcmp(syspath, enum_gpio_chip_dev_list_[1])) {
+    mock_dev = const_cast<MockDevice*>(&gpio_chip2_dev_);
+  } else if (!strcmp(syspath, enum_dutflaga_gpio_dev_list_[0])) {
+    mock_dev = const_cast<MockDevice*>(&dutflaga_gpio_dev_);
+  } else if (!strcmp(syspath, enum_dutflagb_gpio_dev_list_[0])) {
+    mock_dev = const_cast<MockDevice*>(&dutflagb_gpio_dev_);
+  } else {
+    ADD_FAILURE();
+    return NULL;
+  }
+
+  EXPECT_FALSE(mock_dev->is_used);
+  if (mock_dev->is_used)
+    return NULL;
+  mock_dev->is_used = true;
+
+  return reinterpret_cast<struct udev_device*>(mock_dev);
+}
+
+struct udev_list_entry* MultiChipGpioMockUdevInterface::EnumerateGetListEntry(
+    struct udev_enumerate* udev_enum) {
+  const size_t udev_enum_id = UdevEnumToId(udev_enum);
+
+  // Return a list of devices corresponding to the provided enumeration.
+  switch (udev_enum_id) {
+    case 0:
+      return reinterpret_cast<struct udev_list_entry*>(
+          enum_gpio_chip_dev_list_);
+    case 1:
+      return reinterpret_cast<struct udev_list_entry*>(
+          enum_dutflaga_gpio_dev_list_);
+    case 2:
+      return reinterpret_cast<struct udev_list_entry*>(
+          enum_dutflagb_gpio_dev_list_);
+  }
+
+  ADD_FAILURE();  // can't get here
+  return NULL;
+}
+
+
+}  // namespace chromeos_update_engine