| # -*- coding: utf-8 -*- |
| # Copyright 2018 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. |
| |
| """Unittests for cros_oobe_autoconfig.py""" |
| |
| from __future__ import print_function |
| |
| import json |
| import os |
| import pwd |
| import sys |
| |
| from chromite.lib import constants |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_test_lib |
| from chromite.lib import image_lib |
| from chromite.lib import osutils |
| from chromite.scripts import cros_oobe_autoconfig |
| |
| pytestmark = [cros_test_lib.pytestmark_inside_only, |
| cros_test_lib.pytestmark_skip('https://crbug.com/1000761')] |
| |
| |
| assert sys.version_info >= (3, 6), 'This module requires Python 3.6+' |
| |
| |
| _TEST_DOMAIN = 'test.com' |
| _TEST_CLI_PARAMETERS = ( |
| 'image.bin', '--x-demo-mode', '--x-network-onc', '{}', |
| '--x-network-auto-connect', '--x-eula-send-statistics', |
| '--x-eula-auto-accept', '--x-update-skip', '--x-wizard-auto-enroll', |
| '--enrollment-domain', _TEST_DOMAIN) |
| _TEST_CONFIG_JSON = { |
| 'demo-mode': True, |
| 'network-onc': '{}', |
| 'network-auto-connect': True, |
| 'eula-send-statistics': True, |
| 'eula-auto-accept': True, |
| 'update-skip': True, |
| 'wizard-auto-enroll': True |
| } |
| _IMAGE_SIZE = 4 * 1024 * 1024 |
| _BLOCK_SIZE = 4096 |
| _SECTOR_SIZE = 512 |
| _STATEFUL_SIZE = _IMAGE_SIZE // 2 |
| _STATEFUL_OFFSET = 120 * _SECTOR_SIZE |
| |
| |
| class SanitizeDomainTests(cros_test_lib.TestCase): |
| """Tests for SanitizeDomain()""" |
| |
| def testASCII(self): |
| """Tests that ASCII-only domains are not mangled.""" |
| self.assertEqual(cros_oobe_autoconfig.SanitizeDomain('FoO.cOm'), 'foo.com') |
| |
| def testUnicodeCase(self): |
| """Tests that ASCII-only domains are not mangled.""" |
| self.assertEqual(cros_oobe_autoconfig.SanitizeDomain(u'föo.com'), |
| 'xn--fo-fka.com') |
| self.assertEqual(cros_oobe_autoconfig.SanitizeDomain(u'fÖo.com'), |
| 'xn--fo-fka.com') |
| |
| def testHomographs(self): |
| """Tests that a Unicode domain is punycoded.""" |
| # "tеѕt.com" looks like "test.com" but isn't! |
| self.assertEqual(cros_oobe_autoconfig.SanitizeDomain( |
| u't\u0435\u0455t.com'), 'xn--tt-nlc2k.com') |
| |
| |
| class PrepareImageTests(cros_test_lib.MockTempDirTestCase): |
| """Tests for PrepareImage()""" |
| |
| def setUp(self): |
| """Create a small test disk image for testing.""" |
| self.image = os.path.join(self.tempdir, 'image.bin') |
| state = os.path.join(self.tempdir, 'state.bin') |
| |
| # Allocate space for the disk image and stateful partition. |
| osutils.AllocateFile(self.image, _IMAGE_SIZE) |
| osutils.AllocateFile(state, _STATEFUL_SIZE) |
| |
| commands = ( |
| # Format the stateful image as ext4. |
| ['/sbin/mkfs.ext4', state], |
| # Create the GPT headers and entry for the stateful partition. |
| ['cgpt', 'create', self.image], |
| ['cgpt', 'boot', '-p', self.image], |
| ['cgpt', 'add', self.image, '-t', 'data', |
| '-l', str(constants.CROS_PART_STATEFUL), |
| '-b', str(_STATEFUL_OFFSET // _SECTOR_SIZE), |
| '-s', str(_STATEFUL_SIZE // _SECTOR_SIZE), '-i', '1'], |
| # Copy the stateful partition into the GPT image. |
| ['dd', 'if=%s' % state, 'of=%s' % self.image, 'conv=notrunc', 'bs=4K', |
| 'seek=%d' % (_STATEFUL_OFFSET // _BLOCK_SIZE), |
| 'count=%s' % (_STATEFUL_SIZE // _BLOCK_SIZE)], |
| ['sync']) |
| for cmd in commands: |
| cros_build_lib.run(cmd, quiet=True) |
| |
| # Run the preparation script on the image. |
| cros_oobe_autoconfig.main([self.image] + list(_TEST_CLI_PARAMETERS)[1:]) |
| |
| # Mount the image's stateful partition for inspection. |
| self.mount_tmp = os.path.join(self.tempdir, 'mount') |
| osutils.SafeMakedirs(self.mount_tmp) |
| self.mount_ctx = image_lib.LoopbackPartitions(self.image, self.mount_tmp) |
| self.mount = os.path.join(self.mount_tmp, |
| 'dir-%s' % constants.CROS_PART_STATEFUL) |
| |
| self.oobe_autoconf_path = os.path.join(self.mount, 'unencrypted', |
| 'oobe_auto_config') |
| self.config_path = os.path.join(self.oobe_autoconf_path, 'config.json') |
| self.domain_path = os.path.join(self.oobe_autoconf_path, |
| 'enrollment_domain') |
| |
| def testChronosOwned(self): |
| """Test that the OOBE autoconfig directory is owned by chronos.""" |
| with self.mount_ctx: |
| # TODO(mikenichols): Remove unneeded mount call once context |
| # handling is in place, http://crrev/c/1795578 |
| _ = self.mount_ctx.Mount((constants.CROS_PART_STATEFUL,))[0] |
| chronos_uid = pwd.getpwnam('chronos').pw_uid |
| self.assertExists(self.oobe_autoconf_path) |
| self.assertEqual(os.stat(self.config_path).st_uid, chronos_uid) |
| |
| def testConfigContents(self): |
| """Test that the config JSON matches the correct data.""" |
| with self.mount_ctx: |
| # TODO(mikenichols): Remove unneeded mount call once context |
| # handling is in place, http://crrev/c/1795578 |
| _ = self.mount_ctx.Mount((constants.CROS_PART_STATEFUL,))[0] |
| with open(self.config_path) as fp: |
| data = json.load(fp) |
| self.assertEqual(data, _TEST_CONFIG_JSON) |
| |
| def testDomainContents(self): |
| """Test that the domain file matches the correct data.""" |
| with self.mount_ctx: |
| # TODO(mikenichols): Remove unneeded mount call once context |
| # handling is in place, http://crrev/c/1795578 |
| _ = self.mount_ctx.Mount((constants.CROS_PART_STATEFUL,))[0] |
| self.assertEqual(osutils.ReadFile(self.domain_path), _TEST_DOMAIN) |
| |
| |
| class GetConfigContentTests(cros_test_lib.MockTestCase): |
| """Tests for GetConfigContent()""" |
| |
| def testBasic(self): |
| """Test that config is generated correctly with all options.""" |
| opts = cros_oobe_autoconfig.ParseArguments(_TEST_CLI_PARAMETERS) |
| conf = cros_oobe_autoconfig.GetConfigContent(opts) |
| self.assertEqual(json.loads(conf), _TEST_CONFIG_JSON) |
| |
| def testUnspecified(self): |
| """Test that config is generated correctly with some options missing.""" |
| cli = list(_TEST_CLI_PARAMETERS) |
| cli.remove('--x-update-skip') |
| expected = dict(_TEST_CONFIG_JSON) |
| expected['update-skip'] = False |
| |
| opts = cros_oobe_autoconfig.ParseArguments(cli) |
| conf = cros_oobe_autoconfig.GetConfigContent(opts) |
| self.assertEqual(json.loads(conf), expected) |
| |
| |
| class MainTests(cros_test_lib.MockTestCase): |
| """Tests for main()""" |
| |
| def setUp(self): |
| self.PatchObject(cros_oobe_autoconfig, 'PrepareImage') |
| |
| def testBasic(self): |
| """Simple smoke test""" |
| cros_oobe_autoconfig.main(_TEST_CLI_PARAMETERS) |