#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0

import argparse
import unittest

import _test_damo_common

_test_damo_common.add_damo_dir_to_syspath()

import _damo_sysinfo
import _damon
import _damon_args
import _damon_features
import _damon_sysfs

def set_damon_sysfs_features():
    avail_features = []
    for name, support in {
            'debugfs/init_regions': True, 'sysfs/init_regions': True,
            'debugfs/schemes': True, 'sysfs/schemes': True,
            'debugfs/schemes_stat_qt_exceed': True,
            'sysfs/schemes_stat_qt_exceed': True,
            'debugfs/init_regions_target_idx': True,
            'debugfs/schemes_prioritization': True,
            'sysfs/schemes_prioritization': True,
            'sysfs/schemes_tried_regions': False, 'debugfs/record': False,
            'debugfs/schemes_time_quota': True,
            'sysfs/schemes_time_quota': True, 'sysfs/fvaddr': False,
            'debugfs/paddr': True, 'sysfs/paddr': True,
            'debugfs/schemes_wmarks': True, 'sysfs/schemes_wmarks': True,
            'debugfs/schemes_size_quota': True,
            'sysfs/schemes_size_quota': True,
            'debugfs/schemes_stat_succ': True, 'sysfs/schemes_stat_succ': True,
            'debugfs/vaddr': True, 'sysfs/vaddr': True,
            }.items():
        if not support:
            continue
        for feature in _damon_features.features_list:
            if feature.name == name:
                avail_features.append(feature)
    _damo_sysinfo.system_info = _damo_sysinfo.SystemInfo(
            damo_version=None, kernel_version=None,
            avail_damon_features=avail_features)

