<commit_before>import shared
import ConfigParser
import sys
import os
import locale
import random
import string
import platform
from distutils.version import StrictVersion

from namecoin import ensureNamecoinOptions

storeConfigFilesInSameDirectoryAsProgramByDefault = False  # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder.

def _loadTrustedPeer():
    try:
        trustedPeer = shared.config.get('bitmessagesettings', 'trustedpeer')
    except ConfigParser.Error:
        # This probably means the trusted peer wasn't specified so we
        # can just leave it as None
        return

    host, port = trustedPeer.split(':')
    shared.trustedPeer = shared.Peer(host, int(port))

def loadConfig():
    if shared.appdata:
        shared.config.read(shared.appdata + 'keys.dat')
        #shared.appdata must have been specified as a startup option.
        try:
            shared.config.get('bitmessagesettings', 'settingsversion')
            print 'Loading config files from directory specified on startup: ' + shared.appdata
            needToCreateKeysFile = False
        except:
            needToCreateKeysFile = True

    else:
        shared.config.read('keys.dat')
        try:
            shared.config.get('bitmessagesettings', 'settingsversion')
            print 'Loading config files from same directory as program.'
            needToCreateKeysFile = False
            shared.appdata = ''
        except:
            # Could not load the keys.dat file in the program directory. Perhaps it
            # is in the appdata directory.
            shared.appdata = shared.lookupAppdataFolder()
            shared.config.read(shared.appdata + 'keys.dat')
            try:
                shared.config.get('bitmessagesettings', 'settingsversion')
                print 'Loading existing config files from', shared.appdata
                needToCreateKeysFile = False
            except:
                needToCreateKeysFile = True

    if needToCreateKeysFile:
        # This appears to be the first time running the program; there is
        # no config file (or it cannot be accessed). Create config file.
        shared.config.add_section('bitmessagesettings')
        shared.config.set('bitmessagesettings', 'settingsversion', '10')
        shared.config.set('bitmessagesettings', 'port', '8444')
        shared.config.set(
            'bitmessagesettings', 'timeformat', '%%a, %%d %%b %%Y  %%I:%%M %%p')
        shared.config.set('bitmessagesettings', 'blackwhitelist', 'black')
        shared.config.set('bitmessagesettings', 'startonlogon', 'false')
        if 'linux' in sys.platform:
            shared.config.set(
                'bitmessagesettings', 'minimizetotray', 'false')
                              # This isn't implimented yet and when True on
                              # Ubuntu causes Bitmessage to disappear while
                              # running when minimized.
        else:
            shared.config.set(
                'bitmessagesettings', 'minimizetotray', 'true')
        shared.config.set(
            'bitmessagesettings', 'showtraynotifications', 'true')
        shared.config.set('bitmessagesettings', 'startintray', 'false')
        shared.config.set('bitmessagesettings', 'socksproxytype', 'none')
        shared.config.set(
            'bitmessagesettings', 'sockshostname', 'localhost')
        shared.config.set('bitmessagesettings', 'socksport', '9050')
        shared.config.set(
            'bitmessagesettings', 'socksauthentication', 'false')
        shared.config.set(
            'bitmessagesettings', 'sockslisten', 'false')
        shared.config.set('bitmessagesettings', 'socksusername', '')
        shared.config.set('bitmessagesettings', 'sockspassword', '')
        shared.config.set('bitmessagesettings', 'keysencrypted', 'false')
        shared.config.set(
            'bitmessagesettings', 'messagesencrypted', 'true')
        shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(
            shared.networkDefaultProofOfWorkNonceTrialsPerByte))
        shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(
            shared.networkDefaultPayloadLengthExtraBytes))
        shared.config.set('bitmessagesettings', 'minimizeonclose', 'false')
        shared.config.set(
            'bitmessagesettings', 'maxacceptablenoncetrialsperbyte', '0')
        shared.config.set(
            'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0')
        shared.config.set('bitmessagesettings', 'dontconnect', 'true')
        shared.config.set('bitmessagesettings', 'userlocale', 'system')
        shared.config.set('bitmessagesettings', 'useidenticons', 'True')
        shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons
        shared.config.set('bitmessagesettings', 'replybelow', 'False')
        shared.config.set('bitmessagesettings', 'maxdownloadrate', '0')
        shared.config.set('bitmessagesettings', 'maxuploadrate', '0')
        shared.config.set('bitmessagesettings', 'ttl', '367200')

        #boostrap via psi.i2p
        shared.config.set('bitmessagesettings', 'bootstrapeepsite', 'avviiexdngd32ccoy4kuckvc3mkf53ycvzbz6vz75vzhv4tbpk5a.b32.i2p')

         #start:UI setting to stop trying to send messages after X days/months
        shared.config.set(
            'bitmessagesettings', 'stopresendingafterxdays', '')
        shared.config.set(
            'bitmessagesettings', 'stopresendingafterxmonths', '')
        #shared.config.set(
        #    'bitmessagesettings', 'timeperiod', '-1')
        #end

        # Are you hoping to add a new option to the keys.dat file? You're in
        # the right place for adding it to users who install the software for
        # the first time. But you must also add it to the keys.dat file of
        # existing users. To do that, search the class_sqlThread.py file for the
        # text: "right above this line!"

        ensureNamecoinOptions()

        if storeConfigFilesInSameDirectoryAsProgramByDefault:
            # Just use the same directory as the program and forget about
            # the appdata folder
            shared.appdata = ''
            print 'Creating new config files in same directory as program.'
        else:
            print 'Creating new config files in', shared.appdata
            if not os.path.exists(shared.appdata):
                os.makedirs(shared.appdata)
        if not sys.platform.startswith('win'):
            os.umask(0o077)
        shared.writeKeysFile()

    _loadTrustedPeer()

def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
    try:
        VER_THIS=StrictVersion(platform.version())
        if sys.platform[0:3]=="win":
            return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS
        return False
    except Exception as err:
        print "Info: we could not tell whether your OS is limited to having very view half open connections because we couldn't interpret the platform version. Don't worry; we'll assume that it is not limited. This tends to occur on Raspberry Pis. :", err
        return False
<commit_msg>Change default bootstrap eepsite URL<commit_after>import shared
import ConfigParser
import sys
import os
import locale
import random
import string
import platform
from distutils.version import StrictVersion

from namecoin import ensureNamecoinOptions

storeConfigFilesInSameDirectoryAsProgramByDefault = False  # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder.

def _loadTrustedPeer():
    try:
        trustedPeer = shared.config.get('bitmessagesettings', 'trustedpeer')
    except ConfigParser.Error:
        # This probably means the trusted peer wasn't specified so we
        # can just leave it as None
        return

    host, port = trustedPeer.split(':')
    shared.trustedPeer = shared.Peer(host, int(port))

def loadConfig():
    if shared.appdata:
        shared.config.read(shared.appdata + 'keys.dat')
        #shared.appdata must have been specified as a startup option.
        try:
            shared.config.get('bitmessagesettings', 'settingsversion')
            print 'Loading config files from directory specified on startup: ' + shared.appdata
            needToCreateKeysFile = False
        except:
            needToCreateKeysFile = True

    else:
        shared.config.read('keys.dat')
        try:
            shared.config.get('bitmessagesettings', 'settingsversion')
            print 'Loading config files from same directory as program.'
            needToCreateKeysFile = False
            shared.appdata = ''
        except:
            # Could not load the keys.dat file in the program directory. Perhaps it
            # is in the appdata directory.
            shared.appdata = shared.lookupAppdataFolder()
            shared.config.read(shared.appdata + 'keys.dat')
            try:
                shared.config.get('bitmessagesettings', 'settingsversion')
                print 'Loading existing config files from', shared.appdata
                needToCreateKeysFile = False
            except:
                needToCreateKeysFile = True

    if needToCreateKeysFile:
        # This appears to be the first time running the program; there is
        # no config file (or it cannot be accessed). Create config file.
        shared.config.add_section('bitmessagesettings')
        shared.config.set('bitmessagesettings', 'settingsversion', '10')
        shared.config.set('bitmessagesettings', 'port', '8444')
        shared.config.set(
            'bitmessagesettings', 'timeformat', '%%a, %%d %%b %%Y  %%I:%%M %%p')
        shared.config.set('bitmessagesettings', 'blackwhitelist', 'black')
        shared.config.set('bitmessagesettings', 'startonlogon', 'false')
        if 'linux' in sys.platform:
            shared.config.set(
                'bitmessagesettings', 'minimizetotray', 'false')
                              # This isn't implimented yet and when True on
                              # Ubuntu causes Bitmessage to disappear while
                              # running when minimized.
        else:
            shared.config.set(
                'bitmessagesettings', 'minimizetotray', 'true')
        shared.config.set(
            'bitmessagesettings', 'showtraynotifications', 'true')
        shared.config.set('bitmessagesettings', 'startintray', 'false')
        shared.config.set('bitmessagesettings', 'socksproxytype', 'none')
        shared.config.set(
            'bitmessagesettings', 'sockshostname', 'localhost')
        shared.config.set('bitmessagesettings', 'socksport', '9050')
        shared.config.set(
            'bitmessagesettings', 'socksauthentication', 'false')
        shared.config.set(
            'bitmessagesettings', 'sockslisten', 'false')
        shared.config.set('bitmessagesettings', 'socksusername', '')
        shared.config.set('bitmessagesettings', 'sockspassword', '')
        shared.config.set('bitmessagesettings', 'keysencrypted', 'false')
        shared.config.set(
            'bitmessagesettings', 'messagesencrypted', 'true')
        shared.config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(
            shared.networkDefaultProofOfWorkNonceTrialsPerByte))
        shared.config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(
            shared.networkDefaultPayloadLengthExtraBytes))
        shared.config.set('bitmessagesettings', 'minimizeonclose', 'false')
        shared.config.set(
            'bitmessagesettings', 'maxacceptablenoncetrialsperbyte', '0')
        shared.config.set(
            'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0')
        shared.config.set('bitmessagesettings', 'dontconnect', 'true')
        shared.config.set('bitmessagesettings', 'userlocale', 'system')
        shared.config.set('bitmessagesettings', 'useidenticons', 'True')
        shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons
        shared.config.set('bitmessagesettings', 'replybelow', 'False')
        shared.config.set('bitmessagesettings', 'maxdownloadrate', '0')
        shared.config.set('bitmessagesettings', 'maxuploadrate', '0')
        shared.config.set('bitmessagesettings', 'ttl', '367200')

        #boostrap via psi.i2p
        shared.config.set('bitmessagesettings', 'bootstrapeepsite', '4kecr3jdeiuihtuib7dgtsq4sd2jicxyiaclrn26xhvhqw7ocrma.b32.i2p')

         #start:UI setting to stop trying to send messages after X days/months
        shared.config.set(
            'bitmessagesettings', 'stopresendingafterxdays', '')
        shared.config.set(
            'bitmessagesettings', 'stopresendingafterxmonths', '')
        #shared.config.set(
        #    'bitmessagesettings', 'timeperiod', '-1')
        #end

        # Are you hoping to add a new option to the keys.dat file? You're in
        # the right place for adding it to users who install the software for
        # the first time. But you must also add it to the keys.dat file of
        # existing users. To do that, search the class_sqlThread.py file for the
        # text: "right above this line!"

        ensureNamecoinOptions()

        if storeConfigFilesInSameDirectoryAsProgramByDefault:
            # Just use the same directory as the program and forget about
            # the appdata folder
            shared.appdata = ''
            print 'Creating new config files in same directory as program.'
        else:
            print 'Creating new config files in', shared.appdata
            if not os.path.exists(shared.appdata):
                os.makedirs(shared.appdata)
        if not sys.platform.startswith('win'):
            os.umask(0o077)
        shared.writeKeysFile()

    _loadTrustedPeer()

