Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests for the bundled compiler and compressor classes #531

Merged
merged 6 commits into from
Feb 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ docs/_build/
coverage/
tests/static/
tests/assets/js/dummy.js
tests/node_modules/
.tox/
.DS_Store
.idea
Expand All @@ -21,3 +22,6 @@ tests/assets/js/dummy.js
.pydevproject
.ropeproject
__pycache__
npm-debug.log
tests/npm-cache
django-pipeline-*/
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ recursive-include pipeline/jinja2 *.html *.jinja
include AUTHORS LICENSE README.rst HISTORY.rst
recursive-include tests *
recursive-exclude tests *.pyc *.pyo
recursive-exclude tests/node_modules *
recursive-exclude tests/npm-cache *
recursive-exclude tests/npm *
include docs/Makefile docs/make.bat docs/conf.py
recursive-include docs *.rst
3 changes: 2 additions & 1 deletion pipeline/compilers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from pipeline.conf import settings
from pipeline.exceptions import CompilerError
from pipeline.utils import to_class
from pipeline.utils import to_class, set_std_streams_blocking


class Compiler(object):
Expand Down Expand Up @@ -120,6 +120,7 @@ def execute_command(self, command, cwd=None, stdout_captured=None):
stdout=stdout,
stderr=subprocess.PIPE)
_, stderr = compiling.communicate()
set_std_streams_blocking()

if compiling.returncode != 0:
stdout_captured = None # Don't save erroneous result.
Expand Down
3 changes: 2 additions & 1 deletion pipeline/compressors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from pipeline.conf import settings
from pipeline.exceptions import CompressorError
from pipeline.utils import to_class, relpath
from pipeline.utils import to_class, relpath, set_std_streams_blocking

URL_DETECTOR = r"""url\((['"]){0,1}\s*(.*?)["']{0,1}\)"""
URL_REPLACER = r"""url\(__EMBED__(.+?)(\?\d+)?\)"""
Expand Down Expand Up @@ -248,6 +248,7 @@ def execute_command(self, command, content):
if content:
content = smart_bytes(content)
stdout, stderr = pipe.communicate(content)
set_std_streams_blocking()
if stderr.strip() and pipe.returncode != 0:
raise CompressorError(stderr)
elif self.verbose:
Expand Down
3 changes: 2 additions & 1 deletion pipeline/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os
import collections
import shlex

Expand Down Expand Up @@ -101,7 +102,7 @@ def __getitem__(self, key):
value = self.settings[key]
if key.endswith(("_BINARY", "_ARGUMENTS")):
if isinstance(value, string_types):
return tuple(shlex.split(value))
return tuple(shlex.split(value, posix=(os.name == 'posix')))
return tuple(value)
return value

Expand Down
24 changes: 24 additions & 0 deletions pipeline/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from __future__ import unicode_literals

try:
import fcntl
except ImportError:
# windows
fcntl = None

import importlib
import mimetypes
import posixpath
import os
import sys

try:
from urllib.parse import quote
Expand Down Expand Up @@ -54,3 +62,19 @@ def relpath(path, start=posixpath.curdir):
if not rel_list:
return posixpath.curdir
return posixpath.join(*rel_list)


def set_std_streams_blocking():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if this had a docstring explaining what it does and why it's needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"""
Set stdout and stderr to be blocking.

