authpolicy: Add unit test for DevicePolicyEncoder

Makes sure that all device policies get encoded into protobuf
properly.

Gets rid of a weird hack for integer and boolean policies to make
sure that EncodeInteger and EncodeBoolean wasn't mixed up. If the
two are mixed up now, the unit test fails since the policy is not
actually encoded.

Uses recent changes to generate_policy_source.py to make sure that
all device policies are actually handled and unit tested. Thus, if
protofiles gets uprev'ed and the uprev'er forgets to add new device
policies, this unit test fails.

BUG=chromium:659101
TEST=Compiled, ran tests, made sure it works as described here.

Change-Id: If655f967c231715ded31959d5f8abbaa1577f21a
Reviewed-on: https://chromium-review.googlesource.com/462966
Commit-Ready: Lutz Justen <ljusten@chromium.org>
Tested-by: Lutz Justen <ljusten@chromium.org>
Reviewed-by: Roman Sorokin <rsorokin@chromium.org>
diff --git a/authpolicy/authpolicy.gyp b/authpolicy/authpolicy.gyp
index d38f23d..4f9e491 100644
--- a/authpolicy/authpolicy.gyp
+++ b/authpolicy/authpolicy.gyp
@@ -181,6 +181,7 @@
           'sources': [
             'authpolicy_testrunner.cc',
             'authpolicy_unittest.cc',
+            'policy/device_policy_encoder_unittest.cc',
             'process_executor_unittest.cc',
             'samba_interface_internal_unittest.cc',
           ],
