flashmap: add fmap_unittest.py

- Remove test functionality from fmap.py's main
  function and replace with appropriate unit tests
  in fmap_unittest.py
- Bring over some formatting and lint fixes from
  factory/setup/fmap.py
- Add *.pyc to .gitignore

BUG=chromium:726356
TEST=ran unit tests

Change-Id: I4cb99efa3d924483dfdff9be54f6c3fc10040f3f
Reviewed-on: https://chromium-review.googlesource.com/516242
Commit-Ready: Drew Davenport <ddavenport@chromium.org>
Tested-by: Drew Davenport <ddavenport@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/.gitignore b/.gitignore
index 7fee136..f95d7e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 *.cmd
 *.gcda
 *.gcno
+*.pyc
 .config
 .config.old
 scripts/basic/docproc
diff --git a/fmap.py b/fmap.py
index 3f26b54..21519a4 100644
--- a/fmap.py
+++ b/fmap.py
@@ -51,11 +51,13 @@
   tuple of decoded area flags.
 """
 
+
 import struct
 import sys
 
+
 # constants imported from lib/fmap.h
-FMAP_SIGNATURE = "__FMAP__"
+FMAP_SIGNATURE = '__FMAP__'
 FMAP_VER_MAJOR = 1
 FMAP_VER_MINOR = 0
 FMAP_STRLEN = 32
@@ -82,9 +84,10 @@
     'flags',
 )
 
+
 # format string
-FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % (FMAP_STRLEN)
-FMAP_AREA_FORMAT = "<II%dsH" % (FMAP_STRLEN)
+FMAP_HEADER_FORMAT = '<8sBBQI%dsH' % (FMAP_STRLEN)
+FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
 
 
 def _fmap_decode_header(blob, offset):
@@ -98,8 +101,8 @@
 
   if header['signature'] != FMAP_SIGNATURE:
     raise struct.error('Invalid signature')
-  if header['ver_major'] != FMAP_VER_MAJOR or \
-     header['ver_minor'] != FMAP_VER_MINOR:
+  if (header['ver_major'] != FMAP_VER_MAJOR or
+      header['ver_minor'] != FMAP_VER_MINOR):
     raise struct.error('Incompatible version')
 
   # convert null-terminated names
@@ -134,13 +137,13 @@
             the blob.
   """
   fmap = {}
-  if offset == None:
+  if offset is None:
     # try search magic in fmap
     offset = blob.find(FMAP_SIGNATURE)
   (fmap, size) = _fmap_decode_header(blob, offset)
   fmap['areas'] = []
   offset = offset + size
-  for i in range(fmap['nareas']):
+  for _ in range(fmap['nareas']):
     (area, size) = _fmap_decode_area(blob, offset)
     offset = offset + size
     fmap['areas'].append(area)
@@ -174,13 +177,18 @@
   return blob
 
 
-if __name__ == '__main__':
-  # main entry, do a unit test
-  blob = open('bin/example.bin').read()
+def main():
+  """Decode FMAP from supplied file and print."""
+  if len(sys.argv) < 2:
+    print 'Usage: fmap.py <file>'
+    sys.exit(1)
+
+  filename = sys.argv[1]
+  print 'Decoding FMAP from: %s' % filename
+  blob = open(filename).read()
   obj = fmap_decode(blob)
   print obj
-  blob2 = fmap_encode(obj)
-  obj2 = fmap_decode(blob2)
-  print obj2
-  assert obj == obj2
 
+
+if __name__ == '__main__':
+  main()
diff --git a/fmap_unittest.py b/fmap_unittest.py
new file mode 100644
index 0000000..7006520
--- /dev/null
+++ b/fmap_unittest.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+#
+# 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.
+"""Unit test for fmap module."""
+
+import unittest
+
+import fmap
+
+# Expected decoded fmap structure from bin/example.bin
+_EXAMPLE_BIN_FMAP = {
+    'ver_major': 1,
+    'ver_minor': 0,
+    'name': 'example',
+    'nareas': 4,
+    'base': 0,
+    'signature': '__FMAP__',
+    'areas': [{
+        'FLAGS': ('FMAP_AREA_STATIC',),
+        'size': 128,
+        'flags': 1,
+        'name': 'bootblock',
+        'offset': 0
+    }, {
+        'FLAGS': ('FMAP_AREA_COMPRESSED', 'FMAP_AREA_STATIC'),
+        'size': 128,
+        'flags': 3,
+        'name': 'normal',
+        'offset': 128
+    }, {
+        'FLAGS': ('FMAP_AREA_COMPRESSED', 'FMAP_AREA_STATIC'),
+        'size': 256,
+        'flags': 3,
+        'name': 'fallback',
+        'offset': 256
+    }, {
+        'FLAGS': (),
+        'size': 512,
+        'flags': 0,
+        'name': 'data',
+        'offset': 512
+    }],
+    'size': 1024
+}
+
+
+class FmapTest(unittest.TestCase):
+  """Unit test for fmap module."""
+
+  def setUp(self):
+    with open('bin/example.bin') as f:
+      self.example_blob = f.read()
+
+  def testDecode(self):
+    decoded = fmap.fmap_decode(self.example_blob)
+    self.assertEquals(_EXAMPLE_BIN_FMAP, decoded)
+
+  def testEncode(self):
+    encoded = fmap.fmap_encode(_EXAMPLE_BIN_FMAP)
+    # example.bin contains other binary data besides the fmap
+    self.assertIn(encoded, self.example_blob)
+
+
+if __name__ == '__main__':
+  unittest.main()