#!/usr/bin/env python3
#
# Copyright (C) 2017 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.

from io import StringIO

import datetime
import unittest
import update_crypto_support


def do_update_data(prev_data, current_data, name_dict={}):
    return update_crypto_support.update_data(
        prev_data,
        current_data,
        name_dict,
        72,
        datetime.datetime.utcfromtimestamp(1234567890))


# The timestamp 1234567890 in our text format
LAST_UPDATED_TEXT = '2009-02-13 23:31:30 UTC'


class TestUpdateData(unittest.TestCase):
    maxDiff = None
    def test_find_by_name(self):
        self.assertIsNone(update_crypto_support.find_by_name([], 'foo'))
        self.assertIsNone(
            update_crypto_support.find_by_name([{'name': 'foo'}], 'bar'))
        self.assertEqual(
            update_crypto_support.find_by_name(
                [{'name': 'foo', 'value': 'foo_value'}],
                'foo'),
            {'name': 'foo', 'value': 'foo_value'})
        self.assertEqual(
            update_crypto_support.find_by_name(
                [{'name': 'foo', 'value': 'foo_value'},
                 {'name': 'bar', 'value': 'bar_value'}],
                'bar'),
            {'name': 'bar', 'value': 'bar_value'})
        self.assertEqual(
            update_crypto_support.find_by_name(
                [{'name': 'foo', 'value': 'foo_value'},
                 {'name': 'bar', 'value': 'bar_value'},
                 {'name': 'foo', 'value': 'foo2_value'}],
                'foo'),
            {'name': 'foo', 'value': 'foo_value'})
        self.assertEqual(
            update_crypto_support.find_by_name(
                [{'name': 'foot', 'value': 'foot_value'},
                 {'name': 'bar', 'value': 'bar_value'},
                 {'name': 'foo', 'value': 'foo_value'}],
                'foo'),
            {'name': 'foo', 'value': 'foo_value'})

    def test_sort_by_name(self):
        self.assertEqual(update_crypto_support.sort_by_name([]), [])
        self.assertEqual(
            update_crypto_support.sort_by_name(
                [{'name': 'foot', 'value': 'foot_value'},
                 {'name': 'bar', 'value': 'bar_value'},
                 {'name': 'foo', 'value': 'foo_value'}]),
            [{'name': 'bar', 'value': 'bar_value'},
             {'name': 'foo', 'value': 'foo_value'},
             {'name': 'foot', 'value': 'foot_value'}])
        with self.assertRaises(KeyError):
            update_crypto_support.sort_by_name([{'not_name': 'foo'}])

    def test_get_current_data(self):
        self.assertEqual(update_crypto_support.get_current_data(
            StringIO(
                '''
                BEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                MessageDigest Jim
                Mac Amy
                OtherThing Mary
                END ALGORITHM LIST
                ''')),
            ({'Mac': ['BOB', 'JONES', 'AMY'],
              'MessageDigest': ['JIM']},
             {'AMY': 'Amy',
              'BOB': 'Bob',
              'JONES': 'Jones',
              'JIM': 'Jim'}))
        self.assertEqual(update_crypto_support.get_current_data(
            StringIO(
                '''
                BEGIN ALGORITHM LIST
                Mac Dupe
                Mac Jones
                MessageDigest Jim
                Mac Amy
                Mac Dupe
                OtherThing Mary
                END ALGORITHM LIST
                ''')),
            ({'Mac': ['DUPE', 'JONES', 'AMY', 'DUPE'],
              'MessageDigest': ['JIM']},
             {'AMY': 'Amy',
              'DUPE': 'Dupe',
              'JONES': 'Jones',
              'JIM': 'Jim'}))
        self.assertEqual(update_crypto_support.get_current_data(
            StringIO(
                '''
                Mac NotAValue
                BEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                MessageDigest Jim
                Mac Amy
                OtherThing Mary
                END ALGORITHM LIST
                Mac AlsoNotAValue
                ''')),
            ({'Mac': ['BOB', 'JONES', 'AMY'],
              'MessageDigest': ['JIM']},
             {'AMY': 'Amy',
              'BOB': 'Bob',
              'JONES': 'Jones',
              'JIM': 'Jim'}))
        self.assertEqual(update_crypto_support.get_current_data(
            StringIO(
                '''
                BEGIN ALGORITHM LIST OF LISTS
                Mac NotAValue
                BEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                MessageDigest Jim
                Mac Amy
                OtherThing Mary
                END ALGORITHM LIST
                ''')),
            ({'Mac': ['BOB', 'JONES', 'AMY'],
              'MessageDigest': ['JIM']},
             {'AMY': 'Amy',
              'BOB': 'Bob',
              'JONES': 'Jones',
              'JIM': 'Jim'}))
        with self.assertRaises(EOFError):
            update_crypto_support.get_current_data(StringIO(
                '''
                NOTBEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                MessageDigest Jim
                Mac Amy
                OtherThing Mary
                END ALGORITHM LIST
                '''))
        with self.assertRaises(EOFError):
            update_crypto_support.get_current_data(StringIO(
                '''
                BEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                MessageDigest Jim
                Mac Amy
                OtherThing Mary'''))
        with self.assertRaises(ValueError):
            update_crypto_support.get_current_data(StringIO(
                '''
                BEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                MessageDigest Jim OneTooManyItems
                Mac Amy
                OtherThing Mary
                END ALGORITHM LIST
                '''))
        with self.assertRaises(ValueError):
            update_crypto_support.get_current_data(StringIO(
                '''
                BEGIN ALGORITHM LIST
                Mac Bob
                Mac Jones
                TooFewItems
                MessageDigest Jim
                Mac Amy
                OtherThing Mary
                END ALGORITHM LIST
                '''))

    def test_update_data_no_data(self):
        self.assertEqual(
            do_update_data(
                {'categories': []},
                {}),
            {'categories': [],
             'api_level': '72',
             'last_updated': LAST_UPDATED_TEXT})

        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': []}]},
                {}),
            {'categories': [],
             'api_level': '72',
             'last_updated': LAST_UPDATED_TEXT})

        self.assertEqual(
            do_update_data(
                {'categories': []},
                {'MessageDigest': []}),
            {'categories': [],
             'api_level': '72',
             'last_updated': LAST_UPDATED_TEXT})

    def test_update_data_no_updates(self):
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'SHA-1',
                          'supported_api_levels': '1+'},
                         {'name': 'SHA-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'}]}]},
                {'MessageDigest': ['SHA-1']}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-1',
                      'supported_api_levels': '1+'},
                     {'name': 'SHA-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})

    def test_update_data_new_item(self):
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'SHA-1',
                          'supported_api_levels': '1+'},
                         {'name': 'SHA-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'},
                         {'name': 'SHA-384',
                          'supported_api_levels': '17-32'}]}]},
                {'MessageDigest': ['SHA-1', 'SHA-256', 'SHA-384']}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-1',
                      'supported_api_levels': '1+'},
                     {'name': 'SHA-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'},
                     {'name': 'SHA-256',
                      'supported_api_levels': '72+'},
                     {'name': 'SHA-384',
                      'supported_api_levels': '17-32,72+'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})

    def test_update_data_removed_item(self):
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'SHA-1',
                          'supported_api_levels': '1+'},
                         {'name': 'SHA-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'},
                         {'name': 'SHA-256',
                          'supported_api_levels': '70+'},
                         {'name': 'SHA-384',
                          'supported_api_levels': '71+'},
                         {'name': 'SHA-512',
                          'supported_api_levels': '1-3,17-32,47+'}]}]},
                {'MessageDigest': ['SHA-1']}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-1',
                      'supported_api_levels': '1+'},
                     {'name': 'SHA-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'},
                     {'name': 'SHA-256',
                      'supported_api_levels': '70-71',
                      'deprecated': 'true'},
                     {'name': 'SHA-384',
                      'supported_api_levels': '71',
                      'deprecated': 'true'},
                     {'name': 'SHA-512',
                      'supported_api_levels': '1-3,17-32,47-71',
                      'deprecated': 'true'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})

    def test_update_data_duplicates(self):
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'SHA-1',
                          'supported_api_levels': '1+'},
                         {'name': 'SHA-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'},
                         {'name': 'SHA-1',
                          'supported_api_levels': '7+'}]}]},
                {'MessageDigest': ['SHA-1']}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-1',
                      'supported_api_levels': '1+'},
                     {'name': 'SHA-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})

        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'SHA-1',
                          'supported_api_levels': '1+'},
                         {'name': 'SHA-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'}]}]},
                {'MessageDigest': ['SHA-1', 'SHA-1']}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-1',
                      'supported_api_levels': '1+'},
                     {'name': 'SHA-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})

    def test_update_data_preserve_notes(self):
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'SHA-1',
                          'note': 'SHA-1 note',
                          'supported_api_levels': '1+'},
                         {'name': 'SHA-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'}]}]},
                {'MessageDigest': ['SHA-1']}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-1',
                      'note': 'SHA-1 note',
                      'supported_api_levels': '1+'},
                     {'name': 'SHA-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})

    def test_update_name_matching(self):
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'sha-1',
                          'supported_api_levels': '1+'},
                         {'name': 'Sha-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'},
                         {'name': 'SHA-3',
                          'supported_api_levels': '7+'}]}]},
                {'MessageDigest': ['SHA-1', 'SHA-2', 'SHA-3']},
                {'SHA-1': 'Sha-1', 'SHA-2': 'Sha-2', 'SHA-3': 'Sha-3'}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-3',
                      'supported_api_levels': '7+'},
                     {'name': 'Sha-2',
                      'supported_api_levels': '1-22,72+'},
                     {'name': 'sha-1',
                      'supported_api_levels': '1+'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})
        self.assertEqual(
            do_update_data(
                {'categories': [
                    {'name': 'MessageDigest',
                     'algorithms': [
                         {'name': 'Sha-2',
                          'supported_api_levels': '1-22',
                          'deprecated': 'true'},
                         {'name': 'SHA-3',
                          'supported_api_levels': '7+'},
                         {'name': 'sha-1',
                          'supported_api_levels': '1+'}]}]},
                {'MessageDigest': ['SHA-1', 'SHA-3']},
                {'SHA-1': 'Sha-1', 'SHA-3': 'Sha-3'}),
            {'categories': [
                {'name': 'MessageDigest',
                 'algorithms': [
                     {'name': 'SHA-3',
                      'supported_api_levels': '7+'},
                     {'name': 'Sha-2',
                      'supported_api_levels': '1-22',
                      'deprecated': 'true'},
                     {'name': 'sha-1',
                      'supported_api_levels': '1+'}]}],
                'api_level': '72',
                'last_updated': LAST_UPDATED_TEXT})


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