<commit_before># -*- coding: utf-8 -*-

from django.db import models
from generic.models import GenericModel, FalseFK
from django.utils.translation import ugettext_lazy as _

from users.models import TruffeUser

import datetime
from multiselectfield import MultiSelectField

from rights.utils import AgepolyEditableModel, UnitEditableModel

from django.conf import settings

from django.db.models import Q

class _Unit(GenericModel, AgepolyEditableModel):

    class MetaRightsAgepoly(AgepolyEditableModel.MetaRightsAgepoly):
        access = 'INFORMATIQUE'
        world_ro_access = True

    name = models.CharField(max_length=255)
    id_epfl = models.CharField(max_length=64, blank=True, null=True, help_text=_(u'Utilisé pour la syncronisation des accréditations'))
    description = models.TextField(blank=True, null=True)
    url = models.URLField(blank=True, null=True)

    is_commission = models.BooleanField(default=False, help_text=_(u'Cocher si cette unité est une commission de l\'AGEPoly'))
    is_equipe = models.BooleanField(default=False, help_text=_(u'Cocher si cette unité est une équipe de l\'AGEPoly'))

    parent_herachique = models.ForeignKey('Unit', blank=True, null=True, help_text=_(u'Pour les commission, sélectionner le comité de l\'AGEPoly. Pour les sous-commisions, sélectionner la commission parente. Pour un sous-coaching, sélectionner la commission coaching. Pour le comité de l\'AGEPoly, ne rien mettre.'))

    class MetaData:
        list_display = [
            ('name', _('Nom')),
            ('is_commission', _('Commission ?')),
            ('is_equipe', _(u'Équipe ?')),
            ('parent_herachique', _('Parent')),
            ('president', _('President'))
        ]

        details_display = [
            ('name', _('Nom')),
            ('is_commission', _('Commission ?')),
            ('is_equipe', _(u'Équipe ?')),
            ('parent_herachique', _('Parent')),
            ('president', _('President'))
        ]

        yes_or_no_fields = ['is_commission', 'is_equipe']

        filter_fields = ('name', )

        base_title = _(u'Unités')
        list_title = _(u'Liste de toutes les unités')
        base_icon = 'fa fa-list'
        elem_icon = 'fa fa-group'

        menu_id = 'menu-units-units'

        help_list = _(u"""Les unités sont les différents groups de l'AGEPoly (Comité de l'AGEPoly, commissions, équipes, etc.)

Les unités sont organisées en arbre hérachique, avec le Comité de l'AGEPoly au sommet.""")

    class Meta:
        abstract = True

    def __unicode__(self):
        return self.name

    def rights_can_select(self):
        """Return true if the unit can be selected in the selector menu"""
        return True

    _rights_can_select = lambda unit: True

    def set_rights_can_select(self, f):
        def __tmp():
            return f(self)
        self.rights_can_select = __tmp
        self._rights_can_select = f

    def rights_can_edit(self):
        """Return true if the user has edit right"""
        return True

    _rights_can_edit = lambda unit: True

    def set_rights_can_edit(self, f):
        def __tmp():
            return f(self)
        self.rights_can_edit = __tmp
        self._rights_can_edit = f

    def has_sub(self):
        """Return true if the unit has subunits"""
        return self.unit_set.filter(deleted=False).order_by('name').count() > 0

    def only_one_sub_type(self):
        tt = 0

        if self.sub_com():
            tt += 1
        if self.sub_eqi():
            tt += 1
        if self.sub_grp():
            tt += 1

        return tt == 1

    def sub_com(self):
        """Return the sub units, but only commissions"""
        retour = []
        for unit in self.unit_set.filter(is_commission=True).filter(deleted=False).order_by('name'):
            unit.set_rights_can_select(self._rights_can_select)
            unit.set_rights_can_edit(self._rights_can_edit)
            retour.append(unit)
        return retour

    def sub_eqi(self):
        """Return the sub units, but only groups"""
        retour = []
        for unit in self.unit_set.exclude(is_commission=True).filter(is_equipe=True).filter(deleted=False).order_by('name'):
            unit.set_rights_can_select(self._rights_can_select)
            retour.append(unit)
        return retour

    def sub_grp(self):
        """Return the sub units, without groups or commissions"""
        retour = []
        for unit in self.unit_set.filter(is_commission=False).filter(is_equipe=False).filter(deleted=False).order_by('name'):
            unit.set_rights_can_select(self._rights_can_select)
            retour.append(unit)
        return retour

    def is_user_in_groupe(self, user, access=None, parent_mode=False):

        for accreditation in self.accreditation_set.filter(user=user, end_date=None):
            if accreditation.is_valid():

                # No acces: Only an accred is needed
                if not access:
                    return True

                # If role has acces, ok
                if access in accreditation.role.access:
                    return True

                # Check valid delegations for this accred
                access_delegations = self.accessdelegation_set.filter((Q(user=user) | Q(user=None)) & (Q(role=accreditation.role) | Q(role=None))).all()

                for access_delegation in access_delegations:
                    if access in access_delegation.access and (not parent_mode or access_delegation.valid_for_sub_units):
                        return True

        if self.parent_herachique:
            return self.parent_herachique.is_user_in_groupe(user, access, True)
        return False

    @property
    def president(self):
        return ', '.join([u.user.get_full_name() for u in list(self.accreditation_set.filter(end_date=None, role__pk=settings.PRESIDENT_ROLE_PK))])

    def can_delete(self):

        if self.accreditation_set.count():
            return (False, _(u'Au moins une accéditation existe avec cette unité, impossible de supprimer l\'unité (NB: Historique compris).'))

        return (True, None)

    def current_accreds(self):
        return self.accreditation_set.filter(end_date=None).order_by('role__ordre', 'user__first_name', 'user__last_name')

    def get_users(self):
        return [a.user for a in self.current_accreds()]

class _Role(GenericModel, AgepolyEditableModel):
    """Un role, pour une accred"""

    class MetaRightsAgepoly(AgepolyEditableModel.MetaRightsAgepoly):
        access = 'INFORMATIQUE'
        world_ro_access = True

    name = models.CharField(max_length=255)
    id_epfl = models.CharField(max_length=255, null=True, blank=True, help_text=_(u'Mettre ici l\'ID accred du role pour la syncronisation EPFL'))
    description = models.TextField(null=True, blank=True)
    ordre = models.IntegerField(null=True, blank=True, help_text=_(u'Il n\'est pas possible d\'acréditer la même personne dans la même unité plusieurs fois. Le role avec le plus PETIT ordre sera prit en compte'))

    ACCESS_CHOICES = (
        ('PRESIDENCE', ('Présidence')),
        ('TRESORERIE', ('Trésorerie')),
        ('COMMUNICATION', ('Communication')),
        ('INFORMATIQUE', ('Informatique')),
        ('LOGISTIQUE', ('Logistique')),
        ('SECRETARIAT', ('Secrétariat'))
    )

    access = MultiSelectField(choices=ACCESS_CHOICES, blank=True, null=True)

    def __unicode__(self):
        return self.name

    def get_access(self):
        return ', '.join(self.access)

    class MetaData:
        list_display = [
            ('name', _('Nom')),
            ('id_epfl', _('ID EPFL ?')),
            ('ordre', _(u'Ordre'))
        ]

        details_display = [
            ('name', _('Nom')),
            ('description', _('Description')),
            ('id_epfl', _('ID EPFL ?')),
            ('ordre', _(u'Ordre')),
            ('get_access', _(u'Accès')),
        ]

        filter_fields = ('name', 'id_epfl', 'description')

        base_title = _(u'Roles')
        list_title = _(u'Liste de toutes les roles')
        base_icon = 'fa fa-list'
        elem_icon = 'fa fa-group'

        menu_id = 'menu-units-roles'

        help_list = _(u"""Les roles sont les différents type d'accéditations possible pour une unité.

Certains roles donnent des accès particuliers.
Par exemple, le role 'Trésorier' donne l'accès TRESORERIE. Les droits sont gérés en fonction des accês !""")

    class Meta:
        abstract = True

    def can_delete(self):

        if self.accreditation_set.count():
            return (False, _(u'Au moins une accéditation existe avec ce role, impossible de supprimer le role (NB: Historique compris)'))

        return (True, None)

