Bidirectional test mode signaling over GPIO

* Implements a bidirectional synchronous handshake protocol over
  dut_flaga/b GPIOs. This protocol is designed to return true (test
  mode) only if the DUT is connected to a servo board which implements
  the remote end.

* Includes unit tests for the test mode signaling routine, complete with
  mock/fake implementation of the remote end.

Note that we still do not deploy GpioHandler in actual update
processing, which will be done later.

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

Change-Id: I265407ed735c3e1354e10782ac30566b16caeb20
Reviewed-on: https://gerrit.chromium.org/gerrit/23330
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
diff --git a/gpio_handler_unittest.cc b/gpio_handler_unittest.cc
index a133ac7..53ffb61 100644
--- a/gpio_handler_unittest.cc
+++ b/gpio_handler_unittest.cc
@@ -5,15 +5,9 @@
 #include <gtest/gtest.h>
 
 #include "update_engine/gpio_handler.h"
+#include "update_engine/gpio_mock_file_descriptor.h"
 #include "update_engine/gpio_mock_udev_interface.h"
 
-// Some common strings used by the different cooperating mocks for this module.
-// We use preprocessor constants to allow concatenation at compile-time.
-#define MOCK_GPIO_CHIP_ID     "100"
-#define MOCK_DUTFLAGA_GPIO_ID "101"
-#define MOCK_DUTFLAGB_GPIO_ID "102"
-#define MOCK_SYSFS_PREFIX     "/mock/sys/class/gpio"
-
 namespace chromeos_update_engine {
 
 class StandardGpioHandlerTest : public ::testing::Test {};
@@ -23,7 +17,10 @@
   // all udev resources are deallocated afterwards.  The mock file descriptor is
   // not to be used.
   StandardGpioMockUdevInterface mock_udev;
-  StandardGpioHandler gpio_hander(&mock_udev, false);
+  TestModeGpioMockFileDescriptor
+      mock_file_descriptor(base::TimeDelta::FromSeconds(1));
+  StandardGpioHandler gpio_hander(&mock_udev, &mock_file_descriptor,
+                                  false, false);
   mock_udev.ExpectAllResourcesDeallocated();
   mock_udev.ExpectDiscoverySuccess();
 }
@@ -32,27 +29,230 @@
   // Attempt GPIO discovery with a udev mock that returns two GPIO chip devices.
   // It should fail, of course.  The mock file descriptor is not to be used.
   MultiChipGpioMockUdevInterface mock_udev;
-  StandardGpioHandler gpio_handler(&mock_udev, false);
+  TestModeGpioMockFileDescriptor
+      mock_file_descriptor(base::TimeDelta::FromSeconds(1));
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
   mock_udev.ExpectAllResourcesDeallocated();
   mock_udev.ExpectDiscoveryFail();
 }
 