def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
    try:
        VER_THIS=StrictVersion(platform.version())
        if sys.platform[0:3]=="win":
            return StrictVersion("5.1.2600")<=VER_THIS and StrictVersion("6.0.6000")>=VER_THIS
        return False
    except Exception as err:
        print "Info: we could not tell whether your OS is limited to having very view half open connections because we couldn't interpret the platform version. Don't worry; we'll assume that it is not limited. This tends to occur on Raspberry Pis. :", err
        return False
<|endoftext|>


<commit_before>import sys
from slug import Process, Pipe

def runpy(code):
    return [sys.executable, '-c', code]

def test_successful_command():
    proc = Process(runpy('import sys; sys.exit(0)'))
    proc.start()
    proc.join()
    assert proc.return_code == 0

def test_failed_command():
    proc = Process(runpy('import sys; sys.exit(42)'))
    proc.start()
    proc.join()
    assert proc.return_code == 42

def test_kill_process():
    proc = Process(runpy('input()'))
    proc.start()
    proc.kill()
    assert proc.return_code != 0

def test_terminate_process():
    proc = Process(runpy('input()'))
    proc.start()
    proc.terminate()
    proc.join()
    assert proc.return_code != 0

def test_pipe_output():
    pi = Pipe()
    proc = Process(runpy(r'print("hello", end="\n")'), stdout=pi.side_in)
    proc.start()
    data = pi.side_out.readline()
    # Pipe is closed but process might still be live
    proc.join()  # Commenting this out causes data to be None?
    assert proc.return_code == 0
    assert data == b'hello\n'

def test_pipe_input():
    pi = Pipe()
    proc = Process(runpy(r'import sys; sys.exit(input() == "spam")'), stdin=pi.side_out)
    proc.start()
    pi.side_in.write(b"spam\n")
    pi.side_in.close()
    # Pipe is closed but process might still be live
    proc.join()
    assert proc.return_code == 1

def test_inner_pipe():
    pi = Pipe()
    prod = Process(runpy(r'print("eggs")'), stdout=pi.side_in)
    cons = Process(runpy(r'import sys; sys.exit(input() == "eggs")'), stdin=pi.side_out)
    prod.start()
    cons.start()
    prod.join()
    cons.join()
    assert prod.return_code == 0
    assert cons.return_code == 1

def test_inner_pipe_reversed_order():
    pi = Pipe()
    prod = Process(runpy(r'print("eggs")'), stdout=pi.side_in)
    cons = Process(runpy(r'import sys; sys.exit(input() == "eggs")'), stdin=pi.side_out)
    cons.start()
    prod.start()
    cons.join()
    prod.join()
    assert prod.return_code == 0
    assert cons.return_code == 1

def test_partial_output():
    pi = Pipe()
    proc = Process(
        runpy(r'print("foo", end="\n", flush=True); input(); print("bar", end="\n", flush=True)'),
        stdout=pi.side_in,
    )
    proc.start()
    data = pi.side_out.readline()
    proc.terminate()
    proc.join()
    assert data == b'foo\n'

def test_partial_buffered_output():
    pi = Pipe()
    proc = Process(
        runpy(r'print("foo", end="\n", flush=True); input(); print("bar", end="\n", flush=True)'),
        stdout=pi.side_in,
    )
    proc.start()
    proc.terminate()
    proc.join()
    data = pi.side_out.read(4)
    assert data == b'foo\n'
<commit_msg>Make sure that _all_ writers to the pipe are closed<commit_after>import sys
from slug import Process, Pipe

def runpy(code):
    return [sys.executable, '-c', code]

def test_successful_command():
    proc = Process(runpy('import sys; sys.exit(0)'))
    proc.start()
    proc.join()
    assert proc.return_code == 0

def test_failed_command():
    proc = Process(runpy('import sys; sys.exit(42)'))
    proc.start()
    proc.join()
    assert proc.return_code == 42

def test_kill_process():
    proc = Process(runpy('input()'))
    proc.start()
    proc.kill()
    assert proc.return_code != 0

def test_terminate_process():
    proc = Process(runpy('input()'))
    proc.start()
    proc.terminate()
    proc.join()
    assert proc.return_code != 0

def test_pipe_output():
    pi = Pipe()
    proc = Process(runpy(r'print("hello", end="\n")'), stdout=pi.side_in)
    proc.start()
    data = pi.side_out.readline()
    # Pipe is closed but process might still be live
    proc.join()  # Commenting this out causes data to be None?
    assert proc.return_code == 0
    assert data == b'hello\n'

def test_pipe_input():
    pi = Pipe()
    proc = Process(runpy(r'import sys; sys.exit(input() == "spam")'), stdin=pi.side_out)
    proc.start()
    pi.side_in.write(b"spam\n")
    pi.side_in.close()
    # Pipe is closed but process might still be live
    proc.join()
    assert proc.return_code == 1

def test_inner_pipe():
    pi = Pipe()
    prod = Process(runpy(r'print("eggs")'), stdout=pi.side_in)
    cons = Process(runpy(r'import sys; sys.exit(input() == "eggs")'), stdin=pi.side_out)
    prod.start()
    cons.start()
    prod.join()
    cons.join()
    assert prod.return_code == 0
    assert cons.return_code == 1

def test_inner_pipe_reversed_order():
    pi = Pipe()
    prod = Process(runpy(r'print("eggs")'), stdout=pi.side_in)
    cons = Process(runpy(r'import sys; sys.exit(input() == "eggs")'), stdin=pi.side_out)
    cons.start()
    prod.start()
    cons.join()
    prod.join()
    assert prod.return_code == 0
    assert cons.return_code == 1

def test_partial_output():
    pi = Pipe()
    proc = Process(
        runpy(r'print("foo", end="\n", flush=True); input(); print("bar", end="\n", flush=True)'),
        stdout=pi.side_in,
    )
    proc.start()
    data = pi.side_out.readline()
    proc.terminate()
    proc.join()
    assert data == b'foo\n'

def test_partial_buffered_output():
    pi = Pipe()
    proc = Process(
        runpy(r'print("foo", end="\n", flush=True); input(); print("bar", end="\n", flush=True)'),
        stdout=pi.side_in,
    )
    proc.start()
    pi.side_in.close()  # Remove our reference on this end of the pipe, now that the child has one
    proc.terminate()
    proc.join()
    data = pi.side_out.read()
    assert data == b'foo\n'
<|endoftext|>


<commit_before>#!/usr/bin/env python

import unittest

import mock
import requests

from den import propane

# pylint: disable=missing-docstring
# pylint: disable=protected-access
# pylint: disable=too-many-public-methods

class PropaneTestCase(unittest.TestCase):
    def test_get_api_url(self):
        expected = "https://data.tankutility.com/api"
        actual = propane._get_api_url()
        self.assertEqual(expected, actual)

        expected = "https://data.tankutility.com/api?token=token"
        actual = propane._get_api_url(token="token")
        self.assertEqual(expected, actual)

    @staticmethod
    def test_get_token():
        with mock.patch("den.propane._get_api_url", autospec=True) as url_mock, \
             mock.patch("den.propane.requests.get", autospec=True) as get_mock:
            propane._get_token("user", "password")
            url_mock.assert_called_once_with(path="getToken")
            get_mock.assert_called_once_with(
                url_mock(), auth=requests.auth.HTTPBasicAuth("user", "password"), verify=False)

    @staticmethod
    def test_get_devices():
        with mock.patch("den.propane._get_api_url", autospec=True) as url_mock, \
             mock.patch("den.propane.requests.get", autospec=True) as get_mock:
            propane._get_devices("token")
            url_mock.assert_called_once_with(token="token", path="devices")
            get_mock.assert_called_once_with(url_mock(), verify=False)

    @staticmethod
    def test_get_data():
        with mock.patch("den.propane._get_api_url", autospec=True) as url_mock, \
             mock.patch("den.propane.requests.get", autospec=True) as get_mock:
            propane._get_data("token", "device")
            url_mock.assert_called_once_with(token="token", path="devices/device")
            get_mock.assert_called_once_with(url_mock(), verify=False)