class Accreditation(models.Model, UnitEditableModel):
    unit = models.ForeignKey('Unit')
    user = models.ForeignKey(TruffeUser)
    role = models.ForeignKey('Role')

    start_date = models.DateTimeField(auto_now_add=True)
    end_date = models.DateTimeField(blank=True, null=True)
    validation_date = models.DateTimeField(auto_now_add=True)

    display_name = models.CharField(max_length=255, blank=True, null=True, help_text=_(u'Le nom a afficher dans truffe. Peut être utilisé pour préciser la fonction'))

    no_epfl_sync = models.BooleanField(default=False, help_text=_(u'Checker cette coche pour ne pas sycroniser cette accrédiation au niveau EPFL'))

    class MetaRightsUnit(UnitEditableModel.MetaRightsUnit):
        unit_ro_access = True
        access = 'INFORMATIQUE'

    class MetaRights(UnitEditableModel.MetaRights):
        pass

    def __init__(self, *args, **kwargs):
        super(Accreditation, self).__init__(*args, **kwargs)

        self.MetaRights.rights_update({
            'INGORE_PREZ': _(u'Peut supprimer le dernier président'),
        })

    def exp_date(self):
        """Returne la date d'expiration de l'accred"""
        return self.validation_date + datetime.timedelta(days=365)

    def is_valid(self):
        """Returne true si l'accred est valide"""
        return self.end_date is None

    def get_role_or_display_name(self):
        if self.display_name:
            return str(self.role) + " (" + self.display_name + ")"
        return str(self.role)

    def rights_can_INGORE_PREZ(self, user):
        return self.rights_in_root_unit(user, self.MetaRightsUnit.access)

class _AccessDelegation(GenericModel, UnitEditableModel):
    unit = FalseFK('units.models.Unit')

    access = MultiSelectField(choices=_Role.ACCESS_CHOICES, blank=True, null=True)
    valid_for_sub_units = models.BooleanField(_(u'Valide pour les sous-unités'), default=False, help_text=_(u'Si sélectionné, les accès supplémentaires dans l\'unit courrante seront aussi valide dans les sous-unités'))

    user = models.ForeignKey(TruffeUser, blank=True, null=True, help_text=_(u'(Optionnel !) L\'utilisateur concerné. L\'utilisateur doit disposer d\'une accréditation dans l\'unité'))
    role = FalseFK('units.models.Role', blank=True, null=True, help_text=_(u'(Optionnel !) Le role concerné.'))

    class MetaRightsUnit(UnitEditableModel.MetaRightsUnit):
        unit_ro_access = True
        access = 'INFORMATIQUE'

    class MetaData:
        list_display = [
            ('id', ''),
            ('user', _('User')),
            ('role', _('Role')),
            ('get_access', _(u'Acces'))
        ]

        details_display = [
            ('user', _('User')),
            ('role', _('Role')),
            ('get_access', _(u'Accès supplémentaires')),
            ('valid_for_sub_units', _(u'Valide pour les sous-unités'))
        ]

        filter_fields = ()

        base_title = _(u'Délégation d\'accès')
        list_title = _(u'Liste de toutes les délégations d\'access')
        base_icon = 'fa fa-list'
        elem_icon = 'fa fa-group'

        menu_id = 'menu-units-delegations'

        yes_or_no_fields = ['valid_for_sub_units']

        has_unit = True

        help_list = _(u"""Les délégations d'accès permettent de donner des accès supplémentaires dans une unité.

Les accès sont normalement déterminés en fonction des accréditations, au niveau global.
Par exemple, une personne accréditée en temps que Trésorier dans une unité disposera de l'accès TRÉSOERIE pour l'unité.

Avec les délégations d'accês, il est par exemple possible de donner l'accès "COMMUNICATION" à tout les membres d'une unité en créant une délégations d'accès.

Il est aussi possible de restraindre une délégations â un utilisateurs ou à un role particulier.""")

    class Meta:
        abstract = True

    def get_access(self):
        if self.access:
            return u', '.join(list(self.access))
        else:
            return _(u'Rien')

    def __unicode__(self):
        return _(u'Accês supplémentaire n°%s' % (self.pk,))

    def delete_signal(self):
        self.save_signal()

    def save_signal(self):
        """Cleanup rights"""

        for user in self.unit.get_users():
            user.clear_rights_cache()
<commit_msg>Fix error with update of multiselectfield<commit_after># -*- coding: utf-8 -*-

from django.db import models
from generic.models import GenericModel, FalseFK
from django.utils.translation import ugettext_lazy as _

from users.models import TruffeUser

import datetime
from multiselectfield import MultiSelectField

from rights.utils import AgepolyEditableModel, UnitEditableModel

from django.conf import settings

from django.db.models import Q

class _Unit(GenericModel, AgepolyEditableModel):

    class MetaRightsAgepoly(AgepolyEditableModel.MetaRightsAgepoly):
        access = 'INFORMATIQUE'
        world_ro_access = True

    name = models.CharField(max_length=255)
    id_epfl = models.CharField(max_length=64, blank=True, null=True, help_text=_(u'Utilisé pour la syncronisation des accréditations'))
    description = models.TextField(blank=True, null=True)
    url = models.URLField(blank=True, null=True)

    is_commission = models.BooleanField(default=False, help_text=_(u'Cocher si cette unité est une commission de l\'AGEPoly'))
    is_equipe = models.BooleanField(default=False, help_text=_(u'Cocher si cette unité est une équipe de l\'AGEPoly'))

    parent_herachique = models.ForeignKey('Unit', blank=True, null=True, help_text=_(u'Pour les commission, sélectionner le comité de l\'AGEPoly. Pour les sous-commisions, sélectionner la commission parente. Pour un sous-coaching, sélectionner la commission coaching. Pour le comité de l\'AGEPoly, ne rien mettre.'))

    class MetaData:
        list_display = [
            ('name', _('Nom')),
            ('is_commission', _('Commission ?')),
            ('is_equipe', _(u'Équipe ?')),
            ('parent_herachique', _('Parent')),
            ('president', _('President'))
        ]

        details_display = [
            ('name', _('Nom')),
            ('is_commission', _('Commission ?')),
            ('is_equipe', _(u'Équipe ?')),
            ('parent_herachique', _('Parent')),
            ('president', _('President'))
        ]

        yes_or_no_fields = ['is_commission', 'is_equipe']

        filter_fields = ('name', )

        base_title = _(u'Unités')
        list_title = _(u'Liste de toutes les unités')
        base_icon = 'fa fa-list'
        elem_icon = 'fa fa-group'

        menu_id = 'menu-units-units'

        help_list = _(u"""Les unités sont les différents groups de l'AGEPoly (Comité de l'AGEPoly, commissions, équipes, etc.)

Les unités sont organisées en arbre hérachique, avec le Comité de l'AGEPoly au sommet.""")

    class Meta:
        abstract = True

    def __unicode__(self):
        return self.name

    def rights_can_select(self):
        """Return true if the unit can be selected in the selector menu"""
        return True

    _rights_can_select = lambda unit: True

    def set_rights_can_select(self, f):
        def __tmp():
            return f(self)
        self.rights_can_select = __tmp
        self._rights_can_select = f

    def rights_can_edit(self):
        """Return true if the user has edit right"""
        return True

    _rights_can_edit = lambda unit: True

    def set_rights_can_edit(self, f):
        def __tmp():
            return f(self)
        self.rights_can_edit = __tmp
        self._rights_can_edit = f

    def has_sub(self):
        """Return true if the unit has subunits"""
        return self.unit_set.filter(deleted=False).order_by('name').count() > 0

    def only_one_sub_type(self):
        tt = 0

        if self.sub_com():
            tt += 1
        if self.sub_eqi():
            tt += 1
        if self.sub_grp():
            tt += 1

        return tt == 1

    def sub_com(self):
        """Return the sub units, but only commissions"""
        retour = []
        for unit in self.unit_set.filter(is_commission=True).filter(deleted=False).order_by('name'):
            unit.set_rights_can_select(self._rights_can_select)
            unit.set_rights_can_edit(self._rights_can_edit)
            retour.append(unit)
        return retour

    def sub_eqi(self):
        """Return the sub units, but only groups"""
        retour = []
        for unit in self.unit_set.exclude(is_commission=True).filter(is_equipe=True).filter(deleted=False).order_by('name'):
            unit.set_rights_can_select(self._rights_can_select)
            retour.append(unit)
        return retour

    def sub_grp(self):
        """Return the sub units, without groups or commissions"""
        retour = []
        for unit in self.unit_set.filter(is_commission=False).filter(is_equipe=False).filter(deleted=False).order_by('name'):
            unit.set_rights_can_select(self._rights_can_select)
            retour.append(unit)
        return retour

    def is_user_in_groupe(self, user, access=None, parent_mode=False):

        for accreditation in self.accreditation_set.filter(user=user, end_date=None):
            if accreditation.is_valid():

                # No acces: Only an accred is needed
                if not access:
                    return True

                # If role has acces, ok
                if access in accreditation.role.access:
                    return True

                # Check valid delegations for this accred
                access_delegations = self.accessdelegation_set.filter((Q(user=user) | Q(user=None)) & (Q(role=accreditation.role) | Q(role=None))).all()

                for access_delegation in access_delegations:
                    if access in access_delegation.access and (not parent_mode or access_delegation.valid_for_sub_units):
                        return True

        if self.parent_herachique:
            return self.parent_herachique.is_user_in_groupe(user, access, True)
        return False

    @property
    def president(self):
        return ', '.join([u.user.get_full_name() for u in list(self.accreditation_set.filter(end_date=None, role__pk=settings.PRESIDENT_ROLE_PK))])

    def can_delete(self):

        if self.accreditation_set.count():
            return (False, _(u'Au moins une accéditation existe avec cette unité, impossible de supprimer l\'unité (NB: Historique compris).'))

        return (True, None)

    def current_accreds(self):
        return self.accreditation_set.filter(end_date=None).order_by('role__ordre', 'user__first_name', 'user__last_name')

    def get_users(self):
        return [a.user for a in self.current_accreds()]