+TEST(StandardGpioHandlerTest, TestModeGpioSignalingTest) {
+  // Initialize the GPIO module and test for successful completion of the test
+  // signaling protocol.
+  StandardGpioMockUdevInterface mock_udev;
+  TestModeGpioMockFileDescriptor
+      mock_file_descriptor(base::TimeDelta::FromSeconds(1));
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_TRUE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, DeferredInitTestModeGpioSignalingTest) {
+  // Initialize the GPIO module with deferred initialization, test for
+  // successful completion of the test signaling protocol.
+  StandardGpioMockUdevInterface mock_udev;
+  TestModeGpioMockFileDescriptor
+      mock_file_descriptor(base::TimeDelta::FromSeconds(1));
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   true, false);
+  EXPECT_TRUE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, TestModeGpioSignalingTwiceTest) {
+  // Initialize the GPIO module and query for test signal twice (uncached); the
+  // first query should succeed whereas the second should fail.
+  StandardGpioMockUdevInterface mock_udev;
+  TestModeGpioMockFileDescriptor
+      mock_file_descriptor(base::TimeDelta::FromSeconds(1));
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_TRUE(gpio_handler.IsTestModeSignaled());
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, TestModeGpioSignalingTwiceCachedTest) {
+  // Initialize the GPIO module and query for test signal twice (cached); both
+  // queries should succeed.
+  StandardGpioMockUdevInterface mock_udev;
+  TestModeGpioMockFileDescriptor
+      mock_file_descriptor(base::TimeDelta::FromSeconds(1));
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, true);
+  EXPECT_TRUE(gpio_handler.IsTestModeSignaled());
+  EXPECT_TRUE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
 TEST(StandardGpioHandlerTest, NormalModeGpioSignalingTest) {
   // Initialize the GPIO module, run the signaling procedure, ensure that it
   // concluded that this is a normal mode run.
   StandardGpioMockUdevInterface mock_udev;
-  StandardGpioHandler gpio_handler(&mock_udev, false);
+  NormalModeGpioMockFileDescriptor mock_file_descriptor;
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
   EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
   mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, NonPulledUpNormalModeGpioSignalingTest) {
+  // Initialize the GPIO module with a non-pulled up mock (which means the it
+  // returns a different default signal), run the signaling procedure, ensure
+  // that it concluded that this is a normal mode run.
+  StandardGpioMockUdevInterface mock_udev;
+  NonPulledUpNormalModeGpioMockFileDescriptor mock_file_descriptor;
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
 }
 
 TEST(StandardGpioHandlerTest, DeferredInitNormalModeGpioSignalingTest) {
   // Initialize the GPIO module with deferred discovery, run the signaling
   // procedure, ensure that it concluded that this is a normal mode run.
   StandardGpioMockUdevInterface mock_udev;
-  StandardGpioHandler gpio_handler(&mock_udev, true);
+  NormalModeGpioMockFileDescriptor mock_file_descriptor;
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   true, false);
   EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
   mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, FlipInputDirErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates a GPIO sysfs race/hack,
+  // which causes the input GPIO to flip direction. Ensure that it concludes
+  // that this is a normal mode run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorFlipInputDir);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, ReadInvalidValErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates an invalid value reading
+  // from a GPIO device. Ensure that it concludes that this is a normal mode
+  // run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorReadInvalidVal);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, ReadInvalidDirErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates an invalid value reading
+  // from a GPIO device. Ensure that it concludes that this is a normal mode
+  // run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorReadInvalidDir);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, FailFileOpenErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates an invalid value reading
+  // from a GPIO device. Ensure that it concludes that this is a normal mode
+  // run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorFailFileOpen);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, FailFileReadErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates an invalid value reading
+  // from a GPIO device. Ensure that it concludes that this is a normal mode
+  // run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorFailFileRead);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, FailFileWriteErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates an invalid value reading
+  // from a GPIO device. Ensure that it concludes that this is a normal mode
+  // run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorFailFileWrite);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllGpiosRestoredToDefault();
+}
+
+TEST(StandardGpioHandlerTest, FailFileCloseErrorNormalModeGpioSignalingTest) {
+  // Test the GPIO module with a mock that simulates an invalid value reading
+  // from a GPIO device. Ensure that it concludes that this is a normal mode
+  // run.
+  StandardGpioMockUdevInterface mock_udev;
+  ErrorNormalModeGpioMockFileDescriptor
+      mock_file_descriptor(
+          base::TimeDelta::FromSeconds(1),
+          ErrorNormalModeGpioMockFileDescriptor::kGpioErrorFailFileClose);
+  StandardGpioHandler gpio_handler(&mock_udev, &mock_file_descriptor,
+                                   false, false);
+  EXPECT_FALSE(gpio_handler.IsTestModeSignaled());
+  mock_udev.ExpectAllResourcesDeallocated();
+  mock_file_descriptor.ExpectAllResourcesDeallocated();
+  // Don't test GPIO status restored; since closing of sysfs files fails, all
+  // bets are off.
 }
 
 }  // namespace chromeos_update_engine