if __name__ == "__main__":
    unittest.main(verbosity=2)
<commit_msg>propane - Begin adding get points test.<commit_after>#!/usr/bin/env python

import unittest

import mock
import requests

from den import propane

# pylint: disable=missing-docstring
# pylint: disable=protected-access
# pylint: disable=too-many-public-methods

class PropaneTestCase(unittest.TestCase):
    def test_get_api_url(self):
        expected = "https://data.tankutility.com/api"
        actual = propane._get_api_url()
        self.assertEqual(expected, actual)

        expected = "https://data.tankutility.com/api?token=token"
        actual = propane._get_api_url(token="token")
        self.assertEqual(expected, actual)

    @staticmethod
    def test_get_token():
        with mock.patch("den.propane._get_api_url", autospec=True) as url_mock, \
             mock.patch("den.propane.requests.get", autospec=True) as get_mock:
            propane._get_token("user", "password")
            url_mock.assert_called_once_with(path="getToken")
            get_mock.assert_called_once_with(
                url_mock(), auth=requests.auth.HTTPBasicAuth("user", "password"), verify=False)

    @staticmethod
    def test_get_devices():
        with mock.patch("den.propane._get_api_url", autospec=True) as url_mock, \
             mock.patch("den.propane.requests.get", autospec=True) as get_mock:
            propane._get_devices("token")
            url_mock.assert_called_once_with(token="token", path="devices")
            get_mock.assert_called_once_with(url_mock(), verify=False)

    @staticmethod
    def test_get_data():
        with mock.patch("den.propane._get_api_url", autospec=True) as url_mock, \
             mock.patch("den.propane.requests.get", autospec=True) as get_mock:
            propane._get_data("token", "device")
            url_mock.assert_called_once_with(token="token", path="devices/device")
            get_mock.assert_called_once_with(url_mock(), verify=False)

    def test_points(self):
        with mock.patch("den.propane._get_devices", autospec=True) as devices_mock:
            propane._get_points("token", "device")
            devices_mock.assert_called_once_with("token")

if __name__ == "__main__":
    unittest.main(verbosity=2)
<|endoftext|>


<commit_before>import pytest
from dependencies import DependencyError, Injector, this
from helpers import CodeCollector

# Declarative attribute access.

def test_attribute_getter():
    """
    We can describe attribute access in the `Injector` in declarative
    manner.
    """

    class Foo(object):

        def __init__(self, one, two):
            self.one = one
            self.two = two

        def do(self):
            return self.one + self.two

    class Container(Injector):

        class SubContainer(Injector):
            foo = Foo
            one = 1
            two = 2

        foo = this.SubContainer.foo

    foo = Container.foo
    assert isinstance(foo, Foo)
    assert foo.do() == 3

def test_attribute_getter_few_attributes():
    """
    We resolve attribute access until we find all specified
    attributes.
    """

    class Foo(object):

        def __init__(self, one):
            self.one = one

    class Container(Injector):

        class SubContainer(Injector):
            foo = Foo
            one = 1

        foo = this.SubContainer.foo.one

    assert Container.foo == 1

parent_attr = CodeCollector()

@parent_attr.parametrize
def test_attribute_getter_parent_access(code):
    """We can access attribute of outer container."""

    Container = code()
    assert Container.SubContainer.bar == 1

@parent_attr
def ac7814095628():
    """Declarative injector."""

    class Container(Injector):
        foo = 1

        class SubContainer(Injector):
            bar = (this << 1).foo

    return Container

@parent_attr
def f607abc82079():
    """Let notation."""

    class OuterContainer(Injector):
        foo = 1

    class SubContainer(Injector):
        bar = (this << 1).foo

    Container = OuterContainer.let(SubContainer=SubContainer)

    return Container

few_parent_attr = CodeCollector()

@few_parent_attr.parametrize
def test_attribute_getter_few_parents(code):
    """We can access attribute of outer container in any nesting depth."""

    Container = code()
    assert Container.SubContainer.SubSubContainer.bar == 1

@few_parent_attr
def e477afc961b6():
    """Declarative injector."""

    class Container(Injector):
        foo = 1

        class SubContainer(Injector):

            class SubSubContainer(Injector):
                bar = (this << 2).foo

    return Container

@few_parent_attr
def c4ed4c61e154():
    """Let notation."""

    class OuterContainer(Injector):
        foo = 1

    class SubContainer(Injector):

        class SubSubContainer(Injector):
            bar = (this << 2).foo

    Container = OuterContainer.let(SubContainer=SubContainer)

    return Container

def test_one_subcontainer_multiple_parents():
    """
    Same sub container can be used in many parent containers.  This
    usage should not overlap those containers.
    """

    class SubContainer(Injector):
        foo = (this << 1).foo

    class Container1(Injector):
        foo = 1
        sub = SubContainer

    class Container2(Injector):
        foo = 2
        sub = SubContainer

    assert Container1.sub.foo == 1
    assert Container2.sub.foo == 2

item_access = CodeCollector()

@item_access.parametrize
def test_item_getter(code):
    """
    We can describe item access in the `Injector` in the
    declarative manner.
    """

    result = code()
    assert result == 1

@item_access
def ce642f492941():
    """Get item with string key."""

    class Container(Injector):
        foo = {'one': 1}
        one = this.foo['one']

    result = Container.one

    return result

@item_access
def ffa208dc1130():
    """Get items as many times as we want."""

    class Container(Injector):
        foo = {'one': {'two': 1}}
        two = this.foo['one']['two']

    result = Container.two

    return result

@item_access
def e5c358190fef():
    """Get item from the outer container."""

    class Container(Injector):
        foo = {'bar': {'baz': 1}}

        class SubContainer(Injector):
            spam = (this << 1).foo['bar']['baz']

    result = Container.SubContainer.spam

    return result

@item_access
def ab4cdbf60b2f():
    """Get item from the outer container of any depth level."""

    class Container(Injector):
        foo = {'bar': {'baz': 1}}

        class SubContainer(Injector):

            class SubSubContainer(Injector):
                spam = (this << 2).foo['bar']['baz']

    result = Container.SubContainer.SubSubContainer.spam

    return result

@item_access
def be332433b74d():
    """Get items from list."""

    class Container(Injector):
        foo = [1, 2, 3]
        bar = this.foo[0]

    result = Container.bar

    return result

@item_access
def fe150d5ebe93():
    """Get items from dict with digit keys."""

    class Container(Injector):
        foo = {2: 1}
        bar = this.foo[2]

    result = Container.bar

    return result

@item_access
def dc4fedcd09d8():
    """Get items from dict with tuple keys."""

    class Container(Injector):
        foo = {('x', 1): 1}
        bar = this.foo[('x', 1)]

    result = Container.bar

    return result

def test_item_getter_non_printable_key():
    """
    We can describe item access for keys which can't be presented as
    normal strings.
    """

    class Boom(object):

        def __init__(self, salt):
            self.salt = salt

        def __hash__(self):
            return hash(self.salt)

        def __str__(self):
            return "<boom>"

    boom = Boom('hello')

    class Container(Injector):
        foo = {boom: 1}
        bar = this.foo[boom]

    assert Container.bar == 1

def test_attribute_access_after_item_getter():
    """
    Check we can use attribute access notation after item getter
    notation.
    """

    class Foo(object):
        x = 1

    class Bar(object):
        y = {'foo': Foo}

    class Container(Injector):
        bar = Bar
        baz = this.bar.y['foo'].x

    assert Container.baz == 1

def test_docstrings():
    """Check we can access all API entry points documentation."""

    assert this.__doc__ == (
        'Declare attribute and item access during dependency injection.')

direct_proxy = CodeCollector()

@direct_proxy.parametrize
def test_deny_this_without_attribute_access(code):
    """
    `Thisable` instances can't be injected without pointing to any
    other attribute.
    """

    with pytest.raises(DependencyError) as exc_info:
        code()

    message = str(exc_info.value)
    assert message == "You can not use 'this' directly in the 'Injector'"

@direct_proxy
def b648b6f6a712():
    """Declarative injector."""

    class Container(Injector):
        foo = this

@direct_proxy
def c147d398f4be():
    """Declarative injector with parent access."""

    class Container(Injector):
        foo = (this << 1)

@direct_proxy
def a37783b6d1ad():
    """Let notation."""

    Injector.let(foo=this)

@direct_proxy
def bd05271fb831():
    """Let notation with parent access."""

    Injector.let(foo=(this << 1))

def test_this_deny_non_integers():
    """We can't shift `this` with non number argument."""

    with pytest.raises(ValueError) as exc_info:
        this << 'boom'

    assert str(exc_info.value) == 'Positive integer argument is required'

negative_integers = CodeCollector()

@negative_integers.parametrize
def test_this_deny_negative_integers(code):
    """We can't shift `this` with negative integer."""

    with pytest.raises(ValueError) as exc_info:
        code()

    assert str(exc_info.value) == 'Positive integer argument is required'

