#!/usr/bin/python
#
# Copyright 2010 Google Inc. All Rights Reserved.

__author__ = 'asharif@google.com (Ahmad Sharif)'

import os
import random
import shutil
import tempfile
import unittest

from cros_utils import command_executer
from cros_utils import misc


class DivideAndMergeProfilesTest(unittest.TestCase):

  def tearDown(self):
    shutil.rmtree(self._program_dir)
    for profile_dir in self._profile_dirs:
      shutil.rmtree(profile_dir)

  def setUp(self):
    self._ce = command_executer.GetCommandExecuter()
    self._program_dir = tempfile.mkdtemp()
    self._writeProgram()
    self._writeMakefile()
    with misc.WorkingDirectory(self._program_dir):
      self._ce.RunCommand('make')
    num_profile_dirs = 2
    self._profile_dirs = []
    for i in range(num_profile_dirs):
      profile_dir = tempfile.mkdtemp()
      command = ('GCOV_PREFIX_STRIP=%s GCOV_PREFIX=$(/bin/pwd) '
                 ' %s/program' % (profile_dir.count('/'), self._program_dir))
      with misc.WorkingDirectory(profile_dir):
        self._ce.RunCommand(command)
      self._profile_dirs.append(profile_dir)
    self._merge_program = '/home/build/static/projects/crosstool/profile-merge/v14.5/profile_merge.par'

  def _writeMakefile(self):
    makefile_contents = """
CC = gcc

CFLAGS = -fprofile-generate

SRCS=$(wildcard *.c)

OBJS=$(SRCS:.c=.o)

all: program

program: $(OBJS)
	$(CC) -o $@ $^ $(CFLAGS)

%.o: %.c
	$(CC) -c -o $@ $^ $(CFLAGS)"""

    makefile = os.path.join(self._program_dir, 'Makefile')
    with open(makefile, 'w') as f:
      print >> f, makefile_contents

  def _writeProgram(self, num_files=100):
    for i in range(num_files):
      current_file = os.path.join(self._program_dir, '%s.c' % i)
      with open(current_file, 'w') as f:
        if i != num_files - 1:
          print >> f, 'extern void foo%s();' % (i + 1)
          print >> f, 'void foo%s(){foo%s();}' % (i, i + 1)
        else:
          print >> f, "void foo%s(){printf(\"\");}" % i
        if i == 0:
          print >> f, 'int main(){foo%s(); return 0;}' % i

  def testMerge(self):
    reference_output = self._getReferenceOutput()
    my_output = self._getMyOutput()

    ret = self._diffOutputs(reference_output, my_output)
    shutil.rmtree(my_output)
    shutil.rmtree(reference_output)
    self.assertTrue(ret == 0)

  def _diffOutputs(self, reference, mine):
    command = 'diff -uNr %s %s' % (reference, mine)
    return self._ce.RunCommand(command)

  def _getMyOutput(self, args=''):
    my_output = tempfile.mkdtemp()
    my_merge_program = os.path.join(
        os.path.dirname(__file__), 'divide_and_merge_profiles.py')
    command = ('python %s --inputs=%s --output=%s '
               '--chunk_size=10 '
               '--merge_program=%s '
               '%s' % (my_merge_program, ','.join(self._profile_dirs),
                       my_output, self._merge_program, args))
    self._ce.RunCommand(command)
    return my_output

  def _getReferenceOutput(self, args=''):
    # First do a regular merge.
    reference_output = tempfile.mkdtemp()
    command = ('%s --inputs=%s --output=%s %s' %
               (self._merge_program, ','.join(self._profile_dirs),
                reference_output, args))
    self._ce.RunCommand(command)
    return reference_output

  def testMergeWithMultipliers(self):
    num_profiles = len(self._profile_dirs)
    multipliers = [str(random.randint(0, num_profiles)) \
                   for _ in range(num_profiles)]
    args = '--multipliers=%s' % ','.join(multipliers)

    reference_output = self._getReferenceOutput(args)
    my_output = self._getMyOutput(args)

    ret = self._diffOutputs(reference_output, my_output)

    shutil.rmtree(my_output)
    shutil.rmtree(reference_output)
    self.assertTrue(ret == 0)


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