class _Role(GenericModel, AgepolyEditableModel):
    """Un role, pour une accred"""

    class MetaRightsAgepoly(AgepolyEditableModel.MetaRightsAgepoly):
        access = 'INFORMATIQUE'
        world_ro_access = True

    name = models.CharField(max_length=255)
    id_epfl = models.CharField(max_length=255, null=True, blank=True, help_text=_(u'Mettre ici l\'ID accred du role pour la syncronisation EPFL'))
    description = models.TextField(null=True, blank=True)
    ordre = models.IntegerField(null=True, blank=True, help_text=_(u'Il n\'est pas possible d\'acréditer la même personne dans la même unité plusieurs fois. Le role avec le plus PETIT ordre sera prit en compte'))

    ACCESS_CHOICES = (
        ('PRESIDENCE', ('Présidence')),
        ('TRESORERIE', ('Trésorerie')),
        ('COMMUNICATION', ('Communication')),
        ('INFORMATIQUE', ('Informatique')),
        ('LOGISTIQUE', ('Logistique')),
        ('SECRETARIAT', ('Secrétariat'))
    )

    access = MultiSelectField(choices=ACCESS_CHOICES, blank=True, null=True)

    def __unicode__(self):
        return self.name

    def get_access(self):
        if self.access:
            return u', '.join(list(self.access))

    class MetaData:
        list_display = [
            ('name', _('Nom')),
            ('id_epfl', _('ID EPFL ?')),
            ('ordre', _(u'Ordre'))
        ]

        details_display = [
            ('name', _('Nom')),
            ('description', _('Description')),
            ('id_epfl', _('ID EPFL ?')),
            ('ordre', _(u'Ordre')),
            ('get_access', _(u'Accès')),
        ]

        filter_fields = ('name', 'id_epfl', 'description')

        base_title = _(u'Roles')
        list_title = _(u'Liste de toutes les roles')
        base_icon = 'fa fa-list'
        elem_icon = 'fa fa-group'

        menu_id = 'menu-units-roles'

        help_list = _(u"""Les roles sont les différents type d'accéditations possible pour une unité.

Certains roles donnent des accès particuliers.
Par exemple, le role 'Trésorier' donne l'accès TRESORERIE. Les droits sont gérés en fonction des accês !""")

    class Meta:
        abstract = True

    def can_delete(self):

        if self.accreditation_set.count():
            return (False, _(u'Au moins une accéditation existe avec ce role, impossible de supprimer le role (NB: Historique compris)'))

        return (True, None)

class Accreditation(models.Model, UnitEditableModel):
    unit = models.ForeignKey('Unit')
    user = models.ForeignKey(TruffeUser)
    role = models.ForeignKey('Role')

    start_date = models.DateTimeField(auto_now_add=True)
    end_date = models.DateTimeField(blank=True, null=True)
    validation_date = models.DateTimeField(auto_now_add=True)

    display_name = models.CharField(max_length=255, blank=True, null=True, help_text=_(u'Le nom a afficher dans truffe. Peut être utilisé pour préciser la fonction'))

    no_epfl_sync = models.BooleanField(default=False, help_text=_(u'Checker cette coche pour ne pas sycroniser cette accrédiation au niveau EPFL'))

    class MetaRightsUnit(UnitEditableModel.MetaRightsUnit):
        unit_ro_access = True
        access = 'INFORMATIQUE'

    class MetaRights(UnitEditableModel.MetaRights):
        pass

    def __init__(self, *args, **kwargs):
        super(Accreditation, self).__init__(*args, **kwargs)

        self.MetaRights.rights_update({
            'INGORE_PREZ': _(u'Peut supprimer le dernier président'),
        })

    def exp_date(self):
        """Returne la date d'expiration de l'accred"""
        return self.validation_date + datetime.timedelta(days=365)

    def is_valid(self):
        """Returne true si l'accred est valide"""
        return self.end_date is None

    def get_role_or_display_name(self):
        if self.display_name:
            return str(self.role) + " (" + self.display_name + ")"
        return str(self.role)

    def rights_can_INGORE_PREZ(self, user):
        return self.rights_in_root_unit(user, self.MetaRightsUnit.access)

class _AccessDelegation(GenericModel, UnitEditableModel):
    unit = FalseFK('units.models.Unit')

    access = MultiSelectField(choices=_Role.ACCESS_CHOICES, blank=True, null=True)
    valid_for_sub_units = models.BooleanField(_(u'Valide pour les sous-unités'), default=False, help_text=_(u'Si sélectionné, les accès supplémentaires dans l\'unit courrante seront aussi valide dans les sous-unités'))

    user = models.ForeignKey(TruffeUser, blank=True, null=True, help_text=_(u'(Optionnel !) L\'utilisateur concerné. L\'utilisateur doit disposer d\'une accréditation dans l\'unité'))
    role = FalseFK('units.models.Role', blank=True, null=True, help_text=_(u'(Optionnel !) Le role concerné.'))

    class MetaRightsUnit(UnitEditableModel.MetaRightsUnit):
        unit_ro_access = True
        access = 'INFORMATIQUE'

    class MetaData:
        list_display = [
            ('id', ''),
            ('user', _('User')),
            ('role', _('Role')),
            ('get_access', _(u'Acces'))
        ]

        details_display = [
            ('user', _('User')),
            ('role', _('Role')),
            ('get_access', _(u'Accès supplémentaires')),
            ('valid_for_sub_units', _(u'Valide pour les sous-unités'))
        ]

        filter_fields = ()

        base_title = _(u'Délégation d\'accès')
        list_title = _(u'Liste de toutes les délégations d\'access')
        base_icon = 'fa fa-list'
        elem_icon = 'fa fa-group'

        menu_id = 'menu-units-delegations'

        yes_or_no_fields = ['valid_for_sub_units']

        has_unit = True

        help_list = _(u"""Les délégations d'accès permettent de donner des accès supplémentaires dans une unité.

Les accès sont normalement déterminés en fonction des accréditations, au niveau global.
Par exemple, une personne accréditée en temps que Trésorier dans une unité disposera de l'accès TRÉSOERIE pour l'unité.

Avec les délégations d'accês, il est par exemple possible de donner l'accès "COMMUNICATION" à tout les membres d'une unité en créant une délégations d'accès.

Il est aussi possible de restraindre une délégations â un utilisateurs ou à un role particulier.""")

    class Meta:
        abstract = True

    def get_access(self):
        if self.access:
            return u', '.join(list(self.access))

    def __unicode__(self):
        return _(u'Accês supplémentaire n°%s' % (self.pk,))

    def delete_signal(self):
        self.save_signal()

    def save_signal(self):
        """Cleanup rights"""

        for user in self.unit.get_users():
            user.clear_rights_cache()
<|endoftext|>


<commit_before>"""Tests for basic functioning of Multichain DVI algorithms."""
import itertools
from typing import Callable

import numpy as np
from absl.testing import absltest
from absl.testing import parameterized
from differential_value_iteration.algorithms import mdvi
from differential_value_iteration.environments import micro
from differential_value_iteration.environments import structure

