# Copyright 2017 The Bazel Authors. All rights reserved.
#
# 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 gzip
import io
import os
import socket
import threading
import unittest
from src.test.py.bazel import test_base

# pylint: disable=g-import-not-at-top,g-importing-member
try:
  from BaseHTTPServer import BaseHTTPRequestHandler
except ImportError:
  from http.server import BaseHTTPRequestHandler

# pylint: disable=g-import-not-at-top,g-importing-member
try:
  from BaseHTTPServer import HTTPServer
except ImportError:
  from http.server import HTTPServer


class MemoryStorageHandler(BaseHTTPRequestHandler):
  protocol_version = 'HTTP/1.1'

  def __init__(self, request, client_address, server):
    BaseHTTPRequestHandler.__init__(self, request, client_address, server)

  def do_PUT(self):
    self.send_response(200)
    self.end_headers()
    self.server.storage[self.path] = self.rfile.read(
        int(self.headers['Content-Length']))
    self.finish()

  def do_GET(self):
    if self.path in self.server.storage and 'gzip' in self.headers[
        'Accept-Encoding']:
      out = io.BytesIO()
      with gzip.GzipFile(fileobj=out, mode='w') as f:
        f.write(self.server.storage[self.path])
      self.send_response(200)
      self.send_header('Content-Length', str(len(out.getvalue())))
      self.send_header('Content-Encoding', 'gzip')
      self.end_headers()
      self.wfile.write(out.getvalue())

    else:
      self.send_response(404)
      self.end_headers()
    self.finish()


class CacheDecompressionTest(test_base.TestBase):

  def setUp(self):
    test_base.TestBase.setUp(self)
    server_port = self.GetFreeTCPPort()
    self.httpd = HTTPServer(('localhost', server_port), MemoryStorageHandler)
    self.httpd.storage = {}
    self.url = 'http://localhost:{}'.format(server_port)
    self.background = threading.Thread(target=self.httpd.serve_forever)
    self.background.start()

  def tearDown(self):
    self.httpd.shutdown()
    self.background.join()
    test_base.TestBase.tearDown(self)

  def testDecompressionWorks(self):
    content = ['Hello HTTP']
    self.ScratchFile('WORKSPACE')
    self.ScratchFile('input.txt', content)
    self.ScratchFile('BUILD', [
        'genrule(',
        '    name = "genrule",',
        '    cmd = "cp \\"$<\\" \\"$@\\"",',
        '    srcs = ["//:input.txt"],',
        '    outs = ["genrule.txt"],',
        ')',
    ])

    exit_code, _, stderr = self.RunBazel(
        ['build', '//:genrule.txt', '--remote_cache', self.url])
    self.AssertExitCode(exit_code, 0, stderr)
    self.assertNotIn('INFO: 1 process: 1 remote cache hit.', stderr)
    self.assertNotIn('HTTP version 1.1 is required', stderr)

    exit_code, _, stderr = self.RunBazel(['clean', '--expunge'])
    self.AssertExitCode(exit_code, 0, stderr)

    exit_code, _, stderr = self.RunBazel(
        ['build', '//:genrule.txt', '--remote_cache', self.url])
    self.AssertExitCode(exit_code, 0, stderr)
    self.assertIn('INFO: 1 process: 1 remote cache hit.', stderr)
    self.assertNotIn('HTTP version 1.1 is required', stderr)

    exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-genfiles'])
    self.AssertExitCode(exit_code, 0, stderr)
    bazel_genfiles = stdout[0]

    self.AssertFileContentEqual(
        os.path.join(bazel_genfiles, 'genrule.txt'), content)

  def GetFreeTCPPort(self):
    tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp.bind(('localhost', 0))
    _, port = tcp.getsockname()
    tcp.close()
    return port

  def AssertFileContentEqual(self, file_path, entry):
    with open(file_path, 'r') as f:
      self.assertEqual(f.read().splitlines(), entry)


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