dtoc: Allow specifying the base directory for tests

The base directory of U-Boot, where the source is, it currently calculated
from the directory of the dtb_platdata.py script. If this is installed
elsewhere that will not work. Also it is inconvenient for tests.

Add a parameter to allow specifying this base directory.

To test this, pass a temporary directory with some files in it and check
that they are passed to scan_driver().

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py
index 4696ff6..acb9689 100644
--- a/tools/dtoc/dtb_platdata.py
+++ b/tools/dtoc/dtb_platdata.py
@@ -199,6 +199,7 @@
                     value: dict containing structure fields:
                         key (str): Field name
                         value: Prop object with field information
+        _basedir (str): Base directory of source tree
     """
     def __init__(self, dtb_fname, include_disabled, warning_disabled,
                  drivers_additional=None):
@@ -214,6 +215,7 @@
         self._drivers_additional = drivers_additional or []
         self._dirnames = [None] * len(Ftype)
         self._struct_data = collections.OrderedDict()
+        self._basedir = None
 
     def get_normalized_compat_name(self, node):
         """Get a node's normalized compat name
@@ -439,15 +441,17 @@
                     continue
                 self._driver_aliases[alias[1]] = alias[0]
 
-    def scan_drivers(self):
+    def scan_drivers(self, basedir=None):
         """Scan the driver folders to build a list of driver names and aliases
 
         This procedure will populate self._drivers and self._driver_aliases
 
         """
-        basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
-        if basedir == '':
-            basedir = './'
+        if not basedir:
+            basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
+            if basedir == '':
+                basedir = './'
+        self._basedir = basedir
         for (dirpath, _, filenames) in os.walk(basedir):
             for fname in filenames:
                 if not fname.endswith('.c'):
@@ -831,7 +835,7 @@
 
 
 def run_steps(args, dtb_file, include_disabled, output, output_dirs,
-              warning_disabled=False, drivers_additional=None):
+              warning_disabled=False, drivers_additional=None, basedir=None):
     """Run all the steps of the dtoc tool
 
     Args:
@@ -846,6 +850,8 @@
             drivers
         drivers_additional (list): List of additional drivers to use during
             scanning
+        basedir (str): Base directory of U-Boot source code. Defaults to the
+            grandparent of this file's directory
     Raises:
         ValueError: if args has no command, or an unknown command
     """
@@ -856,7 +862,7 @@
 
     plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled,
                        drivers_additional)
-    plat.scan_drivers()
+    plat.scan_drivers(basedir)
     plat.scan_dtb()
     plat.scan_tree()
     plat.scan_reg_sizes()
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py
index d56cd31..c517fd5 100755
--- a/tools/dtoc/test_dtoc.py
+++ b/tools/dtoc/test_dtoc.py
@@ -12,9 +12,11 @@
 import collections
 import glob
 import os
+import shutil
 import struct
 import tempfile
 import unittest
+from unittest import mock
 
 from dtb_platdata import conv_name_to_c
 from dtb_platdata import get_compat_name
@@ -981,3 +983,35 @@
         self.assertEqual(
             {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'},
             leafs)
+
+    def test_scan_dirs(self):
+        """Test scanning of source directories"""
+        def add_file(fname):
+            pathname = os.path.join(indir, fname)
+            dirname = os.path.dirname(pathname)
+            os.makedirs(dirname, exist_ok=True)
+            tools.WriteFile(pathname, '', binary=False)
+            fname_list.append(pathname)
+
+        try:
+            outdir = tools.GetOutputDir()
+            indir = tempfile.mkdtemp(prefix='dtoc.')
+            dtb_file = get_dtb_file('dtoc_test_simple.dts')
+
+            fname_list = []
+            add_file('fname.c')
+            add_file('dir/fname2.c')
+
+            # Mock out scan_driver and check that it is called with the
+            # expected files
+            with mock.patch.object(dtb_platdata.DtbPlatdata, "scan_driver") \
+                    as mocked:
+                dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir],
+                                       True, basedir=indir)
+            self.assertEqual(2, len(mocked.mock_calls))
+            self.assertEqual(mock.call(fname_list[0]),
+                             mocked.mock_calls[0])
+            self.assertEqual(mock.call(fname_list[1]),
+                             mocked.mock_calls[1])
+        finally:
+            shutil.rmtree(indir)