class MDVITest(parameterized.TestCase):

  @parameterized.parameters(itertools.product(
      (micro.create_mrp1, micro.create_mrp2),
      (False, True),
      (np.float32, np.float64)))
  def test_mdvi_sync_converges(self,
      env_constructor: Callable[[np.dtype], structure.MarkovRewardProcess],
      r_bar_scalar: bool, dtype: np.dtype):
    if dtype == np.float64:
      tolerance_places = 8
    else:
      tolerance_places = 4 if env_constructor == micro.create_mrp2 else 6
    environment = env_constructor(dtype)
    initial_r_bar = 0. if r_bar_scalar else np.full(environment.num_states,
                                                    0., dtype)
    algorithm = mdvi.Evaluation(
        mrp=environment,
        step_size=.1,
        beta=.1,
        initial_r_bar=initial_r_bar,
        initial_values=np.zeros(environment.num_states, dtype=dtype),
        synchronized=True)

    for _ in range(250):
      changes = algorithm.update()

    with self.subTest('did_not_diverge'):
      self.assertFalse(algorithm.diverged())
    with self.subTest('maintained_types'):
      self.assertTrue(algorithm.types_ok())
    with self.subTest('converged'):
      self.assertAlmostEqual(np.sum(np.abs(changes)), 0.,
                             places=tolerance_places)

  @parameterized.parameters(
      itertools.product((False, True), (np.float32, np.float64)))
  def test_mdvi_async_converges(self, r_bar_scalar: bool, dtype: np.dtype):
    tolerance_places = 6 if dtype is np.float32 else 7
    environment = micro.create_mrp1(dtype)
    initial_r_bar = 0. if r_bar_scalar else np.full(environment.num_states,
                                                    0., dtype)
    algorithm = mdvi.Evaluation(
        mrp=environment,
        step_size=.15,
        beta=.15,
        initial_r_bar=initial_r_bar,
        initial_values=np.zeros(environment.num_states, dtype=dtype),
        synchronized=False)

    for _ in range(75):
      change_sum = 0.
      for _ in range(environment.num_states):
        change = algorithm.update()
        change_sum += np.abs(change)

    with self.subTest('did_not_diverge'):
      self.assertFalse(algorithm.diverged())
    with self.subTest('maintained_types'):
      self.assertTrue(algorithm.types_ok())
    with self.subTest('converged'):
      self.assertAlmostEqual(change_sum, 0., places=tolerance_places)

if __name__ == '__main__':
  absltest.main()
<commit_msg>Adds MRP2 to the mdvi async test.<commit_after>"""Tests for basic functioning of Multichain DVI algorithms."""
import itertools
from typing import Callable

import numpy as np
from absl.testing import absltest
from absl.testing import parameterized
from differential_value_iteration.algorithms import mdvi
from differential_value_iteration.environments import micro
from differential_value_iteration.environments import structure

class MDVITest(parameterized.TestCase):

  @parameterized.parameters(itertools.product(
      (micro.create_mrp1, micro.create_mrp2),
      (False, True),
      (np.float32, np.float64)))
  def test_mdvi_sync_converges(self,
      env_constructor: Callable[[np.dtype], structure.MarkovRewardProcess],
      r_bar_scalar: bool, dtype: np.dtype):
    if dtype == np.float64:
      tolerance_places = 8
    else:
      tolerance_places = 4 if env_constructor == micro.create_mrp2 else 6
    environment = env_constructor(dtype)
    initial_r_bar = 0. if r_bar_scalar else np.full(environment.num_states,
                                                    0., dtype)
    algorithm = mdvi.Evaluation(
        mrp=environment,
        step_size=.1,
        beta=.1,
        initial_r_bar=initial_r_bar,
        initial_values=np.zeros(environment.num_states, dtype=dtype),
        synchronized=True)

    for _ in range(250):
      changes = algorithm.update()

    with self.subTest('did_not_diverge'):
      self.assertFalse(algorithm.diverged())
    with self.subTest('maintained_types'):
      self.assertTrue(algorithm.types_ok())
    with self.subTest('converged'):
      self.assertAlmostEqual(np.sum(np.abs(changes)), 0.,
                             places=tolerance_places)

  @parameterized.parameters(itertools.product(
      (micro.create_mrp1, micro.create_mrp2),
      (False, True),
      (np.float32, np.float64)))
  def test_mdvi_async_converges(self,
      env_constructor: Callable[[np.dtype], structure.MarkovRewardProcess],
      r_bar_scalar: bool, dtype: np.dtype):
    if dtype == np.float64:
      tolerance_places = 8
    else:
      tolerance_places = 6 if env_constructor == micro.create_mrp2 else 6
    environment = env_constructor(dtype)
    initial_r_bar = 0. if r_bar_scalar else np.full(environment.num_states,
                                                    0., dtype)
    algorithm = mdvi.Evaluation(
        mrp=environment,
        step_size=.1,
        beta=.1,
        initial_r_bar=initial_r_bar,
        initial_values=np.zeros(environment.num_states, dtype=dtype),
        synchronized=False)

    for _ in range(250):
      change_sum = 0.
      for _ in range(environment.num_states):
        change = algorithm.update()
        change_sum += np.abs(change)

    with self.subTest('did_not_diverge'):
      self.assertFalse(algorithm.diverged())
    with self.subTest('maintained_types'):
      self.assertTrue(algorithm.types_ok())
    with self.subTest('converged'):
      self.assertAlmostEqual(change_sum, 0., places=tolerance_places)

if __name__ == '__main__':
  absltest.main()
<|endoftext|>


<commit_before>""" Web runtime based on NW.js

https://github.com/nwjs/nw.js

Dev notes
---------

Apps must have a way to be "unique", so that if you create 2 apps with Flexx,
they won't group on the taskbar, and are preferably easily discovered in the
process manager.

The "name" in the manifest is one option. Note that an appdata dir is
created for each used name, unless one sets --user-data-dir, as we do.

The "description" in the manifest, not sure where that turns up.

The name of the executable is the main thing to change.

"""

import os.path as op
import os
import re
import sys
import json
import struct
import shutil
from urllib.request import urlopen

from .. import config
from .common import DesktopRuntime
from ._manage import RUNTIME_DIR, download_runtime, create_temp_app_dir

# http://docs.nwjs.io/en/latest/References/Manifest%20Format
def get_manifest_template():
    return {"name": "flexx_nw_app",
            "main": "",
            "nodejs": False,
            # "single-instance": False,  # Deprecated; is always True
            "description": "an app made with Flexx ui",
            "version": "1.0",
            "keywords": [],

            "window": {
                "title": "",
                "icon": "",
                "toolbar": False,
                "frame": True,
                "width": 640,
                "height": 480,
                "position": "center",
                "resizable": True,
                "min_width": 10,
                "min_height": 10,
                #"max_width": 800,
                #"max_height": 600,
                "always-on-top": False,
                "fullscreen": False,
                "kiosk": False,
                "transparent": False,
                "show_in_taskbar": True,
                "show": True
                },

            "webkit": {
                "plugin": True,
                "java": False
                },

            # Tweak chromium: es3 == webgl2, single process makes things
            # more compact, but does not work on OS X.
            "chromium-args": ' '.join(['--enable-unsafe-es3-apis',
                                       # '--single-process',
                                       ]),
            }

def fix_libudef(dest):
    """ Fix the dependency for libudef by making a link to libudef.so.1.

    github.com/rogerwang/node-webkit/wiki/The-solution-of-lacking-libudev.so.0
    """

    paths = ["/lib/x86_64-linux-gnu/libudev.so.1",  # Ubuntu, Xubuntu, Mint
             "/usr/lib64/libudev.so.1",  # SUSE, Fedora
             "/usr/lib/libudev.so.1",  # Arch, Fedora 32bit
             "/lib/i386-linux-gnu/libudev.so.1",  # Ubuntu 32bit
             ]

    target = op.join(dest, 'libudev.so.0')
    for path in paths:
        if op.isfile(path) and not op.isfile(target):
            os.symlink(path, target)