@negative_integers
def xsJWb2lx6EMs():
    """Minus one."""

    this << -1

@negative_integers
def nvm3ybp98vGm():
    """Zero."""

    this << 0

too_many = CodeCollector()

@too_many.parametrize
def test_require_more_parents_that_injector_actually_has(code):
    """
    If we shift more that container levels available, we should
    provide meaningful message to user.
    """

    with pytest.raises(DependencyError) as exc_info:
        code()

    assert str(exc_info.value) == ('You tries to shift this more '
                                   'times that Injector has levels')

@too_many
def s6lduD7BJpxW():
    """Declarative Injector."""

    class Container(Injector):

        foo = (this << 1).foo

    Container.foo

@too_many
def bUICVObtDZ4I():
    """Declarative Injected with nested layer."""

    class Container(Injector):

        class SubContainer(Injector):

            foo = (this << 2).foo

    Container.SubContainer.foo

@too_many
def ww6xNI4YrNr6():
    """Let notation."""

    Injector.let(foo=(this << 1).foo).foo

@too_many
def rN3suiVzhqMM():
    """Let notation with nested layer."""

    Injector.let(
        SubContainer=Injector.let(foo=(this << 2).foo),
    ).SubContainer.foo

attribute_error = CodeCollector()

@attribute_error.parametrize
def test_attribute_error_on_parent_access(code):
    """
    We should raise `AttributeError` if we have correct number of
    parents but specify wrong attribute name.
    """

    with pytest.raises(AttributeError) as exc_info:
        code()

    assert str(exc_info.value) in set([
        "'Container' object has no attribute 'bar'",
        "'Injector' object has no attribute 'bar'",
    ])

@attribute_error
def t1jn9RI9v42t():
    """Declarative Injector."""

    class Container(Injector):

        foo = this.bar

    Container.foo

@attribute_error
def yOEj1qQfsXHy():
    """Declarative Injected with nested layer."""

    class Container(Injector):

        class SubContainer(Injector):

            foo = (this << 1).bar

    Container.SubContainer.foo

@attribute_error
def vnmkIELBH3MN():
    """Let notation."""

    Injector.let(foo=this.bar).foo

@attribute_error
def pG9M52ZRQr2S():
    """Let notation with nested layer."""

    Injector.let(
        SubContainer=Injector.let(foo=(this << 1).bar),
    ).SubContainer.foo

circle_links = CodeCollector()

@pytest.mark.xfail  # FIXME: Make this work.
@circle_links.parametrize
def test_circle_links(code):
    """
    We can detect loops in the container hierarchy which will trigger
    recursion during injection process.  We need to raise
    `DependencyError` the same way we do with circle dependencies.
    """

    with pytest.raises(DependencyError) as exc_info:
        code()

    assert str(exc_info.value) in set([
        "'foo' is a circle link in the 'Container' injector",
        "'foo' is a circle link in the 'Injector' injector",
    ])

@circle_links
def kSSnnkw6CNPx():
    """Declarative injector.  Same level."""

    class Container(Injector):

        foo = this.foo

    Container.foo

@circle_links
def ai0hNOPGX2PI():
    """Declarative injector.  Same level.  Long loop."""

    class Container(Injector):

        foo = this.bar
        bar = this.baz
        baz = this.foo

    Container.foo

@circle_links
def n8NHZqiZN43Q():
    """Let notation.  Same level."""

    Injector.let(foo=this.foo).foo

@circle_links
def ySnRrxW6M79T():
    """Let notation.  Same level.  Long loop."""

    Injector.let(foo=this.bar, bar=this.baz, baz=this.foo).foo

@circle_links
def eaK6IxW88SNh():
    """Declarative injector.  One level deep."""

    class Container(Injector):

        foo = this.SubContainer.bar

        class SubContainer(Injector):

            bar = (this << 1).foo

    Container.foo

@circle_links
def nWhKtJb16yg6():
    """Let notation.  One level deep."""

    Injector.let(
        foo=this.SubContainer.bar,
        SubContainer=Injector.let(bar=(this << 1).foo),
    ).foo

@circle_links
def mF4akoHlg84C():
    """Declarative injector.  Two level deep."""

    class Container(Injector):

        foo = this.SubContainer.SubSubContainer.bar

        class SubContainer(Injector):

            class SubSubContainer(Injector):

                bar = (this << 2).foo

    Container.foo

@circle_links
def bCw8LPUeVK6J():
    """Let notation.  Two level deep."""

    Injector.let(
        foo=this.SubContainer.SubSubContainer.bar,
        SubContainer=Injector.let(
            SubSubContainer=Injector.let(
                bar=(this << 2).foo,
            ),
        ),
    ).foo

@circle_links
def eHyErh9kExHG():
    """Declarative injector.  Two level deep.  Each level has link."""

    class Container(Injector):

        foo = this.SubContainer.bar

        class SubContainer(Injector):

            bar = this.SubSubContainer.baz

            class SubSubContainer(Injector):

                baz = (this << 2).foo

    Container.foo

@circle_links
def q0KytyVbE2XA():
    """Let notation.  Two level deep.  Each level has link."""

    Injector.let(
        foo=this.SubContainer.bar,
        SubContainer=Injector.let(
            bar=this.SubSubContainer.baz,
            SubSubContainer=Injector.let(
                baz=(this << 2).foo,
            ),
        ),
    ).foo

@circle_links
def vAyZepNGAUjY():
    """Declarative injector.  Cross injector links."""

    class Container(Injector):

        class SubContainer1(Injector):

            bar = (this << 1).SubContainer2.baz

        class SubContainer2(Injector):

            baz = (this << 1).SubContainer1.bar

    Container.SubContainer1.bar

@circle_links
def bLRoCCj9uNOp():
    """Let notation.  Cross injector links."""

    Injector.let(
        SubContainer1=Injector.let(
            bar=(this << 1).SubContainer2.baz,
        ),
        SubContainer2=Injector.let(
            baz=(this << 1).SubContainer1.bar,
        ),
    ).SubContainer1.bar

@circle_links
def oClqGRmWJAkA():
    """Declarative injector.  Over item access."""

    class Container(Injector):

        class SubContainer(Injector):

            foo = (this << 1).bar['sub'].foo

        bar = {'sub': SubContainer}

    Container.SubContainer.foo

@circle_links
def t41yMywZuPhA():
    """Let notation.  Over item access."""

    SubContainer = Injector.let(foo=(this << 1).bar['sub'].foo)
    Injector.let(
        SubContainer=SubContainer,
        bar={'sub': SubContainer},
    ).SubContainer.foo
<commit_msg>Add failed test to protect circle links lookup from false positives.<commit_after>import pytest
from dependencies import DependencyError, Injector, this
from helpers import CodeCollector

# Declarative attribute access.

def test_attribute_getter():
    """
    We can describe attribute access in the `Injector` in declarative
    manner.
    """

    class Foo(object):

        def __init__(self, one, two):
            self.one = one
            self.two = two

        def do(self):
            return self.one + self.two

    class Container(Injector):

        class SubContainer(Injector):
            foo = Foo
            one = 1
            two = 2

        foo = this.SubContainer.foo

    foo = Container.foo
    assert isinstance(foo, Foo)
    assert foo.do() == 3

def test_attribute_getter_few_attributes():
    """
    We resolve attribute access until we find all specified
    attributes.
    """

    class Foo(object):

        def __init__(self, one):
            self.one = one

    class Container(Injector):

        class SubContainer(Injector):
            foo = Foo
            one = 1

        foo = this.SubContainer.foo.one

    assert Container.foo == 1

parent_attr = CodeCollector()

@parent_attr.parametrize
def test_attribute_getter_parent_access(code):
    """We can access attribute of outer container."""

    Container = code()
    assert Container.SubContainer.bar == 1

@parent_attr
def ac7814095628():
    """Declarative injector."""

    class Container(Injector):
        foo = 1

        class SubContainer(Injector):
            bar = (this << 1).foo

    return Container

@parent_attr
def f607abc82079():
    """Let notation."""

    class OuterContainer(Injector):
        foo = 1

    class SubContainer(Injector):
        bar = (this << 1).foo

    Container = OuterContainer.let(SubContainer=SubContainer)

    return Container

few_parent_attr = CodeCollector()

@few_parent_attr.parametrize
def test_attribute_getter_few_parents(code):
    """We can access attribute of outer container in any nesting depth."""

    Container = code()
    assert Container.SubContainer.SubSubContainer.bar == 1

@few_parent_attr
def e477afc961b6():
    """Declarative injector."""

    class Container(Injector):
        foo = 1

        class SubContainer(Injector):

            class SubSubContainer(Injector):
                bar = (this << 2).foo

    return Container

@few_parent_attr
def c4ed4c61e154():
    """Let notation."""

    class OuterContainer(Injector):
        foo = 1

    class SubContainer(Injector):

        class SubSubContainer(Injector):
            bar = (this << 2).foo

    Container = OuterContainer.let(SubContainer=SubContainer)

    return Container

def test_one_subcontainer_multiple_parents():
    """
    Same sub container can be used in many parent containers.  This
    usage should not overlap those containers.
    """

    class SubContainer(Injector):
        bar = (this << 1).foo

    class Container1(Injector):
        foo = 1
        sub = SubContainer

    class Container2(Injector):
        foo = 2
        sub = SubContainer

    assert Container1.sub.bar == 1
    assert Container2.sub.bar == 2

item_access = CodeCollector()

@item_access.parametrize
def test_item_getter(code):
    """
    We can describe item access in the `Injector` in the
    declarative manner.
    """

    result = code()
    assert result == 1

