From 587f3756003111864a37229eee60f053b83738ab Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 20:31:30 -0500 Subject: [PATCH 01/14] Include build dependencies Freeze the new dependencies so we can have a more complete build process to test the application. - Include invoke for tasks. - Include behave for acceptance testing. - Include hypothesis for property-based testing. - Include pylint for linting. Signed-off-by: Ryan Y --- requirements.txt | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index df73317..e27e6e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,20 @@ +astroid==1.4.4 +behave==1.2.5 +colorama==0.3.6 +enum34==1.1.2 Flask==0.9 -Jinja2==2.6 -Werkzeug==0.8.3 -distribute==0.7.3 geopy==0.95 +hypothesis==3.0.0 +invoke==0.12.2 +Jinja2==2.6 +lazy-object-proxy==1.2.1 +parse==1.6.6 +parse-type==0.3.4 protobuf==2.5.0 +pylint==1.5.4 pymongo==2.4.1 -pytz==2013b -wsgiref==0.1.2 +pytz==2013b0 +six==1.10.0 +Werkzeug==0.8.3 +wheel==0.24.0 +wrapt==1.10.6 From 8e8ec5e9dd01d7375ad20fb5156e3559df48ce18 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 20:36:51 -0500 Subject: [PATCH 02/14] Include default pylint config Generate the standard pylint config and write it to .pylintrc so we have a baseline to configure pylint if we need. Signed-off-by: Ryan Y --- .pylintrc | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 .pylintrc diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..37718f1 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,378 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception From 2315d3d8fc78ef533bbb8e4fe7235964bd045e9e Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 20:45:36 -0500 Subject: [PATCH 03/14] Include stub acceptance testing files Include environment script and steps module script so we can start to work on acceptance tests to better define the application. Signed-off-by: Ryan Y --- features/environment.py | 0 features/steps/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 features/environment.py create mode 100644 features/steps/__init__.py diff --git a/features/environment.py b/features/environment.py new file mode 100644 index 0000000..e69de29 diff --git a/features/steps/__init__.py b/features/steps/__init__.py new file mode 100644 index 0000000..e69de29 From a6083569d6d5f4044e03aeb57030edfd74bb30c7 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 20:46:50 -0500 Subject: [PATCH 04/14] Include basic build file Create tasks.py so we can invoke the various tasks. - Define test task to run unittest/hypothesis tests. - Define lint task to run pylint. - Define acceptance task to run behave. - Define debug task to run a local instance of the API. Signed-off-by: Ryan Y --- tasks.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tasks.py diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..c37b640 --- /dev/null +++ b/tasks.py @@ -0,0 +1,22 @@ +from invoke import run, task + +@task +def acceptance(): + run("behave") + +@task +def test(): + run("python -m unittest discover -s test/") + +@task +def lint(): + run("pylint web/app.py test/*.py") + +@task +def debug(): + run("python web/app.py") + +@task(default = True, pre = [test, lint]) +def default(): + print "Build complete!" + From cffec058c6febba5f95d7ae6484f3565f40f2e6f Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 20:54:02 -0500 Subject: [PATCH 05/14] Update README with invoke info Include a section on the various invoke tasks civic hackers may run. Signed-off-by: Ryan Y --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 3b5764a..d85ae88 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ HRT exposes the real time location of their buses at `ftp://216.54.15.3/Anrd/hrt ## Setup for Local Development 1. Install [Python 2](http://wiki.python.org/moin/BeginnersGuide/Download) +2. Install [pip](https://pip.pypa.io/en/stable/installing/) 2. Install [virtualenv](https://pypi.python.org/pypi/virtualenv) 3. Clone this repo 4. Create a virtual environment in the top level directory of the repo @@ -73,6 +74,16 @@ If you would like to develop the scripts, you will need your own MongoDB instanc 3. Browse to `http://0.0.0.0:5000/` +### Invoke + +Use `inv` or `invoke` to run various tasks associated with the project. + +* _test_ - Runs the unit and property tests. +* _lint_ - Runs pylint to ensure good style. +* _acceptance_ - Runs the acceptance tests. +* _debug_ - Runs the flask app locally. Does the same thing as + `cd web && python app.py`. + ## Deployment ### Scripts From 0e94d87801c5113602a428e7158ad2fa52052537 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 20:56:07 -0500 Subject: [PATCH 06/14] Ignore swap files Remove vim swap files so we can keep a clean git status line. Signed-off-by: Ryan Y --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7a2d776..a19b924 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ web/debug.py # ide files .idea/* + +# swap files +*.swp From 3f41734f37b6b436eaaa46c7e2f12633c7388fb3 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 21:20:02 -0500 Subject: [PATCH 07/14] Define arrival times feature Create the arrival times feature so we can start to define the steps and ensure a basic level of functionality. Signed-off-by: Ryan Y --- features/arrival_times.feature | 13 +++++++++++++ features/steps/arrival_steps.py | 12 ++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 features/arrival_times.feature create mode 100644 features/steps/arrival_steps.py diff --git a/features/arrival_times.feature b/features/arrival_times.feature new file mode 100644 index 0000000..a8eb43f --- /dev/null +++ b/features/arrival_times.feature @@ -0,0 +1,13 @@ +# language: en + +# https://github.com/Code4HR/hrt-bus-api/issues/79 +Feature: Arrival times + As a citizen + I want to get arrival times for public transportation + so I can rely on HRT + + Scenario: Stop arrival times + Given the adherence data from the API + When I look up a certain stop + Then I will get approximate arrival times + diff --git a/features/steps/arrival_steps.py b/features/steps/arrival_steps.py new file mode 100644 index 0000000..644fd7d --- /dev/null +++ b/features/steps/arrival_steps.py @@ -0,0 +1,12 @@ +@given(u'the adherence data from the API') +def step_impl(context): + raise NotImplementedError(u'STEP: Given the adherence data from the API') + +@when(u'I look up a certain stop') +def step_impl(context): + raise NotImplementedError(u'STEP: When I look up a certain stop') + +@then(u'I will get approximate arrival times') +def step_impl(context): + raise NotImplementedError(u'STEP: Then I will get approximate arrival times') + From 4db19c0ccb46ff8895017e15d1645cf49fad27a1 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 22:15:44 -0500 Subject: [PATCH 08/14] Use standard Cucumber indentation Switch from 4 to 2 spaces to keep with a more consistent theme for Gherkin feature files across the community. Signed-off-by: Ryan Y --- features/arrival_times.feature | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/features/arrival_times.feature b/features/arrival_times.feature index a8eb43f..1e59161 100644 --- a/features/arrival_times.feature +++ b/features/arrival_times.feature @@ -2,12 +2,12 @@ # https://github.com/Code4HR/hrt-bus-api/issues/79 Feature: Arrival times - As a citizen - I want to get arrival times for public transportation - so I can rely on HRT + As a citizen + I want to get arrival times for public transportation + so I can rely on HRT - Scenario: Stop arrival times - Given the adherence data from the API - When I look up a certain stop - Then I will get approximate arrival times + Scenario: Stop arrival times + Given the adherence data from the API + When I look up a certain stop + Then I will get approximate arrival times From fd47e43e20ae9ac5c8294b236a410d3d8fb56f4e Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 17 Feb 2016 22:19:23 -0500 Subject: [PATCH 09/14] Ignore coverage files Include the coverage.py coverage file in the ignore list so we continue to have a clean commit status. Signed-off-by: Ryan Y --- .coveragerc | 6 ++++++ .gitignore | 3 +++ tasks.py | 3 ++- tests.py | 11 +++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .coveragerc create mode 100644 tests.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..6832535 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +branch = True +source = web + +[report] +fail_under = 100 diff --git a/.gitignore b/.gitignore index a19b924..aa8521e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ web/debug.py # swap files *.swp + +# coverage file +.coverage diff --git a/tasks.py b/tasks.py index c37b640..c48f31f 100644 --- a/tasks.py +++ b/tasks.py @@ -6,7 +6,8 @@ def acceptance(): @task def test(): - run("python -m unittest discover -s test/") + run("coverage run tests.py") + run("coverage report") @task def lint(): diff --git a/tests.py b/tests.py new file mode 100644 index 0000000..8c4de63 --- /dev/null +++ b/tests.py @@ -0,0 +1,11 @@ +import unittest + +def suite(): + suite = unittest.TestSuite() + suite.addTests(unittest.TestLoader().discover('test')) + return suite + +if __name__ == '__main__': + runner = unittest.TextTestRunner() + test_suite = suite() + runner.run(test_suite) From d85303aa06bc2ef5017d4e4782ff1d0961bccbb5 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Fri, 19 Feb 2016 00:13:56 -0500 Subject: [PATCH 10/14] Include unit test stub areas Include stub files for future unit tests based on a seeming organisation of classes I think will naturally occur once we start refactoring a bit. - Comments represent thoughts, notes, not state. Don't take them too seriously. - Include package files to get tests to use implementation functions. Signed-off-by: Ryan Y --- test/__init__.py | 0 test/test_bus_handler.py | 52 ++++++++++++++ test/test_general_handler.py | 45 ++++++++++++ test/test_gtfs_handler | 136 +++++++++++++++++++++++++++++++++++ test/test_gtfs_handler.py | 33 +++++++++ test/test_stop_handler.py | 48 +++++++++++++ test/test_stop_handler_v2.py | 50 +++++++++++++ web/__init__.py | 0 8 files changed, 364 insertions(+) create mode 100644 test/__init__.py create mode 100644 test/test_bus_handler.py create mode 100644 test/test_general_handler.py create mode 100644 test/test_gtfs_handler create mode 100644 test/test_gtfs_handler.py create mode 100644 test/test_stop_handler.py create mode 100644 test/test_stop_handler_v2.py create mode 100644 web/__init__.py diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_bus_handler.py b/test/test_bus_handler.py new file mode 100644 index 0000000..0ecf734 --- /dev/null +++ b/test/test_bus_handler.py @@ -0,0 +1,52 @@ +import unittest +from web.app import getActiveRoutes, getBusesOnRoute, getBusesByRoute, getBusHistory + +class TestBusHandler(unittest.TestCase): + """ + ------------------- + # class RouteFormatter: + - (8) dthandler : (obj -> datetime?) + (11) support_jsonp : a -> ([arg] -> [[kwarg]] -> response) + ------------------- + + ------------------- + # class BusApi: + - (17) app : Flask + - (13) db : Connection + - (7) collectionPrefix : string + ------------------- + + + ============================= + + # class BusHandler: + + + @getActiveRoutes : [routes].json + # def active: + - app + - db + - collectionPrefix + (support_jsonp) + + @getBusesOnRoute : route.id -> [checkin].json + # def on_route: + - app + - dthandler + - db + (support_jsonp) + + + @getBusesByRoute : [route.id]? -> [checkin].json + # def in_routes(routes): + - app + - dthandler + - db + (support_jsonp) + + @getBusHistory : bus.id -> [checkin].json + - app + - dthandler + - db + (support_jsonp) + """ diff --git a/test/test_general_handler.py b/test/test_general_handler.py new file mode 100644 index 0000000..94773b1 --- /dev/null +++ b/test/test_general_handler.py @@ -0,0 +1,45 @@ +import unittest +from web.app import beforeRequest, index, busfinder, getApiInfo + +class TestGeneralHandler(unittest.TestCase): + """ + ------------------- + # class RouteFormatter: + - (8) dthandler : (obj -> datetime?) + (11) support_jsonp : a -> ([arg] -> [[kwarg]] -> response) + ------------------- + + ------------------- + # class BusApi: + - (17) app : Flask + - (13) db : Connection + - (7) collectionPrefix : string + ------------------- + + + ============================= + + # class GeneralHandler: + - curDateTime : datetime + + !!@beforeRequest : () + - app + - db + - curDateTime + + @index : =>/busfinder + - app + + + @busfinder : view? -> /busfinder.html + - app + + @getApiInfo : api + - app + - dthandler + - db + - curDateTime + - collectionPrefix + (support_jsonp) + """ + diff --git a/test/test_gtfs_handler b/test/test_gtfs_handler new file mode 100644 index 0000000..4710d02 --- /dev/null +++ b/test/test_gtfs_handler @@ -0,0 +1,136 @@ +import unittest +from web.app import getBusesAtStop + +class TestGeneralHandler(unittest.TestCase): + """ + # class GeneralHandler: + - curDateTime : datetime + + !!@beforeRequest : () + - app + - db + - curDateTime + + @index : =>/busfinder + - app + + + @busfinder : view? -> /busfinder.html + - app + + @getApiInfo : api + - app + - dthandler + - db + - curDateTime + - collectionPrefix + (support_jsonp) + + =========================== + + # class GtfsRoutes: + + + @tripUpdate : feed (?) + - app + - db + + @vehiclePosition : feed (?) + - app + - db + + + =========================== + + # class BusHandler: + + + @getActiveRoutes : [routes].json + # def active: + - app + - db + - collectionPrefix + (support_jsonp) + + @getBusesOnRoute : route.id -> [checkin].json + # def on_route: + - app + - dthandler + - db + (support_jsonp) + + + @getBusesByRoute : [route.id]? -> [checkin].json + # def in_routes(routes): + - app + - dthandler + - db + (support_jsonp) + + @getBusHistory : bus.id -> [checkin].json + - app + - dthandler + - db + (support_jsonp) + + + =========================== + + # class StopHandler: + + + @getStopsNearIntersection : city -> intersection -> [stop].json + - app + (getStopsNear) + + @getStopsNear : lat -> lng -> [stop].json # v1 + - app + - db + - collectionPrefix + (support_jsonp) + + @getStopsById : ids_string -> [stop].json + - app + - db + - collectionPrefix + (support_jsonp) + + @getNextBus : route.id -> stop.id -> [stop].json + - app + - dthandler + - db + - collectionPrefix + (support_jsonp) + + + ============================ + + # class StopHandlerV2: + + + @get_stops_near : lat? -> long? -> capacity? -> [stop].json # v2 + - app + - dthandler + - db + - collectionPrefix + (support_jsonp) + (find_buses_at_stop) + find_buses_at_stop : stop.id -> [scheduled_stop] + - db + - collectionPrefix + + @getBusesAtStop : stop.id -> [scheduled_stop].json # v1 + - app + - dthandler + (support_jsonp) + (find_buses_at_stop) + + @get_buses_at_stop : stop.id -> [scheduled_stop].json # v2 + - app + - dthandler + (support_jsonp) + (find_buses_at_stop) + + """ + def test_get_buses_at_stop_v1(): + diff --git a/test/test_gtfs_handler.py b/test/test_gtfs_handler.py new file mode 100644 index 0000000..35bf336 --- /dev/null +++ b/test/test_gtfs_handler.py @@ -0,0 +1,33 @@ +import unittest +from web.app import tripUpdate, vehiclePosition + +class TestGtfsHandler(unittest.TestCase): + """ + ------------------- + # class RouteFormatter: + - (8) dthandler : (obj -> datetime?) + (11) support_jsonp : a -> ([arg] -> [[kwarg]] -> response) + ------------------- + + ------------------- + # class BusApi: + - (17) app : Flask + - (13) db : Connection + - (7) collectionPrefix : string + ------------------- + + + ============================= + + # class GtfsHandler: + + + @tripUpdate : feed (?) + - app + - db + + @vehiclePosition : feed (?) + - app + - db + """ + diff --git a/test/test_stop_handler.py b/test/test_stop_handler.py new file mode 100644 index 0000000..ac46405 --- /dev/null +++ b/test/test_stop_handler.py @@ -0,0 +1,48 @@ +import unittest +from web.app import getStopsNearIntersection, getStopsNear, getNextBus + +class TestStopHandler(unittest.TestCase): + """ + ------------------- + # class RouteFormatter: + - (8) dthandler : (obj -> datetime?) + (11) support_jsonp : a -> ([arg] -> [[kwarg]] -> response) + ------------------- + + ------------------- + # class BusApi: + - (17) app : Flask + - (13) db : Connection + - (7) collectionPrefix : string + ------------------- + + + ============================= + + # class StopHandler: + + + @getStopsNearIntersection : city -> intersection -> [stop].json + - app + (getStopsNear) + + @getStopsNear : lat -> lng -> [stop].json # v1 + - app + - db + - collectionPrefix + (support_jsonp) + + @getStopsById : ids_string -> [stop].json + - app + - db + - collectionPrefix + (support_jsonp) + + @getNextBus : route.id -> stop.id -> [stop].json + - app + - dthandler + - db + - collectionPrefix + (support_jsonp) + """ + diff --git a/test/test_stop_handler_v2.py b/test/test_stop_handler_v2.py new file mode 100644 index 0000000..a4b74cc --- /dev/null +++ b/test/test_stop_handler_v2.py @@ -0,0 +1,50 @@ +import unittest +from web.app import getBusesAtStop + +class TestStopHandlerV2(unittest.TestCase): + """ + ------------------- + # class RouteFormatter: + - (8) dthandler : (obj -> datetime?) + (11) support_jsonp : a -> ([arg] -> [[kwarg]] -> response) + ------------------- + + ------------------- + # class BusApi: + - (17) app : Flask + - (13) db : Connection + - (7) collectionPrefix : string + ------------------- + + + ============================= + + # class StopHandlerV2: + + + @get_stops_near : lat? -> long? -> capacity? -> [stop].json # v2 + - app + - dthandler + - db + - collectionPrefix + (support_jsonp) + (find_buses_at_stop) + find_buses_at_stop : stop.id -> [scheduled_stop] + - db + - collectionPrefix + + @getBusesAtStop : stop.id -> [scheduled_stop].json # v1 + - app + - dthandler + (support_jsonp) + (find_buses_at_stop) + + @get_buses_at_stop : stop.id -> [scheduled_stop].json # v2 + - app + - dthandler + (support_jsonp) + (find_buses_at_stop) + + """ + def test_get_buses_at_stop_v1(self): + self.assertEqual(True, True) diff --git a/web/__init__.py b/web/__init__.py new file mode 100644 index 0000000..e69de29 From 43af2bc2f70a60c468129f6f54edfc5275038e1f Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Fri, 19 Feb 2016 01:43:33 -0500 Subject: [PATCH 11/14] Remove extra file Remove the copy of the GTFS handler so we can actually know what we want to test. Signed-off-by: Ryan Y --- test/test_gtfs_handler | 136 ----------------------------------------- 1 file changed, 136 deletions(-) delete mode 100644 test/test_gtfs_handler diff --git a/test/test_gtfs_handler b/test/test_gtfs_handler deleted file mode 100644 index 4710d02..0000000 --- a/test/test_gtfs_handler +++ /dev/null @@ -1,136 +0,0 @@ -import unittest -from web.app import getBusesAtStop - -class TestGeneralHandler(unittest.TestCase): - """ - # class GeneralHandler: - - curDateTime : datetime - - !!@beforeRequest : () - - app - - db - - curDateTime - - @index : =>/busfinder - - app - - - @busfinder : view? -> /busfinder.html - - app - - @getApiInfo : api - - app - - dthandler - - db - - curDateTime - - collectionPrefix - (support_jsonp) - - =========================== - - # class GtfsRoutes: - - - @tripUpdate : feed (?) - - app - - db - - @vehiclePosition : feed (?) - - app - - db - - - =========================== - - # class BusHandler: - - - @getActiveRoutes : [routes].json - # def active: - - app - - db - - collectionPrefix - (support_jsonp) - - @getBusesOnRoute : route.id -> [checkin].json - # def on_route: - - app - - dthandler - - db - (support_jsonp) - - - @getBusesByRoute : [route.id]? -> [checkin].json - # def in_routes(routes): - - app - - dthandler - - db - (support_jsonp) - - @getBusHistory : bus.id -> [checkin].json - - app - - dthandler - - db - (support_jsonp) - - - =========================== - - # class StopHandler: - - - @getStopsNearIntersection : city -> intersection -> [stop].json - - app - (getStopsNear) - - @getStopsNear : lat -> lng -> [stop].json # v1 - - app - - db - - collectionPrefix - (support_jsonp) - - @getStopsById : ids_string -> [stop].json - - app - - db - - collectionPrefix - (support_jsonp) - - @getNextBus : route.id -> stop.id -> [stop].json - - app - - dthandler - - db - - collectionPrefix - (support_jsonp) - - - ============================ - - # class StopHandlerV2: - - - @get_stops_near : lat? -> long? -> capacity? -> [stop].json # v2 - - app - - dthandler - - db - - collectionPrefix - (support_jsonp) - (find_buses_at_stop) - find_buses_at_stop : stop.id -> [scheduled_stop] - - db - - collectionPrefix - - @getBusesAtStop : stop.id -> [scheduled_stop].json # v1 - - app - - dthandler - (support_jsonp) - (find_buses_at_stop) - - @get_buses_at_stop : stop.id -> [scheduled_stop].json # v2 - - app - - dthandler - (support_jsonp) - (find_buses_at_stop) - - """ - def test_get_buses_at_stop_v1(): - From 4c83c9daeecb2acc2679bf0fd8ae5185df505ea7 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 2 Mar 2016 20:52:46 -0500 Subject: [PATCH 12/14] Include clean target Remove .pyc files with the clean target. Signed-off-by: Ryan Y --- tasks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tasks.py b/tasks.py index c48f31f..105480a 100644 --- a/tasks.py +++ b/tasks.py @@ -1,5 +1,9 @@ from invoke import run, task +@task +def clean(): + run("rm -rf **/*.pyc") + @task def acceptance(): run("behave") From 4b119ea36d7d6c182b8976e1acf049fda075b41e Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 2 Mar 2016 20:53:36 -0500 Subject: [PATCH 13/14] Change linting parameters Have the maximum line length go to 80 and remove docstring warnings from linting. Signed-off-by: Ryan Y --- .pylintrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pylintrc b/.pylintrc index 37718f1..8ff6fc5 100644 --- a/.pylintrc +++ b/.pylintrc @@ -59,7 +59,7 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating +disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating,missing-docstring [REPORTS] @@ -128,7 +128,7 @@ notes=FIXME,XXX,TODO [FORMAT] # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=80 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ From 4c12fd316282e3cc568be8b1c595f2ddc87a7c18 Mon Sep 17 00:00:00 2001 From: Ryan Y Date: Wed, 2 Mar 2016 20:56:13 -0500 Subject: [PATCH 14/14] Set up some of the acceptance testing Ignore Selenium output files, include dependencies and include the acceptance test Selenium browser setup using PhantomJS. Signed-off-by: Ryan Y --- .gitignore | 1 + features/environment.py | 4 ++++ requirements.txt | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index aa8521e..ce4c284 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ web/debug.py # swap files *.swp +ghostdriver.log # coverage file .coverage diff --git a/features/environment.py b/features/environment.py index e69de29..1ac7f0f 100644 --- a/features/environment.py +++ b/features/environment.py @@ -0,0 +1,4 @@ +from selenium import webdriver + +def before_all(context): + context.browser = webdriver.PhantomJS() diff --git a/requirements.txt b/requirements.txt index e27e6e3..21e75ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ astroid==1.4.4 behave==1.2.5 colorama==0.3.6 +coverage==4.0.3 enum34==1.1.2 Flask==0.9 geopy==0.95 @@ -11,9 +12,13 @@ lazy-object-proxy==1.2.1 parse==1.6.6 parse-type==0.3.4 protobuf==2.5.0 +py==1.4.31 pylint==1.5.4 pymongo==2.4.1 +pytest==2.9.0 +pytest-cov==2.2.1 pytz==2013b0 +selenium==2.52.0 six==1.10.0 Werkzeug==0.8.3 wheel==0.24.0