This is called after Popen.communicate() to revert stdout and stderr back
to be blocking (the default) in the event that the process to which they
were passed manipulated one or both file descriptors to be non-blocking.
"""
if not fcntl:
return
for f in (sys.__stdout__, sys.__stderr__):
fileno = f.fileno()
flags = fcntl.fcntl(fileno, fcntl.F_GETFL)
fcntl.fcntl(fileno, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)
12 changes: 12 additions & 0 deletions tests/assets/compilers/coffee/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(function() {
var cube, square;

square = function(x) {
return x * x;
};

cube = function(x) {
return square(x) * x;
};

}).call(this);
2 changes: 2 additions & 0 deletions tests/assets/compilers/coffee/input.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
square = (x) -> x * x
cube = (x) -> square(x) * x
27 changes: 27 additions & 0 deletions tests/assets/compilers/es6/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";

// Expression bodies
var odds = evens.map(function (v) {
return v + 1;
});
var nums = evens.map(function (v, i) {
return v + i;
});

// Statement bodies
nums.forEach(function (v) {
if (v % 5 === 0) fives.push(v);
});

// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends: function printFriends() {
var _this = this;

this._friends.forEach(function (f) {
return console.log(_this._name + " knows " + f);
});
}
};
19 changes: 19 additions & 0 deletions tests/assets/compilers/es6/input.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});

// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
3 changes: 3 additions & 0 deletions tests/assets/compilers/less/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.a {
width: 1px;
}
5 changes: 5 additions & 0 deletions tests/assets/compilers/less/input.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@a: 1;

.a {
width: (@a + 0px);
}
6 changes: 6 additions & 0 deletions tests/assets/compilers/livescript/expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(function(){
var times;
times = function(x, y){
return x * y;
};
}).call(this);
2 changes: 2 additions & 0 deletions tests/assets/compilers/livescript/input.ls
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
times = (x, y) ->
x * y
5 changes: 5 additions & 0 deletions tests/assets/compilers/scss/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.a .b {
display: none; }

.c .d {
display: block; }
10 changes: 10 additions & 0 deletions tests/assets/compilers/scss/input.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.a {
.b {
display: none;
}
}
.c {
.d {
display: block;
}
}
3 changes: 3 additions & 0 deletions tests/assets/compilers/stylus/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.a {
color: #000;
}
2 changes: 2 additions & 0 deletions tests/assets/compilers/stylus/input.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.a
color: black
1 change: 1 addition & 0 deletions tests/assets/compressors/closure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(function(){(function(){window.concat=function(){console.log(arguments)}})();(function(){window.cat=function(){console.log("hello world")}})()}).call(this);
1 change: 1 addition & 0 deletions tests/assets/compressors/cssmin.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.concat{display:none}.concatenate{display:block}
1 change: 1 addition & 0 deletions tests/assets/compressors/csstidy.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.concat{display:none;}.concatenate{display:block;}
1 change: 1 addition & 0 deletions tests/assets/compressors/jsmin.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/assets/compressors/slimit.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/assets/compressors/uglifyjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(function(){(function(){window.concat=function(){console.log(arguments)}})();(function(){window.cat=function(){console.log("hello world")}})()}).call(this);
1 change: 1 addition & 0 deletions tests/assets/compressors/yuglify.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.concat{display:none}.concatenate{display:block}
1 change: 1 addition & 0 deletions tests/assets/compressors/yuglify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(function(){(function(){window.concat=function(){console.log(arguments)}})(),function(){window.cat=function(){console.log("hello world")}}()}).call(this);
1 change: 1 addition & 0 deletions tests/assets/compressors/yui.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.concat{display:none}.concatenate{display:block}
1 change: 1 addition & 0 deletions tests/assets/compressors/yui.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "django-pipeline-tests",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should include "private": true to prevent any accidental publication on npm.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, good call

"private": true,
"version": "1.0.0",
"description": "Pipeline is an asset packaging library for Django.",
"author": "Timothée Peignier <[email protected]>",
"license": "MIT",
"readmeFilename": "../README.rst",
"repository": {
"type": "git",
"url": "git://github.com/jazzband/django-pipeline.git"
},
"dependencies": {
"babel-cli": "^6.4.5",
"babel-preset-es2015": "^6.3.13",
"coffee-script": "^1.10.0",
"less": "^2.5.3",
"livescript": "^1.4.0",
"node-sass": "^3.4.2",
"stylus": "^0.53.0",
"cssmin": "^0.4.3",
"google-closure-compiler": "^20151216.2.0",
"uglify-js": "^2.6.1",
"yuglify": "^0.1.4",
"yuicompressor": "^2.4.8"
}
}
42 changes: 42 additions & 0 deletions tests/scripts/npm_install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python
"""
A cross-platform compatible `npm install` call, checking whether npm is
in fact installed on the system first (and on windows, checking that the
npm version is at least 3.0 because of a bug in 2.x with MAX_PATH)
"""
import distutils.spawn
import os
from pkg_resources import parse_version
import re
import subprocess
import sys


def main():
tests_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
if os.name == 'nt':
try:
npm_paths = subprocess.check_output(['where', 'npm.cmd'])
except subprocess.CalledProcessError:
return
else:
npm_bin = re.split(r'\r?\n', npm_paths)[0]
else:
npm_bin = distutils.spawn.find_executable('npm')
if not npm_bin:
return
if os.name == 'nt':
os.environ.setdefault('APPDATA', '.')
npm_version = subprocess.check_output([npm_bin, '--version']).strip()
# Skip on windows if npm version is less than 3 because of
# MAX_PATH issues in version 2
if parse_version(npm_version) < parse_version('3.0'):
return
pipe = subprocess.Popen([npm_bin, 'install'],
cwd=tests_dir, stdout=sys.stdout, stderr=sys.stderr)
pipe.communicate()
sys.exit(pipe.returncode)


if __name__ == '__main__':
main()
46 changes: 46 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import glob
import os
import distutils.spawn


def local_path(path):
Expand Down Expand Up @@ -58,6 +60,8 @@ def local_path(path):

PIPELINE = {
'PIPELINE_ENABLED': True,
'JS_COMPRESSOR': None,
'CSS_COMPRESSOR': None,
'STYLESHEETS': {
'screen': {
'source_filenames': (
Expand Down Expand Up @@ -118,6 +122,48 @@ def local_path(path):
}
}

NODE_MODULES_PATH = local_path('node_modules')
NODE_BIN_PATH = os.path.join(NODE_MODULES_PATH, '.bin')
NODE_EXE_PATH = distutils.spawn.find_executable('node')
JAVA_EXE_PATH = distutils.spawn.find_executable('java')
CSSTIDY_EXE_PATH = distutils.spawn.find_executable('csstidy')
HAS_NODE = os.path.exists(NODE_BIN_PATH) and NODE_EXE_PATH
HAS_JAVA = bool(JAVA_EXE_PATH)
HAS_CSSTIDY = bool(CSSTIDY_EXE_PATH)

if HAS_NODE:
def node_exe_path(command):
exe_ext = '.cmd' if os.name == 'nt' else ''
return os.path.join(NODE_BIN_PATH, "%s%s" % (command, exe_ext))

PIPELINE.update({
'SASS_BINARY': node_exe_path('node-sass'),
'COFFEE_SCRIPT_BINARY': node_exe_path('coffee'),
'COFFEE_SCRIPT_ARGUMENTS': ['--no-header'],
'LESS_BINARY': node_exe_path('lessc'),
'BABEL_BINARY': node_exe_path('babel'),
'BABEL_ARGUMENTS': ['--presets', 'es2015'],
'STYLUS_BINARY': node_exe_path('stylus'),
'LIVE_SCRIPT_BINARY': node_exe_path('lsc'),
'LIVE_SCRIPT_ARGUMENTS': ['--no-header'],
'YUGLIFY_BINARY': node_exe_path('yuglify'),
'UGLIFYJS_BINARY': node_exe_path('uglifyjs'),
'CSSMIN_BINARY': node_exe_path('cssmin'),
})

if HAS_NODE and HAS_JAVA:
PIPELINE.update({
'CLOSURE_BINARY': [
JAVA_EXE_PATH, '-jar',
os.path.join(NODE_MODULES_PATH, 'google-closure-compiler', 'compiler.jar')],
'YUI_BINARY': [
JAVA_EXE_PATH, '-jar',
glob.glob(os.path.join(NODE_MODULES_PATH, 'yuicompressor', 'build', '*.jar'))[0]]
})

if HAS_CSSTIDY:
PIPELINE.update({'CSSTIDY_BINARY': CSSTIDY_EXE_PATH})

TEMPLATE_DIRS = (
local_path('templates'),
)
Expand Down
8 changes: 8 additions & 0 deletions tests/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# -*- coding: utf-8 flake8: noqa -*-
import os
import sys


if sys.platform.startswith('win'):
os.environ.setdefault('NUMBER_OF_PROCESSORS', '1')


from .test_compiler import *
from .test_compressor import *
from .test_template import *
Expand Down
Loading