@item_access
def ce642f492941():
    """Get item with string key."""

    class Container(Injector):
        foo = {'one': 1}
        one = this.foo['one']

    result = Container.one

    return result

@item_access
def ffa208dc1130():
    """Get items as many times as we want."""

    class Container(Injector):
        foo = {'one': {'two': 1}}
        two = this.foo['one']['two']

    result = Container.two

    return result

@item_access
def e5c358190fef():
    """Get item from the outer container."""

    class Container(Injector):
        foo = {'bar': {'baz': 1}}

        class SubContainer(Injector):
            spam = (this << 1).foo['bar']['baz']

    result = Container.SubContainer.spam

    return result

@item_access
def ab4cdbf60b2f():
    """Get item from the outer container of any depth level."""

    class Container(Injector):
        foo = {'bar': {'baz': 1}}

        class SubContainer(Injector):

            class SubSubContainer(Injector):
                spam = (this << 2).foo['bar']['baz']

    result = Container.SubContainer.SubSubContainer.spam

    return result

@item_access
def be332433b74d():
    """Get items from list."""

    class Container(Injector):
        foo = [1, 2, 3]
        bar = this.foo[0]

    result = Container.bar

    return result

@item_access
def fe150d5ebe93():
    """Get items from dict with digit keys."""

    class Container(Injector):
        foo = {2: 1}
        bar = this.foo[2]

    result = Container.bar

    return result

@item_access
def dc4fedcd09d8():
    """Get items from dict with tuple keys."""

    class Container(Injector):
        foo = {('x', 1): 1}
        bar = this.foo[('x', 1)]

    result = Container.bar

    return result

def test_item_getter_non_printable_key():
    """
    We can describe item access for keys which can't be presented as
    normal strings.
    """

    class Boom(object):

        def __init__(self, salt):
            self.salt = salt

        def __hash__(self):
            return hash(self.salt)

        def __str__(self):
            return "<boom>"

    boom = Boom('hello')

    class Container(Injector):
        foo = {boom: 1}
        bar = this.foo[boom]

    assert Container.bar == 1

def test_attribute_access_after_item_getter():
    """
    Check we can use attribute access notation after item getter
    notation.
    """

    class Foo(object):
        x = 1

    class Bar(object):
        y = {'foo': Foo}

    class Container(Injector):
        bar = Bar
        baz = this.bar.y['foo'].x

    assert Container.baz == 1

def test_docstrings():
    """Check we can access all API entry points documentation."""

    assert this.__doc__ == (
        'Declare attribute and item access during dependency injection.')

direct_proxy = CodeCollector()

@direct_proxy.parametrize
def test_deny_this_without_attribute_access(code):
    """
    `Thisable` instances can't be injected without pointing to any
    other attribute.
    """

    with pytest.raises(DependencyError) as exc_info:
        code()

    message = str(exc_info.value)
    assert message == "You can not use 'this' directly in the 'Injector'"

@direct_proxy
def b648b6f6a712():
    """Declarative injector."""

    class Container(Injector):
        foo = this

@direct_proxy
def c147d398f4be():
    """Declarative injector with parent access."""

    class Container(Injector):
        foo = (this << 1)

@direct_proxy
def a37783b6d1ad():
    """Let notation."""

    Injector.let(foo=this)

@direct_proxy
def bd05271fb831():
    """Let notation with parent access."""

    Injector.let(foo=(this << 1))

def test_this_deny_non_integers():
    """We can't shift `this` with non number argument."""

    with pytest.raises(ValueError) as exc_info:
        this << 'boom'

    assert str(exc_info.value) == 'Positive integer argument is required'

negative_integers = CodeCollector()

@negative_integers.parametrize
def test_this_deny_negative_integers(code):
    """We can't shift `this` with negative integer."""

    with pytest.raises(ValueError) as exc_info:
        code()

    assert str(exc_info.value) == 'Positive integer argument is required'

@negative_integers
def xsJWb2lx6EMs():
    """Minus one."""

    this << -1

@negative_integers
def nvm3ybp98vGm():
    """Zero."""

    this << 0

too_many = CodeCollector()

@too_many.parametrize
def test_require_more_parents_that_injector_actually_has(code):
    """
    If we shift more that container levels available, we should
    provide meaningful message to user.
    """

    with pytest.raises(DependencyError) as exc_info:
        code()

    assert str(exc_info.value) == ('You tries to shift this more '
                                   'times that Injector has levels')

@too_many
def s6lduD7BJpxW():
    """Declarative Injector."""

    class Container(Injector):

        foo = (this << 1).bar

    Container.foo

@too_many
def bUICVObtDZ4I():
    """Declarative Injected with nested layer."""

    class Container(Injector):

        class SubContainer(Injector):

            foo = (this << 2).bar

    Container.SubContainer.foo

@too_many
def ww6xNI4YrNr6():
    """Let notation."""

    Injector.let(foo=(this << 1).bar).foo

@too_many
def rN3suiVzhqMM():
    """Let notation with nested layer."""

    Injector.let(
        SubContainer=Injector.let(foo=(this << 2).bar),
    ).SubContainer.foo

attribute_error = CodeCollector()

@attribute_error.parametrize
def test_attribute_error_on_parent_access(code):
    """
    We should raise `AttributeError` if we have correct number of
    parents but specify wrong attribute name.
    """

    with pytest.raises(AttributeError) as exc_info:
        code()

    assert str(exc_info.value) in set([
        "'Container' object has no attribute 'bar'",
        "'Injector' object has no attribute 'bar'",
    ])

@attribute_error
def t1jn9RI9v42t():
    """Declarative Injector."""

    class Container(Injector):

        foo = this.bar

    Container.foo

@attribute_error
def yOEj1qQfsXHy():
    """Declarative Injected with nested layer."""

    class Container(Injector):

        class SubContainer(Injector):

            foo = (this << 1).bar

    Container.SubContainer.foo

@attribute_error
def vnmkIELBH3MN():
    """Let notation."""

    Injector.let(foo=this.bar).foo

@attribute_error
def pG9M52ZRQr2S():
    """Let notation with nested layer."""

    Injector.let(
        SubContainer=Injector.let(foo=(this << 1).bar),
    ).SubContainer.foo

circle_links = CodeCollector()

@pytest.mark.xfail  # FIXME: Make this work.
@circle_links.parametrize
def test_circle_links(code):
    """
    We can detect loops in the container hierarchy which will trigger
    recursion during injection process.  We need to raise
    `DependencyError` the same way we do with circle dependencies.
    """

    with pytest.raises(DependencyError) as exc_info:
        code()

    assert str(exc_info.value) in set([
        "'foo' is a circle link in the 'Container' injector",
        "'foo' is a circle link in the 'Injector' injector",
    ])

@circle_links
def kSSnnkw6CNPx():
    """Declarative injector.  Same level."""

    class Container(Injector):

        foo = this.foo

    Container.foo

@circle_links
def ai0hNOPGX2PI():
    """Declarative injector.  Same level.  Long loop."""

    class Container(Injector):

        foo = this.bar
        bar = this.baz
        baz = this.foo

    Container.foo

@circle_links
def n8NHZqiZN43Q():
    """Let notation.  Same level."""

    Injector.let(foo=this.foo).foo

@circle_links
def ySnRrxW6M79T():
    """Let notation.  Same level.  Long loop."""

    Injector.let(foo=this.bar, bar=this.baz, baz=this.foo).foo

@circle_links
def eaK6IxW88SNh():
    """Declarative injector.  One level deep."""

    class Container(Injector):

        foo = this.SubContainer.bar

        class SubContainer(Injector):

            bar = (this << 1).foo

    Container.foo

@circle_links
def nWhKtJb16yg6():
    """Let notation.  One level deep."""

    Injector.let(
        foo=this.SubContainer.bar,
        SubContainer=Injector.let(bar=(this << 1).foo),
    ).foo

@circle_links
def mF4akoHlg84C():
    """Declarative injector.  Two level deep."""

    class Container(Injector):

        foo = this.SubContainer.SubSubContainer.bar

        class SubContainer(Injector):

            class SubSubContainer(Injector):

                bar = (this << 2).foo

    Container.foo

@circle_links
def bCw8LPUeVK6J():
    """Let notation.  Two level deep."""

    Injector.let(
        foo=this.SubContainer.SubSubContainer.bar,
        SubContainer=Injector.let(
            SubSubContainer=Injector.let(
                bar=(this << 2).foo,
            ),
        ),
    ).foo

@circle_links
def eHyErh9kExHG():
    """Declarative injector.  Two level deep.  Each level has link."""

    class Container(Injector):

        foo = this.SubContainer.bar

        class SubContainer(Injector):

            bar = this.SubSubContainer.baz

            class SubSubContainer(Injector):

                baz = (this << 2).foo

    Container.foo

@circle_links
def q0KytyVbE2XA():
    """Let notation.  Two level deep.  Each level has link."""

    Injector.let(
        foo=this.SubContainer.bar,
        SubContainer=Injector.let(
            bar=this.SubSubContainer.baz,
            SubSubContainer=Injector.let(
                baz=(this << 2).foo,
            ),
        ),
    ).foo

@circle_links
def vAyZepNGAUjY():
    """Declarative injector.  Cross injector links."""

    class Container(Injector):

        class SubContainer1(Injector):

            bar = (this << 1).SubContainer2.baz

        class SubContainer2(Injector):

            baz = (this << 1).SubContainer1.bar

    Container.SubContainer1.bar