class NWRuntime(DesktopRuntime):
    """ Desktop runtime based on NW.js (http://nwjs.io/, formerly
    node-webkit), which uses the Chromium engine. Flexx takes
    care of downloading (and updating) the runtime when needed.

    This runtime is visible in the task manager under a custom process name
    (``sys.executable + '-ui'``), making it easy to spot in the task manager,
    and avoids task-bar grouping. Compared to the Firefox app runtime,
    this runtime uses more processes and memory, but is generally faster.
    """

    def _get_name(self):
        return 'nw'

    def _get_exe_name(self, dir):
        if sys.platform.startswith('win'):
            return op.join(dir, 'nw.exe')
        elif sys.platform.startswith('darwin'):
            return op.join(dir, 'nwjs.app', 'Contents', 'MacOS', 'nwjs')
        else:
            return op.join(dir, 'nw')

    def _get_exe(self):
        cur_version = self.get_current_version() or ''
        if cur_version:
            dir = op.join(RUNTIME_DIR, self.get_name() + '_' + cur_version)
            exe = op.realpath(self._get_exe_name(dir))
            if op.isfile(exe):
                return exe

    def _get_version(self):
        return self.get_current_version()  # todo: rename to _get_chached_version or something

    def _get_latest_version(self):
        """" Get latest version of the NW runtime.
        """
        text = urlopen('https://dl.nwjs.io/').read().decode('utf-8', 'ignore')
        versions = re.findall('href\=\"v(.+?)\"', text)
        versions_int = []
        for v in versions:
            v = v.strip('/')
            try:
                versions_int.append(tuple([int(i) for i in v.split('.')]))
            except Exception:
                pass
        versions_int.sort()
        return '.'.join([str(i) for i in versions_int[-1]])

    def _get_download_url(self, version, platform=None):
        """ Given a version, get the url where it can be downloaded.
        """
        platform = platform or sys.platform
        if platform.startswith('win'):
            plat, ext = '-win', '.zip'
        elif platform.startswith('linux'):
            plat, ext = '-linux', '.tar.gz'
        elif platform.startswith('darwin'):
            plat, ext = '-osx', '.zip'
        else:
            raise RuntimeError('Unsupported platform')
        plat += '-x64' if (struct.calcsize('P') == 8) else '-x64'
        return 'https://dl.nwjs.io/v' + version + '/nwjs-v' + version + plat + ext

    def _install_runtime(self):
        latest_version = self._get_latest_version()
        url = self._get_download_url(latest_version)
        download_runtime(self.get_name(), latest_version, url)

    def _test_platform(self):
        if not (sys.platform.startswith('win') or
                sys.platform.startswith('linux') or
                sys.platform.startswith('darwin')):
            raise RuntimeError('NW.js runtime is not supported on platform ' +
                               sys.platform)
            # todo: show GUI? that wont work either, but if dialite has a nice fallback, we might as well use that to notify the user

    def _launch_tab(self, url):
        raise RuntimeError('NW runtime cannot launch tabs.')

    def _launch_app(self, url):

        self._test_platform()

        # Get dir to store app definition
        app_path = create_temp_app_dir('nw')
        id = op.basename(app_path).split('_', 1)[1]

        # Prepare profile dir for NW/Chromium to let --user-data-dir point to.
        # This dir is unique for each instance of the app, but because it is
        # inside the app_path, it gets automatically cleaned up.
        profile_dir = op.join(app_path, 'stub_profile')
        if not op.isdir(profile_dir):
            os.mkdir(profile_dir)

        self._kwargs['title'] = self._kwargs.get('title', 'NW.js runtime')

        # Get runtime exe
        if config.nw_exe:
            # User specifies the executable, we're not going to worry about version
            exe = flexx.config.nw_exe
        else:
            # We install the runtime, based on a minimal required version
            exe = self._get_exe_name(self.get_runtime(config.nw_min_version))
