#!/usr/bin/env python
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import optimize_webui
import os
import shutil
import tempfile
import unittest


_HERE_DIR = os.path.dirname(__file__)


class OptimizeWebUiTest(unittest.TestCase):
  def setUp(self):
    self._out_folder = None
    self._tmp_dirs = []
    self._tmp_src_dir = None

  def tearDown(self):
    for tmp_dir in self._tmp_dirs:
      shutil.rmtree(tmp_dir)

  def _write_file_to_src_dir(self, file_path, file_contents):
    if not self._tmp_src_dir:
      self._tmp_src_dir = self._create_tmp_dir()
    file_path_normalized = os.path.normpath(os.path.join(self._tmp_src_dir,
                                                         file_path))
    file_dir = os.path.dirname(file_path_normalized)
    if not os.path.exists(file_dir):
      os.makedirs(file_dir)
    with open(file_path_normalized, 'w') as tmp_file:
      tmp_file.write(file_contents)

  def _create_tmp_dir(self):
    # TODO(dbeam): support cross-drive paths (i.e. d:\ vs c:\).
    tmp_dir = tempfile.mkdtemp(dir=_HERE_DIR)
    self._tmp_dirs.append(tmp_dir)
    return tmp_dir

  def _read_out_file(self, file_name):
    assert self._out_folder
    return open(os.path.join(self._out_folder, file_name), 'r').read()

  def _run_optimize(self, input_args):
    assert not self._out_folder
    self._out_folder = self._create_tmp_dir()
    # TODO(dbeam): make it possible to _run_optimize twice? Is that useful?
    args = input_args + [
      '--depfile', os.path.join(self._out_folder, 'depfile.d'),
      '--host', 'fake-host',
      '--input', self._tmp_src_dir,
      '--out_folder', self._out_folder,
    ]
    optimize_webui.main(args)

  def _write_files_to_src_dir(self):
    self._write_file_to_src_dir('element.html', '<div>got here!</div>')
    self._write_file_to_src_dir('element.js', "alert('yay');")
    self._write_file_to_src_dir('element_in_dir/element_in_dir.html',
                                '<script src="element_in_dir.js">')
    self._write_file_to_src_dir('element_in_dir/element_in_dir.js',
                                "alert('hello from element_in_dir');")
    self._write_file_to_src_dir('ui.html', '''
<link rel="import" href="element.html">
<link rel="import" href="element_in_dir/element_in_dir.html">
<script src="element.js"></script>
''')

  def _write_v3_files_to_src_dir(self):
    self._write_file_to_src_dir('element.js', "alert('yay');")
    self._write_file_to_src_dir('element_in_dir/element_in_dir.js',
                                "alert('hello from element_in_dir');")
    self._write_file_to_src_dir('ui.js', '''
import './element.js';
import './element_in_dir/element_in_dir.js';
''')
    self._write_file_to_src_dir('ui.html', '''
<script type="module" src="ui.js"></script>
''')

  def _write_v3_files_with_resources_to_src_dir(self):
    resources_path = os.path.join(
        _HERE_DIR.replace('\\', '/'), 'gen', 'ui', 'webui', 'resources', 'js',
        'fake_resource.m.js')
    os.makedirs(os.path.dirname(resources_path))
    self._tmp_dirs.append('gen')
    with open(resources_path, 'w') as tmp_file:
      tmp_file.write("alert('hello from shared resource');")

    self._write_file_to_src_dir('element.js', '''
import 'chrome://resources/js/action_link.js';
alert('yay');
''')
    self._write_file_to_src_dir('element_in_dir/element_in_dir.js', '''
import {foo} from 'chrome://resources/js/fake_resource.m.js';
import '../strings.m.js';
alert('hello from element_in_dir');
''')
    self._write_file_to_src_dir('ui.js', '''
import 'chrome://fake-host/strings.m.js';
import './element.js';
import './element_in_dir/element_in_dir.js';
''')
    self._write_file_to_src_dir('ui.html', '''
<script type="module" src="ui.js"></script>
''')

  def _check_output_html(self, out_html):
    self.assertNotIn('element.html', out_html)
    self.assertNotIn('element.js', out_html)
    self.assertNotIn('element_in_dir.html', out_html)
    self.assertNotIn('element_in_dir.js', out_html)
    self.assertIn('got here!', out_html)

  def _check_output_js(self, output_js_name):
    output_js = self._read_out_file(output_js_name)
    self.assertIn('yay', output_js)
    self.assertIn('hello from element_in_dir', output_js)

  def _check_output_depfile(self, has_html):
    depfile_d = self._read_out_file('depfile.d')
    self.assertIn('element.js', depfile_d)
    self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'),
                  depfile_d)
    if (has_html):
      self.assertIn('element.html', depfile_d)
      self.assertIn(os.path.normpath('element_in_dir/element_in_dir.html'),
                    depfile_d)

  def testSimpleOptimize(self):
    self._write_files_to_src_dir()
    args = [
      '--html_in_files', 'ui.html',
      '--html_out_files', 'fast.html',
      '--js_out_files', 'fast.js',
    ]
    self._run_optimize(args)

    fast_html = self._read_out_file('fast.html')
    self._check_output_html(fast_html)
    self.assertIn('<script src="fast.js"></script>', fast_html)
    self._check_output_js('fast.js')
    self._check_output_depfile(True)

  def testV3SimpleOptimize(self):
    self._write_v3_files_to_src_dir()
    args = [
      '--js_module_in_files', 'ui.js',
      '--js_out_files', 'ui.rollup.js',
    ]
    self._run_optimize(args)

    self._check_output_js('ui.rollup.js')
    self._check_output_depfile(False)

  def testV3OptimizeWithResources(self):
    self._write_v3_files_with_resources_to_src_dir()
    args = [
      '--js_module_in_files', 'ui.js',
      '--js_out_files', 'ui.rollup.js',
    ]
    self._run_optimize(args)

    ui_rollup_js = self._read_out_file('ui.rollup.js')
    self.assertIn('yay', ui_rollup_js)
    self.assertIn('hello from element_in_dir', ui_rollup_js)
    self.assertIn('hello from shared resource', ui_rollup_js)

    depfile_d = self._read_out_file('depfile.d')
    self.assertIn('element.js', depfile_d)
    self.assertIn(os.path.normpath('element_in_dir/element_in_dir.js'),
                  depfile_d)
    self.assertIn(
        os.path.normpath('../../../../ui/webui/resources/js/action_link.js'),
        depfile_d)
    self.assertIn(
        os.path.normpath('../gen/ui/webui/resources/js/fake_resource.m.js'),
        depfile_d)

  def testV3MultiBundleOptimize(self):
    self._write_v3_files_to_src_dir()
    self._write_file_to_src_dir('lazy_element.js',
                                "alert('hello from lazy_element');")
    self._write_file_to_src_dir('lazy.js', '''
import './lazy_element.js';
import './element_in_dir/element_in_dir.js';
''')

    args = [
      '--js_module_in_files', 'ui.js', 'lazy.js',
      '--js_out_files', 'ui.rollup.js', 'lazy.rollup.js', 'shared.rollup.js',
    ]
    self._run_optimize(args)

    # Check that the shared element is in the shared bundle and the non-shared
    # elements are in the individual bundles.
    ui_js = self._read_out_file('ui.rollup.js')
    self.assertIn('yay', ui_js)
    self.assertNotIn('hello from lazy_element', ui_js)
    self.assertNotIn('hello from element_in_dir', ui_js)

    lazy_js = self._read_out_file('lazy.rollup.js')
    self.assertNotIn('yay', lazy_js)
    self.assertIn('hello from lazy_element', lazy_js)
    self.assertNotIn('hello from element_in_dir', lazy_js)

    shared_js = self._read_out_file('shared.rollup.js')
    self.assertNotIn('yay', shared_js)
    self.assertNotIn('hello from lazy_element', shared_js)
    self.assertIn('hello from element_in_dir', shared_js)

    # All 3 JS files should be in the depfile.
    self._check_output_depfile(False)
    depfile_d = self._read_out_file('depfile.d')
    self.assertIn('lazy_element.js', depfile_d)

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