2019-02-20 17:01:38 +00:00
|
|
|
"""
|
|
|
|
Tests for ``allmydata.test.eliotutil``.
|
|
|
|
"""
|
|
|
|
|
2019-02-25 16:52:50 +00:00
|
|
|
from __future__ import (
|
|
|
|
unicode_literals,
|
|
|
|
print_function,
|
|
|
|
absolute_import,
|
|
|
|
division,
|
|
|
|
)
|
|
|
|
|
2019-02-21 17:06:36 +00:00
|
|
|
from pprint import pformat
|
2019-02-25 16:52:50 +00:00
|
|
|
from sys import stdout
|
|
|
|
import logging
|
|
|
|
|
|
|
|
from fixtures import (
|
|
|
|
TempDir,
|
|
|
|
)
|
|
|
|
from testtools import (
|
|
|
|
TestCase,
|
|
|
|
)
|
|
|
|
from testtools.matchers import (
|
|
|
|
Is,
|
|
|
|
MatchesStructure,
|
|
|
|
Equals,
|
|
|
|
AfterPreprocessing,
|
|
|
|
)
|
|
|
|
from testtools.twistedsupport import (
|
|
|
|
has_no_result,
|
|
|
|
succeeded,
|
|
|
|
)
|
2019-02-21 17:06:36 +00:00
|
|
|
|
2019-02-20 17:01:38 +00:00
|
|
|
from eliot import (
|
|
|
|
Message,
|
2019-02-25 16:52:50 +00:00
|
|
|
FileDestination,
|
2019-02-21 17:06:36 +00:00
|
|
|
start_action,
|
2019-02-20 17:01:38 +00:00
|
|
|
)
|
|
|
|
from eliot.twisted import DeferredContext
|
2019-02-21 17:06:36 +00:00
|
|
|
from eliot.testing import (
|
|
|
|
capture_logging,
|
|
|
|
assertHasAction,
|
|
|
|
)
|
2019-02-20 17:01:38 +00:00
|
|
|
|
2019-02-21 17:06:36 +00:00
|
|
|
from twisted.internet.defer import (
|
|
|
|
Deferred,
|
|
|
|
succeed,
|
|
|
|
)
|
2019-02-20 17:01:38 +00:00
|
|
|
from twisted.internet.task import deferLater
|
|
|
|
from twisted.internet import reactor
|
|
|
|
|
2019-02-21 17:06:36 +00:00
|
|
|
from .eliotutil import (
|
|
|
|
eliot_logged_test,
|
2019-02-21 17:54:10 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
from ..util.eliotutil import (
|
2019-02-21 17:06:36 +00:00
|
|
|
eliot_friendly_generator_function,
|
|
|
|
inline_callbacks,
|
2019-02-25 16:52:50 +00:00
|
|
|
_parse_destination_description,
|
|
|
|
_EliotLogging,
|
|
|
|
)
|
|
|
|
from .common import (
|
|
|
|
SyncTestCase,
|
|
|
|
AsyncTestCase,
|
2019-02-21 17:06:36 +00:00
|
|
|
)
|
2019-02-20 17:01:38 +00:00
|
|
|
|
2019-02-25 16:52:50 +00:00
|
|
|
class EliotLoggedTestTests(AsyncTestCase):
|
2019-02-21 17:06:36 +00:00
|
|
|
@eliot_logged_test
|
2019-02-20 17:01:38 +00:00
|
|
|
def test_returns_none(self):
|
|
|
|
Message.log(hello="world")
|
|
|
|
|
2019-02-21 17:06:36 +00:00
|
|
|
@eliot_logged_test
|
2019-02-20 17:01:38 +00:00
|
|
|
def test_returns_fired_deferred(self):
|
|
|
|
Message.log(hello="world")
|
|
|
|
return succeed(None)
|
|
|
|
|
2019-02-21 17:06:36 +00:00
|
|
|
@eliot_logged_test
|
2019-02-20 17:01:38 +00:00
|
|
|
def test_returns_unfired_deferred(self):
|
|
|
|
Message.log(hello="world")
|
2019-02-21 17:06:36 +00:00
|
|
|
# @eliot_logged_test automatically gives us an action context but it's
|
|
|
|
# still our responsibility to maintain it across stack-busting
|
|
|
|
# operations.
|
2019-02-20 17:01:38 +00:00
|
|
|
d = DeferredContext(deferLater(reactor, 0.0, lambda: None))
|
|
|
|
d.addCallback(lambda ignored: Message.log(goodbye="world"))
|
|
|
|
# We didn't start an action. We're not finishing an action.
|
|
|
|
return d.result
|
2019-02-21 17:06:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def assert_logged_messages_contain_fields(testcase, logged_messages, expected_fields):
|
|
|
|
testcase.assertEqual(len(logged_messages), len(expected_fields))
|
|
|
|
actual_fields = list(
|
|
|
|
{key: msg.message[key] for key in expected if key in msg.message}
|
|
|
|
for (msg, expected)
|
|
|
|
in zip(logged_messages, expected_fields)
|
|
|
|
)
|
|
|
|
testcase.assertEqual(actual_fields, expected_fields)
|
|
|
|
|
|
|
|
|
|
|
|
def assert_logged_action_contains_messages(testcase, logger, expected_action, expected_fields):
|
|
|
|
action = assertHasAction(
|
|
|
|
testcase,
|
|
|
|
logger,
|
|
|
|
expected_action,
|
|
|
|
True,
|
|
|
|
)
|
|
|
|
assert_logged_messages_contain_fields(
|
|
|
|
testcase,
|
|
|
|
action.children,
|
|
|
|
expected_fields,
|
|
|
|
)
|
|
|
|
|
|
|
|
def assert_expected_action_tree(testcase, logger, expected_action_type, expected_type_tree):
|
|
|
|
logged_action = assertHasAction(
|
|
|
|
testcase,
|
|
|
|
logger,
|
|
|
|
expected_action_type,
|
|
|
|
True,
|
|
|
|
)
|
|
|
|
type_tree = logged_action.type_tree()
|
|
|
|
testcase.assertEqual(
|
|
|
|
{expected_action_type: expected_type_tree},
|
|
|
|
type_tree,
|
|
|
|
"Logger had messages:\n{}".format(pformat(logger.messages, indent=4)),
|
|
|
|
)
|
|
|
|
|
|
|
|
def assert_generator_logs_action_tree(testcase, generator_function, logger, expected_action_type, expected_type_tree):
|
|
|
|
list(eliot_friendly_generator_function(generator_function)())
|
|
|
|
assert_expected_action_tree(
|
|
|
|
testcase,
|
|
|
|
logger,
|
|
|
|
expected_action_type,
|
|
|
|
expected_type_tree,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2019-02-25 16:52:50 +00:00
|
|
|
class EliotFriendlyGeneratorFunctionTests(SyncTestCase):
|
2019-02-21 17:06:36 +00:00
|
|
|
# Get our custom assertion failure messages *and* the standard ones.
|
|
|
|
longMessage = True
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_none(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"hello")
|
|
|
|
yield
|
|
|
|
Message.log(message_type=u"goodbye")
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
list(g())
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action",
|
|
|
|
[u"hello", u"yielded", u"goodbye"],
|
|
|
|
)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_value(self, logger):
|
|
|
|
expected = object()
|
|
|
|
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"hello")
|
|
|
|
yield expected
|
|
|
|
Message.log(message_type=u"goodbye")
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
self.assertEqual([expected], list(g()))
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action",
|
|
|
|
[u"hello", u"yielded", u"goodbye"],
|
|
|
|
)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_inside_another_action(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
with start_action(action_type=u"confounding-factor"):
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
yield None
|
|
|
|
Message.log(message_type=u"c")
|
|
|
|
Message.log(message_type=u"d")
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
list(g())
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action",
|
|
|
|
[u"a",
|
|
|
|
{u"confounding-factor": [u"b", u"yielded", u"c"]},
|
|
|
|
u"d",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_inside_nested_actions(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
with start_action(action_type=u"confounding-factor"):
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
yield None
|
|
|
|
with start_action(action_type=u"double-confounding-factor"):
|
|
|
|
yield None
|
|
|
|
Message.log(message_type=u"c")
|
|
|
|
Message.log(message_type=u"d")
|
|
|
|
Message.log(message_type=u"e")
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
list(g())
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [
|
|
|
|
u"a",
|
|
|
|
{u"confounding-factor": [
|
|
|
|
u"b",
|
|
|
|
u"yielded",
|
|
|
|
{u"double-confounding-factor": [
|
|
|
|
u"yielded",
|
|
|
|
u"c",
|
|
|
|
]},
|
|
|
|
u"d",
|
|
|
|
]},
|
|
|
|
u"e",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_generator_and_non_generator(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
yield
|
|
|
|
with start_action(action_type=u"action-a"):
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
yield
|
|
|
|
Message.log(message_type=u"c")
|
|
|
|
|
|
|
|
Message.log(message_type=u"d")
|
|
|
|
yield
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
generator = g()
|
|
|
|
next(generator)
|
|
|
|
Message.log(message_type=u"0")
|
|
|
|
next(generator)
|
|
|
|
Message.log(message_type=u"1")
|
|
|
|
next(generator)
|
|
|
|
Message.log(message_type=u"2")
|
|
|
|
self.assertRaises(StopIteration, lambda: next(generator))
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [
|
|
|
|
u"a",
|
|
|
|
u"yielded",
|
|
|
|
u"0",
|
|
|
|
{
|
|
|
|
u"action-a": [
|
|
|
|
u"b",
|
|
|
|
u"yielded",
|
|
|
|
u"c",
|
|
|
|
],
|
|
|
|
},
|
|
|
|
u"1",
|
|
|
|
u"d",
|
|
|
|
u"yielded",
|
|
|
|
u"2",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_concurrent_generators(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g(which):
|
|
|
|
Message.log(message_type=u"{}-a".format(which))
|
|
|
|
with start_action(action_type=which):
|
|
|
|
Message.log(message_type=u"{}-b".format(which))
|
|
|
|
yield
|
|
|
|
Message.log(message_type=u"{}-c".format(which))
|
|
|
|
Message.log(message_type=u"{}-d".format(which))
|
|
|
|
|
|
|
|
gens = [g(u"1"), g(u"2")]
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
while gens:
|
|
|
|
for g in gens[:]:
|
|
|
|
try:
|
|
|
|
next(g)
|
|
|
|
except StopIteration:
|
|
|
|
gens.remove(g)
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [
|
|
|
|
u"1-a",
|
|
|
|
{u"1": [
|
|
|
|
u"1-b",
|
|
|
|
u"yielded",
|
|
|
|
u"1-c",
|
|
|
|
]},
|
|
|
|
u"2-a",
|
|
|
|
{u"2": [
|
|
|
|
u"2-b",
|
|
|
|
u"yielded",
|
|
|
|
u"2-c",
|
|
|
|
]},
|
|
|
|
u"1-d",
|
|
|
|
u"2-d",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2019-02-21 18:11:44 +00:00
|
|
|
@capture_logging(None)
|
|
|
|
def test_close_generator(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
try:
|
|
|
|
yield
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
finally:
|
|
|
|
Message.log(message_type=u"c")
|
|
|
|
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
gen = g()
|
|
|
|
next(gen)
|
|
|
|
gen.close()
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [
|
|
|
|
u"a",
|
|
|
|
u"yielded",
|
|
|
|
u"c",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2019-02-22 13:39:38 +00:00
|
|
|
@capture_logging(None)
|
|
|
|
def test_nested_generators(self, logger):
|
|
|
|
@eliot_friendly_generator_function
|
|
|
|
def g(recurse):
|
|
|
|
with start_action(action_type=u"a-recurse={}".format(recurse)):
|
|
|
|
Message.log(message_type=u"m-recurse={}".format(recurse))
|
|
|
|
if recurse:
|
|
|
|
set(g(False))
|
|
|
|
else:
|
|
|
|
yield
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
set(g(True))
|
|
|
|
|
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [{
|
|
|
|
u"a-recurse=True": [
|
|
|
|
u"m-recurse=True", {
|
|
|
|
u"a-recurse=False": [
|
|
|
|
u"m-recurse=False",
|
|
|
|
u"yielded",
|
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
|
|
|
)
|
|
|
|
|
2019-02-21 17:06:36 +00:00
|
|
|
|
2019-02-25 16:52:50 +00:00
|
|
|
class InlineCallbacksTests(SyncTestCase):
|
2019-02-21 17:06:36 +00:00
|
|
|
# Get our custom assertion failure messages *and* the standard ones.
|
|
|
|
longMessage = True
|
|
|
|
|
|
|
|
def _a_b_test(self, logger, g):
|
|
|
|
with start_action(action_type=u"the-action"):
|
2019-02-25 16:52:50 +00:00
|
|
|
self.assertThat(g(), succeeded(Is(None)))
|
2019-02-21 17:06:36 +00:00
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [
|
|
|
|
u"a",
|
|
|
|
u"yielded",
|
|
|
|
u"b",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_none(self, logger):
|
|
|
|
@inline_callbacks
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
yield
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
|
|
|
|
self._a_b_test(logger, g)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_fired_deferred(self, logger):
|
|
|
|
@inline_callbacks
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
yield succeed(None)
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
|
|
|
|
self._a_b_test(logger, g)
|
|
|
|
|
|
|
|
@capture_logging(None)
|
|
|
|
def test_yield_unfired_deferred(self, logger):
|
|
|
|
waiting = Deferred()
|
|
|
|
|
|
|
|
@inline_callbacks
|
|
|
|
def g():
|
|
|
|
Message.log(message_type=u"a")
|
|
|
|
yield waiting
|
|
|
|
Message.log(message_type=u"b")
|
|
|
|
|
|
|
|
with start_action(action_type=u"the-action"):
|
|
|
|
d = g()
|
2019-02-25 16:52:50 +00:00
|
|
|
self.assertThat(waiting, has_no_result())
|
2019-02-21 17:06:36 +00:00
|
|
|
waiting.callback(None)
|
2019-02-25 16:52:50 +00:00
|
|
|
self.assertThat(d, succeeded(Is(None)))
|
2019-02-21 17:06:36 +00:00
|
|
|
assert_expected_action_tree(
|
|
|
|
self,
|
|
|
|
logger,
|
|
|
|
u"the-action", [
|
|
|
|
u"a",
|
|
|
|
u"yielded",
|
|
|
|
u"b",
|
|
|
|
],
|
|
|
|
)
|
2019-02-25 16:52:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ParseDestinationDescriptionTests(SyncTestCase):
|
2019-02-25 18:15:09 +00:00
|
|
|
"""
|
|
|
|
Tests for ``_parse_destination_description``.
|
|
|
|
"""
|
2019-02-25 16:52:50 +00:00
|
|
|
def test_stdout(self):
|
|
|
|
"""
|
|
|
|
A ``file:`` description with a path of ``-`` causes logs to be written to
|
|
|
|
stdout.
|
|
|
|
"""
|
|
|
|
reactor = object()
|
|
|
|
self.assertThat(
|
|
|
|
_parse_destination_description("file:-")(reactor),
|
|
|
|
Equals(FileDestination(stdout)),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_regular_file(self):
|
|
|
|
"""
|
|
|
|
A ``file:`` description with any path other than ``-`` causes logs to be
|
|
|
|
written to a file with that name.
|
|
|
|
"""
|
|
|
|
tempdir = TempDir()
|
|
|
|
self.useFixture(tempdir)
|
|
|
|
|
|
|
|
reactor = object()
|
|
|
|
path = tempdir.join("regular_file")
|
|
|
|
|
|
|
|
self.assertThat(
|
|
|
|
_parse_destination_description("file:{}".format(path))(reactor),
|
|
|
|
MatchesStructure(
|
|
|
|
file=MatchesStructure(
|
|
|
|
path=Equals(path),
|
|
|
|
rotateLength=AfterPreprocessing(bool, Equals(True)),
|
|
|
|
maxRotatedFiles=AfterPreprocessing(bool, Equals(True)),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Opt out of the great features of common.SyncTestCase because we're
|
|
|
|
# interacting with Eliot in a very obscure, particular, fragile way. :/
|
|
|
|
class EliotLoggingTests(TestCase):
|
|
|
|
"""
|
|
|
|
Tests for ``_EliotLogging``.
|
|
|
|
"""
|
|
|
|
def test_stdlib_event_relayed(self):
|
|
|
|
"""
|
|
|
|
An event logged using the stdlib logging module is delivered to the Eliot
|
|
|
|
destination.
|
|
|
|
"""
|
|
|
|
collected = []
|
|
|
|
service = _EliotLogging([collected.append])
|
|
|
|
service.startService()
|
|
|
|
self.addCleanup(service.stopService)
|
|
|
|
|
|
|
|
# The first destination added to the global log destinations gets any
|
|
|
|
# buffered messages delivered to it. We don't care about those.
|
|
|
|
# Throw them on the floor. Sorry.
|
|
|
|
del collected[:]
|
|
|
|
|
|
|
|
logging.critical("oh no")
|
|
|
|
self.assertThat(
|
|
|
|
collected,
|
|
|
|
AfterPreprocessing(
|
|
|
|
len,
|
|
|
|
Equals(1),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_twisted_event_relayed(self):
|
|
|
|
"""
|
|
|
|
An event logged with a ``twisted.logger.Logger`` is delivered to the Eliot
|
|
|
|
destination.
|
|
|
|
"""
|
|
|
|
collected = []
|
|
|
|
service = _EliotLogging([collected.append])
|
|
|
|
service.startService()
|
|
|
|
self.addCleanup(service.stopService)
|
|
|
|
|
|
|
|
from twisted.logger import Logger
|
|
|
|
Logger().critical("oh no")
|
|
|
|
self.assertThat(
|
|
|
|
collected,
|
|
|
|
AfterPreprocessing(
|
|
|
|
len, Equals(1),
|
|
|
|
),
|
|
|
|
)
|