`
            # Change exe to avoid grouping + easier recognition in task manager
            if exe and op.isfile(op.realpath(exe)):
                exe = self._get_app_exe(exe, app_path)

        # Populate app definition
        # name must be a unique, lowercase alpha-numeric name without spaces.
        # It may include "." or "_" or "-" characters. Normally, NW.js stores
        # the app's profile data under the directory named name, but we
        # overload user-data-dir.
        D = get_manifest_template()
        # D['name'] = normalize('name-of-app-class')
        D['description'] += ' (%s)' % id
        D['main'] = url
        D['window']['title'] = self._kwargs.get('title', 'nw.js runtime')

        # Set size (position can be null, center, mouse)
        size = self._kwargs.get('size', (640, 480))
        D['window']['width'], D['window']['height'] = size[0], size[1]

        # Icon (note that icon is "overloaded" if nw is wrapped in a runtime.app)
        if self._kwargs.get('icon'):
            icon = self._kwargs.get('icon')
            icon_path = op.join(app_path, 'app.png')  # nw can handle ico
            icon.write(icon_path)
            D['window']['icon'] = 'app%i.png' % icon.image_sizes()[0]

        # Write app manifest
        with open(op.join(app_path, 'package.json'), 'wb') as f:
            f.write(json.dumps(D, indent=4).encode())

        # Fix libudef bug
        llp = os.getenv('LD_LIBRARY_PATH', '')
        if sys.platform.startswith('linux'):
            fix_libudef(app_path)
            llp = app_path + os.pathsep + llp

        # Launch
        cmd = [exe, app_path, '--user-data-dir=' + profile_dir]
        self._start_subprocess(cmd, LD_LIBRARY_PATH=llp)
<commit_msg>pff how did that get there<commit_after>""" Web runtime based on NW.js

https://github.com/nwjs/nw.js

Dev notes
---------

Apps must have a way to be "unique", so that if you create 2 apps with Flexx,
they won't group on the taskbar, and are preferably easily discovered in the
process manager.

The "name" in the manifest is one option. Note that an appdata dir is
created for each used name, unless one sets --user-data-dir, as we do.

The "description" in the manifest, not sure where that turns up.

The name of the executable is the main thing to change.

"""

import os.path as op
import os
import re
import sys
import json
import struct
import shutil
from urllib.request import urlopen

from .. import config
from .common import DesktopRuntime
from ._manage import RUNTIME_DIR, download_runtime, create_temp_app_dir

# http://docs.nwjs.io/en/latest/References/Manifest%20Format
def get_manifest_template():
    return {"name": "flexx_nw_app",
            "main": "",
            "nodejs": False,
            # "single-instance": False,  # Deprecated; is always True
            "description": "an app made with Flexx ui",
            "version": "1.0",
            "keywords": [],

            "window": {
                "title": "",
                "icon": "",
                "toolbar": False,
                "frame": True,
                "width": 640,
                "height": 480,
                "position": "center",
                "resizable": True,
                "min_width": 10,
                "min_height": 10,
                #"max_width": 800,
                #"max_height": 600,
                "always-on-top": False,
                "fullscreen": False,
                "kiosk": False,
                "transparent": False,
                "show_in_taskbar": True,
                "show": True
                },

            "webkit": {
                "plugin": True,
                "java": False
                },

            # Tweak chromium: es3 == webgl2, single process makes things
            # more compact, but does not work on OS X.
            "chromium-args": ' '.join(['--enable-unsafe-es3-apis',
                                       # '--single-process',
                                       ]),
            }

def fix_libudef(dest):
    """ Fix the dependency for libudef by making a link to libudef.so.1.

    github.com/rogerwang/node-webkit/wiki/The-solution-of-lacking-libudev.so.0
    """

    paths = ["/lib/x86_64-linux-gnu/libudev.so.1",  # Ubuntu, Xubuntu, Mint
             "/usr/lib64/libudev.so.1",  # SUSE, Fedora
             "/usr/lib/libudev.so.1",  # Arch, Fedora 32bit
             "/lib/i386-linux-gnu/libudev.so.1",  # Ubuntu 32bit
             ]

    target = op.join(dest, 'libudev.so.0')
    for path in paths:
        if op.isfile(path) and not op.isfile(target):
            os.symlink(path, target)

class NWRuntime(DesktopRuntime):
    """ Desktop runtime based on NW.js (http://nwjs.io/, formerly
    node-webkit), which uses the Chromium engine. Flexx takes
    care of downloading (and updating) the runtime when needed.

    This runtime is visible in the task manager under a custom process name
    (``sys.executable + '-ui'``), making it easy to spot in the task manager,
    and avoids task-bar grouping. Compared to the Firefox app runtime,
    this runtime uses more processes and memory, but is generally faster.
    """

    def _get_name(self):
        return 'nw'

    def _get_exe_name(self, dir):
        if sys.platform.startswith('win'):
            return op.join(dir, 'nw.exe')
        elif sys.platform.startswith('darwin'):
            return op.join(dir, 'nwjs.app', 'Contents', 'MacOS', 'nwjs')
        else:
            return op.join(dir, 'nw')

    def _get_exe(self):
        cur_version = self.get_current_version() or ''
        if cur_version:
            dir = op.join(RUNTIME_DIR, self.get_name() + '_' + cur_version)
            exe = op.realpath(self._get_exe_name(dir))
            if op.isfile(exe):
                return exe

    def _get_version(self):
        return self.get_current_version()  # todo: rename to _get_chached_version or something

    def _get_latest_version(self):
        """" Get latest version of the NW runtime.
        """
        text = urlopen('https://dl.nwjs.io/').read().decode('utf-8', 'ignore')
        versions = re.findall('href\=\"v(.+?)\"', text)
        versions_int = []
        for v in versions:
            v = v.strip('/')
            try:
                versions_int.append(tuple([int(i) for i in v.split('.')]))
            except Exception:
                pass
        versions_int.sort()
        return '.'.join([str(i) for i in versions_int[-1]])

    def _get_download_url(self, version, platform=None):
        """ Given a version, get the url where it can be downloaded.
        """
        platform = platform or sys.platform
        if platform.startswith('win'):
            plat, ext = '-win', '.zip'
        elif platform.startswith('linux'):
            plat, ext = '-linux', '.tar.gz'
        elif platform.startswith('darwin'):
            plat, ext = '-osx', '.zip'
        else:
            raise RuntimeError('Unsupported platform')
        plat += '-x64' if (struct.calcsize('P') == 8) else '-x64'
        return 'https://dl.nwjs.io/v' + version + '/nwjs-v' + version + plat + ext

    def _install_runtime(self):
        latest_version = self._get_latest_version()
        url = self._get_download_url(latest_version)
        download_runtime(self.get_name(), latest_version, url)

    def _test_platform(self):
        if not (sys.platform.startswith('win') or
                sys.platform.startswith('linux') or
                sys.platform.startswith('darwin')):
            raise RuntimeError('NW.js runtime is not supported on platform ' +
                               sys.platform)
            # todo: show GUI? that wont work either, but if dialite has a nice fallback, we might as well use that to notify the user

    def _launch_tab(self, url):
        raise RuntimeError('NW runtime cannot launch tabs.')

    def _launch_app(self, url):

        self._test_platform()

        # Get dir to store app definition
        app_path = create_temp_app_dir('nw')
        id = op.basename(app_path).split('_', 1)[1]

        # Prepare profile dir for NW/Chromium to let --user-data-dir point to.
        # This dir is unique for each instance of the app, but because it is
        # inside the app_path, it gets automatically cleaned up.
        profile_dir = op.join(app_path, 'stub_profile')
        if not op.isdir(profile_dir):
            os.mkdir(profile_dir)

        self._kwargs['title'] = self._kwargs.get('title', 'NW.js runtime')

        # Get runtime exe
        if config.nw_exe:
            # User specifies the executable, we're not going to worry about version
            exe = flexx.config.nw_exe
        else:
            # We install the runtime, based on a minimal required version
            exe = self._get_exe_name(self.get_runtime(config.nw_min_version))

            # Change exe to avoid grouping + easier recognition in task manager
            if exe and op.isfile(op.realpath(exe)):
                exe = self._get_app_exe(exe, app_path)

        # Populate app definition
        # name must be a unique, lowercase alpha-numeric name without spaces.
        # It may include "." or "_" or "-" characters. Normally, NW.js stores
        # the app's profile data under the directory named name, but we
        # overload user-data-dir.
        D = get_manifest_template()
        # D['name'] = normalize('name-of-app-class')
        D['description'] += ' (%s)' % id
        D['main'] = url
        D['window']['title'] = self._kwargs.get('title', 'nw.js runtime')

        # Set size (position can be null, center, mouse)
        size = self._kwargs.get('size', (640, 480))
        D['window']['width'], D['window']['height'] = size[0], size[1]

        # Icon (note that icon is "overloaded" if nw is wrapped in a runtime.app)
        if self._kwargs.get('icon'):
            icon = self._kwargs.get('icon')
            icon_path = op.join(app_path, 'app.png')  # nw can handle ico
            icon.write(icon_path)
            D['window']['icon'] = 'app%i.png' % icon.image_sizes()[0]

        # Write app manifest
        with open(op.join(app_path, 'package.json'), 'wb') as f:
            f.write(json.dumps(D, indent=4).encode())

        # Fix libudef bug
        llp = os.getenv('LD_LIBRARY_PATH', '')
        if sys.platform.startswith('linux'):
            fix_libudef(app_path)
            llp = app_path + os.pathsep + llp

        # Launch
        cmd = [exe, app_path, '--user-data-dir=' + profile_dir]
        self._start_subprocess(cmd, LD_LIBRARY_PATH=llp)
<|endoftext|>


<commit_before>"""Tests for assess_budget module."""

import logging
import json
from random import randint
import StringIO
from subprocess import CalledProcessError

from mock import (
    Mock,
    patch,
)

from assess_budget import (
    _try_greater_than_limit_budget,
    _try_negative_budget,
    assess_budget,
    assess_budget_limit,
    assess_create_budget,
    assess_list_budgets,
    assess_set_budget,
    assess_show_budget,
    main,
    parse_args,
)
from jujupy import (
    fake_juju_client,
)
from tests import (
    parse_error,
    TestCase,
)
from utility import (
    JujuAssertionError,
)

class TestParseArgs(TestCase):

    def test_common_args(self):
        args = parse_args(["an-env", "/bin/juju", "/tmp/logs", "an-env-mod"])
        self.assertEqual("an-env", args.env)
        self.assertEqual("/bin/juju", args.juju_bin)
        self.assertEqual("/tmp/logs", args.logs)
        self.assertEqual("an-env-mod", args.temp_env_name)
        self.assertEqual(False, args.debug)

    def test_help(self):
        fake_stdout = StringIO.StringIO()
        with parse_error(self) as fake_stderr:
            with patch("sys.stdout", fake_stdout):
                parse_args(["--help"])
        self.assertEqual("", fake_stderr.getvalue())

class TestMain(TestCase):

    def test_main(self):
        argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"]
        client = Mock(spec=["is_jes_enabled"])
        with patch('assess_budget.subprocess.check_output',
                   autospec=True):
            with patch("assess_budget.configure_logging",
                       autospec=True) as mock_cl:
                with patch('assess_budget.client_from_config',
                           return_value=client) as mock_cfc:
                    with patch("assess_budget.assess_budget",
                               autospec=True) as mock_assess:
                        main(argv)
        mock_cl.assert_called_once_with(logging.DEBUG)
        mock_cfc.assert_called_once_with('an-env', "/bin/juju", False)
        mock_assess.assert_called_once_with(client)

class TestAssess(TestCase):

    def setUp(self):
        super(TestAssess, self).setUp()
        self.fake_client = fake_juju_client()
        self.budget_name = 'test'
        self.budget_limit = randint(1000, 10000)
        self.budget_value = str(randint(100, 900))

    def test_assess_budget(self):
        show_b = patch("assess_budget.assess_show_budget",
                       autospec=True)
        list_b = patch("assess_budget.assess_list_budgets",
                       autospec=True)
        set_b = patch("assess_budget.assess_set_budget",
                      autospec=True)
        create_b = patch("assess_budget.assess_create_budget",
                         autospec=True)
        b_limit = patch("assess_budget.assess_budget_limit",
                        autospec=True)
        init_b = patch("assess_budget._try_setting_budget",
                       autospec=True)
        expect_b = patch("assess_budget._set_budget_value_expectations",
                         autospec=True)
        update_e = patch("assess_budget._get_budgets", autospec=True)

        with show_b as show_b_mock, list_b as list_b_mock, \
            set_b as set_b_mock, create_b as create_b_mock, \
            b_limit as b_limit_mock, init_b as init_b_mock, \
                expect_b as expect_b_mock, update_e as update_e_mock:
                with patch("assess_budget.json.loads"):
                    with patch("assess_budget.randint",
                               return_value=self.budget_value):
                        assess_budget(self.fake_client)

                        init_b_mock.assert_called_once_with(self.fake_client,
                                                            self.budget_name,
                                                            '0')
                        expect_b_mock.assert_called_once_with(
                            update_e_mock(self.fake_client),
                            self.budget_name, self.budget_value)
                        b_limit_mock.assert_called_once_with(0)
                        create_b_mock.assert_called_once_with(
                            self.fake_client, self.budget_name,
                            self.budget_value, 0)
                        set_b_mock.assert_called_once_with(
                            self.fake_client, self.budget_name,
                            self.budget_value, 0)
                        show_b_mock.assert_called_once_with(
                            self.fake_client, self.budget_name,
                            self.budget_value)
                        list_b_mock.assert_called_once_with(
                            self.fake_client, update_e_mock(self.fake_client))

                        self.assertEqual(init_b_mock.call_count, 1)
                        self.assertEqual(expect_b_mock.call_count, 1)
                        self.assertEqual(b_limit_mock.call_count, 1)
                        self.assertEqual(create_b_mock.call_count, 1)
                        self.assertEqual(set_b_mock.call_count, 1)
                        self.assertEqual(show_b_mock.call_count, 1)
                        self.assertEqual(list_b_mock.call_count, 1)

class TestAssessShowBudget(TestAssess):

    def setUp(self):
        super(TestAssessShowBudget, self).setUp()
        self.fake_json = json.loads('{"limit":"' + self.budget_value +
                                    '","total":{"usage":"0%"}}')

    def test_assess_show_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_json):
                    assess_show_budget(self.fake_client, self.budget_name,
                                       self.budget_value)

    def test_raises_budget_usage_error(self):
        error_usage = randint(1, 100)
        self.fake_json['total']['usage'] = error_usage
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_json):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_show_budget(self.fake_client, self.budget_name,
                                       self.budget_value)
                self.assertEqual(ex.exception.message,
                                 'Budget usage found {}, expected 0%'.format(
                                    error_usage))

    def test_raises_budget_limit_error(self):
        self.fake_json['limit'] = 0
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_json):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_show_budget(self.fake_client, self.budget_name,
                                       self.budget_value)
                self.assertEqual(ex.exception.message,
                                 'Budget limit found 0, expected {}'.format(
                                     self.budget_value))

class TestAssessListBudgets(TestAssess):

    def setUp(self):
        super(TestAssessListBudgets, self).setUp()
        snippet = '[{"budget": "test", "limit": "300"}]'
        self.fake_budgets_json = json.loads('{"budgets":' + snippet + '}')
        self.fake_budget_json = json.loads(snippet)
        unexpected_snippet = '[{"budget": "test", "limit": "100"}]'
        self.fake_unexpected_budgets_json = json.loads(
            '{"budgets":' + unexpected_snippet + '}')
        self.fake_unexpected_budget_json = json.loads(unexpected_snippet)

    def test_assess_list_budgets(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_budgets_json):
                assess_list_budgets(self.fake_client, self.fake_budget_json)

    def test_raises_list_mismatch(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_unexpected_budgets_json):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_list_budgets(
                        self.fake_client, self.fake_budget_json)
                self.assertEqual(ex.exception.message,
                                 'Found: {}\nExpected: {}'.format(
                                     self.fake_unexpected_budget_json,
                                     self.fake_budget_json))

class TestAssessSetBudget(TestAssess):

    def test_assess_set_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads"):
                with patch("assess_budget._try_setting_budget"):
                    with patch("assess_budget.assert_set_budget"):
                        assess_set_budget(self.fake_client, self.budget_name,
                                          self.budget_value, self.budget_limit)

    def test_raises_on_exceed_credit_limit(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads"):
                with patch("assess_budget._try_setting_budget"):
                    with self.assertRaises(JujuAssertionError) as ex:
                        _try_greater_than_limit_budget(self.fake_client,
                                                       self.budget_name,
                                                       self.budget_limit)
                    self.assertEqual(ex.exception.message,
                                     'Credit limit exceeded')

    def test_raises_on_negative_budget(self):
        self.budget_limit = -abs(self.budget_limit)
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads"):
                with patch("assess_budget._try_setting_budget"):
                    with self.assertRaises(JujuAssertionError) as ex:
                        _try_negative_budget(self.fake_client,
                                             self.budget_name)
                    self.assertEqual(ex.exception.message,
                                     'Negative budget allowed')

class TestAssessCreateBudget(TestAssess):

    def test_assess_create_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.create_budget"):
                assess_create_budget(self.fake_client, self.budget_name,
                                     self.budget_value, self.budget_limit)

    def test_raises_duplicate_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.create_budget",
                       side_effect=JujuAssertionError):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_create_budget(self.fake_client, self.budget_name,
                                         self.budget_value, self.budget_limit)
                self.assertEqual(ex.exception.message,
                                 'Added duplicate budget')

    def test_raises_creation_error(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.create_budget",
                       side_effect=CalledProcessError(1, 'foo', 'bar')):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_create_budget(self.fake_client, self.budget_name,
                                         self.budget_value, self.budget_limit)
                self.assertEqual(ex.exception.message,
                                 "Error testing create-budget: ['bar', '']")

class TestAssessBudgetLimit(TestAssess):

    def test_assess_budget_limt(self):
        budget_limit = randint(1, 10000)
        assess_budget_limit(budget_limit)

    def test_raises_error_on_negative_limit(self):
        neg_budget_limit = randint(-1000, -1)
        with self.assertRaises(JujuAssertionError) as ex:
            assess_budget_limit(neg_budget_limit)
        self.assertEqual(ex.exception.message,
                         'Negative Budget Limit {}'.format(neg_budget_limit))
<commit_msg>Update tests.<commit_after>"""Tests for assess_budget module."""

import logging
import json
from random import randint
import StringIO
from subprocess import CalledProcessError

from mock import (
    Mock,
    patch,
)

from assess_budget import (
    _try_greater_than_limit_budget,
    _try_negative_budget,
    assess_budget,
    assess_budget_limit,
    assess_create_budget,
    assess_list_budgets,
    assess_set_budget,
    assess_show_budget,
    main,
    parse_args,
)
from jujupy import (
    fake_juju_client,
)
from tests import (
    parse_error,
    TestCase,
)
from utility import (
    JujuAssertionError,
)

class TestParseArgs(TestCase):

    def test_common_args(self):
        args = parse_args(["an-env", "/bin/juju", "/tmp/logs", "an-env-mod"])
        self.assertEqual("an-env", args.env)
        self.assertEqual("/bin/juju", args.juju_bin)
        self.assertEqual("/tmp/logs", args.logs)
        self.assertEqual("an-env-mod", args.temp_env_name)
        self.assertEqual(False, args.debug)

    def test_help(self):
        fake_stdout = StringIO.StringIO()
        with parse_error(self) as fake_stderr:
            with patch("sys.stdout", fake_stdout):
                parse_args(["--help"])
        self.assertEqual("", fake_stderr.getvalue())

class TestMain(TestCase):

    def test_main(self):
        argv = ["an-env", "/bin/juju", "/tmp/logs", "an-env-mod", "--verbose"]
        client = Mock(spec=["is_jes_enabled"])
        with patch('assess_budget.subprocess.check_output',
                   autospec=True):
            with patch("assess_budget.configure_logging",
                       autospec=True) as mock_cl:
                with patch('assess_budget.client_from_config',
                           return_value=client) as mock_cfc:
                    with patch("assess_budget.assess_budget",
                               autospec=True) as mock_assess:
                        main(argv)
        mock_cl.assert_called_once_with(logging.DEBUG)
        mock_cfc.assert_called_once_with('an-env', "/bin/juju", False)
        mock_assess.assert_called_once_with(client)

class TestAssess(TestCase):

    def setUp(self):
        super(TestAssess, self).setUp()
        self.fake_client = fake_juju_client()
        self.budget_name = 'personal'
        self.budget_limit = randint(1000, 10000)
        self.budget_value = str(randint(100, 900))

    def test_assess_budget(self):
        show_b = patch("assess_budget.assess_show_budget",
                       autospec=True)
        list_b = patch("assess_budget.assess_list_budgets",
                       autospec=True)
        set_b = patch("assess_budget.assess_set_budget",
                      autospec=True)
        create_b = patch("assess_budget.assess_create_budget",
                         autospec=True)
        b_limit = patch("assess_budget.assess_budget_limit",
                        autospec=True)
        init_b = patch("assess_budget._try_setting_budget",
                       autospec=True)
        expect_b = patch("assess_budget._set_budget_value_expectations",
                         autospec=True)
        update_e = patch("assess_budget._get_budgets", autospec=True)

        with show_b as show_b_mock, list_b as list_b_mock, \
            set_b as set_b_mock, create_b as create_b_mock, \
            b_limit as b_limit_mock, init_b as init_b_mock, \
                expect_b as expect_b_mock, update_e as update_e_mock:
                with patch("assess_budget.json.loads"):
                    with patch("assess_budget.randint",
                               return_value=self.budget_value):
                        assess_budget(self.fake_client)

                        init_b_mock.assert_called_once_with(self.fake_client,
                                                            self.budget_name,
                                                            '0')
                        expect_b_mock.assert_called_once_with(
                            update_e_mock(self.fake_client),
                            self.budget_name, self.budget_value)
                        b_limit_mock.assert_called_once_with(0)
                        create_b_mock.assert_called_once_with(
                            self.fake_client, self.budget_name,
                            self.budget_value, 0)
                        set_b_mock.assert_called_once_with(
                            self.fake_client, self.budget_name,
                            self.budget_value, 0)
                        show_b_mock.assert_called_once_with(
                            self.fake_client, self.budget_name,
                            self.budget_value)
                        list_b_mock.assert_called_once_with(
                            self.fake_client, update_e_mock(self.fake_client))

                        self.assertEqual(init_b_mock.call_count, 1)
                        self.assertEqual(expect_b_mock.call_count, 1)
                        self.assertEqual(b_limit_mock.call_count, 1)
                        self.assertEqual(create_b_mock.call_count, 1)
                        self.assertEqual(set_b_mock.call_count, 1)
                        self.assertEqual(show_b_mock.call_count, 1)
                        self.assertEqual(list_b_mock.call_count, 1)

class TestAssessShowBudget(TestAssess):

    def setUp(self):
        super(TestAssessShowBudget, self).setUp()
        self.fake_json = json.loads('{"limit":"' + self.budget_value +
                                    '","total":{"usage":"0%"}}')

    def test_assess_show_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_json):
                    assess_show_budget(self.fake_client, self.budget_name,
                                       self.budget_value)

    def test_raises_budget_usage_error(self):
        error_usage = randint(1, 100)
        self.fake_json['total']['usage'] = error_usage
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_json):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_show_budget(self.fake_client, self.budget_name,
                                       self.budget_value)
                self.assertEqual(ex.exception.message,
                                 'Budget usage found {}, expected 0%'.format(
                                    error_usage))

    def test_raises_budget_limit_error(self):
        self.fake_json['limit'] = 0
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_json):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_show_budget(self.fake_client, self.budget_name,
                                       self.budget_value)
                self.assertEqual(ex.exception.message,
                                 'Budget limit found 0, expected {}'.format(
                                     self.budget_value))

class TestAssessListBudgets(TestAssess):

    def setUp(self):
        super(TestAssessListBudgets, self).setUp()
        snippet = '[{"wallet": "personal", "limit": "300"}]'
        self.fake_budgets_json = json.loads('{"wallets":' + snippet + '}')
        self.fake_budget_json = json.loads(snippet)
        unexpected_snippet = '[{"wallet": "personal", "limit": "100"}]'
        self.fake_unexpected_budgets_json = json.loads(
            '{"wallets":' + unexpected_snippet + '}')
        self.fake_unexpected_budget_json = json.loads(unexpected_snippet)

    def test_assess_list_budgets(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_budgets_json):
                assess_list_budgets(self.fake_client, self.fake_budget_json)

    def test_raises_list_mismatch(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads",
                       return_value=self.fake_unexpected_budgets_json):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_list_budgets(
                        self.fake_client, self.fake_budget_json)
                self.assertEqual(ex.exception.message,
                                 'Found: {}\nExpected: {}'.format(
                                     self.fake_unexpected_budget_json,
                                     self.fake_budget_json))

class TestAssessSetBudget(TestAssess):

    def test_assess_set_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads"):
                with patch("assess_budget._try_setting_budget"):
                    with patch("assess_budget.assert_set_budget"):
                        assess_set_budget(self.fake_client, self.budget_name,
                                          self.budget_value, self.budget_limit)

    def test_raises_on_exceed_credit_limit(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads"):
                with patch("assess_budget._try_setting_budget"):
                    with self.assertRaises(JujuAssertionError) as ex:
                        _try_greater_than_limit_budget(self.fake_client,
                                                       self.budget_name,
                                                       self.budget_limit)
                    self.assertEqual(ex.exception.message,
                                     'Credit limit exceeded')

    def test_raises_on_negative_budget(self):
        self.budget_limit = -abs(self.budget_limit)
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.json.loads"):
                with patch("assess_budget._try_setting_budget"):
                    with self.assertRaises(JujuAssertionError) as ex:
                        _try_negative_budget(self.fake_client,
                                             self.budget_name)
                    self.assertEqual(ex.exception.message,
                                     'Negative budget allowed')

class TestAssessCreateBudget(TestAssess):

    def test_assess_create_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.create_budget"):
                assess_create_budget(self.fake_client, self.budget_name,
                                     self.budget_value, self.budget_limit)

    def test_raises_duplicate_budget(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.create_budget",
                       side_effect=JujuAssertionError):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_create_budget(self.fake_client, self.budget_name,
                                         self.budget_value, self.budget_limit)
                self.assertEqual(ex.exception.message,
                                 'Added duplicate budget')

    def test_raises_creation_error(self):
        with patch.object(self.fake_client, 'get_juju_output'):
            with patch("assess_budget.create_budget",
                       side_effect=CalledProcessError(1, 'foo', 'bar')):
                with self.assertRaises(JujuAssertionError) as ex:
                    assess_create_budget(self.fake_client, self.budget_name,
                                         self.budget_value, self.budget_limit)
                self.assertEqual(ex.exception.message,
                                 "Error testing create-budget: ['bar', '']")

class TestAssessBudgetLimit(TestAssess):

    def test_assess_budget_limt(self):
        budget_limit = randint(1, 10000)
        assess_budget_limit(budget_limit)

    def test_raises_error_on_negative_limit(self):
        neg_budget_limit = randint(-1000, -1)
        with self.assertRaises(JujuAssertionError) as ex:
            assess_budget_limit(neg_budget_limit)
        self.assertEqual(ex.exception.message,
                         'Negative Budget Limit {}'.format(neg_budget_limit))
<|endoftext|>


<commit_before>from hdl_toolkit.hdlObjects.specialValues import READ, WRITE
from hdl_toolkit.simulator.agents.agentBase import SyncAgentBase
from hdl_toolkit.simulator.shortcuts import oscilate

class BramPort_withoutClkAgent(SyncAgentBase):
    """
    @ivar requests: list of tuples (request type, address) - used for driver
    @ivar data:     list of data in memory, used for monitor
    """
    def __init__(self, intf, clk=None, rstn=None):
        super().__init__(intf, clk=clk, rstn=rstn, allowNoReset=True)

        self.requests = []
        self.readPending = False
        self.readed = []

    def doReq(self, s, req):
        rw = req[0]
        addr = req[1]

        if rw == READ:
            rw = 0
            wdata = None
            self.readPending = True

        elif rw == WRITE:
            wdata = req[2]
            rw = 1
        else:
            raise NotImplementedError(rw)

        intf = self.intf
        s.w(rw, intf.we)
        s.w(addr, intf.addr)
        s.w(wdata, intf.din)

    def onReadReq(self, s, addr):
        """
        on readReqRecieved
        """
        raise NotImplementedError()

    def onWriteReq(self, s, addr, data):
        raise NotImplementedError()

    def monitor(self, s):
        intf = self.intf
        if self.enable and s.read(intf.en).val:
            we = self.read(intf.we)
            addr = self.read(intf.addr)
            if we.val:
                data = self.read(intf.din)
                self.onWriteReq(s, addr, data)
            else:
                self.onReadReq(s, addr)

    def driver(self, s):
        intf = self.intf
        readPending = self.readPending
        if self.requests and self.enable:
            req = self.requests.pop(0)
            if req is None:
                s.w(0, intf.en)
                self.readPending = False
            else:
                self.doReq(s, req)
                s.w(1, intf.en)
        else:
            s.w(0, intf.en)
            self.readPending = False

        if readPending:
            d = s.r(intf.dout)
            self.readed.append(d)

class BramPortAgent(BramPort_withoutClkAgent):
    def __init__(self, intf, clk=None, rstn=None):
        if clk is None:
            clk = intf.clk
        super().__init__(intf, clk=clk, rstn=rstn)

    def getSubDrivers(self):
        yield oscilate(self.intf.clk)
<commit_msg>clk driver for bram port with clk<commit_after>from hdl_toolkit.hdlObjects.specialValues import READ, WRITE
from hdl_toolkit.simulator.agents.agentBase import SyncAgentBase
from hdl_toolkit.simulator.shortcuts import oscilate

class BramPort_withoutClkAgent(SyncAgentBase):
    """
    @ivar requests: list of tuples (request type, address) - used for driver
    @ivar data:     list of data in memory, used for monitor
    """
    def __init__(self, intf, clk=None, rstn=None):
        super().__init__(intf, clk=clk, rstn=rstn, allowNoReset=True)

        self.requests = []
        self.readPending = False
        self.readed = []

    def doReq(self, s, req):
        rw = req[0]
        addr = req[1]

        if rw == READ:
            rw = 0
            wdata = None
            self.readPending = True

        elif rw == WRITE:
            wdata = req[2]
            rw = 1
        else:
            raise NotImplementedError(rw)

        intf = self.intf
        s.w(rw, intf.we)
        s.w(addr, intf.addr)
        s.w(wdata, intf.din)

    def onReadReq(self, s, addr):
        """
        on readReqRecieved
        """
        raise NotImplementedError()

    def onWriteReq(self, s, addr, data):
        raise NotImplementedError()

    def monitor(self, s):
        intf = self.intf

        yield s.updateComplete
        if self.enable and s.read(intf.en).val:
            we = self.read(intf.we)
            addr = self.read(intf.addr)
            if we.val:
                data = self.read(intf.din)
                self.onWriteReq(s, addr, data)
            else:
                self.onReadReq(s, addr)

    def getDrivers(self):
        drivers = [self.driver]
        try:
            clk = self.intf.clk
        except AttributeError:
            return drivers
        drivers.append(oscilate(clk))
        return drivers

    def driver(self, s):
        intf = self.intf
        readPending = self.readPending
        if self.requests and self.enable:
            req = self.requests.pop(0)
            if req is None:
                s.w(0, intf.en)
                self.readPending = False
            else:
                self.doReq(s, req)
                s.w(1, intf.en)
        else:
            s.w(0, intf.en)
            self.readPending = False

        if readPending:
            yield s.updateComplete
            d = s.r(intf.dout)
            self.readed.append(d)

class BramPortAgent(BramPort_withoutClkAgent):
    def __init__(self, intf, clk=None, rstn=None):
        if clk is None:
            clk = intf.clk
        super().__init__(intf, clk=clk, rstn=rstn)

    def getSubDrivers(self):
        yield oscilate(self.intf.clk)
<|endoftext|>