Add script to generate /etc/os-release

/etc/os-release is the standard way to set global variables for boards.
As those values can come from different places, we cannot modify /etc/os-release
in ebuild without causing file conflicts.

This allows packages to define a field in /etc/os-release by installing a single
file in /etc/os-release.d/. os-release will then be generated from the list of
files when building the image.

BUG=chromium:420784
TEST=unittests.
TEST=Set a field in /etc/os-release and /etc/os-release.d/. Build an image.
  Both fields are present on the image's /etc/os-release.

Change-Id: I22d43ab4478f35b54cdc26dda27e917277049368
Reviewed-on: https://chromium-review.googlesource.com/221761
Commit-Ready: Andrey Ulanov <andreyu@google.com>
Tested-by: Andrey Ulanov <andreyu@google.com>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/cros_generate_os_release_unittest.py b/scripts/cros_generate_os_release_unittest.py
new file mode 100644
index 0000000..1dffa0f
--- /dev/null
+++ b/scripts/cros_generate_os_release_unittest.py
@@ -0,0 +1,55 @@
+# Copyright 2016 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.
+
+"""Test cros_generate_os_release."""
+
+from __future__ import print_function
+
+import os
+
+from chromite.lib import cros_build_lib
+from chromite.lib import cros_test_lib
+from chromite.lib import osutils
+import cros_generate_os_release
+
+
+class CrosGenerateOsReleaseTest(cros_test_lib.TempDirTestCase):
+  """Tests GenerateOsRelease."""
+
+  def setUp(self):
+    # Use a fresh tempdir as the root for each test case.
+    self.osrelease = os.path.join(self.tempdir, "etc", "os-release")
+    self.osreleased = os.path.join(self.tempdir, "etc", "os-release.d")
+    osutils.SafeMakedirs(self.osreleased)
+
+  def testOnlyOsRelease(self):
+    """Tests the script without /etc/os-release."""
+    osutils.WriteFile(os.path.join(self.osreleased, "TEST"), "hello")
+    cros_generate_os_release.GenerateOsRelease(self.tempdir)
+    self.assertEquals("TEST=hello\n", osutils.ReadFile(self.osrelease))
+
+  def testOnlyOsReleaseD(self):
+    """Tests the script without /etc/os-release.d."""
+    osutils.RmDir(self.osreleased)
+    osutils.WriteFile(self.osrelease, "TEST=bonjour\n")
+
+    cros_generate_os_release.GenerateOsRelease(self.tempdir)
+    self.assertEquals("TEST=bonjour\n", osutils.ReadFile(self.osrelease))
+
+  def testFailOnDuplicate(self):
+    """Tests with a field set both in os-release and os-release.d/."""
+    osutils.WriteFile(os.path.join(self.osreleased, "TEST"), "hello")
+    osutils.WriteFile(self.osrelease, "TEST=bonjour")
+
+    self.assertRaises(cros_build_lib.DieSystemExit,
+                      cros_generate_os_release.GenerateOsRelease, self.tempdir)
+
+  def testNormal(self):
+    """Normal scenario: both os-release and os-release.d are present."""
+    osutils.WriteFile(os.path.join(self.osreleased, "TEST1"), "hello")
+    osutils.WriteFile(self.osrelease, "TEST2=bonjour")
+
+    cros_generate_os_release.GenerateOsRelease(self.tempdir)
+    self.assertEquals("TEST1=hello\nTEST2=bonjour\n",
+                      osutils.ReadFile(self.osrelease))