[CIFuzz][helper] Fix external project support and add tests. (#6161)

diff --git a/infra/helper.py b/infra/helper.py
index 246c9e7..e9209713 100755
--- a/infra/helper.py
+++ b/infra/helper.py
@@ -91,7 +91,7 @@
   @property
   def dockerfile_path(self):
     """Returns path to the project Dockerfile."""
-    return os.path.join(self.path, 'Dockerfile')
+    return os.path.join(self.build_integration_path, 'Dockerfile')
 
   @property
   def language(self):
@@ -193,8 +193,9 @@
 
   # Use hacky method for extracting attributes so that ShellTest works.
   # TODO(metzman): Fix this.
-  is_external = getattr(parsed_args, 'is_external', False)
-  build_integration_path = getattr(parsed_args, 'build_integration_path', False)
+  is_external = getattr(parsed_args, 'external', False)
+  build_integration_path = getattr(parsed_args, 'build_integration_path',
+                                   DEFAULT_RELATIVE_BUILD_INTEGRATION_PATH)
 
   parsed_args.project = Project(parsed_args.project, is_external,
                                 build_integration_path)
@@ -459,17 +460,11 @@
     image_project = 'oss-fuzz-base'
     docker_build_dir = os.path.join(OSS_FUZZ_DIR, 'infra', 'base-images',
                                     image_name)
-    docker_file_path = None
-  elif project.is_external:
-    # External projects need to use the repo root as the build directory.
-    docker_file_path = os.path.join(project.build_integration_path,
-                                    'Dockerfile')
-    docker_build_dir = project.path
-    image_project = 'oss-fuzz'
+    dockerfile_path = os.path.join(docker_build_dir, 'Dockerfile')
   else:
     if not check_project_exists(project):
       return False
-    docker_file_path = None
+    dockerfile_path = project.dockerfile_path
     docker_build_dir = project.path
     image_project = 'oss-fuzz'
 
@@ -480,12 +475,10 @@
   if not cache:
     build_args.append('--no-cache')
 
-  build_args += ['-t', 'gcr.io/%s/%s' % (image_project, image_name)]
-  if docker_file_path:
-    build_args += [
-        '--file',
-        docker_file_path,
-    ]
+  build_args += [
+      '-t',
+      'gcr.io/%s/%s' % (image_project, image_name), '--file', dockerfile_path
+  ]
   build_args.append(docker_build_dir)
   return docker_build(build_args)
 
diff --git a/infra/helper_test.py b/infra/helper_test.py
index cb9e6a6..572a767 100644
--- a/infra/helper_test.py
+++ b/infra/helper_test.py
@@ -15,6 +15,7 @@
 
 import datetime
 import os
+import tempfile
 import unittest
 from unittest import mock
 
@@ -57,17 +58,20 @@
   def test_pull(self, mocked_pull_images, _):
     """Tests that pull=True is handled properly."""
     image_name = 'base-image'
-    helper.build_image_impl(helper.Project(image_name), pull=True)
+    self.assertTrue(
+        helper.build_image_impl(helper.Project(image_name), pull=True))
     mocked_pull_images.assert_called_with()
 
   @mock.patch('helper.docker_build')
   def test_base_image(self, mocked_docker_build):
     """Tests that build_image_impl works as intended with a base-image."""
     image_name = 'base-image'
-    helper.build_image_impl(helper.Project(image_name))
+    self.assertTrue(helper.build_image_impl(helper.Project(image_name)))
+    build_dir = os.path.join(helper.OSS_FUZZ_DIR,
+                             'infra/base-images/base-image')
     mocked_docker_build.assert_called_with([
-        '-t', 'gcr.io/oss-fuzz-base/base-image',
-        os.path.join(helper.OSS_FUZZ_DIR, 'infra/base-images/base-image')
+        '-t', 'gcr.io/oss-fuzz-base/base-image', '--file',
+        os.path.join(build_dir, 'Dockerfile'), build_dir
     ])
 
   @mock.patch('helper.docker_build')
@@ -75,26 +79,30 @@
     """Tests that build_image_impl works as intended with an OSS-Fuzz
     project."""
     project_name = 'example'
-    helper.build_image_impl(helper.Project(project_name))
+    self.assertTrue(helper.build_image_impl(helper.Project(project_name)))
+    build_dir = os.path.join(helper.OSS_FUZZ_DIR, 'projects', project_name)
     mocked_docker_build.assert_called_with([
-        '-t', 'gcr.io/oss-fuzz/example',
-        os.path.join(helper.OSS_FUZZ_DIR, 'projects/example')
+        '-t', 'gcr.io/oss-fuzz/example', '--file',
+        os.path.join(build_dir, 'Dockerfile'), build_dir
     ])
 
   @mock.patch('helper.docker_build')
   def test_external_project(self, mocked_docker_build):
     """Tests that build_image_impl works as intended with a non-OSS-Fuzz
     project."""
-    project_src_path = '/example'
-    build_integration_path = 'build-integration'
-    project = helper.Project(project_src_path,
-                             is_external=True,
-                             build_integration_path=build_integration_path)
-    helper.build_image_impl(project)
-    mocked_docker_build.assert_called_with([
-        '-t', 'gcr.io/oss-fuzz/example', '--file',
-        '/example/build-integration/Dockerfile', project_src_path
-    ])
+    with tempfile.TemporaryDirectory() as temp_dir:
+      project_src_path = os.path.join(temp_dir, 'example')
+      os.mkdir(project_src_path)
+      build_integration_path = 'build-integration'
+      project = helper.Project(project_src_path,
+                               is_external=True,
+                               build_integration_path=build_integration_path)
+      self.assertTrue(helper.build_image_impl(project))
+      mocked_docker_build.assert_called_with([
+          '-t', 'gcr.io/oss-fuzz/example', '--file',
+          os.path.join(project_src_path, build_integration_path, 'Dockerfile'),
+          project_src_path
+      ])
 
 
 class GenerateImplTest(fake_filesystem_unittest.TestCase):
@@ -131,3 +139,80 @@
                        build_integration_path=build_integration_path))
     self._verify_templated_files(templates.EXTERNAL_TEMPLATES,
                                  build_integration_path)
