#!/usr/bin/env python3
#
# Copyright (C) 2022 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import hashlib
import logging
import os
import subprocess
import tempfile
import unittest

import google.protobuf.text_format as text_format
import provenance_metadata_pb2

logger = logging.getLogger(__name__)

def run(args, verbose=None, **kwargs):
  """Creates and returns a subprocess.Popen object.

  Args:
    args: The command represented as a list of strings.
    verbose: Whether the commands should be shown. Default to the global
        verbosity if unspecified.
    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
        stdin, etc. stdout and stderr will default to subprocess.PIPE and
        subprocess.STDOUT respectively unless caller specifies any of them.
        universal_newlines will default to True, as most of the users in
        releasetools expect string output.

  Returns:
    A subprocess.Popen object.
  """
  if 'stdout' not in kwargs and 'stderr' not in kwargs:
    kwargs['stdout'] = subprocess.PIPE
    kwargs['stderr'] = subprocess.STDOUT
  if 'universal_newlines' not in kwargs:
    kwargs['universal_newlines'] = True
  if verbose:
    logger.info("  Running: \"%s\"", " ".join(args))
  return subprocess.Popen(args, **kwargs)


def run_and_check_output(args, verbose=None, **kwargs):
  """Runs the given command and returns the output.

  Args:
    args: The command represented as a list of strings.
    verbose: Whether the commands should be shown. Default to the global
        verbosity if unspecified.
    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
        stdin, etc. stdout and stderr will default to subprocess.PIPE and
        subprocess.STDOUT respectively unless caller specifies any of them.

  Returns:
    The output string.

  Raises:
    ExternalError: On non-zero exit from the command.
  """
  proc = run(args, verbose=verbose, **kwargs)
  output, _ = proc.communicate()
  if output is None:
    output = ""
  if verbose:
    logger.info("%s", output.rstrip())
  if proc.returncode != 0:
    raise RuntimeError(
        "Failed to run command '{}' (exit code {}):\n{}".format(
            args, proc.returncode, output))
  return output

def run_host_command(args, verbose=None, **kwargs):
  host_build_top = os.environ.get("ANDROID_BUILD_TOP")
  if host_build_top:
    host_command_dir = os.path.join(host_build_top, "out/host/linux-x86/bin")
    args[0] = os.path.join(host_command_dir, args[0])
  return run_and_check_output(args, verbose, **kwargs)

def sha256(s):
  h = hashlib.sha256()
  h.update(bytearray(s, 'utf-8'))
  return h.hexdigest()

class ProvenanceMetaDataToolTest(unittest.TestCase):

  def test_gen_provenance_metadata(self):
    artifact_content = "test artifact"
    artifact_file = tempfile.mktemp()
    with open(artifact_file,"wt") as f:
      f.write(artifact_content)

    attestation_file = artifact_file + ".intoto.jsonl"
    with open(attestation_file, "wt") as af:
      af.write("attestation file")

    metadata_file = tempfile.mktemp()
    cmd = ["gen_provenance_metadata"]
    cmd.extend(["--module_name", "a"])
    cmd.extend(["--artifact_path", artifact_file])
    cmd.extend(["--install_path", "b"])
    cmd.extend(["--metadata_path", metadata_file])
    output = run_host_command(cmd)
    self.assertEqual(output, "")

    with open(metadata_file,"rt") as f:
      data = f.read()
      provenance_metadata = provenance_metadata_pb2.ProvenanceMetadata()
      text_format.Parse(data, provenance_metadata)
      self.assertEqual(provenance_metadata.module_name, "a")
      self.assertEqual(provenance_metadata.artifact_path, artifact_file)
      self.assertEqual(provenance_metadata.artifact_install_path, "b")
      self.assertEqual(provenance_metadata.artifact_sha256, sha256(artifact_content))
      self.assertEqual(provenance_metadata.attestation_path, attestation_file)

    os.remove(artifact_file)
    os.remove(metadata_file)
    os.remove(attestation_file)

if __name__ == '__main__':
  unittest.main(verbosity=2)