diff --git a/authpolicy/policy/device_policy_encoder.cc b/authpolicy/policy/device_policy_encoder.cc
index baddaac..db65360 100644
--- a/authpolicy/policy/device_policy_encoder.cc
+++ b/authpolicy/policy/device_policy_encoder.cc
@@ -20,34 +20,42 @@
 
 namespace policy {
 
+constexpr std::pair<const char*, int> kConnectionTypes[] = {
+    std::make_pair(
+        shill::kTypeEthernet,
+        em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_ETHERNET),
+    std::make_pair(
+        shill::kTypeWifi,
+        em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_WIFI),
+    std::make_pair(
+        shill::kTypeWimax,
+        em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_WIMAX),
+    std::make_pair(
+        shill::kTypeBluetooth,
+        em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_BLUETOOTH),
+    std::make_pair(
+        shill::kTypeCellular,
+        em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_CELLULAR)};
+
+constexpr size_t kConnectionTypesSize = arraysize(kConnectionTypes);
+
+static_assert(
+    em::AutoUpdateSettingsProto_ConnectionType_ConnectionType_ARRAYSIZE ==
+        static_cast<int>(kConnectionTypesSize),
+    "Add all values here");
+
 namespace {
 
-const std::pair<const char*, em::AutoUpdateSettingsProto_ConnectionType>
-    kConnectionTypes[] = {
-      std::make_pair(
-          shill::kTypeEthernet,
-          em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_ETHERNET),
-      std::make_pair(
-          shill::kTypeWifi,
-          em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_WIFI),
-      std::make_pair(
-          shill::kTypeWimax,
-          em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_WIMAX),
-      std::make_pair(
-          shill::kTypeBluetooth,
-          em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_BLUETOOTH),
-      std::make_pair(
-          shill::kTypeCellular,
-          em::AutoUpdateSettingsProto_ConnectionType_CONNECTION_TYPE_CELLULAR)};
-
-// Translates string connection types to enums
+// Translates string connection types to enums.
 bool DecodeConnectionType(const std::string& value,
                           em::AutoUpdateSettingsProto_ConnectionType* type) {
   DCHECK(type);
 
   for (size_t n = 0; n < arraysize(kConnectionTypes); ++n) {
     if (value.compare(kConnectionTypes[n].first) == 0) {
-      *type = kConnectionTypes[n].second;
+      int int_type = kConnectionTypes[n].second;
+      DCHECK(em::AutoUpdateSettingsProto_ConnectionType_IsValid(int_type));
+      *type = static_cast<em::AutoUpdateSettingsProto_ConnectionType>(int_type);
       return true;
     }
   }
@@ -56,13 +64,15 @@
   return false;
 }
 
+// Parses the |json| string to a base::DictionaryValue. Returns nullptr on error
+// and sets the |error| string.
 std::unique_ptr<base::DictionaryValue> JsonToDictionary(const std::string& json,
                                                         std::string* error) {
   DCHECK(error);
   std::unique_ptr<base::Value> root = base::JSONReader::ReadAndReturnError(
       json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, error);
   if (!root)
-    return nullptr;
+    return nullptr;  // |error| is set by ReadAndReturnError().
 
   std::unique_ptr<base::DictionaryValue> dict_value =
       base::DictionaryValue::From(std::move(root));
@@ -85,16 +95,16 @@
 
 void DevicePolicyEncoder::EncodeLoginPolicies(
     em::ChromeDeviceSettingsProto* policy) const {
-  EncodeBoolean(key::kDeviceGuestModeEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceGuestModeEnabled, [policy](bool value) {
     policy->mutable_guest_mode_enabled()->set_guest_mode_enabled(value);
   });
-  EncodeBoolean(key::kDeviceRebootOnShutdown, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceRebootOnShutdown, [policy](bool value) {
     policy->mutable_reboot_on_shutdown()->set_reboot_on_shutdown(value);
   });
-  EncodeBoolean(key::kDeviceShowUserNamesOnSignin, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceShowUserNamesOnSignin, [policy](bool value) {
     policy->mutable_show_user_names()->set_show_user_names(value);
   });
-  EncodeBoolean(key::kDeviceAllowNewUsers, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceAllowNewUsers, [policy](bool value) {
     policy->mutable_allow_new_users()->set_allow_new_users(value);
   });
   EncodeStringList(key::kDeviceUserWhitelist,
@@ -103,7 +113,7 @@
                      for (const std::string& value : values)
                        list->add_user_whitelist(value);
                    });
-  EncodeBoolean(key::kDeviceEphemeralUsersEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceEphemeralUsersEnabled, [policy](bool value) {
     policy->mutable_ephemeral_users_enabled()->set_ephemeral_users_enabled(
         value);
   });
@@ -116,34 +126,34 @@
       key::kDeviceLocalAccountAutoLoginId, [policy](const std::string& value) {
         policy->mutable_device_local_accounts()->set_auto_login_id(value);
       });
-  EncodeInteger(key::kDeviceLocalAccountAutoLoginDelay, [policy](Int value) {
+  EncodeInteger(key::kDeviceLocalAccountAutoLoginDelay, [policy](int value) {
     policy->mutable_device_local_accounts()->set_auto_login_delay(value);
   });
   EncodeBoolean(
-      key::kDeviceLocalAccountAutoLoginBailoutEnabled, [policy](Bool value) {
+      key::kDeviceLocalAccountAutoLoginBailoutEnabled, [policy](bool value) {
         policy->mutable_device_local_accounts()->set_enable_auto_login_bailout(
             value);
       });
   EncodeBoolean(key::kDeviceLocalAccountPromptForNetworkWhenOffline,
-                [policy](Bool value) {
+                [policy](bool value) {
                   policy->mutable_device_local_accounts()
                       ->set_prompt_for_network_when_offline(value);
                 });
 
-  EncodeBoolean(key::kSupervisedUsersEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kSupervisedUsersEnabled, [policy](bool value) {
     policy->mutable_supervised_users_settings()->set_supervised_users_enabled(
         value);
   });
-  EncodeBoolean(key::kDeviceTransferSAMLCookies, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceTransferSAMLCookies, [policy](bool value) {
     policy->mutable_saml_settings()->set_transfer_saml_cookies(value);
   });
-  EncodeInteger(key::kLoginAuthenticationBehavior, [policy](Int value) {
+  EncodeInteger(key::kLoginAuthenticationBehavior, [policy](int value) {
     policy->mutable_login_authentication_behavior()
         ->set_login_authentication_behavior(
             static_cast<em::LoginAuthenticationBehaviorProto_LoginBehavior>(
-                value.value_));
+                value));
   });
-  EncodeBoolean(key::kDeviceAllowBluetooth, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceAllowBluetooth, [policy](bool value) {
     policy->mutable_allow_bluetooth()->set_allow_bluetooth(value);
   });
   EncodeStringList(key::kLoginVideoCaptureAllowedUrls,
@@ -176,7 +186,7 @@
 
 void DevicePolicyEncoder::EncodeNetworkPolicies(
     em::ChromeDeviceSettingsProto* policy) const {
-  EncodeBoolean(key::kDeviceDataRoamingEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceDataRoamingEnabled, [policy](bool value) {
     policy->mutable_data_roaming_enabled()->set_data_roaming_enabled(value);
   });
 
@@ -195,7 +205,7 @@
                      << (!error.empty() ? error : value) << "' for policy '"
                      << key::kNetworkThrottlingEnabled
                      << "', ignoring. Expected: "
-                     << "'{\"enabled\"=<True/False>, \"upload_rate_kbits\""
+                     << "'{\"enabled\"=<true/false>, \"upload_rate_kbits\""
                      << "=<kbits>, \"download_rate_kbits\"=<kbits>}'.";
           return;
         }
@@ -215,42 +225,42 @@
 
 void DevicePolicyEncoder::EncodeReportingPolicies(
     em::ChromeDeviceSettingsProto* policy) const {
-  EncodeBoolean(key::kReportDeviceVersionInfo, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceVersionInfo, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_version_info(value);
   });
-  EncodeBoolean(key::kReportDeviceActivityTimes, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceActivityTimes, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_activity_times(value);
   });
-  EncodeBoolean(key::kReportDeviceBootMode, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceBootMode, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_boot_mode(value);
   });
-  EncodeBoolean(key::kReportDeviceLocation, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceLocation, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_location(value);
   });
-  EncodeBoolean(key::kReportDeviceNetworkInterfaces, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceNetworkInterfaces, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_network_interfaces(value);
   });
-  EncodeBoolean(key::kReportDeviceUsers, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceUsers, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_users(value);
   });
-  EncodeBoolean(key::kReportDeviceHardwareStatus, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceHardwareStatus, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_hardware_status(value);
   });
-  EncodeBoolean(key::kReportDeviceSessionStatus, [policy](Bool value) {
+  EncodeBoolean(key::kReportDeviceSessionStatus, [policy](bool value) {
     policy->mutable_device_reporting()->set_report_session_status(value);
   });
-  EncodeBoolean(key::kReportUploadFrequency, [policy](Bool value) {
+  EncodeBoolean(key::kReportUploadFrequency, [policy](bool value) {
     policy->mutable_device_reporting()->set_device_status_frequency(value);
   });
 
-  EncodeBoolean(key::kHeartbeatEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kHeartbeatEnabled, [policy](bool value) {
     policy->mutable_device_heartbeat_settings()->set_heartbeat_enabled(value);
   });
-  EncodeInteger(key::kHeartbeatFrequency, [policy](Int value) {
+  EncodeInteger(key::kHeartbeatFrequency, [policy](int value) {
     policy->mutable_device_heartbeat_settings()->set_heartbeat_frequency(value);
   });
 
-  EncodeBoolean(key::kLogUploadEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kLogUploadEnabled, [policy](bool value) {
     policy->mutable_device_log_upload_settings()->set_system_log_upload_enabled(
         value);
   });
@@ -262,21 +272,22 @@
                [policy](const std::string& value) {
                  policy->mutable_release_channel()->set_release_channel(value);
                });
-  EncodeBoolean(key::kChromeOsReleaseChannelDelegated, [policy](Bool value) {
+  EncodeBoolean(key::kChromeOsReleaseChannelDelegated, [policy](bool value) {
     policy->mutable_release_channel()->set_release_channel_delegated(value);
   });
 
-  EncodeBoolean(key::kDeviceAutoUpdateDisabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceAutoUpdateDisabled, [policy](bool value) {
     policy->mutable_auto_update_settings()->set_update_disabled(value);
   });
-  EncodeString(key::kDeviceTargetVersionPrefix, [policy](
-                                                    const std::string& value) {
-    policy->mutable_auto_update_settings()->set_target_version_prefix(value);
-  });
+  EncodeString(
+      key::kDeviceTargetVersionPrefix, [policy](const std::string& value) {
+        policy->mutable_auto_update_settings()->set_target_version_prefix(
+            value);
+      });
   // target_version_display_name is not actually a policy, but a display
   // string for target_version_prefix, so we ignore it. It seems to be
   // unreferenced as well.
-  EncodeInteger(key::kDeviceUpdateScatterFactor, [policy](Int value) {
+  EncodeInteger(key::kDeviceUpdateScatterFactor, [policy](int value) {
     policy->mutable_auto_update_settings()->set_scatter_factor_in_seconds(
         value);
   });
@@ -289,17 +300,17 @@
                          list->add_allowed_connection_types(type);
                      }
                    });
-  EncodeBoolean(key::kDeviceUpdateHttpDownloadsEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceUpdateHttpDownloadsEnabled, [policy](bool value) {
     policy->mutable_auto_update_settings()->set_http_downloads_enabled(value);
   });
-  EncodeBoolean(key::kRebootAfterUpdate, [policy](Bool value) {
+  EncodeBoolean(key::kRebootAfterUpdate, [policy](bool value) {
     policy->mutable_auto_update_settings()->set_reboot_after_update(value);
   });
-  EncodeBoolean(key::kDeviceAutoUpdateP2PEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceAutoUpdateP2PEnabled, [policy](bool value) {
     policy->mutable_auto_update_settings()->set_p2p_enabled(value);
   });
 
-  EncodeBoolean(key::kAllowKioskAppControlChromeVersion, [policy](Bool value) {
+  EncodeBoolean(key::kAllowKioskAppControlChromeVersion, [policy](bool value) {
     policy->mutable_allow_kiosk_app_control_chrome_version()
         ->set_allow_kiosk_app_control_chrome_version(value);
   });
@@ -308,30 +319,30 @@
 void DevicePolicyEncoder::EncodeAccessibilityPolicies(
     em::ChromeDeviceSettingsProto* policy) const {
   EncodeBoolean(key::kDeviceLoginScreenDefaultLargeCursorEnabled,
-                [policy](Bool value) {
+                [policy](bool value) {
                   policy->mutable_accessibility_settings()
                       ->set_login_screen_default_large_cursor_enabled(value);
                 });
   EncodeBoolean(key::kDeviceLoginScreenDefaultSpokenFeedbackEnabled,
-                [policy](Bool value) {
+                [policy](bool value) {
                   policy->mutable_accessibility_settings()
                       ->set_login_screen_default_spoken_feedback_enabled(value);
                 });
   EncodeBoolean(key::kDeviceLoginScreenDefaultHighContrastEnabled,
-                [policy](Bool value) {
+                [policy](bool value) {
                   policy->mutable_accessibility_settings()
                       ->set_login_screen_default_high_contrast_enabled(value);
                 });
   EncodeInteger(
-      key::kDeviceLoginScreenDefaultScreenMagnifierType, [policy](Int value) {
+      key::kDeviceLoginScreenDefaultScreenMagnifierType, [policy](int value) {
         policy->mutable_accessibility_settings()
             ->set_login_screen_default_screen_magnifier_type(
                 static_cast<em::AccessibilitySettingsProto_ScreenMagnifierType>(
-                    value.value_));
+                    value));
       });
   EncodeBoolean(
       key::kDeviceLoginScreenDefaultVirtualKeyboardEnabled,
-      [policy](Bool value) {
+      [policy](bool value) {
         policy->mutable_accessibility_settings()
             ->set_login_screen_default_virtual_keyboard_enabled(value);
       });
@@ -339,33 +350,33 @@
 
 void DevicePolicyEncoder::EncodeGenericPolicies(
     em::ChromeDeviceSettingsProto* policy) const {
-  EncodeInteger(key::kDevicePolicyRefreshRate, [policy](Int value) {
+  EncodeInteger(key::kDevicePolicyRefreshRate, [policy](int value) {
     policy->mutable_device_policy_refresh_rate()
         ->set_device_policy_refresh_rate(value);
   });
 
-  EncodeBoolean(key::kDeviceMetricsReportingEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceMetricsReportingEnabled, [policy](bool value) {
     policy->mutable_metrics_enabled()->set_metrics_enabled(value);
   });
 
   EncodeString(key::kSystemTimezone, [policy](const std::string& value) {
     policy->mutable_system_timezone()->set_timezone(value);
   });
-  EncodeInteger(key::kSystemTimezoneAutomaticDetection, [policy](Int value) {
+  EncodeInteger(key::kSystemTimezoneAutomaticDetection, [policy](int value) {
     policy->mutable_system_timezone()->set_timezone_detection_type(
         static_cast<em::SystemTimezoneProto_AutomaticTimezoneDetectionType>(
-            value.value_));
+            value));
   });
-  EncodeBoolean(key::kSystemUse24HourClock, [policy](Bool value) {
+  EncodeBoolean(key::kSystemUse24HourClock, [policy](bool value) {
     policy->mutable_use_24hour_clock()->set_use_24hour_clock(value);
   });
 
   EncodeBoolean(
-      key::kDeviceAllowRedeemChromeOsRegistrationOffers, [policy](Bool value) {
+      key::kDeviceAllowRedeemChromeOsRegistrationOffers, [policy](bool value) {
         policy->mutable_allow_redeem_offers()->set_allow_redeem_offers(value);
       });
 
-  EncodeInteger(key::kUptimeLimit, [policy](Int value) {
+  EncodeInteger(key::kUptimeLimit, [policy](int value) {
     policy->mutable_uptime_limit()->set_uptime_limit(value);
   });
 
@@ -381,11 +392,11 @@
                  policy->mutable_variations_parameter()->set_parameter(value);
                });
 
-  EncodeBoolean(key::kAttestationEnabledForDevice, [policy](Bool value) {
+  EncodeBoolean(key::kAttestationEnabledForDevice, [policy](bool value) {
     policy->mutable_attestation_settings()->set_attestation_enabled(value);
   });
   EncodeBoolean(
-      key::kAttestationForContentProtectionEnabled, [policy](Bool value) {
+      key::kAttestationForContentProtectionEnabled, [policy](bool value) {
         policy->mutable_attestation_settings()->set_content_protection_enabled(
             value);
       });
@@ -396,11 +407,11 @@
                      ->set_login_screen_power_management(value);
                });
 
-  EncodeBoolean(key::kDeviceBlockDevmode, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceBlockDevmode, [policy](bool value) {
     policy->mutable_system_settings()->set_block_devmode(value);
   });
 
-  EncodeInteger(key::kExtensionCacheSize, [policy](Int value) {
+  EncodeInteger(key::kExtensionCacheSize, [policy](int value) {
     policy->mutable_extension_cache_size()->set_extension_cache_size(value);
   });
 
@@ -410,9 +421,9 @@
                      ->set_login_screen_domain_auto_complete(value);
                });
 
-  EncodeInteger(key::kDisplayRotationDefault, [policy](Int value) {
+  EncodeInteger(key::kDisplayRotationDefault, [policy](int value) {
     policy->mutable_display_rotation_default()->set_display_rotation_default(
-        static_cast<em::DisplayRotationDefaultProto_Rotation>(value.value_));
+        static_cast<em::DisplayRotationDefaultProto_Rotation>(value));
   });
 
   EncodeStringList(
@@ -440,7 +451,7 @@
         }
       });
 
-  EncodeBoolean(key::kDeviceQuirksDownloadEnabled, [policy](Bool value) {
+  EncodeBoolean(key::kDeviceQuirksDownloadEnabled, [policy](bool value) {
     policy->mutable_quirks_download_enabled()->set_quirks_download_enabled(
         value);
   });
@@ -451,8 +462,7 @@
 }
 
 void DevicePolicyEncoder::EncodeBoolean(
-    const char* policy_name,
-    const BooleanPolicyCallback& set_policy) const {
+    const char* policy_name, const BooleanPolicyCallback& set_policy) const {
   // Try to get policy value from dict.
   const base::Value* value = dict_->GetValue(policy_name);
   if (!value)
@@ -469,12 +479,11 @@
             << (bool_value ? "true" : "false");
 
   // Create proto and set value.
-  set_policy(Bool(bool_value));
+  set_policy(bool_value);
 }
 
 void DevicePolicyEncoder::EncodeInteger(
-    const char* policy_name,
-    const IntegerPolicyCallback& set_policy) const {
+    const char* policy_name, const IntegerPolicyCallback& set_policy) const {
   // Try to get policy value from dict.
   const base::Value* value = dict_->GetValue(policy_name);
   if (!value)
@@ -490,12 +499,11 @@
   LOG(INFO) << " int " << policy_name << " = " << int_value;
 
   // Create proto and set value.
-  set_policy(Int(int_value));
+  set_policy(int_value);
 }
 
 void DevicePolicyEncoder::EncodeString(
-    const char* policy_name,
-    const StringPolicyCallback& set_policy) const {
+    const char* policy_name, const StringPolicyCallback& set_policy) const {
   // Try to get policy value from dict.
   const base::Value* value = dict_->GetValue(policy_name);
   if (!value)
@@ -515,8 +523,7 @@
 }
 
 void DevicePolicyEncoder::EncodeStringList(
-    const char* policy_name,
-    const StringListPolicyCallback& set_policy) const {
+    const char* policy_name, const StringListPolicyCallback& set_policy) const {
   // Try to get policy key from dict.
   const RegistryDict* key = dict_->GetKey(policy_name);
   if (!key)
diff --git a/authpolicy/policy/device_policy_encoder.h b/authpolicy/policy/device_policy_encoder.h
index 0c13241..9d2340b 100644
--- a/authpolicy/policy/device_policy_encoder.h
+++ b/authpolicy/policy/device_policy_encoder.h
@@ -7,6 +7,7 @@
 
 #include <functional>
 #include <string>
+#include <utility>
 #include <vector>
 
 namespace enterprise_management {
@@ -15,6 +16,13 @@
 
 namespace policy {
 
+// Connection types for the key::kDeviceUpdateAllowedConnectionTypes policy,
+// exposed for tests. The int is actually enterprise_management::
+// AutoUpdateSettingsProto_ConnectionType, but we don't want to include
+// chrome_device_policy.pb.h here in this header.
+extern const std::pair<const char*, int> kConnectionTypes[];
+extern const size_t kConnectionTypesSize;
+
 class RegistryDict;
 
 // Private helper class used to convert a RegistryDict into a device policy
@@ -29,29 +37,10 @@
       enterprise_management::ChromeDeviceSettingsProto* policy) const;
 
  private:
-  // For BooleanPolicyCallback and IntegerPolicyCallback we have to wrap bools
-  // and ints in order to prevent implicit type conversion of lambda functions.
-  // If we use bool and int, both
-  //   EncodeBoolean(key::kPolicy, [policy](int value) { /* set value */ });
-  // and
-  //   EncodeInteger(key::kPolicy, [policy](bool value) { /* set value */ });
-  // are perfectly legal, which is very error prone.
-  struct Bool {
-    explicit Bool(bool value) : value_(value) { }
-    operator bool() const { return value_; }
-    bool value_;
-  };
-
-  struct Int {
-    explicit Int(int value) : value_(value) { }
-    operator int() const { return value_; }
-    int value_;
-  };
-
   // Callbacks to set policy values. StringListPolicyCallback actually appends
   // a string to the list. It does not set the whole list.
-  using BooleanPolicyCallback = std::function<void(Bool)>;
-  using IntegerPolicyCallback = std::function<void(Int)>;
+  using BooleanPolicyCallback = std::function<void(bool)>;
+  using IntegerPolicyCallback = std::function<void(int)>;
   using StringPolicyCallback = std::function<void(const std::string&)>;
   using StringListPolicyCallback =
       std::function<void(const std::vector<std::string>&)>;
diff --git a/authpolicy/policy/device_policy_encoder_unittest.cc b/authpolicy/policy/device_policy_encoder_unittest.cc
new file mode 100644
index 0000000..d1ab275
--- /dev/null
+++ b/authpolicy/policy/device_policy_encoder_unittest.cc
@@ -0,0 +1,415 @@
+// Copyright 2017 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 <memory>
+#include <unordered_set>
+#include <utility>
+
+#include <base/memory/ptr_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/values.h>
+#include <components/policy/core/common/registry_dict.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "authpolicy/policy/device_policy_encoder.h"
+#include "bindings/chrome_device_policy.pb.h"
+#include "bindings/policy_constants.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+// Converts a repeated string field to a vector.
+std::vector<std::string> ToVector(
+    const google::protobuf::RepeatedPtrField<std::string>& repeated_field) {
+  return std::vector<std::string>(repeated_field.begin(), repeated_field.end());
+}
+
+// Converts a repeated int field to a vector.
+std::vector<int> ToVector(
+    const google::protobuf::RepeatedField<int>& repeated_field) {
+  return std::vector<int>(repeated_field.begin(), repeated_field.end());
+}
+
+}  // namespace
+
+// Checks whether all device policies are properly encoded from RegistryDict
+// into em::ChromeDeviceSettingsProto. Makes sure no device policy is missing.
+class DevicePolicyEncoderTest : public ::testing::Test {
+ public:
+  DevicePolicyEncoderTest() {}
+  ~DevicePolicyEncoderTest() override {}
+
+ protected:
+  // Clears |policy|, encodes |value| as value for the boolean policy |key| and
+  // marks |key| as handled.
+  void EncodeBoolean(em::ChromeDeviceSettingsProto* policy,
+                     const char* key,
+                     bool value) {
+    EncodeValue(policy, key, base::MakeUnique<base::FundamentalValue>(value));
+  }
+
+  // Clears |policy|, encodes |value| as value for the integer policy |key| and
+  // marks |key| as handled.
+  void EncodeInteger(em::ChromeDeviceSettingsProto* policy,
+                     const char* key,
+                     int value) {
+    EncodeValue(policy, key, base::MakeUnique<base::FundamentalValue>(value));
+  }
+
+  // Clears |policy|, encodes |value| as value for the string policy |key| and
+  // marks |key| as handled.
+  void EncodeString(em::ChromeDeviceSettingsProto* policy,
+                    const char* key,
+                    const std::string& value) {
+    EncodeValue(policy, key, base::MakeUnique<base::StringValue>(value));
+  }
+
+  // Clears |policy|, encodes |value| as value for the string list policy |key|
+  // and marks |key| as handled.
+  void EncodeStringList(em::ChromeDeviceSettingsProto* policy,
+                        const char* key,
+                        const std::vector<std::string>& value) {
+    auto value_dict = base::MakeUnique<RegistryDict>();
+    for (int n = 0; n < static_cast<int>(value.size()); ++n) {
+      value_dict->SetValue(base::IntToString(n + 1),
+                           base::MakeUnique<base::StringValue>(value[n]));
+    }
+    RegistryDict root_dict;
+    root_dict.SetKey(key, std::move(value_dict));
+    DevicePolicyEncoder encoder(&root_dict);
+    *policy = em::ChromeDeviceSettingsProto();
+    encoder.EncodeDevicePolicy(policy);
+    handled_policy_keys_.insert(key);
+  }
+
+  // Marks a policy |key| as handled.
+  void HandleUnsupported(const char* key) { handled_policy_keys_.insert(key); }
+
+  // Keeps track of handled device policies. Used to detect device policies that
+  // device_policy_encoder forgets to encode.
+  std::unordered_set<std::string> handled_policy_keys_;
+
+ private:
+  // Clears |policy|, encodes |value| as value for the given |key| and marks
+  // |key| as handled.
+  void EncodeValue(em::ChromeDeviceSettingsProto* policy,
+                   const char* key,
+                   std::unique_ptr<base::Value> value) {
+    RegistryDict dict;
+    dict.SetValue(key, std::move(value));
+    DevicePolicyEncoder encoder(&dict);
+    *policy = em::ChromeDeviceSettingsProto();
+    encoder.EncodeDevicePolicy(policy);
+    handled_policy_keys_.insert(key);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(DevicePolicyEncoderTest);
+};
+
+TEST_F(DevicePolicyEncoderTest, TestEncoding) {
+  // Note that kStringList can't be constexpr, so we put them all here.
+  const bool kBool = true;
+  const int kInt = 123;
+  const std::string kString = "val1";
+  const std::vector<std::string> kStringList = {"val1", "val2", "val3"};
+
+  em::ChromeDeviceSettingsProto policy;
+
+  //
+  // Login policies.
+  //
+
+  EncodeBoolean(&policy, key::kDeviceGuestModeEnabled, kBool);
+  EXPECT_EQ(kBool, policy.guest_mode_enabled().guest_mode_enabled());
+
+  EncodeBoolean(&policy, key::kDeviceRebootOnShutdown, kBool);
+  EXPECT_EQ(kBool, policy.reboot_on_shutdown().reboot_on_shutdown());
+
+  EncodeBoolean(&policy, key::kDeviceShowUserNamesOnSignin, kBool);
+  EXPECT_EQ(kBool, policy.show_user_names().show_user_names());
+
+  EncodeBoolean(&policy, key::kDeviceAllowNewUsers, kBool);
+  EXPECT_EQ(kBool, policy.allow_new_users().allow_new_users());
+
+  EncodeStringList(&policy, key::kDeviceUserWhitelist, kStringList);
+  EXPECT_EQ(kStringList, ToVector(policy.user_whitelist().user_whitelist()));
+
+  EncodeBoolean(&policy, key::kDeviceEphemeralUsersEnabled, kBool);
+  EXPECT_EQ(kBool, policy.ephemeral_users_enabled().ephemeral_users_enabled());
+
+  // Unsupported, see device_policy_encoder.cc for explanation.
+  HandleUnsupported(key::kDeviceLocalAccounts);
+  EncodeString(&policy, key::kDeviceLocalAccountAutoLoginId, kString);
+  EXPECT_EQ(kString, policy.device_local_accounts().auto_login_id());
+  EncodeInteger(&policy, key::kDeviceLocalAccountAutoLoginDelay, kInt);
+  EXPECT_EQ(kInt, policy.device_local_accounts().auto_login_delay());
+  EncodeBoolean(
+      &policy, key::kDeviceLocalAccountAutoLoginBailoutEnabled, kBool);
+  EXPECT_EQ(kBool, policy.device_local_accounts().enable_auto_login_bailout());
+  EncodeBoolean(
+      &policy, key::kDeviceLocalAccountPromptForNetworkWhenOffline, kBool);
+  EXPECT_EQ(kBool,
+            policy.device_local_accounts().prompt_for_network_when_offline());
+
+  EncodeBoolean(&policy, key::kSupervisedUsersEnabled, kBool);
+  EXPECT_EQ(kBool,
+            policy.supervised_users_settings().supervised_users_enabled());
+
+  EncodeBoolean(&policy, key::kDeviceTransferSAMLCookies, kBool);
+  EXPECT_EQ(kBool, policy.saml_settings().transfer_saml_cookies());
+
+  // The encoder of this policy converts ints to LoginBehavior enums.
+  EncodeInteger(&policy,
+                key::kLoginAuthenticationBehavior,
+                em::LoginAuthenticationBehaviorProto::SAML_INTERSTITIAL);
+  EXPECT_EQ(
+      em::LoginAuthenticationBehaviorProto::SAML_INTERSTITIAL,
+      policy.login_authentication_behavior().login_authentication_behavior());
+
+  EncodeBoolean(&policy, key::kDeviceAllowBluetooth, kBool);
+  EXPECT_EQ(kBool, policy.allow_bluetooth().allow_bluetooth());
+  EncodeStringList(&policy, key::kLoginVideoCaptureAllowedUrls, kStringList);
+  EXPECT_EQ(kStringList,
+            ToVector(policy.login_video_capture_allowed_urls().urls()));
+  EncodeStringList(&policy, key::kDeviceLoginScreenAppInstallList, kStringList);
+  EXPECT_EQ(kStringList,
+            ToVector(policy.device_login_screen_app_install_list()
+                         .device_login_screen_app_install_list()));
+  EncodeStringList(&policy, key::kDeviceLoginScreenLocales, kStringList);
+  EXPECT_EQ(kStringList,
+            ToVector(policy.login_screen_locales().login_screen_locales()));
+  EncodeStringList(&policy, key::kDeviceLoginScreenInputMethods, kStringList);
+  EXPECT_EQ(
+      kStringList,
+      ToVector(
+          policy.login_screen_input_methods().login_screen_input_methods()));
+
+  //
+  // Network policies.
+  //
+
+  EncodeBoolean(&policy, key::kDeviceDataRoamingEnabled, kBool);
+  EXPECT_EQ(kBool, policy.data_roaming_enabled().data_roaming_enabled());
+
+  // The encoder of this policy converts a json string to separate values.
+  EncodeString(&policy,
+               key::kNetworkThrottlingEnabled,
+               "{\"enabled\":true,"
+               " \"upload_rate_kbits\":128,"
+               " \"download_rate_kbits\":256}");
+  EXPECT_EQ(true, policy.network_throttling().enabled());
+  EXPECT_EQ(128, policy.network_throttling().upload_rate_kbits());
+  EXPECT_EQ(256, policy.network_throttling().download_rate_kbits());
+
+  EncodeString(&policy, key::kDeviceOpenNetworkConfiguration, kString);
+  EXPECT_EQ(kString,
+            policy.open_network_configuration().open_network_configuration());
+
+  //
+  // Reporting policies.
+  //
+
+  EncodeBoolean(&policy, key::kReportDeviceVersionInfo, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_version_info());
+  EncodeBoolean(&policy, key::kReportDeviceActivityTimes, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_activity_times());
+  EncodeBoolean(&policy, key::kReportDeviceBootMode, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_boot_mode());
+  EncodeBoolean(&policy, key::kReportDeviceLocation, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_location());
+  EncodeBoolean(&policy, key::kReportDeviceNetworkInterfaces, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_network_interfaces());
+  EncodeBoolean(&policy, key::kReportDeviceUsers, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_users());
+  EncodeBoolean(&policy, key::kReportDeviceHardwareStatus, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_hardware_status());
+  EncodeBoolean(&policy, key::kReportDeviceSessionStatus, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().report_session_status());
+  EncodeBoolean(&policy, key::kReportUploadFrequency, kBool);
+  EXPECT_EQ(kBool, policy.device_reporting().device_status_frequency());
+
+  EncodeBoolean(&policy, key::kHeartbeatEnabled, kBool);
+  EXPECT_EQ(kBool, policy.device_heartbeat_settings().heartbeat_enabled());
+  EncodeInteger(&policy, key::kHeartbeatFrequency, kInt);
+  EXPECT_EQ(kInt, policy.device_heartbeat_settings().heartbeat_frequency());
+
+  EncodeBoolean(&policy, key::kLogUploadEnabled, kBool);
+  EXPECT_EQ(kBool,
+            policy.device_log_upload_settings().system_log_upload_enabled());
+
+  //
+  // Auto update policies.
+  //
+
+  EncodeString(&policy, key::kChromeOsReleaseChannel, kString);
+  EXPECT_EQ(kString, policy.release_channel().release_channel());
+  EncodeBoolean(&policy, key::kChromeOsReleaseChannelDelegated, kBool);
+  EXPECT_EQ(kBool, policy.release_channel().release_channel_delegated());
+
+  EncodeBoolean(&policy, key::kDeviceAutoUpdateDisabled, kBool);
+  EXPECT_EQ(kBool, policy.auto_update_settings().update_disabled());
+  EncodeString(&policy, key::kDeviceTargetVersionPrefix, kString);
+  EXPECT_EQ(kString, policy.auto_update_settings().target_version_prefix());
+
+  EncodeInteger(&policy, key::kDeviceUpdateScatterFactor, kInt);
+  EXPECT_EQ(kInt, policy.auto_update_settings().scatter_factor_in_seconds());
+  // The encoder of this policy converts connection type strings to enums.
+  std::vector<std::string> str_types;
+  std::vector<int> enum_types;
+  for (size_t n = 0; n < kConnectionTypesSize; ++n) {
+    str_types.push_back(kConnectionTypes[n].first);
+    enum_types.push_back(kConnectionTypes[n].second);
+  }
+  EncodeStringList(
+      &policy, key::kDeviceUpdateAllowedConnectionTypes, str_types);
+  EXPECT_EQ(enum_types,
+            ToVector(policy.auto_update_settings().allowed_connection_types()));
+  EncodeBoolean(&policy, key::kDeviceUpdateHttpDownloadsEnabled, kBool);
+  EXPECT_EQ(kBool, policy.auto_update_settings().http_downloads_enabled());
+  EncodeBoolean(&policy, key::kRebootAfterUpdate, kBool);
+  EXPECT_EQ(kBool, policy.auto_update_settings().reboot_after_update());
+  EncodeBoolean(&policy, key::kDeviceAutoUpdateP2PEnabled, kBool);
+  EXPECT_EQ(kBool, policy.auto_update_settings().p2p_enabled());
+
+  EncodeBoolean(&policy, key::kAllowKioskAppControlChromeVersion, kBool);
+  EXPECT_EQ(kBool,
+            policy.allow_kiosk_app_control_chrome_version()
+                .allow_kiosk_app_control_chrome_version());
+
+  //
+  // Accessibility policies.
+  //
+
+  EncodeBoolean(
+      &policy, key::kDeviceLoginScreenDefaultLargeCursorEnabled, kBool);
+  EXPECT_EQ(kBool,
+            policy.accessibility_settings()
+                .login_screen_default_large_cursor_enabled());
+  EncodeBoolean(
+      &policy, key::kDeviceLoginScreenDefaultSpokenFeedbackEnabled, kBool);
+  EXPECT_EQ(kBool,
+            policy.accessibility_settings()
+                .login_screen_default_spoken_feedback_enabled());
+  EncodeBoolean(
+      &policy, key::kDeviceLoginScreenDefaultHighContrastEnabled, kBool);
+  EXPECT_EQ(kBool,
+            policy.accessibility_settings()
+                .login_screen_default_high_contrast_enabled());
+  // The encoder of this policy converts ints to ScreenMagnifierType enums.
+  EncodeInteger(&policy,
+                key::kDeviceLoginScreenDefaultScreenMagnifierType,
+                em::AccessibilitySettingsProto::SCREEN_MAGNIFIER_TYPE_FULL);
+  EXPECT_EQ(em::AccessibilitySettingsProto::SCREEN_MAGNIFIER_TYPE_FULL,
+            policy.accessibility_settings()
+                .login_screen_default_screen_magnifier_type());
+  EncodeBoolean(
+      &policy, key::kDeviceLoginScreenDefaultVirtualKeyboardEnabled, kBool);
+  EXPECT_EQ(kBool,
+            policy.accessibility_settings()
+                .login_screen_default_virtual_keyboard_enabled());
+
+  //
+  // Generic policies.
+  //
+
+  EncodeInteger(&policy, key::kDevicePolicyRefreshRate, kInt);
+  EXPECT_EQ(kInt,
+            policy.device_policy_refresh_rate().device_policy_refresh_rate());
+
+  EncodeBoolean(&policy, key::kDeviceMetricsReportingEnabled, kBool);
+  EXPECT_EQ(kBool, policy.metrics_enabled().metrics_enabled());
+
+  EncodeString(&policy, key::kSystemTimezone, kString);
+  EXPECT_EQ(kString, policy.system_timezone().timezone());
+  // The encoder of this policy converts ints to AutomaticTimezoneDetectionType
+  // enums.
+  EncodeInteger(&policy,
+                key::kSystemTimezoneAutomaticDetection,
+                em::SystemTimezoneProto::IP_ONLY);
+  EXPECT_EQ(em::SystemTimezoneProto::IP_ONLY,
+            policy.system_timezone().timezone_detection_type());
+
+  EncodeBoolean(&policy, key::kSystemUse24HourClock, kBool);
+  EXPECT_EQ(kBool, policy.use_24hour_clock().use_24hour_clock());
+
+  EncodeBoolean(
+      &policy, key::kDeviceAllowRedeemChromeOsRegistrationOffers, kBool);
+  EXPECT_EQ(kBool, policy.allow_redeem_offers().allow_redeem_offers());
+
+  EncodeInteger(&policy, key::kUptimeLimit, kInt);
+  EXPECT_EQ(kInt, policy.uptime_limit().uptime_limit());
+
+  EncodeStringList(&policy, key::kDeviceStartUpFlags, kStringList);
+  EXPECT_EQ(kStringList, ToVector(policy.start_up_flags().flags()));
+
+  EncodeString(&policy, key::kDeviceVariationsRestrictParameter, kString);
+  EXPECT_EQ(kString, policy.variations_parameter().parameter());
+
+  EncodeBoolean(&policy, key::kAttestationEnabledForDevice, kBool);
+  EXPECT_EQ(kBool, policy.attestation_settings().attestation_enabled());
+  EncodeBoolean(&policy, key::kAttestationForContentProtectionEnabled, kBool);
+  EXPECT_EQ(kBool, policy.attestation_settings().content_protection_enabled());
+
+  EncodeString(&policy, key::kDeviceLoginScreenPowerManagement, kString);
+  EXPECT_EQ(
+      kString,
+      policy.login_screen_power_management().login_screen_power_management());
+
+  EncodeBoolean(&policy, key::kDeviceBlockDevmode, kBool);
+  EXPECT_EQ(kBool, policy.system_settings().block_devmode());
+
+  EncodeInteger(&policy, key::kExtensionCacheSize, kInt);
+  EXPECT_EQ(kInt, policy.extension_cache_size().extension_cache_size());
+
+  EncodeString(&policy, key::kDeviceLoginScreenDomainAutoComplete, kString);
+  EXPECT_EQ(kString,
+            policy.login_screen_domain_auto_complete()
+                .login_screen_domain_auto_complete());
+
+  // The encoder of this policy converts ints to Rotation enums.
+  EncodeInteger(&policy,
+                key::kDisplayRotationDefault,
+                em::DisplayRotationDefaultProto::ROTATE_180);
+  EXPECT_EQ(em::DisplayRotationDefaultProto::ROTATE_180,
+            policy.display_rotation_default().display_rotation_default());
+
+  // The encoder of this policy converts a json string to separate values.
+  EncodeStringList(&policy,
+                   key::kUsbDetachableWhitelist,
+                   {"{\"vendor_id\":123, \"product_id\":234}",
+                    "{\"vendor_id\":345, \"product_id\":456}"});
+  const auto& whitelist_proto = policy.usb_detachable_whitelist();
+  EXPECT_EQ(123, whitelist_proto.id().Get(0).vendor_id());
+  EXPECT_EQ(234, whitelist_proto.id().Get(0).product_id());
+  EXPECT_EQ(345, whitelist_proto.id().Get(1).vendor_id());
+  EXPECT_EQ(456, whitelist_proto.id().Get(1).product_id());
+
+  EncodeBoolean(&policy, key::kDeviceQuirksDownloadEnabled, kBool);
+  EXPECT_EQ(kBool, policy.quirks_download_enabled().quirks_download_enabled());
+
+  EncodeString(&policy, key::kDeviceWallpaperImage, kString);
+  EXPECT_EQ(kString, policy.device_wallpaper_image().device_wallpaper_image());
+
+  //
+  // Check whether all device policies have been handled.
+  //
+
+  std::vector<std::string> unhandled_policy_keys;
+  for (const char** key = kDevicePolicyKeys; *key; ++key) {
+    if (handled_policy_keys_.find(*key) == handled_policy_keys_.end())
+      unhandled_policy_keys.push_back(*key);
+  }
+  EXPECT_TRUE(unhandled_policy_keys.empty())
+      << "Unhandled policy detected.\n"
+      << "Please handle the following policies in "
+      << "device_policy_encoder.cc and device_policy_encoder_unittest.cc:\n"
+      << "  " << base::JoinString(unhandled_policy_keys, "\n  ");
+}
+
+}  // namespace policy