+
+
+class ProjectTest(fake_filesystem_unittest.TestCase):
+  """Tests for Project class."""
+
+  def setUp(self):
+    self.project_name = 'project'
+    self.internal_project = helper.Project(self.project_name)
+    self.external_project_path = os.path.join('path', 'to', self.project_name)
+    self.external_project = helper.Project(self.external_project_path,
+                                           is_external=True)
+    self.setUpPyfakefs()
+
+  def test_init_external_project(self):
+    """Tests __init__ method for external projects."""
+    self.assertEqual(self.external_project.name, self.project_name)
+    self.assertEqual(self.external_project.path, self.external_project_path)
+    self.assertEqual(
+        self.external_project.build_integration_path,
+        os.path.join(self.external_project_path,
+                     helper.DEFAULT_RELATIVE_BUILD_INTEGRATION_PATH))
+
+  def test_init_internal_project(self):
+    """Tests __init__ method for internal projects."""
+    self.assertEqual(self.internal_project.name, self.project_name)
+    path = os.path.join(helper.OSS_FUZZ_DIR, 'projects', self.project_name)
+    self.assertEqual(self.internal_project.path, path)
+    self.assertEqual(self.internal_project.build_integration_path, path)
+
+  def test_dockerfile_path_internal_project(self):
+    """Tests that dockerfile_path works as intended."""
+    self.assertEqual(
+        self.internal_project.dockerfile_path,
+        os.path.join(helper.OSS_FUZZ_DIR, 'projects', self.project_name,
+                     'Dockerfile'))
+
+  def test_dockerfile_path_external_project(self):
+    """Tests that dockerfile_path works as intended."""
+    self.assertEqual(
+        self.external_project.dockerfile_path,
+        os.path.join(self.external_project_path,
+                     helper.DEFAULT_RELATIVE_BUILD_INTEGRATION_PATH,
+                     'Dockerfile'))
+
+  def test_out(self):
+    """Tests that out works as intended."""
+    out_dir = self.internal_project.out
+    self.assertEqual(
+        out_dir,
+        os.path.join(helper.OSS_FUZZ_DIR, 'build', 'out', self.project_name))
+    self.assertTrue(os.path.exists(out_dir))
+
+  def test_work(self):
+    """Tests that work works as intended."""
+    work_dir = self.internal_project.work
+    self.assertEqual(
+        work_dir,
+        os.path.join(helper.OSS_FUZZ_DIR, 'build', 'work', self.project_name))
+    self.assertTrue(os.path.exists(work_dir))
+
+  def test_corpus(self):
+    """Tests that corpus works as intended."""
+    corpus_dir = self.internal_project.corpus
+    self.assertEqual(
+        corpus_dir,
+        os.path.join(helper.OSS_FUZZ_DIR, 'build', 'corpus', self.project_name))
+    self.assertTrue(os.path.exists(corpus_dir))
+
+  def test_language_internal_project(self):
+    """Tests that language works as intended for an internal project."""
+    project_yaml_path = os.path.join(self.internal_project.path, 'project.yaml')
+    self.fs.create_file(project_yaml_path, contents='language: python')
+    self.assertEqual(self.internal_project.language, 'python')
+
+  def test_language_external_project(self):
+    """Tests that language works as intended for an external project."""
+    self.assertEqual(self.external_project.language, 'c++')