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

import copy
import os
import tempfile
from typing import MutableMapping, Optional
import unittest

import binary_size_differ
import binary_sizes

_EXAMPLE_BLOBS_BEFORE = """
{
  "web_engine": [
    {
      "merkle": "77e876447dd2daaaab7048d646e87fe8b6d9fecef6cbfcc4af30b8fbfa50b881",
      "path": "locales/ta.pak",
      "bytes": 17916,
      "is_counted": true,
      "size": 16384
    },
    {
      "merkle": "5f1932b8c9fe954f3c3fdb34ab2089d2af34e5a0cef90cad41a1cd37d92234bf",
      "path": "lib/libEGL.so",
      "bytes": 226960,
      "is_counted": true,
      "size": 90112
    },
    {
      "merkle": "9822fc0dd95cdd1cc46b5c6632a928a6ad19b76ed0157397d82a2f908946fc34",
      "path": "meta.far",
      "bytes": 24576,
      "is_counted": false,
      "size": 16384
    },
    {
      "merkle": "090aed4593c4f7d04a3ad80e9971c0532dd5b1d2bdf4754202cde510a88fd220",
      "path": "locales/ru.pak",
      "bytes": 11903,
      "is_counted": true,
      "size": 16384
    }
  ]
}
"""


class BinarySizeDifferTest(unittest.TestCase):
  def ChangePackageSize(
      self,
      packages: MutableMapping[str, binary_sizes.PackageSizes],
      name: str,
      compressed_increase: int,
      uncompressed_increase: Optional[int] = None):
    if uncompressed_increase is None:
      uncompressed_increase = compressed_increase
    original_package = packages[name]
    new_package = binary_sizes.PackageSizes(
        compressed=original_package.compressed + compressed_increase,
        uncompressed=original_package.uncompressed + uncompressed_increase)
    packages[name] = new_package

  def testComputePackageDiffs(self):
    # TODO(1309977): Disabled on Windows because Windows doesn't allow opening a
    # NamedTemporaryFile by name.
    if os.name == 'nt':
      return

    SUCCESS = 0
    FAILURE = 1
    ROLLER_SIZE_WARNING = 2
    with tempfile.NamedTemporaryFile(mode='w') as before_file:
      before_file.write(_EXAMPLE_BLOBS_BEFORE)
      before_file.flush()
      blobs = binary_sizes.ReadPackageBlobsJson(before_file.name)
      sizes = binary_sizes.GetPackageSizes(blobs)
      binary_sizes.WritePackageSizesJson(before_file.name, sizes)

      # No change.
      growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                      before_file.name)
      self.assertEqual(growth['status_code'], SUCCESS)
      self.assertEqual(growth['compressed']['web_engine'], 0)

      after_file = tempfile.NamedTemporaryFile(mode='w', delete=True)
      after_file.close()
      try:
        # Increase a blob, but below the limit.
        other_sizes = copy.deepcopy(sizes)
        self.ChangePackageSize(other_sizes, 'web_engine', 8 * 1024)
        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)

        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                        after_file.name)
        self.assertEqual(growth['status_code'], SUCCESS)
        self.assertEqual(growth['compressed']['web_engine'], 8 * 1024)

        # Increase beyond the limit (adds another 8k)
        self.ChangePackageSize(other_sizes, 'web_engine', 8 * 1024 + 1)
        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                        after_file.name)
        self.assertEqual(growth['status_code'], FAILURE)
        self.assertEqual(growth['compressed']['web_engine'], 16 * 1024 + 1)
        self.assertIn('check failed', growth['summary'])
        self.assertIn(f'web_engine (compressed) grew by {16 * 1024 + 1} bytes',
                      growth['summary'])

        # Increase beyond the limit, but compressed does not increase.
        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
        self.ChangePackageSize(other_sizes,
                               'web_engine',
                               16 * 1024 + 1,
                               uncompressed_increase=0)
        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                        after_file.name)
        self.assertEqual(growth['uncompressed']['web_engine'], SUCCESS)
        self.assertEqual(growth['status_code'], SUCCESS)
        self.assertEqual(growth['compressed']['web_engine'], 16 * 1024 + 1)

        # Increase beyond the limit, but compressed goes down.
        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
        self.ChangePackageSize(other_sizes,
                               'web_engine',
                               16 * 1024 + 1,
                               uncompressed_increase=-4 * 1024)
        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                        after_file.name)
        self.assertEqual(growth['status_code'], SUCCESS)
        self.assertEqual(growth['compressed']['web_engine'], 16 * 1024 + 1)

        # Increase beyond the second limit. Fails, regardless of uncompressed.
        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
        self.ChangePackageSize(other_sizes,
                               'web_engine',
                               100 * 1024 + 1,
                               uncompressed_increase=-4 * 1024)
        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                        after_file.name)
        self.assertEqual(growth['status_code'], FAILURE)
        self.assertEqual(growth['compressed']['web_engine'], 100 * 1024 + 1)

        # Increase beyond the second limit, but roller authored CL.
        binary_sizes.WritePackageSizesJson(before_file.name, other_sizes)
        self.ChangePackageSize(other_sizes,
                               'web_engine',
                               100 * 1024 + 1,
                               uncompressed_increase=-4 * 1024)
        binary_sizes.WritePackageSizesJson(after_file.name, other_sizes)
        growth = binary_size_differ.ComputePackageDiffs(before_file.name,
                                                        after_file.name,
                                                        author='big-autoroller')
        self.assertEqual(growth['status_code'], ROLLER_SIZE_WARNING)
        self.assertEqual(growth['compressed']['web_engine'], 100 * 1024 + 1)
        self.assertNotIn('check failed', growth['summary'])
        self.assertIn('growth by an autoroller will be ignored',
                      growth['summary'])
        self.assertIn(f'web_engine (compressed) grew by {100 * 1024 + 1} bytes',
                      growth['summary'])
      finally:
        os.remove(after_file.name)


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