@circle_links
def bLRoCCj9uNOp():
    """Let notation.  Cross injector links."""

    Injector.let(
        SubContainer1=Injector.let(
            bar=(this << 1).SubContainer2.baz,
        ),
        SubContainer2=Injector.let(
            baz=(this << 1).SubContainer1.bar,
        ),
    ).SubContainer1.bar

@circle_links
def oClqGRmWJAkA():
    """Declarative injector.  Over item access."""

    class Container(Injector):

        class SubContainer(Injector):

            foo = (this << 1).bar['sub'].foo

        bar = {'sub': SubContainer}

    Container.SubContainer.foo

@circle_links
def t41yMywZuPhA():
    """Let notation.  Over item access."""

    SubContainer = Injector.let(foo=(this << 1).bar['sub'].foo)
    Injector.let(
        SubContainer=SubContainer,
        bar={'sub': SubContainer},
    ).SubContainer.foo

@pytest.mark.xfail  # FIXME: Make this work.
def test_false_positive_loop_lookup_protection():
    """
    We should allow proxy to point to objects with the same name if it
    does not create a circle link.
    """

    class Container(Injector):

        foo = this.SubContainer.baz

        class SubContainer(Injector):

            baz = this.foo
            foo = 1

    assert Container.foo == 1
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
test_pycrawl
----------------------------------

Tests for `pycrawl` module.
"""

import errno
from functools import wraps
import os
import re
import shutil
import time
import unittest
try:  # Python 3
    from urllib.parse import urlparse
except:  # Python 2
    from urlparse import urlparse

import bs4

from tests.run_server import run_server, stop_server
from pycrawl import pycrawl

def run_main_with_url(url):
    """Run main on the given URL, then delete the downloaded files after."""
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            argv = ['pycrawl.py', url]
            try:
                pycrawl.main(argv)
                f(*args, **kwargs)
            finally:
                # remove created download dir
                try:
                    basename = urlparse(url).hostname
                    shutil.rmtree(basename)
                except OSError as e:
                    if e.errno == errno.ENOENT:
                        pass
                    else:
                        raise
        return wrapper
    return decorator

class TestPycrawl(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.server = run_server()
        time.sleep(0.5)  # give it time to start up

    @classmethod
    def tearDownClass(cls):
        stop_server(cls.server)

    def setUp(self):
        pass

    def tearDown(self):
        pass

    @run_main_with_url('http://nonexistentsite.nes/badpath')
    def test_nonexistent_url(self):
        self.assertFalse(
            os.path.isfile('nonexistentsite.nes/badpath'))

    @run_main_with_url('http://localhost:8000/Python_logo_100x100.jpg')
    def test_non_html_url(self):
        self.assertTrue(os.path.isfile('localhost/Python_logo_100x100.jpg'))

    @run_main_with_url('http://localhost:8000')
    def test_valid_url_downloads_site(self):
        with open('localhost/__root__') as f:
            root = bs4.BeautifulSoup(f)
        self.assertTrue(bool(root.find(text=re.compile("Text"))))
        # now try a page linked from the first
        with open('localhost/local-relative.html') as f:
            relative = bs4.BeautifulSoup(f)
        self.assertTrue(bool(relative.find(text=re.compile("relative"))))
        # and one linked via an explicit local domain
        with open('localhost/local-explicit.html') as f:
            explicit = bs4.BeautifulSoup(f)
        self.assertTrue(bool(explicit.find(text=re.compile("explicit"))))

if __name__ == '__main__':
    unittest.main()
<commit_msg>add failing test for URL updates in content<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
test_pycrawl
----------------------------------

Tests for `pycrawl` module.
"""

import errno
from functools import wraps
import os
import re
import shutil
import time
import unittest
try:  # Python 3
    from urllib.parse import urlparse
except:  # Python 2
    from urlparse import urlparse

import bs4

from tests.run_server import run_server, stop_server
from pycrawl import pycrawl

def run_main_with_url(url):
    """Run main on the given URL, then delete the downloaded files after."""
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            argv = ['pycrawl.py', url]
            try:
                pycrawl.main(argv)
                f(*args, **kwargs)
            finally:
                # remove created download dir
                try:
                    basename = urlparse(url).hostname
                    shutil.rmtree(basename)
                except OSError as e:
                    if e.errno == errno.ENOENT:
                        pass
                    else:
                        raise
        return wrapper
    return decorator

class TestPycrawl(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.server = run_server()
        time.sleep(0.5)  # give it time to start up

    @classmethod
    def tearDownClass(cls):
        stop_server(cls.server)

    def setUp(self):
        pass

    def tearDown(self):
        pass

    @run_main_with_url('http://nonexistentsite.nes/badpath')
    def test_nonexistent_url(self):
        self.assertFalse(
            os.path.isfile('nonexistentsite.nes/badpath'))

    @run_main_with_url('http://localhost:8000/Python_logo_100x100.jpg')
    def test_non_html_url(self):
        self.assertTrue(os.path.isfile('localhost/Python_logo_100x100.jpg'))

    @run_main_with_url('http://localhost:8000')
    def test_valid_url_downloads_site(self):
        with open('localhost/__root__') as f:
            root = bs4.BeautifulSoup(f)
        self.assertTrue(bool(root.find(text=re.compile("Text"))))
        rel_link = root.find('a', text=re.compile("relative"))['href']
        self.assertIsNone(urlparse(rel_link).hostname,
                          "relative links are unchanged")
        abs_link = root.find('a', text=re.compile("explicit"))['href']
        self.assertIsNone(urlparse(abs_link).hostname,
                          "absolute local links are changed to relative")

        # now try a page linked from the first
        with open('localhost/local-relative.html') as f:
            relative = bs4.BeautifulSoup(f)
        self.assertTrue(bool(relative.find(text=re.compile("relative"))))

        # and one linked via an explicit local domain
        with open('localhost/local-explicit.html') as f:
            explicit = bs4.BeautifulSoup(f)
        self.assertTrue(bool(explicit.find(text=re.compile("explicit"))))

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


<commit_before>import unittest
import httpretty
import factory
import json

from endpoints import AlgoliaEndpoint
from hnscraper import Scraper, TooManyItemsException

class ItemFactory(factory.Factory):
  FACTORY_FOR = dict
  objectID = 21
  created_at_i = 42
  title = "Test item"

class ResponseFactory(factory.Factory):
  FACTORY_FOR = dict

  nbPages = 0

  hits = [ItemFactory(), ItemFactory()]
  nbHits = factory.LazyAttribute(lambda x: x.nbPages * len(x.hits))
  hitsPerPage = factory.LazyAttribute(lambda x: len(x.hits))

class TestScraper(unittest.TestCase):

  @httpretty.activate
  def test_scrape(self):
    hits = [ItemFactory(), ItemFactory()]

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=hits),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertListEqual(hits, resp)

  @httpretty.activate
  def test_scrape_generator(self):
    hits = [ItemFactory(), ItemFactory()]

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(pages=2, hits=hits),
                           content_type="application/json")

    gen = Scraper().scrape(tag="test", since=42)
    resp = gen.next()
    self.assertEqual(resp, hits[0])
    resp = gen.next()
    self.assertEqual(resp, hits[1])

  @httpretty.activate
  def test_scrape_all_fields_are_returned(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=[item]),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertItemsEqual(resp[0].keys(),
        ["objectID", "created_at_i", "title"])

  @httpretty.activate
  def test_scrape_translate_fields(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=[item]),
                           content_type="application/json")

    fields = {
        "test": "created_at_i"
    }

    resp = list(Scraper().scrape(tag="test", since=42, fields=fields))
    self.assertItemsEqual(resp[0].keys(), ["test"])

  @httpretty.activate
  def test_scrape_translate_invalid_field(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=[item]),
                           content_type="application/json")

    fields = {
        "test": "missing"
    }

    resp = list(Scraper().scrape(tag="test", since=42, fields=fields))
    self.assertItemsEqual(resp[0].keys(), [])

  @httpretty.activate
  def test_scrape_multiple_pages(self):
    PAGES = 2

    hits = [ItemFactory(), ItemFactory()]

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(PAGES, hits),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertListEqual(hits * PAGES, resp)

  @httpretty.activate
  def test_scrape_page_limit(self):
    hits = [ItemFactory(), ItemFactory()]
    pages= [httpretty.Response(body=json.dumps(
            ResponseFactory(hits=hits)
        ))
    ]

    lastPage = ResponseFactory()
    # Trick the scraper in thinking it reached the last page but there are more
    # items available.
    lastPage["nbHits"] = 3
    lastPage["hits"] = []  # this needs to be empty

    pages.append(httpretty.Response(body=json.dumps(lastPage)))

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=pages,
                           content_type="application/json")

    with self.assertRaises(TooManyItemsException):
      resp = list(Scraper().scrape(tag="test", since=42))

  @httpretty.activate
  def test_scrape_no_items(self):
    lastPage = ResponseFactory()
    lastPage["nbHits"] = 0
    lastPage["hits"] = []

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           body=json.dumps(lastPage),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertListEqual(resp, [])

  def _createPages(self, pages=1, hits=None):
    resp = [
        httpretty.Response(body=json.dumps(
            ResponseFactory(pages=pages, hits=hits)
        ))
    ] * pages

    lastPage = ResponseFactory()
    lastPage["nbHits"] = 0
    lastPage["hits"] = []

    resp.append(httpretty.Response(body=json.dumps(lastPage)))

    return resp

<commit_msg>Add test_scrape_correct_request test.<commit_after>import unittest
import httpretty
import factory
import json

from endpoints import AlgoliaEndpoint
from hnscraper import Scraper, TooManyItemsException

