Added more tests and a couple of cosmetic fixes

This commit is contained in:
Adolfo Gómez García 2022-08-30 17:40:29 +02:00
parent db12077304
commit 8b540c350a
7 changed files with 156 additions and 26 deletions

View File

@ -49,7 +49,7 @@ class AssignedAndUnusedTests(UDSTransactionTestCase):
config.GlobalConfig.CHECK_UNUSED_TIME.set('600')
AssignedAndUnused.setup()
# All created user services has "in_use" to False, os_state and state to USABLE
self.userServices = fixtures_services.newUserServiceForTesting(count=32)
self.userServices = fixtures_services.createUserServiceForTesting(count=32)
def test_assigned_unused(self):
for us in self.userServices: # Update state date to now

View File

@ -53,7 +53,7 @@ class HangedCleanerTests(UDSTransactionTestCase):
config.GlobalConfig.MAX_REMOVAL_TIME.set(MAX_INIT)
HangedCleaner.setup()
# All created user services has "in_use" to False, os_state and state to USABLE
self.userServices = fixtures_services.newUserServiceForTesting(
self.userServices = fixtures_services.createUserServiceForTesting(
count=TEST_SERVICES
)

View File

@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import typing
import logging
from uds.core.util.state import State
from uds.core.workers.servicepools_cache_updater import ServiceCacheUpdater
from uds.core.environment import Environment
from ...utils.test import UDSTransactionTestCase
from ...fixtures import services as services_fixtures
if typing.TYPE_CHECKING:
from uds import models
class ServiceCacheUpdaterTests(UDSTransactionTestCase):
servicePool: 'models.ServicePool'
def setUp(self) -> None:
ServiceCacheUpdater.setup()
userService = services_fixtures.createUserServiceForTesting()[0]
self.servicePool = userService.deployed_service
userService.delete() # empty all
def numberOfRemovingOrCanced(self) -> int:
return self.servicePool.userServices.filter(state__in=[State.REMOVABLE, State.CANCELED]).count()
def runCacheUpdater(self, times: int) -> int:
for _ in range(times):
updater = ServiceCacheUpdater(Environment.getTempEnv())
updater.run()
# Test user service will cancel automatically so it will not get in "removable" state (on remove start, it will tell it has been removed)
return self.servicePool.userServices.count() - self.numberOfRemovingOrCanced()
def setCache(self, initial: typing.Optional[int] = None, cache: typing.Optional[int] = None, cache2: typing.Optional[int] = None, max: typing.Optional[int] = None) -> None:
self.servicePool.initial_srvs = self.servicePool.initial_srvs if initial is None else initial
self.servicePool.cache_l1_srvs = self.servicePool.cache_l1_srvs if cache is None else cache
self.servicePool.cache_l2_srvs = self.servicePool.cache_l2_srvs if cache2 is None else cache2
self.servicePool.max_srvs = self.servicePool.max_srvs if max is None else max
self.servicePool.save()
def test_initial(self) -> None:
self.setCache(initial=100, cache=10, max=500)
self.assertEqual(self.runCacheUpdater(self.servicePool.initial_srvs + 10), self.servicePool.initial_srvs)
def test_remove(self) -> None:
self.setCache(initial=100, cache=110, max=500)
self.runCacheUpdater(self.servicePool.cache_l1_srvs)
# Now again, decrease cache to original, must remove ten elements
mustDelete = self.servicePool.cache_l1_srvs - self.servicePool.initial_srvs
self.setCache(cache=10)
self.assertEqual(self.runCacheUpdater(mustDelete), self.servicePool.initial_srvs)
self.assertEqual(self.numberOfRemovingOrCanced(), mustDelete)
def test_max(self) -> None:
self.setCache(initial=100, cache=10, max=50)
self.assertEqual(self.runCacheUpdater(self.servicePool.initial_srvs + 10), self.servicePool.max_srvs)
self.setCache(cache=200)
self.assertEqual(self.runCacheUpdater(self.servicePool.initial_srvs + 10), self.servicePool.max_srvs)
def test_cache(self) -> None:
self.setCache(initial=10, cache=100, max=500)
# Try to "overcreate" cache elements (must create 100, that is "cache" (bigger than initial))
self.assertEqual(self.runCacheUpdater(self.servicePool.cache_l1_srvs + 10), self.servicePool.cache_l1_srvs)
def test_provider_preparing_limits(self) -> None:
from uds.services.Test.provider import TestProvider
TestProvider.maxPreparingServices = 10
self.setCache(initial=100, cache=10, max=50)
# Try to "overcreate" cache elements but provider limits it to 10
self.assertEqual(self.runCacheUpdater(self.servicePool.cache_l1_srvs + 10), 10)
# Delete all userServices
self.servicePool.userServices.all().delete()
# Now, set provider limit to 0. Minumum aceptable is 1, so 1 will be created
TestProvider.maxPreparingServices = 0
self.assertEqual(self.runCacheUpdater(self.servicePool.cache_l1_srvs + 10), 1)
def test_provider_removing_limits(self) -> None:
from uds.services.Test.provider import TestProvider
TestProvider.maxRemovingServices = 10
self.setCache(initial=0, cache=50, max=50)
# Try to "overcreate" cache elements but provider limits it to 10
self.runCacheUpdater(self.servicePool.cache_l1_srvs)
# Now set cache to a lower value
self.setCache(cache=10)
# Execute updater, must remove 10 elements (maxRemovingServices)
self.assertEqual(self.runCacheUpdater(10), 40)

View File

@ -155,7 +155,7 @@ def createSingleTestingUserServiceStructure(
return user_service
def newUserServiceForTesting(
def createUserServiceForTesting(
count: int = 1,
type_: typing.Union[
typing.Literal['managed'], typing.Literal['unmanaged']

View File

@ -94,7 +94,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
states = [State.PREPARING, State.USABLE, State.REMOVING, State.REMOVABLE]
return Q(state__in=states)
def __checkMaxDeployedReached(self, servicePool: ServicePool) -> None:
def _checkMaxDeployedReached(self, servicePool: ServicePool) -> None:
"""
Checks if maxDeployed for the service has been reached, and, if so,
raises an exception that no more services of this kind can be reached
@ -105,7 +105,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
return
numberOfServices = servicePool.userServices.filter(
state__in=[State.PREPARING, State.USABLE]
self.getStateFilter(servicePool)
).count()
if serviceInstance.maxDeployed <= numberOfServices:
@ -113,14 +113,14 @@ class UserServiceManager(metaclass=singleton.Singleton):
'Max number of allowed deployments for service reached'
)
def __createCacheAtDb(
def _createCacheAtDb(
self, publication: ServicePoolPublication, cacheLevel: int
) -> UserService:
"""
Private method to instatiate a cache element at database with default states
"""
# Checks if maxDeployed has been reached and if so, raises an exception
self.__checkMaxDeployedReached(publication.deployed_service)
self._checkMaxDeployedReached(publication.deployed_service)
now = getSqlDatetime()
return publication.userServices.create(
cache_level=cacheLevel,
@ -134,13 +134,13 @@ class UserServiceManager(metaclass=singleton.Singleton):
in_use=False,
)
def __createAssignedAtDb(
def _createAssignedAtDb(
self, publication: ServicePoolPublication, user: User
) -> UserService:
"""
Private method to instatiate an assigned element at database with default state
"""
self.__checkMaxDeployedReached(publication.deployed_service)
self._checkMaxDeployedReached(publication.deployed_service)
now = getSqlDatetime()
return publication.userServices.create(
cache_level=0,
@ -154,7 +154,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
in_use=False,
)
def __createAssignedAtDbForNoPublication(
def _createAssignedAtDbForNoPublication(
self, servicePool: ServicePool, user: User
) -> UserService:
"""
@ -162,7 +162,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
There is cases where deployed services do not have publications (do not need them), so we need this method to create
an UserService with no publications, and create them from an ServicePool
"""
self.__checkMaxDeployedReached(servicePool)
self._checkMaxDeployedReached(servicePool)
now = getSqlDatetime()
return servicePool.userServices.create(
cache_level=0,
@ -187,7 +187,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
cacheLevel,
publication,
)
cache = self.__createCacheAtDb(publication, cacheLevel)
cache = self._createCacheAtDb(publication, cacheLevel)
ci = cache.getInstance()
state = ci.deployForCache(cacheLevel)
@ -214,7 +214,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
publication,
)
if publication:
assigned = self.__createAssignedAtDb(publication, user)
assigned = self._createAssignedAtDb(publication, user)
else:
raise Exception(
'Invalid publication creating service assignation: {} {}'.format(
@ -223,7 +223,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
)
else:
logger.debug('Creating a new assigned element for user %s', user)
assigned = self.__createAssignedAtDbForNoPublication(servicePool, user)
assigned = self._createAssignedAtDbForNoPublication(servicePool, user)
assignedInstance = assigned.getInstance()
state = assignedInstance.deployForUser(user)
@ -251,7 +251,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
publication,
)
if publication:
assigned = self.__createAssignedAtDb(publication, user)
assigned = self._createAssignedAtDb(publication, user)
else:
raise Exception(
'Invalid publication creating service assignation: {} {}'.format(
@ -264,7 +264,7 @@ class UserServiceManager(metaclass=singleton.Singleton):
assignableId,
user,
)
assigned = self.__createAssignedAtDbForNoPublication(servicePool, user)
assigned = self._createAssignedAtDbForNoPublication(servicePool, user)
# Now, get from serviceInstance the data
assignedInstance = assigned.getInstance()

View File

@ -390,7 +390,7 @@ class Service(Module):
@classmethod
def canAssign(cls) -> bool:
"""
Helper to query if a class is custom (implements getJavascript method)
Helper to query if a class is assignable (can be assigned to an user manually)
"""
return (
cls.listAssignables is not Service.listAssignables

View File

@ -71,15 +71,11 @@ class TestProvider(services.ServiceProvider):
# : mark it as _ (using gettext_noop)
iconFile = 'provider.png'
# now comes the form fields
# There is always two fields that are requested to the admin, that are:
# Service Name, that is a name that the admin uses to name this provider
# Description, that is a short description that the admin gives to this provider
# Now we are going to add a few fields that we need to use this provider
# Remember that these are "dummy" fields, that in fact are not required
# but used for sample purposes
# If we don't indicate an order, the output order of fields will be
# "random"
# Max preparing concurrent services
maxPreparingServices = 1000 # a lot, this in fact will not make anything
# Mas removing concurrent services
maxRemovingServices = 1000 # a lot, this in fact will not make anything
# Simple data for testing pourposes
@dataclasses.dataclass