class TestDamonArgs(unittest.TestCase):
    def test_damon_ctxs_for(self):
        _damon._damon_fs = _damon_sysfs
        set_damon_sysfs_features()

        parser = argparse.ArgumentParser()
        _damon_args.set_argparser(
                parser, add_record_options=False, min_help=True)

        args = parser.parse_args(
                ('--sample 5000 --aggr 100000 --updr 1000000 ' +
                    '--minr 10 --maxr 1000 --regions=123-456 paddr').split())
        err = _damon_args.deduce_target_update_args(args)
        self.assertEqual(err, None)
        ctxs, err = _damon_args.damon_ctxs_for(args)
        self.assertEqual(err, None)
        ctx = ctxs[0]
        self.assertEqual(ctx, _damon.DamonCtx('paddr',
            [_damon.DamonTarget(None, [_damon.DamonRegion(123, 456)])],
            _damon.DamonIntervals(5000, 100000, 1000000),
            _damon.DamonNrRegionsRange(10, 1000), []))

        self.assertEqual(ctx, _damon.DamonCtx('paddr',
            [_damon.DamonTarget(None, [_damon.DamonRegion(123, 456)])],
            _damon.DamonIntervals(5000, 100000, 1000000),
            _damon.DamonNrRegionsRange(10, 1000), []))

        args = parser.parse_args(
                ('--sample 5ms --aggr 100ms --updr 1s ' +
                    '--minr 10 --maxr 1,000 --regions=1K-4K paddr').split())
        err = _damon_args.deduce_target_update_args(args)
        self.assertEqual(err, None)
        ctxs, err = _damon_args.damon_ctxs_for(args)
        self.assertEqual(err, None)
        ctx = ctxs[0]
        self.assertEqual(ctx, _damon.DamonCtx('paddr',
            [_damon.DamonTarget(None, [_damon.DamonRegion(1024, 4096)])],
            _damon.DamonIntervals(5000, 100000, 1000000),
            _damon.DamonNrRegionsRange(10, 1000), []))

        parser = argparse.ArgumentParser()
        _damon_args.set_argparser(
                parser, add_record_options=False, min_help=True)

        args = parser.parse_args(
                ('--sample 5ms --aggr 100ms --updr 1s ' +
                    '--minr 10 --maxr 1,000 --regions=1K-4K ' +
                    '--ops paddr').split())
        ctxs, err = _damon_args.damon_ctxs_for(args)
        self.assertEqual(err, None)
        ctx = ctxs[0]
        self.assertEqual(ctx, _damon.DamonCtx('paddr',
            [_damon.DamonTarget(None, [_damon.DamonRegion(1024, 4096)])],
            _damon.DamonIntervals(5000, 100000, 1000000),
            _damon.DamonNrRegionsRange(10, 1000), []))

    def test_damon_intervals_for(self):
        parser = argparse.ArgumentParser()
        _damon_args.set_monitoring_damos_common_args(parser)
        _damon_args.set_monitoring_argparser(parser)
        _damon_args.set_monitoring_attrs_pinpoint_argparser(parser)
        _damon_args.set_damos_argparser(parser, hide_help=False)

        args = parser.parse_args(
                '--monitoring_intervals 4ms 120ms 1.5s'.split())
        _damon_args.fillup_none_ctx_args(args)
        intervals = _damon_args.damon_intervals_for(
                args.monitoring_intervals[0], args.sample[0], args.aggr[0],
                args.updr[0], args.monitoring_intervals_goal[0])
        self.assertEqual(intervals, _damon.DamonIntervals('4ms', '120ms',
            '1.5s'))

        args = parser.parse_args('--sample 7ms'.split())
        _damon_args.fillup_none_ctx_args(args)
        intervals = _damon_args.damon_intervals_for(
                args.monitoring_intervals[0], args.sample[0], args.aggr[0],
                args.updr[0], args.monitoring_intervals_goal[0])

        self.assertEqual(intervals, _damon.DamonIntervals('7ms'))

    def test_damon_nr_regions_range_for(self):
        parser = argparse.ArgumentParser()
        _damon_args.set_monitoring_damos_common_args(parser)
        _damon_args.set_monitoring_argparser(parser)
        _damon_args.set_monitoring_attrs_pinpoint_argparser(parser)
        _damon_args.set_damos_argparser(parser, hide_help=False)

        args = parser.parse_args(
                '--monitoring_nr_regions_range 25 5000'.split())
        _damon_args.fillup_none_ctx_args(args)
        nr_range = _damon_args.damon_nr_regions_range_for(
                args.monitoring_nr_regions_range[0], args.minr[0], args.maxr[0])
        self.assertEqual(nr_range, _damon.DamonNrRegionsRange(25, 5000))

    def test_merge_ranges(self):
        merged = _damon_args.merge_ranges(
                [[10, 20], [25, 40], [40, 90], [90, 120], [125, 135],
                 [125, 145], [135, 150]])
        self.assertEqual(merged, [[10, 20], [25, 120], [125, 150]])

    def test_damos_filter_format_v2(self):
        f, e, n = _damon_args.damos_options_to_filter_v2(
                'allow anon'.split())
        self.assertEqual(
                f, _damon.DamosFilter(
                    filter_type='anon', matching=True, allow=True))
        self.assertEqual(n, 2)
        f, e, n = _damon_args.damos_options_to_filter_v2(
                'allow none anon'.split())
        self.assertEqual(
                f, _damon.DamosFilter(
                    filter_type='anon', matching=False, allow=True))
        self.assertEqual(n, 3)
        f, e, n = _damon_args.damos_options_to_filter_v2(
                'reject none anon'.split())
        self.assertEqual(
                f, _damon.DamosFilter(
                    filter_type='anon', matching=False, allow=False))
        self.assertEqual(n, 3)
        f, e, n = _damon_args.damos_options_to_filter_v2(
                'allow memcg a/b/c'.split())
        self.assertEqual(
                f, _damon.DamosFilter(
                    filter_type='memcg', matching=True, allow=True,
                    memcg_path='a/b/c'))
        self.assertEqual(n, 3)

    def test_convert_damos_filter_v1_to_v2(self):
        question_expects = [
                ['anon matching', 'reject anon'],
                ['anon matching allow', 'allow anon'],
                ['anon matching reject', 'reject anon'],
                ['anon nomatching', 'reject none anon'],
                ['anon nomatching allow', 'allow none anon'],
                ['anon nomatching reject', 'reject none anon'],

                ['memcg matching a/b/c', 'reject memcg a/b/c'],
                ['memcg matching a/b/c allow', 'allow memcg a/b/c'],
                ['memcg matching a/b/c reject', 'reject memcg a/b/c'],
                ['memcg nomatching a/b/c', 'reject none memcg a/b/c'],
                ['memcg nomatching a/b/c allow', 'allow none memcg a/b/c'],
                ['memcg nomatching a/b/c reject', 'reject none memcg a/b/c'],

                ['young matching', 'reject young'],
                ['young matching allow', 'allow young'],
                ['young matching reject', 'reject young'],
                ['young nomatching', 'reject none young'],
                ['young nomatching allow', 'allow none young'],
                ['young nomatching reject', 'reject none young'],

                ['addr matching 123 567', 'reject addr 123 567'],
                ['addr matching 123 567 allow', 'allow addr 123 567'],
                ['addr matching 123 567 reject', 'reject addr 123 567'],
                ['addr nomatching 123 567', 'reject none addr 123 567'],
                ['addr nomatching 123 567 allow', 'allow none addr 123 567'],
                ['addr nomatching 123 567 reject', 'reject none addr 123 567'],

                ['target matching 1', 'reject target 1'],
                ['target matching 1 allow', 'allow target 1'],
                ['target matching 1 reject', 'reject target 1'],
                ['target nomatching 1', 'reject none target 1'],
                ['target nomatching 1 allow', 'allow none target 1'],
                ['target nomatching 1 reject', 'reject none target 1'],

                ]
        for question, expect in question_expects:
            answer, err = _damon_args.convert_damos_filter_v1_to_v2(
                    question.split())
            self.assertEqual(err, None)
            self.assertEqual(answer, expect.split())

    def test_damos_options_to_filters(self):
        question_expects = [
                [['anon matching', 'reject anon'],
                 _damon.DamosFilter(
                     filter_type='anon', matching=True, allow=False)],
                [['anon matching allow', 'allow anon'],
                 _damon.DamosFilter(
                     filter_type='anon', matching=True, allow=True)],
                [['anon matching reject', 'reject anon'],
                 _damon.DamosFilter(
                     filter_type='anon', matching=True, allow=False)],
                [['anon nomatching', 'reject none anon'],
                 _damon.DamosFilter(
                     filter_type='anon', matching=False, allow=False)],
                [['anon nomatching allow', 'allow none anon'],
                 _damon.DamosFilter(
                     filter_type='anon', matching=False, allow=True)],
                [['anon nomatching reject', 'reject none anon'],
                 _damon.DamosFilter(
                     filter_type='anon', matching=False, allow=False)],

                [['memcg matching a/b/c', 'reject memcg a/b/c'],
                 _damon.DamosFilter(
                     filter_type='memcg', matching=True, allow=False,
                     memcg_path='a/b/c')],
                [['memcg matching a/b/c allow', 'allow memcg a/b/c'],
                 _damon.DamosFilter(
                     filter_type='memcg', matching=True, allow=True,
                     memcg_path='a/b/c')],
                [['memcg matching a/b/c reject', 'reject memcg a/b/c'],
                 _damon.DamosFilter(
                     filter_type='memcg', matching=True, allow=False,
                     memcg_path='a/b/c')],
                [['memcg nomatching a/b/c', 'reject none memcg a/b/c'],
                 _damon.DamosFilter(
                     filter_type='memcg', matching=False, allow=False,
                     memcg_path='a/b/c')],
                [['memcg nomatching a/b/c allow', 'allow none memcg a/b/c'],
                 _damon.DamosFilter(
                     filter_type='memcg', matching=False, allow=True,
                     memcg_path='a/b/c')],
                [['memcg nomatching a/b/c reject', 'reject none memcg a/b/c'],
                 _damon.DamosFilter(
                     filter_type='memcg', matching=False, allow=False,
                     memcg_path='a/b/c')],

                [['young matching', 'reject young'],
                 _damon.DamosFilter(
                     filter_type='young', matching=True, allow=False)],
                [['young matching allow', 'allow young'],
                 _damon.DamosFilter(
                     filter_type='young', matching=True, allow=True)],
                [['young matching reject', 'reject young'],
                 _damon.DamosFilter(
                     filter_type='young', matching=True, allow=False)],
                [['young nomatching', 'reject none young'],
                 _damon.DamosFilter(
                     filter_type='young', matching=False, allow=False)],
                [['young nomatching allow', 'allow none young'],
                 _damon.DamosFilter(
                     filter_type='young', matching=False, allow=True)],
                [['young nomatching reject', 'reject none young'],
                 _damon.DamosFilter(
                     filter_type='young', matching=False, allow=False)],

                [['addr matching 123 456', 'reject addr 123 456'],
                 _damon.DamosFilter(
                     filter_type='addr', matching=True, allow=False,
                     address_range=_damon.DamonRegion(123, 456))],
                [['addr matching 123 456 allow', 'allow addr 123 456'],
                 _damon.DamosFilter(
                     filter_type='addr', matching=True, allow=True,
                     address_range=_damon.DamonRegion(123, 456))],
                [['addr matching 123 456 reject', 'reject addr 123 456'],
                 _damon.DamosFilter(
                     filter_type='addr', matching=True, allow=False,
                     address_range=_damon.DamonRegion(123, 456))],
                [['addr nomatching 123 456', 'reject none addr 123 456'],
                 _damon.DamosFilter(
                     filter_type='addr', matching=False, allow=False,
                     address_range=_damon.DamonRegion(123, 456))],
                [['addr nomatching 123 456 allow', 'allow none addr 123 456'],
                 _damon.DamosFilter(
                     filter_type='addr', matching=False, allow=True,
                     address_range=_damon.DamonRegion(123, 456))],
                [['addr nomatching 123 456 reject', 'reject none addr 123 456'],
                 _damon.DamosFilter(
                     filter_type='addr', matching=False, allow=False,
                     address_range=_damon.DamonRegion(123, 456))],

                [['target matching 1', 'reject target 1'],
                 _damon.DamosFilter(
                     filter_type='target', matching=True, allow=False,
                     damon_target_idx='1')],
                [['target matching 1 allow', 'allow target 1'],
                 _damon.DamosFilter(
                     filter_type='target', matching=True, allow=True,
                     damon_target_idx='1')],
                [['target matching 1 reject', 'reject target 1'],
                 _damon.DamosFilter(
                     filter_type='target', matching=True, allow=False,
                     damon_target_idx='1')],
                [['target nomatching 1', 'reject none target 1'],
                 _damon.DamosFilter(
                     filter_type='target', matching=False, allow=False,
                     damon_target_idx='1')],
                [['target nomatching 1 allow', 'allow none target 1'],
                 _damon.DamosFilter(
                     filter_type='target', matching=False, allow=True,
                     damon_target_idx='1')],
                [['target nomatching 1 reject', 'reject none target 1'],
                 _damon.DamosFilter(
                     filter_type='target', matching=False, allow=False,
                     damon_target_idx='1')],
                ]
        for questions, expect in question_expects:
            for question in questions:
                answer, err = _damon_args.damos_options_to_filters(
                        [question.split()])
                self.assertEqual(err, None)
                self.assertEqual(answer, [expect])

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