class ItemFactory(factory.Factory):
  FACTORY_FOR = dict
  objectID = 21
  created_at_i = 42
  title = "Test item"

class ResponseFactory(factory.Factory):
  FACTORY_FOR = dict

  nbPages = 0

  hits = [ItemFactory(), ItemFactory()]
  nbHits = factory.LazyAttribute(lambda x: x.nbPages * len(x.hits))
  hitsPerPage = factory.LazyAttribute(lambda x: len(x.hits))

class TestScraper(unittest.TestCase):

  @httpretty.activate
  def test_scrape(self):
    hits = [ItemFactory(), ItemFactory()]

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=hits),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertListEqual(hits, resp)

  @httpretty.activate
  def test_scrape_generator(self):
    hits = [ItemFactory(), ItemFactory()]

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(pages=2, hits=hits),
                           content_type="application/json")

    gen = Scraper().scrape(tag="test", since=42)
    resp = gen.next()
    self.assertEqual(resp, hits[0])
    resp = gen.next()
    self.assertEqual(resp, hits[1])

  @httpretty.activate
  def test_scrape_all_fields_are_returned(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=[item]),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertItemsEqual(resp[0].keys(),
        ["objectID", "created_at_i", "title"])

  @httpretty.activate
  def test_scrape_translate_fields(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=[item]),
                           content_type="application/json")

    fields = {
        "test": "created_at_i"
    }

    resp = list(Scraper().scrape(tag="test", since=42, fields=fields))
    self.assertItemsEqual(resp[0].keys(), ["test"])

  @httpretty.activate
  def test_scrape_translate_invalid_field(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(hits=[item]),
                           content_type="application/json")

    fields = {
        "test": "missing"
    }

    resp = list(Scraper().scrape(tag="test", since=42, fields=fields))
    self.assertItemsEqual(resp[0].keys(), [])

  @httpretty.activate
  def test_scrape_multiple_pages(self):
    PAGES = 2

    hits = [ItemFactory(), ItemFactory()]

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(PAGES, hits),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertListEqual(hits * PAGES, resp)

  @httpretty.activate
  def test_scrape_page_limit(self):
    hits = [ItemFactory(), ItemFactory()]
    pages= [httpretty.Response(body=json.dumps(
            ResponseFactory(hits=hits)
        ))
    ]

    lastPage = ResponseFactory()
    # Trick the scraper in thinking it reached the last page but there are more
    # items available.
    lastPage["nbHits"] = 3
    lastPage["hits"] = []  # this needs to be empty

    pages.append(httpretty.Response(body=json.dumps(lastPage)))

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=pages,
                           content_type="application/json")

    with self.assertRaises(TooManyItemsException):
      resp = list(Scraper().scrape(tag="test", since=42))

  @httpretty.activate
  def test_scrape_no_items(self):
    lastPage = ResponseFactory()
    lastPage["nbHits"] = 0
    lastPage["hits"] = []

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           body=json.dumps(lastPage),
                           content_type="application/json")

    resp = list(Scraper().scrape(tag="test", since=42))
    self.assertListEqual(resp, [])

  @httpretty.activate
  def test_scrape_correct_request(self):
    item = ItemFactory()

    httpretty.register_uri(httpretty.GET, AlgoliaEndpoint.URL,
                           responses=self._createPages(pages=2, hits=[item]),
                           content_type="application/json")

    gen = Scraper().scrape(tag="test", since=42, until=43)

    gen.next()
    self.assertDictEqual(httpretty.last_request().querystring,
        {
          "numericFilters": ["created_at_i>42,created_at_i<43"],
          "tags": ["test"],
          "page": ["0"]
        }
    )

    gen.next()
    self.assertDictEqual(httpretty.last_request().querystring,
        {
          "numericFilters": ["created_at_i>42,created_at_i<43"],
          "tags": ["test"],
          "page": ["1"]
        }
    )

  def _createPages(self, pages=1, hits=None):
    resp = [
        httpretty.Response(body=json.dumps(
            ResponseFactory(pages=pages, hits=hits)
        ))
    ] * pages

    lastPage = ResponseFactory()
    lastPage["nbHits"] = 0
    lastPage["hits"] = []

    resp.append(httpretty.Response(body=json.dumps(lastPage)))

    return resp

<|endoftext|>


<commit_before>import tests.fixtures
from datetime import datetime
from piot.model import AnalogSensorReading
from piot.model import DigitalSensorReading
from piot.model import NotificationEvent
from piot.model import SensorReading
from piot.sensor.base import BaseAnalogSensor, BaseDigitalSensor
from piot.service import NotificationPersistenceService
from piot.service import SensorReadingPersistenceService
from piot.service import transaction_scope
from tests.fixtures import sensormodule, sqlalchemy
from tests import mocks
from unittest.mock import MagicMock

