blob: a0796be40c216b250f5ef7f239678c84acd5b965 [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unittests for the cache.py module."""
from __future__ import print_function
import mock
import os
from chromite.lib import gs_unittest
from chromite.lib import cros_test_lib
from chromite.lib import cache
from chromite.lib import osutils
from chromite.lib import partial_mock
from chromite.lib import retry_util
class CacheReferenceTest(cros_test_lib.TestCase):
"""Tests for CacheReference.
Largely focused on exercising the API other objects expect from it.
"""
# pylint: disable=protected-access
def setUp(self):
# These are the funcs CacheReference expects the cache object to have.
spec = (
'_GetKeyPath',
'_Insert',
'_InsertText',
'_KeyExists',
'_LockForKey',
'_Remove',
)
self.cache = mock.Mock(spec=spec)
self.lock = mock.MagicMock()
self.cache._LockForKey.return_value = self.lock
def testContext(self):
"""Verify we can use it as a context manager."""
# We should set the acquire member and grab/release the lock.
ref = cache.CacheReference(self.cache, 'key')
self.assertFalse(ref.acquired)
self.assertFalse(self.lock.__enter__.called)
with ref as newref:
self.assertEqual(ref, newref)
self.assertTrue(ref.acquired)
self.assertTrue(self.lock.__enter__.called)
self.assertFalse(self.lock.__exit__.called)
self.assertFalse(ref.acquired)
self.assertTrue(self.lock.__exit__.called)
def testPath(self):
"""Verify we get a file path for the ref."""
self.cache._GetKeyPath.return_value = '/foo/bar'
ref = cache.CacheReference(self.cache, 'key')
self.assertEqual(ref.path, '/foo/bar')
self.cache._GetKeyPath.assert_called_once_with('key')
def testLocking(self):
"""Verify Acquire & Release work as expected."""
ref = cache.CacheReference(self.cache, 'key')
# Check behavior when the lock is free.
self.assertRaises(AssertionError, ref.Release)
self.assertFalse(ref.acquired)
# Check behavior when the lock is held.
self.assertEqual(ref.Acquire(), None)
self.assertRaises(AssertionError, ref.Acquire)
self.assertTrue(ref.acquired)
# Check behavior after the lock is freed.
self.assertEqual(ref.Release(), None)
self.assertFalse(ref.acquired)
def testExists(self):
"""Verify Exists works when the entry is not in the cache."""
ref = cache.CacheReference(self.cache, 'key')
self.cache._KeyExists.return_value = False
self.assertFalse(ref.Exists())
def testExistsMissing(self):
"""Verify Exists works when the entry is in the cache."""
ref = cache.CacheReference(self.cache, 'key')
self.cache._KeyExists.return_value = True
self.assertTrue(ref.Exists())
def testAssign(self):
"""Verify Assign works as expected."""
ref = cache.CacheReference(self.cache, 'key')
ref.Assign('/foo')
self.cache._Insert.assert_called_once_with('key', '/foo')
def testAssignText(self):
"""Verify AssignText works as expected."""
ref = cache.CacheReference(self.cache, 'key')
ref.AssignText('text!')
self.cache._InsertText.assert_called_once_with('key', 'text!')
def testRemove(self):
"""Verify Remove works as expected."""
ref = cache.CacheReference(self.cache, 'key')
ref.Remove()
self.cache._Remove.assert_called_once_with('key')
def testSetDefault(self):
"""Verify SetDefault works when the entry is not in the cache."""
ref = cache.CacheReference(self.cache, 'key')
self.cache._KeyExists.return_value = False
ref.SetDefault('/foo')
self.cache._Insert.assert_called_once_with('key', '/foo')
def testSetDefaultExists(self):
"""Verify SetDefault works when the entry is in the cache."""
ref = cache.CacheReference(self.cache, 'key')
self.cache._KeyExists.return_value = True
ref.SetDefault('/foo')
self.assertFalse(self.cache._Insert.called)
class CacheTestCase(cros_test_lib.MockTempDirTestCase):
"""Tests for any type of Cache object."""
def setUp(self):
self.gs_mock = self.StartPatcher(gs_unittest.GSContextMock())
def _testAssign(self):
"""Verify we can assign a file to the cache and get it back out."""
key = ('foo', 'bar')
data = r'text!\nthere'
path = os.path.join(self.tempdir, 'test-file')
osutils.WriteFile(path, data)
with self.cache.Lookup(key) as ref:
self.assertFalse(ref.Exists())
ref.Assign(path)
self.assertTrue(ref.Exists())
self.assertEqual(osutils.ReadFile(ref.path), data)
with self.cache.Lookup(key) as ref:
self.assertTrue(ref.Exists())
self.assertEqual(osutils.ReadFile(ref.path), data)
def _testAssignData(self):
"""Verify we can assign data to the cache and get it back out."""
key = ('foo', 'bar')
data = r'text!\nthere'
with self.cache.Lookup(key) as ref:
self.assertFalse(ref.Exists())
ref.AssignText(data)
self.assertTrue(ref.Exists())
self.assertEqual(osutils.ReadFile(ref.path), data)
with self.cache.Lookup(key) as ref:
self.assertTrue(ref.Exists())
self.assertEqual(osutils.ReadFile(ref.path), data)
def _testRemove(self):
"""Verify we can remove entries from the cache."""
key = ('foo', 'bar')
data = r'text!\nthere'
with self.cache.Lookup(key) as ref:
self.assertFalse(ref.Exists())
ref.AssignText(data)
self.assertTrue(ref.Exists())
ref.Remove()
self.assertFalse(ref.Exists())
class DiskCacheTest(CacheTestCase):
"""Tests for DiskCache."""
def setUp(self):
self.cache = cache.DiskCache(self.tempdir)
testAssign = CacheTestCase._testAssign
testAssignData = CacheTestCase._testAssignData
testRemove = CacheTestCase._testRemove
class RemoteCacheTest(CacheTestCase):
"""Tests for RemoteCache."""
def setUp(self):
self.cache = cache.RemoteCache(self.tempdir)
testAssign = CacheTestCase._testAssign
testAssignData = CacheTestCase._testAssignData
testRemove = CacheTestCase._testRemove
def testFetchFile(self):
"""Verify we handle file:// URLs."""
key = ('file', 'foo')
data = 'daaaaata'
path = os.path.join(self.tempdir, 'test-file')
url = 'file://%s' % path
osutils.WriteFile(path, data)
with self.cache.Lookup(key) as ref:
self.assertFalse(ref.Exists())
ref.Assign(url)
self.assertTrue(ref.Exists())
self.assertEqual(osutils.ReadFile(ref.path), data)
def testFetchNonGs(self):
"""Verify we fetch remote URLs and save the result."""
def _Fetch(*args, **_kwargs):
# Probably shouldn't assume this ordering, but best way for now.
cmd = args[0]
local_path = cmd[-1]
osutils.Touch(local_path)
self.PatchObject(retry_util, 'RunCurl', side_effect=_Fetch)
schemes = ('ftp', 'http', 'https')
for scheme in schemes:
key = (scheme, 'foo')
url = '%s://some.site.localdomain/file_go_boom' % scheme
with self.cache.Lookup(key) as ref:
self.assertFalse(ref.Exists())
ref.Assign(url)
self.assertTrue(ref.Exists())
def testFetchGs(self):
"""Verify we fetch from Google Storage and save the result."""
# pylint: disable=unused-argument
def _Fetch(_ctx, cmd, capture_output):
# Touch file we tried to copy too.
osutils.Touch(cmd[-1])
self.gs_mock.AddCmdResult(
['cp', '-v', '--', partial_mock.Ignore(), partial_mock.Ignore()],
side_effect=_Fetch)
key = ('gs',)
url = 'gs://some.site.localdomain/file_go_boom'
with self.cache.Lookup(key) as ref:
self.assertFalse(ref.Exists())
ref.Assign(url)
self.assertTrue(ref.Exists())
class TarballCacheTest(CacheTestCase):
"""Tests for TarballCache."""
def setUp(self):
self.cache = cache.RemoteCache(self.tempdir)
testAssign = CacheTestCase._testAssign
testAssignData = CacheTestCase._testAssignData
testRemove = CacheTestCase._testRemove