class TestBaseSQLAlchemyService:
    def test_all_returns_all_model_objects(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)

        reading2 = AnalogSensorReading()
        service.create(reading2)

        assert len(service.all()) == 2

    def test_get_returns_single_model_object(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)

        reading2 = AnalogSensorReading()
        service.create(reading2)

        assert service.get(reading.id).id == reading.id

    def test_update_updates_single_model_object(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        original_reading = AnalogSensorReading()
        original_reading.name = 'original'
        original_reading.value = 1
        original_reading.unit = 'original'

        service.create(original_reading)

        persisted_reading = service.get(original_reading.id)

        persisted_reading.name = 'new'
        persisted_reading.value = 2
        persisted_reading.unit = 'new'

        service.update(persisted_reading)

        updated_reading = service.get(original_reading.id)

        assert updated_reading.name == 'new'
        assert updated_reading.value == 2
        assert updated_reading.unit == 'new'

    def test_delete_deletes_single_model_object(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)
        service.delete(reading)

        assert len(service.all()) == 0

class TestNotificationPersistenceService:
    def test_get_newest_notification_returns_newest_record(self, sqlalchemy):
        sensor_service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        reading.name = 'Test Sensor'
        sensor_service.create(reading)

        old_notification = NotificationEvent()
        old_notification.sensor_id = reading.id
        old_notification.timestamp = datetime(2015, 1, 1)

        new_notification = NotificationEvent()
        new_notification.sensor_id = reading.id
        new_notification.timestamp = datetime(2016, 1, 1)

        service = NotificationPersistenceService()

        # Insert in reverse order to confirm order by
        service.create(new_notification)
        service.create(old_notification)

        newest_timestamp = new_notification.timestamp
        db_timestamp = service.get_newest_notification(reading.name).timestamp

        assert db_timestamp == newest_timestamp

    def test_get_newest_notification_excludes_other_sensors(self, sqlalchemy):
        reading_service = SensorReadingPersistenceService()
        notification_service = NotificationPersistenceService()

        included_sensor = DigitalSensorReading()
        included_sensor.name = 'included'
        reading_service.create(included_sensor)

        excluded_sensor = DigitalSensorReading()
        excluded_sensor.name = 'excluded'
        reading_service.create(excluded_sensor)

        excluded_notification = NotificationEvent()
        excluded_notification.sensor_id = excluded_sensor.id
        excluded_notification.timestamp = datetime(2016, 1, 1)
        notification_service.create(excluded_notification)

        included_notification = NotificationEvent()
        included_notification.sensor_id = included_sensor.id
        included_notification.timestamp = datetime(2015, 1, 1)
        notification_service.create(included_notification)

        assert notification_service.get_newest_notification('included') \
            .timestamp == included_notification.timestamp

class TestSensorReadingPersistenceService:
    def test_sensor_reading(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = SensorReading()
        service.create(reading)

        assert service.get(reading.id).id == reading.id

    def test_analog_sensor_reading(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)

        assert service.get(reading.id).id == reading.id

    def test_digital_sensor_reading(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = DigitalSensorReading()
        service.create(reading)

        assert service.get(reading.id).id == reading.id

    def test_polymorphic(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = SensorReading()
        analog = AnalogSensorReading()
        digital = DigitalSensorReading()

        service.create(reading)
        service.create(analog)
        service.create(digital)

        assert len(service.all()) == 3

    def test_all_reversed(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        older = AnalogSensorReading()
        older.timestamp = datetime.now()

        newer = DigitalSensorReading()
        newer.timestamp = datetime.now()

        service.create(older)
        service.create(newer)

        paged = service.all_reversed(0, 100)
        assert paged.page_number == 0
        assert paged.page_size == 100
        assert paged.total_pages == 1

        readings = paged.elements
        assert readings[0].timestamp == newer.timestamp
        assert readings[1].timestamp == older.timestamp

        assert len(readings) == 2

class TestSensorReadingSchedulingService:
    def test_sensor_reading_job_analog_below_normal(self, sensormodule,
                                                    sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogBelowNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog below normal'
        assert sensor.value == 0
        assert sensor.unit == 'below normal analog unit'

        sensormodule.sms_notification.send_notification.assert_called_with(
                'analog below normal notification')

    def test_sensor_reading_job_analog_normal(self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog normal'
        assert sensor.value == 1
        assert sensor.unit == 'normal analog unit'

        assert not sensormodule.sms_notification.send_notification.called

    def test_sensor_reading_job_analog_above_normal(self, sensormodule,
                                                    sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogAboveNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog above normal'
        assert sensor.value == 2
        assert sensor.unit == 'above normal analog unit'

        sensormodule.sms_notification.send_notification.assert_called_with(
                'analog above normal notification')

    def test_sensor_reading_job_analog_sentinel(self, sensormodule,
                                                sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogSentinelSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog sentinel'
        assert sensor.value == 3
        assert sensor.unit == 'analog sentinel unit'

        assert not sensormodule.sms_notification.send_notification.called

    def test_sensor_reading_job_digital_normal(self, sensormodule,
                                               sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'digital normal'
        assert sensor.value is False

        assert not sensormodule.sms_notification.send_notification.called

    def test_sensor_reading_job_digital_abnormal(self, sensormodule,
                                                 sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'digital abnormal'
        assert sensor.value is True

        sensormodule.sms_notification.send_notification.assert_called_with(
            'digital abnormal notification')

    def test_sensor_reading_job_no_notifications_within_four_hours(
            self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensormodule.sensor_reading_job()
        sensormodule.sensor_reading_job()

        sensor_persistence = NotificationPersistenceService()

        assert len(sensor_persistence.all()) == 1

    def test_sensor_reading_job_notifications_after_four_hours(
            self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensor_persistence = NotificationPersistenceService()

        sensormodule.sensor_reading_job()

        notification = sensor_persistence.get_newest_notification(
            'digital abnormal')
        notification.timestamp = datetime(1970, 1, 1)
        sensor_persistence.update(notification)

        sensormodule.sensor_reading_job()

        assert len(sensor_persistence.all()) == 2

    def test_sensor_reading_job_handles_io_error(
            self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensormodule.sms_notification.send_notification.side_effect = IOError()

        sensormodule.sensor_reading_job()

class TestTransactionScope:
    def test_rollback(self, sqlalchemy):
        try:
            with transaction_scope() as session:
                reading = SensorReading()
                session.add(reading)

                raise ValueError('Test Transactions!')
        except ValueError:
            pass

        service = SensorReadingPersistenceService()
        assert len(service.all()) == 0
<commit_msg>Added Sensor Paging<commit_after>import tests.fixtures
from datetime import datetime
from piot.model import AnalogSensorReading
from piot.model import DigitalSensorReading
from piot.model import NotificationEvent
from piot.model import SensorReading
from piot.sensor.base import BaseAnalogSensor, BaseDigitalSensor
from piot.service import NotificationPersistenceService
from piot.service import SensorReadingPersistenceService
from piot.service import transaction_scope
from tests.fixtures import sensormodule, sqlalchemy
from tests import mocks
from unittest.mock import MagicMock

class TestBaseSQLAlchemyService:
    def test_all_returns_all_model_objects(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)

        reading2 = AnalogSensorReading()
        service.create(reading2)

        assert len(service.all()) == 2

    def test_get_returns_single_model_object(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)

        reading2 = AnalogSensorReading()
        service.create(reading2)

        assert service.get(reading.id).id == reading.id

    def test_update_updates_single_model_object(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        original_reading = AnalogSensorReading()
        original_reading.name = 'original'
        original_reading.value = 1
        original_reading.unit = 'original'

        service.create(original_reading)

        persisted_reading = service.get(original_reading.id)

        persisted_reading.name = 'new'
        persisted_reading.value = 2
        persisted_reading.unit = 'new'

        service.update(persisted_reading)

        updated_reading = service.get(original_reading.id)

        assert updated_reading.name == 'new'
        assert updated_reading.value == 2
        assert updated_reading.unit == 'new'

    def test_delete_deletes_single_model_object(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)
        service.delete(reading)

        assert len(service.all()) == 0

class TestNotificationPersistenceService:
    def test_get_newest_notification_returns_newest_record(self, sqlalchemy):
        sensor_service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        reading.name = 'Test Sensor'
        sensor_service.create(reading)

        old_notification = NotificationEvent()
        old_notification.sensor_id = reading.id
        old_notification.timestamp = datetime(2015, 1, 1)

        new_notification = NotificationEvent()
        new_notification.sensor_id = reading.id
        new_notification.timestamp = datetime(2016, 1, 1)

        service = NotificationPersistenceService()

        # Insert in reverse order to confirm order by
        service.create(new_notification)
        service.create(old_notification)

        newest_timestamp = new_notification.timestamp
        db_timestamp = service.get_newest_notification(reading.name).timestamp

        assert db_timestamp == newest_timestamp

    def test_get_newest_notification_excludes_other_sensors(self, sqlalchemy):
        reading_service = SensorReadingPersistenceService()
        notification_service = NotificationPersistenceService()

        included_sensor = DigitalSensorReading()
        included_sensor.name = 'included'
        reading_service.create(included_sensor)

        excluded_sensor = DigitalSensorReading()
        excluded_sensor.name = 'excluded'
        reading_service.create(excluded_sensor)

        excluded_notification = NotificationEvent()
        excluded_notification.sensor_id = excluded_sensor.id
        excluded_notification.timestamp = datetime(2016, 1, 1)
        notification_service.create(excluded_notification)

        included_notification = NotificationEvent()
        included_notification.sensor_id = included_sensor.id
        included_notification.timestamp = datetime(2015, 1, 1)
        notification_service.create(included_notification)

        assert notification_service.get_newest_notification('included') \
            .timestamp == included_notification.timestamp

class TestSensorReadingPersistenceService:
    def test_sensor_reading(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = SensorReading()
        service.create(reading)

        assert service.get(reading.id).id == reading.id

    def test_analog_sensor_reading(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = AnalogSensorReading()
        service.create(reading)

        assert service.get(reading.id).id == reading.id

    def test_digital_sensor_reading(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = DigitalSensorReading()
        service.create(reading)

        assert service.get(reading.id).id == reading.id

    def test_polymorphic(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        reading = SensorReading()
        analog = AnalogSensorReading()
        digital = DigitalSensorReading()

        service.create(reading)
        service.create(analog)
        service.create(digital)

        assert len(service.all()) == 3

    def test_all_reversed(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        older = AnalogSensorReading()
        older.timestamp = datetime.now()

        newer = DigitalSensorReading()
        newer.timestamp = datetime.now()

        service.create(older)
        service.create(newer)

        paged = service.all_reversed(0, 100)
        assert paged.page_number == 0
        assert paged.page_size == 100
        assert paged.total_pages == 1

        readings = paged.elements
        assert readings[0].timestamp == newer.timestamp
        assert readings[1].timestamp == older.timestamp

        assert len(readings) == 2

    def test_all_reversed_bad_paging(self, sqlalchemy):
        service = SensorReadingPersistenceService()

        older = AnalogSensorReading()
        older.timestamp = datetime.now()

        newer = DigitalSensorReading()
        newer.timestamp = datetime.now()

        service.create(older)
        service.create(newer)

        paged = service.all_reversed(1000, 1)
        assert paged.elements == []

        paged = service.all_reversed(-1, 1)
        assert paged.elements == []

class TestSensorReadingSchedulingService:
    def test_sensor_reading_job_analog_below_normal(self, sensormodule,
                                                    sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogBelowNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog below normal'
        assert sensor.value == 0
        assert sensor.unit == 'below normal analog unit'

        sensormodule.sms_notification.send_notification.assert_called_with(
                'analog below normal notification')

    def test_sensor_reading_job_analog_normal(self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog normal'
        assert sensor.value == 1
        assert sensor.unit == 'normal analog unit'

        assert not sensormodule.sms_notification.send_notification.called

    def test_sensor_reading_job_analog_above_normal(self, sensormodule,
                                                    sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogAboveNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog above normal'
        assert sensor.value == 2
        assert sensor.unit == 'above normal analog unit'

        sensormodule.sms_notification.send_notification.assert_called_with(
                'analog above normal notification')

    def test_sensor_reading_job_analog_sentinel(self, sensormodule,
                                                sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockAnalogSentinelSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'analog sentinel'
        assert sensor.value == 3
        assert sensor.unit == 'analog sentinel unit'

        assert not sensormodule.sms_notification.send_notification.called

    def test_sensor_reading_job_digital_normal(self, sensormodule,
                                               sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalNormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'digital normal'
        assert sensor.value is False

        assert not sensormodule.sms_notification.send_notification.called

    def test_sensor_reading_job_digital_abnormal(self, sensormodule,
                                                 sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensormodule.sensor_reading_job()

        sensor_persistence = SensorReadingPersistenceService()
        sensor = sensor_persistence.all()[0]

        assert sensor.name == 'digital abnormal'
        assert sensor.value is True

        sensormodule.sms_notification.send_notification.assert_called_with(
            'digital abnormal notification')

    def test_sensor_reading_job_no_notifications_within_four_hours(
            self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensormodule.sensor_reading_job()
        sensormodule.sensor_reading_job()

        sensor_persistence = NotificationPersistenceService()

        assert len(sensor_persistence.all()) == 1

    def test_sensor_reading_job_notifications_after_four_hours(
            self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensor_persistence = NotificationPersistenceService()

        sensormodule.sensor_reading_job()

        notification = sensor_persistence.get_newest_notification(
            'digital abnormal')
        notification.timestamp = datetime(1970, 1, 1)
        sensor_persistence.update(notification)

        sensormodule.sensor_reading_job()

        assert len(sensor_persistence.all()) == 2

    def test_sensor_reading_job_handles_io_error(
            self, sensormodule, sqlalchemy):
        tests.fixtures.mock_sensors = [mocks.MockDigitalAbnormalSensor]

        sensormodule.sms_notification.send_notification.side_effect = IOError()

        sensormodule.sensor_reading_job()

class TestTransactionScope:
    def test_rollback(self, sqlalchemy):
        try:
            with transaction_scope() as session:
                reading = SensorReading()
                session.add(reading)

                raise ValueError('Test Transactions!')
        except ValueError:
            pass

        service = SensorReadingPersistenceService()
        assert len(service.all()) == 0
<|endoftext|>