Repository: sasstools/sass-lint
Pull request #448 (2015-12-10 22:04:49)

Release/1.4.0
v1.4.0


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a59c78..2fe1217 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,48 @@
 # Sass Lint Changelog
 
+## v1.4.0
+**December 10, 2015**
+
+The long overdue update!
+
+**Changes**
+* The config file can now be cached for a small performance boost [#279](https://github.com/sasstools/sass-lint/issues/279)
+* Added an `ignore-custom-properties` option to the property sort order rule, allowing you to ignore/include non standard properties in your property sort orders [#302](https://github.com/sasstools/sass-lint/issues/302)
+* Streamlined the `force-pseudo-nesting`, `force-element-nesting` and `force-attribute-nesting` rules [#323](https://github.com/sasstools/sass-lint/pull/323)
+* Improved the testing of our config handling [#403](https://github.com/sasstools/sass-lint/issues/403)
+* Corrected the naming of the `no-misspelled-properties` tests [#405](https://github.com/sasstools/sass-lint/pull/405)
+* Updated some of our dependencies to their latest versions [#428](https://github.com/sasstools/sass-lint/pull/428)
+* `no-trailing-zero` now acts similar to the `no-unnecessary-mantissa` rule of scss-lint in that it warns about unnecessary decimals [#438](https://github.com/sasstools/sass-lint/issues/438)
+
+**CLI**
+* [-s --syntax] Syntax flag allows you to specify syntax of the file(s) you wish to lint [#381](https://github.com/sasstools/sass-lint/issues/381)
+
+**New Rules**
+* [space-around-operator](https://github.com/sasstools/sass-lint/blob/master/docs/rules/space-around-operator.md)
+* [class-name-format](https://github.com/sasstools/sass-lint/blob/master/docs/rules/class-name-format.md)
+* [id-name-format](https://github.com/sasstools/sass-lint/blob/master/docs/rules/id-name-format.md)
+* [property-units](https://github.com/sasstools/sass-lint/blob/master/docs/rules/property-units.md)
+* [bem-depth](https://github.com/sasstools/sass-lint/blob/master/docs/rules/bem-depth.md)
+
+**Fixes**
+* Pre emptive fix for `space-around-operator` with negative values [#394](https://github.com/sasstools/sass-lint/issues/394)
+* Pre emptive fix for `space-around-operator` with percentage values [#425](https://github.com/sasstools/sass-lint/issues/425)
+* `no-trailing-zero` now works as expected with 0 values after a decimal [#439](https://github.com/sasstools/sass-lint/issues/439)
+* Percentage values are now correctly handled and reported in the `shorthand-values` rule [#435](https://github.com/sasstools/sass-lint/issues/435)
+* `function-name-format` no longer incorrectly reports on valid default CSS & Sass functions [#442](https://github.com/sasstools/sass-lint/pull/442)
+* Corrected a typo in config file documentation [#384](https://github.com/sasstools/sass-lint/pull/384)
+
+**Brought to you by**
+
+* [Sam Richard](https://github.com/Snugug)
+* [Ben Griffith](https://github.com/bgriffith)
+* [Dan Purdy](https://github.com/DanPurdy)
+* [Ben Rothman](https://github.com/benthemonkey)
+* [Don Abrams](https://github.com/donabrams)
+* [Kaelig](https://github.com/kaelig)
+
+**A big thankyou to everyone who reported issues or contributed to the discussion around issues**
+
 ## v1.3.3
 **November 16, 2015**
 
diff --git a/README.md b/README.md
index 0264636..76122db 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop)
+# Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop) [![Dependency Status](https://david-dm.org/sasstools/sass-lint.svg)](https://david-dm.org/sasstools/sass-lint#info=dependencies&view=list) [![Dev Dependency Status](https://david-dm.org/sasstools/sass-lint/dev-status.svg)](https://david-dm.org/sasstools/sass-lint#info=devDependencies&view=list)
 
 A Node-only Sass linter for both `sass` and `scss` syntax!
 
@@ -12,6 +12,8 @@ npm install sass-lint --save-dev
 
 Use the [Sample Config](docs/sass-lint.yml) as a guide to create your `.sass-lint.yml` in the root of where you are running Sass Lint from. The default configuration can be found [here](https://github.com/sasstools/sass-lint/blob/master/lib/config/sass-lint.yml).
 
+*Migrating from SCSS-Lint*: If you already have a config for SCSS-Lint, you can instantly convert it to the equivalent Sass Lint config at [sasstools.github.io/make-sass-lint-config](http://sasstools.github.io/make-sass-lint-config/).
+
 ### Options
 
 The following are options that you can use to config the Sass Linter.
diff --git a/bin/sass-lint.js b/bin/sass-lint.js
index 2ef42ee..afaf34c 100755
--- a/bin/sass-lint.js
+++ b/bin/sass-lint.js
@@ -32,6 +32,7 @@ program
   .option('-v, --verbose', 'verbose output')
   .option('-f, --format [format]', 'pass one of the available eslint formats')
   .option('-o, --output [output]', 'the path and filename where you would like output to be written')
+  .option('-s, --syntax [syntax]', 'syntax to evaluate the file(s) with (either sass or scss)')
   .parse(process.argv);
 
 
@@ -51,6 +52,10 @@ if (program.ignore && program.ignore !== true) {
   }
 }
 
+if (program.syntax && ['sass', 'scss'].indexOf(program.syntax) > -1) {
+  configOptions.syntax = program.syntax;
+}
+
 if (program.format && program.format !== true) {
   if (configOptions.hasOwnProperty('options')) {
     configOptions.options.formatter = program.format;
diff --git a/data/functions.yml b/data/functions.yml
new file mode 100644
index 0000000..d0bf1a0
--- /dev/null
+++ b/data/functions.yml
@@ -0,0 +1,129 @@
+  abs
+  adjust-color
+  adjust-hue
+  alpha
+  annotation
+  append
+  blue
+  blur
+  brightness
+  calc
+  call
+  ceil
+  change-color
+  character-variant
+  circle
+  comparable
+  complement
+  contrast
+  cubic-bezier
+  custom
+  darken
+  desaturate
+  drop-shadow
+  element
+  ellipse
+  feature-exists
+  floor
+  function-exists
+  global-variable-exists
+  grayscale
+  green
+  hue
+  hue-rotate
+  hsl
+  hsla
+  if
+  index
+  inset
+  inspect
+  invert
+  is-hex-str
+  is-superselector
+  join
+  keywords
+  length
+  lighten
+  lightness
+  linear-gradient
+  list-separator
+  map-get
+  map-has-key
+  map-keys
+  map-merge
+  map-remove
+  map-values
+  matrix
+  matrix3d
+  min
+  max
+  mix
+  mixin-exists
+  nth
+  ornaments
+  opacify
+  opacity
+  percentage
+  perspective
+  polygon
+  quote
+  radial-gradient
+  random
+  rect
+  red
+  repeat
+  repeating-linear-gradient
+  repeating-radial-gradient
+  rgb
+  rgba
+  rotate
+  rotateX
+  rotateY
+  rotateZ
+  rotate3d
+  round
+  saturate
+  saturation
+  scale
+  scale-color
+  scaleX
+  scaleY
+  scaleZ
+  scale3d
+  selector-nest
+  selector-append
+  selector-extend
+  selector-replace
+  selector-unify
+  simple-selectors
+  selector-parse
+  set-nth
+  sepia
+  skewX
+  skewY
+  steps
+  str-index
+  str-insert
+  str-length
+  str-slice
+  styleset
+  stylistic
+  swash
+  symbols
+  to-lower-case
+  to-upper-case
+  translate
+  translateX
+  translateY
+  translateZ
+  translate3d
+  tran­spa­ren­tiz­e
+  type-of
+  unique-id
+  unit
+  unitless
+  unquote
+  url
+  var
+  variable-exists
+  zip
diff --git a/docs/cli/readme.md b/docs/cli/readme.md
index 0b06def..160ed34 100644
--- a/docs/cli/readme.md
+++ b/docs/cli/readme.md
@@ -18,5 +18,6 @@ Command Line Flag        | Description
 `-i`,`--ignore [pattern]` | A pattern that should be ignored from linting. Multiple patterns can be used by separating each pattern by `, `. Patterns should be wrapped in quotes (will be merged with other ignore options)
 `-o`,`--output [output]`  | The path plus file name relative to where Sass Lint is being run from where the output should be written to.
 `-q`,`--no-exit`          | Prevents the CLI from throwing an error if there is one (useful for development work)
+`-s`,`--syntax`           | Syntax to evaluate the given file(s) with, either sass or scss. Use with care: overrides filename extension-based syntax detection.
 `-v`,`--verbose`          | Verbose output (fully formatted output)
 `-V`,`--version`          | Outputs the version number of Sass Lint
diff --git a/docs/options/cache-config.md b/docs/options/cache-config.md
new file mode 100644
index 0000000..bf56153
--- /dev/null
+++ b/docs/options/cache-config.md
@@ -0,0 +1,7 @@
+# Cache Config
+
+Option `cache-config` will tell Sass Lint to cache the current configuration for each invocation of sass-lint. This option is disabled by default but you may consider enabling it if you're not planning on modifying your `.sass-lint.yml` config file as there will be a small performance gain.
+
+## Options
+
+* `true`/`false` (defaults to `false`)
diff --git a/docs/rules/bem-depth.md b/docs/rules/bem-depth.md
new file mode 100644
index 0000000..c799e58
--- /dev/null
+++ b/docs/rules/bem-depth.md
@@ -0,0 +1,26 @@
+# BEM Depth
+
+Rule `bem-depth` will enforce how many elements a BEM selector can contain.
+
+## Options
+
+* `max-depth`: number (defaults to `1`)
+
+## Examples
+
+When enabled (assuming `max-depth: 1`) the following are disallowed:
+
+```scss
+
+.block {
+  &__element {
+    &__subelement {
+      // two elements
+    }
+  }
+}
+
+.block__element__subelement__subelement-two {
+  // three elements
+}
+```
diff --git a/docs/rules/class-name-format.md b/docs/rules/class-name-format.md
new file mode 100644
index 0000000..f63b5c5
--- /dev/null
+++ b/docs/rules/class-name-format.md
@@ -0,0 +1,248 @@
+# Class Name Format
+
+Rule `class-name-format` will enforce a convention for class names.
+
+## Options
+
+* `allow-leading-underscore`: `true`/`false` (defaults to `true`)
+* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/),
+[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/),
+or a Regular Expression that the class name must match (e.g. `^[_A-Z]+$`)
+* `convention-explanation`: Custom explanation to display to the user if a class doesn't adhere to the convention
+* `ignore`: Array of names to ignore
+
+## Example 1
+
+Settings:
+- `allow-leading-underscore: true`
+- `convention: hyphenatedlowercase`
+
+When enabled, the following are allowed:
+
+```scss
+.hyphenated-lowercase {
+  content: '';
+
+  &._with-leading-underscore {
+    content: '';
+  }
+}
+
+.foo {
+  @extend .hyphenated-lowercase;
+}
+
+```
+
+When enabled, the following are disallowed:
+
+```scss
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.camelCase {
+  content: '';
+
+  @extend .snake_case;
+}
+```
+
+## Example 2
+
+Settings:
+- `allow-leading-underscore: false`
+- `convention: hyphenatedlowercase`
+
+When enabled, the following are allowed:
+
+```scss
+.hyphenated-lowercase {
+  content: '';
+
+  &.another-hyphenated-lowercase {
+    content: '';
+  }
+}
+
+.foo {
+  @extend .hyphenated-lowercase;
+}
+
+```
+
+When enabled, the following are disallowed:
+
+```scss
+._with-leading-underscore {
+  content: '';
+}
+
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.camelCase {
+  content: '';
+
+  @extend .snake_case;
+}
+```
+
+## Example 3
+
+Settings:
+- `convention: camelcase`
+
+When enabled, the following are allowed:
+
+```scss
+.camelCase {
+  content: '';
+}
+
+.foo {
+  @extend .anotherCamelCase;
+}
+```
+
+When enabled, the following are disallowed:
+
+```scss
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.foo {
+  @extend .snake_case;
+}
+```
+
+## Example 4
+
+Settings:
+- `convention: snakecase`
+
+When enabled, the following are allowed:
+
+```scss
+.snake_case {
+  content: '';
+}
+
+.foo {
+  @extend .another_snake_case;
+}
+```
+
+When enabled, the following are disallowed:
+
+```scss
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.foo {
+  @extend .camelCase;
+}
+```
+
+## Example 5
+
+Settings:
+- `convention: strictbem`
+
+When enabled, the following are allowed:
+
+```scss
+.block-name__elem-name {
+  content: '';
+}
+
+.owner-name_mod-name_mod-val {
+  content: '';
+}
+```
+
+When enabled, the following are disallowed:
+
+```scss
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.foo {
+  @extend .camelCase;
+}
+```
+
+## Example 6
+
+Settings:
+- `convention: hyphenatedbem`
+
+When enabled, the following are allowed:
+
+```scss
+.site-search {
+  color: blue;
+  width: 50px;
+  height: 100%;
+}
+
+.site-search__field {
+  text-decoration: underline;
+}
+
+.site-search--full {
+  color: green;
+}
+```
+
+When enabled, the following are disallowed:
+
+```scss
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.foo {
+  @extend .camelCase;
+}
+```
+
+## Example 7
+
+Settings:
+- `convention: ^[_A-Z]+$`
+- `convention-explanation: 'Class must contain only uppercase letters and underscores'`
+
+When enabled, the following are allowed:
+
+```scss
+.SCREAMING_SNAKE_CASE {
+  content: '';
+}
+
+.foo {
+  @extend .SCREAMING_SNAKE_CASE;
+}
+```
+
+When enabled, the following are disallowed:
+
+(Each line with a class will report `Class must contain only uppercase letters and underscores` when linted.)
+
+```scss
+.HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+.snake_case {
+  content: '';
+}
+
+.foo {
+  @extend .camelCase;
+}
+```
diff --git a/docs/rules/id-name-format.md b/docs/rules/id-name-format.md
new file mode 100644
index 0000000..7b2517c
--- /dev/null
+++ b/docs/rules/id-name-format.md
@@ -0,0 +1,188 @@
+# ID Name Format
+
+Rule `id-name-format` will enforce a convention for ids.
+
+## Options
+
+* `allow-leading-underscore`: `true`/`false` (defaults to `true`)
+* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`,
+or a Regular Expression that the id name must match (e.g. `^[_A-Z]+$`)
+* `convention-explanation`: Custom explanation to display to the user if an id doesn't adhere to the convention
+* `ignore`: Array of names to ignore
+
+## Example 1
+
+Settings:
+- `allow-leading-underscore: true`
+- `convention: hyphenatedlowercase`
+- `ignore: ['IGNORED_SELECTOR']`
+
+When enabled, the following are allowed:
+
+```scss
+#hyphenated-lowercase {
+  content: '';
+
+  &#_with-leading-underscore {
+    content: '';
+  }
+}
+
+#foo {
+  @extend #hyphenated-lowercase;
+}
+
+#IGNORED_SELECTOR {
+  content: '';
+}
+
+```
+
+When enabled, the following are disallowed:
+
+```scss
+#HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+#camelCase {
+  content: '';
+
+  @extend #snake_case;
+}
+```
+
+## Example 2
+
+Settings:
+- `allow-leading-underscore: false`
+- `convention: hyphenatedlowercase`
+
+When enabled, the following are allowed:
+
+```scss
+#hyphenated-lowercase {
+  content: '';
+
+  &#another-hyphenated-lowercase {
+    content: '';
+  }
+}
+
+#foo {
+  @extend #hyphenated-lowercase;
+}
+
+```
+
+When enabled, the following are disallowed:
+
+```scss
+#_with-leading-underscore {
+  content: '';
+}
+
+#HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+#camelCase {
+  content: '';
+
+  @extend #snake_case;
+}
+```
+
+## Example 3
+
+Settings:
+- `convention: camelcase`
+
+When enabled, the following are allowed:
+
+```scss
+#camelCase {
+  content: '';
+}
+
+#foo {
+  @extend #anotherCamelCase;
+}
+```
+
+When enabled, the following are disallowed:
+
+```scss
+#HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+#foo {
+  @extend #snake_case;
+}
+```
+
+## Example 4
+
+Settings:
+- `convention: snakecase`
+
+When enabled, the following are allowed:
+
+```scss
+#snake_case {
+  content: '';
+}
+
+#foo {
+  @extend #another_snake_case;
+}
+```
+
+When enabled, the following are disallowed:
+
+```scss
+#HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+#foo {
+  @extend #camelCase;
+}
+```
+
+## Example 5
+
+Settings:
+- `convention: ^[_A-Z]+$`
+- `convention-explanation: 'IDs must contain only uppercase letters and underscores'`
+
+When enabled, the following are allowed:
+
+```scss
+#SCREAMING_SNAKE_CASE {
+  content: '';
+}
+
+#FOO {
+  @extend #SCREAMING_SNAKE_CASE;
+}
+```
+
+When enabled, the following are disallowed:
+
+(Each line with an id will report `IDs must contain only uppercase letters and underscores` when linted.)
+
+```scss
+#HYPHENATED-UPPERCASE {
+  content: '';
+}
+
+#snake_case {
+  content: '';
+}
+
+#foo {
+  @extend #camelCase;
+}
+```
diff --git a/docs/rules/indentation.md b/docs/rules/indentation.md
index b2180ab..795b8ab 100644
--- a/docs/rules/indentation.md
+++ b/docs/rules/indentation.md
@@ -1,6 +1,6 @@
 # Indentation
 
-Rule `indentations` will enforce an indentation size (in spaces) and ensure that tabs and spaces are not mixed.
+Rule `indentation` will enforce an indentation size (in spaces) and ensure that tabs and spaces are not mixed.
 
 ## Options
 
diff --git a/docs/rules/no-trailing-zero.md b/docs/rules/no-trailing-zero.md
index cb7589b..032a520 100644
--- a/docs/rules/no-trailing-zero.md
+++ b/docs/rules/no-trailing-zero.md
@@ -18,4 +18,8 @@ When enabled, the following are disallowed:
 .foo {
   margin: 0.2500rem;
 }
+
+.foo {
+  margin: 4.0rem;
+}
 ```
diff --git a/docs/rules/property-sort-order.md b/docs/rules/property-sort-order.md
index c291ee1..5acb836 100644
--- a/docs/rules/property-sort-order.md
+++ b/docs/rules/property-sort-order.md
@@ -5,6 +5,7 @@ Rule `property-sort-order` will enforce the order in which declarations are writ
 ## Options
 
 * `order`: `'alphabetical'`, [`'concentric'`](http://rhodesmill.org/brandon/2011/concentric-css/), [`'recess'`](http://twitter.github.io/recess/), [`'smacss'`](http://smacss.com/book/formatting), or `[array of properties]` (defaults to `alphabetical`. Unknown properties are sorted alphabetically)
+* `ignore-custom-properties`: `true`/`false` (defaults to `false`)
 
 Property orders: https://github.com/sasstools/sass-lint/tree/develop/lib/config/property-sort-orders
 
@@ -29,3 +30,72 @@ When enabled (assuming `order: alphabetical`), the following are disallowed:
   content: 'baz';
 }
 ```
+
+### Custom Sort Orders
+
+You have the option to create your own custom property sort orders. These are specified in your `.sass-lint.yml` file as below:
+
+```yaml
+property-sort-order:
+  - 1
+  -
+    order:
+      - border
+      - display
+      - color
+```
+
+When the custom order is specified as above, the following are allowed:
+
+```scss
+.foo {
+  border: 1px solid blue;
+  display: block;
+  color: red;
+}
+```
+
+When the custom order is specified as above, the following are disallowed:
+
+```scss
+.foo {
+  display: block;
+  color: red;
+  border: 1px solid blue;
+}
+```
+
+### Ignore Custom Properties
+
+When `ignore-custom-properties: false` (assume `order: 'alphabetical'`) the following would be allowed
+
+```scss
+.foo {
+  border: 1px solid blue;
+  color: red;
+  composes: heading;
+  display: block;
+}
+```
+
+When `ignore-custom-properties: false` (assume `order: 'alphabetical'`) the following would be disallowed
+
+```scss
+.foo {
+  composes: heading; // not in alphabetical order
+  border: 1px solid blue;
+  color: red;
+  display: block;
+}
+```
+
+When `ignore-custom-properties: true` (assume `order: 'alphabetical'`) the following would be allowed
+
+```scss
+.foo {
+  composes: heading; // custom properties ignored
+  border: 1px solid blue;
+  color: red;
+  display: block;
+}
+```
diff --git a/docs/rules/property-units.md b/docs/rules/property-units.md
new file mode 100644
index 0000000..5335d54
--- /dev/null
+++ b/docs/rules/property-units.md
@@ -0,0 +1,50 @@
+# Property Units
+
+Rule `property-units` will disallow the use of units not specified in `global` or `per-property`. Units specified `per-property` will override the `global` units for that property.
+
+## Options
+
+* `global`: `['em', 'px', 'rem', etc]` defaults to [] or all units allowed
+* `per-property`: `{ width: ['rem', 'px', etc], height: ['rem', 'px', etc], }` defaults to {} or no property-specific units
+
+## Examples
+
+When enabled, `global` is set to `['px']`, and `per-property` is set to `{ width: ['rem'] }` the following are disallowed.
+
+```scss
+.literal {
+    height: 3rem;
+}
+
+.literal-property {
+    width: 3px;
+}
+
+.box-shadow {
+  box-shadow: 1em 1em black, 1em 1em black;
+}
+
+.background {
+  background: 1em solid white;
+}
+
+```
+
+When enabled, `globally-allowed-units` is set to `['em']`, and `units-allowed-for-properties` is set to `{ width: ['rem'] }` the following are allowed.
+
+```scss
+
+.variable {
+    width: $sizes.small;
+}
+
+.function {
+  color: test(2px);
+}
+
+//  using literals as property names
+$sizes: (
+  small: 2px
+);
+```
+
diff --git a/docs/rules/shorthand-values.md b/docs/rules/shorthand-values.md
index 0d37314..4ed0001 100644
--- a/docs/rules/shorthand-values.md
+++ b/docs/rules/shorthand-values.md
@@ -10,6 +10,11 @@ Rule `shorthand-values` will enforce that values in their shorthand form are as
 
 When `allowed-shorthands` is left at default, the following is enforced:
 
+```yml
+# .sass-lint.yml
+shorthand-values: 1
+```
+
 ```scss
 margin: 1px 1px 1px 1px;
 
@@ -31,6 +36,15 @@ margin: 1px 2px 3px;
 
 When `allowed-shorthands` is `[1]`, the following is enforced:
 
+```yml
+# .sass-lint.yml
+shorthand-values:
+  - 1
+  -
+    allowed-shorthands:
+      - 1
+```
+
 ```scss
 margin: 1px 1px 1px 1px;
 
@@ -46,6 +60,16 @@ margin: 1px 2px 1px 2px;
 
 When `allowed-shorthands` is `[1, 2]`, the following is enforced:
 
+```yml
+# .sass-lint.yml
+shorthand-values:
+  - 1
+  -
+    allowed-shorthands:
+      - 1
+      - 2
+```
+
 ```scss
 margin: 1px 1px 1px 1px;
 
diff --git a/docs/rules/space-around-operator.md b/docs/rules/space-around-operator.md
new file mode 100644
index 0000000..2bca233
--- /dev/null
+++ b/docs/rules/space-around-operator.md
@@ -0,0 +1,72 @@
+# Space Around Operator
+
+Rule `space-around-operator` will enforce whether or not a single space should be included before and after the following operators: `+`, `-`, `/`, `*`, `%`, `<`, `>` `==`, `!=`, `<=` and `>=`.
+
+## Options
+
+* `include`: `true`/`false` (defaults to `true`)
+
+## Examples
+
+When `include: true`, the following are allowed. When `include: false`, the following are disallowed:
+
+```scss
+.foo {
+  margin: 5px + 15px;
+}
+
+$foo: 1;
+$bar: 3;
+
+.foo {
+  margin: $foo + $bar + 'px';
+}
+
+$foo: 1 + 1;
+$bar: 2 - 1;
+
+@if $foo == $bar {
+  $baz: 1;
+}
+
+@if ($foo != $bar) {
+  $baz: 1;
+}
+```
+
+When `include: false`, the following are allowed. When `include: true`, the following are disallowed:
+
+```scss
+.foo {
+  margin: 5px+15px;
+}
+
+$foo: 1;
+$bar: 3;
+
+.foo {
+  margin: $foo+$bar+'px';
+}
+
+$foo: 1+1;
+$bar: 2-1;
+
+@if $foo==$bar {
+  $baz: 1;
+}
+
+@if ($foo!=$bar) {
+  $baz: 1;
+}
+```
+
+When `include: true` or `include: false` multiple spaces around operators are disallowed:
+
+```scss
+.foo {
+  margin: 5px   +       15px;
+}
+
+$foo: 1      +1;
+$bar: 2-     1;
+```
diff --git a/docs/sass-lint.yml b/docs/sass-lint.yml
index 436eb89..3de8a76 100644
--- a/docs/sass-lint.yml
+++ b/docs/sass-lint.yml
@@ -18,7 +18,7 @@ files:
 rules:
   extends-before-mixins: 2
   extends-before-declarations: 2
-  placeholders-in-extend: 2
+  placeholder-in-extend: 2
   mixins-before-declarations:
     - 2
     -
@@ -38,6 +38,13 @@ rules:
     - 2
     -
       size: 2
+  property-sort-order:
+    - 1
+    -
+      order:
+        - display
+        - margin
+      ignore-custom-properties: true
   variable-for-property:
     - 2
     -
diff --git a/index.js b/index.js
index 5af2e44..9888eb8 100644
--- a/index.js
+++ b/index.js
@@ -90,7 +90,7 @@ sassLint.lintFiles = function (files, options, configPath) {
   files.forEach(function (file) {
     var lint = that.lintText({
       'text': fs.readFileSync(file),
-      'format': path.extname(file).replace('.', ''),
+      'format': options.syntax ? options.syntax : path.extname(file).replace('.', ''),
       'filename': file
     }, options, configPath);
     results.push(lint);
diff --git a/lib/config.js b/lib/config.js
index d8c9e44..c424b81 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -5,7 +5,12 @@ var yaml = require('js-yaml'),
     path = require('path'),
     merge = require('merge');
 
-var defaults = yaml.safeLoad(fs.readFileSync(path.join(__dirname, 'config', 'sass-lint.yml'), 'utf8'));
+var cacheConfig = {},
+    cacheEnabled = false;
+
+var loadDefaults = function loadDefaults () {
+  return yaml.safeLoad(fs.readFileSync(path.join(__dirname, 'config', 'sass-lint.yml'), 'utf8'));
+};
 
 var findFile = function findFile (configPath, filename) {
   var HOME = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,
@@ -37,13 +42,28 @@ module.exports = function (options, configPath) {
       configMergeExists = false,
       optionsMerge = false,
       optionsMergeExists = false,
+      finalCacheExists = false,
       config = {},
-      finalConfig = {};
+      finalConfig = {},
+      defaults;
 
   // ensure our inline options and rules are not undefined
   options = options ? options : {};
   options.rules = options.rules ? options.rules : {};
 
+  // ensure our user defined cache option is respected
+  if (options.options && options.options.hasOwnProperty('cache-config')) {
+    if (options.options['cache-config'] && Object.keys(cacheConfig).length) {
+      return cacheConfig;
+    }
+  }
+  else {
+    // check to see if the config cache already exists and is enabled
+    if (cacheEnabled && Object.keys(cacheConfig).length) {
+      return cacheConfig;
+    }
+  }
+
   if (options.options && options.options['config-file']) {
     configPath = options.options['config-file'];
   }
@@ -87,7 +107,7 @@ module.exports = function (options, configPath) {
   // order of preference is inline options > user config > default config
   // merge-default-rules defaults to true so each step above should merge with the previous. If at any step merge-default-rules is set to
   // false it should skip that steps merge.
-
+  defaults = loadDefaults();
   finalConfig = merge.recursive(defaults, config, options);
 
   // if merge-default-rules is set to false in user config file then we essentially skip the merging with default rules by overwriting our
@@ -106,5 +126,14 @@ module.exports = function (options, configPath) {
     finalConfig.rules = merge.recursive(finalConfig.rules, options.rules);
   }
 
+  // check to see if our final config contains a cache-config value
+  finalCacheExists = (finalConfig.options && typeof finalConfig.options['cache-config'] !== 'undefined');
+
+  // set our global cache enabled flag here, it will be false by default
+  cacheEnabled = finalCacheExists ? finalConfig.options['cache-config'] : false;
+
+  // set our cached config to our final config
+  cacheConfig = cacheEnabled ? finalConfig : {};
+
   return finalConfig;
 };
diff --git a/lib/config/sass-lint.yml b/lib/config/sass-lint.yml
index cd69e8a..5acfaed 100644
--- a/lib/config/sass-lint.yml
+++ b/lib/config/sass-lint.yml
@@ -35,6 +35,7 @@ rules:
   no-url-protocols: 1
   no-vendor-prefixes: 1
   no-warn: 1
+  property-units: 0
 
   # Nesting
   force-attribute-nesting: 1
@@ -42,12 +43,15 @@ rules:
   force-pseudo-nesting: 1
 
   # Name Formats
+  class-name-format: 1
   function-name-format: 1
+  id-name-format: 0
   mixin-name-format: 1
   placeholder-name-format: 1
   variable-name-format: 1
 
   # Style Guide
+  bem-depth: 0
   border-zero: 1
   brace-style: 1
   clean-import-paths: 1
@@ -72,6 +76,7 @@ rules:
   space-before-bang: 1
   space-after-bang: 1
   space-between-parens: 1
+  space-around-operator: 1
 
   # Final Items
   trailing-semicolon: 1
diff --git a/lib/helpers.js b/lib/helpers.js
index ba4e9fe..71f2e46 100644
--- a/lib/helpers.js
+++ b/lib/helpers.js
@@ -3,7 +3,17 @@
 var util = require('util'),
     fs = require('fs'),
     path = require('path'),
-    yaml = require('js-yaml');
+    yaml = require('js-yaml'),
+    merge = require('merge');
+
+/**
+ * Easy access to the 'merge' library's cloning functionality
+ * @param   {object} obj Object to clone
+ * @returns {object}     Clone of obj
+ */
+var clone = function (obj) {
+  return merge(true, obj);
+};
 
 var helpers = {};
 
@@ -126,18 +136,51 @@ helpers.isLowerCase = function (str) {
   return false;
 };
 
+/**
+ * Determines if a given string adheres to camel-case format
+ * @param   {string}  str String to test
+ * @returns {boolean}     Whether str adheres to camel-case format
+ */
 helpers.isCamelCase = function (str) {
   return /^[a-z][a-zA-Z0-9]*$/.test(str);
 };
 
+/**
+ * Determines if a given string adheres to hyphenated-lowercase format
+ * @param   {string}  str String to test
+ * @returns {boolean}     Whether str adheres to hyphenated-lowercase format
+ */
 helpers.isHyphenatedLowercase = function (str) {
-  return !(/[_A-Z]/.test(str));
+  return !(/[^\-a-z0-9]/.test(str));
 };
 
+/**
+ * Determines if a given string adheres to snake-case format
+ * @param   {string}  str String to test
+ * @returns {boolean}     Whether str adheres to snake-case format
+ */
 helpers.isSnakeCase = function (str) {
   return !(/[^_a-z0-9]/.test(str));
 };
 
+/**
+ * Determines if a given string adheres to strict-BEM format
+ * @param   {string}  str String to test
+ * @returns {boolean}     Whether str adheres to strict-BEM format
+ */
+helpers.isStrictBEM = function (str) {
+  return /^[a-z](\-?[a-z0-9]+)*(__[a-z0-9](\-?[a-z0-9]+)*)?((_[a-z0-9](\-?[a-z0-9]+)*){2})?$/.test(str);
+};
+
+/**
+ * Determines if a given string adheres to hyphenated-BEM format
+ * @param   {string}  str String to test
+ * @returns {boolean}     Whether str adheres to hyphenated-BEM format
+ */
+helpers.isHyphenatedBEM = function (str) {
+  return !(/[A-Z]|-{3}|_{3}|[^_]_[^_]/.test(str));
+};
+
 helpers.isValidHex = function (str) {
   if (str.match(/^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)) {
     return true;
@@ -216,4 +259,199 @@ helpers.mapDelims = function (val) {
   return false;
 };
 
+/**
+ * Constructs a syntax complete selector
+ * @param {object} node - the current node / part of our selector
+ * @returns {object} constructedSelector - The constructed selector
+ * @returns {string} constructedSelector.content - The selector string
+ * @returns {string} constructedSelector.type - The type of the selector
+ */
+helpers.constructSelector = function (node) {
+  var content = node.content,
+      type = '';
+
+  if (node.type === 'id') {
+    content = '#' + node.content;
+    type = 'id';
+  }
+
+  else if (node.type === 'class') {
+    content = '.' + node.content;
+    type = 'class';
+  }
+
+  else if (node.type === 'ident') {
+    content = node.content;
+    type = 'selector';
+  }
+
+  else if (node.type === 'attribute') {
+    var selector = '[';
+
+    node.forEach(function (attrib) {
+      var selectorPiece = helpers.constructSelector(attrib);
+
+      selector += selectorPiece.content;
+    });
+
+    content = selector + ']';
+    type = 'attribute';
+  }
+
+  else if (node.type === 'pseudoClass') {
+    content = ':' + node.content;
+    type = 'pseudoClass';
+  }
+
+  else if (node.type === 'pseudoElement') {
+    content = '::' + node.content;
+    type = 'pseudoElement';
+  }
+
+  else if (node.type === 'nth') {
+    content = '(' + node.content + ')';
+    type = 'nth';
+  }
+
+  else if (node.type === 'nthSelector') {
+    var nthSelector = ':';
+
+    node.forEach(function (attrib) {
+      var selectorPiece = helpers.constructSelector(attrib);
+
+      nthSelector += selectorPiece.content;
+    });
+
+    content = nthSelector;
+    type = 'nthSelector';
+  }
+
+  else if (node.type === 'space') {
+    content = ' ';
+  }
+
+  else if (node.type === 'parentSelector') {
+    content = node.content;
+    type = 'parentSelector';
+  }
+
+  else if (node.type === 'combinator') {
+    content = node.content;
+    type = 'combinator';
+  }
+
+  return {
+    content: content,
+    type: type
+  };
+};
+
+/**
+ * Checks the current selector value against the previous selector value and assesses whether they are
+ * a) currently an enforced selector type for nesting (user specified - all true by default)
+ * b) whether they should be nested
+ * @param {object} currentVal - the current node / part of our selector
+ * @param {object} previousVal - the previous node / part of our selector
+ * @param {array} elements - a complete array of nestable selector types
+ * @param {array} nestable - an array of the types of selector to nest
+ * @returns {object} Returns whether we or we should nest and the previous val
+ */
+helpers.isNestable = function (currentVal, previousVal, elements, nestable) {
+  // check if they are nestable by checking the previous element against one
+  // of the user specified selector types
+  if (elements.indexOf(previousVal) !== -1 && nestable.indexOf(currentVal) !== -1) {
+    return true;
+  }
+
+  return false;
+};
+
+/**
+ * Tries to traverse the AST, following a specified path
+ * @param   {object}  node           Starting node
+ * @param   {array}   traversalPath  Array of Node types to traverse, starting from the first element
+ * @returns {array}                  Nodes at the end of the path. Empty array if the traversal failed
+ */
+helpers.attemptTraversal = function (node, traversalPath) {
+  var i,
+      nextNodeList,
+      currentNodeList = [],
+      processChildNode = function processChildNode (child) {
+        child.forEach(traversalPath[i], function (n) {
+          nextNodeList.push(n);
+        });
+      };
+
+  node.forEach(traversalPath[0], function (n) {
+    currentNodeList.push(n);
+  });
+
+  for (i = 1; i < traversalPath.length; i++) {
+    if (currentNodeList.length === 0) {
+      return [];
+    }
+
+    nextNodeList = [];
+    currentNodeList.forEach(processChildNode);
+    currentNodeList = nextNodeList;
+  }
+
+  return currentNodeList;
+};
+
+/**
+ * Collects all suffix extensions for a selector
+ * @param   {object}  ruleset      ASTNode of type ruleset, containing a selector with nested suffix extensions
+ * @param   {string}  selectorType Node type of the selector (e.g. class, id)
+ * @returns {array}                Array of Nodes with the content property replaced by the complete selector
+ *                                       (without '.', '#', etc) resulting from suffix extensions
+ */
+helpers.collectSuffixExtensions = function (ruleset, selectorType) {
+  var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', 'simpleSelector', selectorType, 'ident']),
+      childSuffixes = helpers.attemptTraversal(ruleset, ['block', 'ruleset']),
+      selectorList = [];
+
+  if (parentSelectors.length === 0) {
+    return [];
+  }
+
+  // Goes recursively through all nodes that look like suffix extensions. There may be multiple parents that are
+  // extended, so lots of looping is required.
+  var processChildSuffix = function (child, parents) {
+    var currentParents = [],
+        selectors = helpers.attemptTraversal(child, ['selector', 'simpleSelector']),
+        nestedChildSuffixes = helpers.attemptTraversal(child, ['block', 'ruleset']);
+
+    selectors.forEach(function (childSuffixNode) {
+      var extendedNode;
+
+      if (childSuffixNode.length >= 2 &&
+        childSuffixNode.contains('parentSelector') &&
+        childSuffixNode.contains('ident')) {
+
+        // append suffix extension to all parent selectors
+        parents.forEach(function (parent) {
+          // clone so we don't modify the actual AST
+          extendedNode = clone(childSuffixNode.first('ident'));
+          extendedNode.content = parent.content + extendedNode.content;
+
+          currentParents.push(extendedNode);
+        });
+      }
+    });
+
+    selectorList = selectorList.concat(currentParents);
+
+    nestedChildSuffixes.forEach(function (childSuffix) {
+      processChildSuffix(childSuffix, currentParents);
+    });
+  };
+
+  childSuffixes.forEach(function (childSuffix) {
+    processChildSuffix(childSuffix, parentSelectors);
+  });
+
+  return parentSelectors.concat(selectorList);
+};
+
 module.exports = helpers;
diff --git a/lib/rules/bem-depth.js b/lib/rules/bem-depth.js
new file mode 100644
index 0000000..9910b76
--- /dev/null
+++ b/lib/rules/bem-depth.js
@@ -0,0 +1,72 @@
+'use strict';
+
+var helpers = require('../helpers');
+
+/**
+ * Get number of BEM elements in
+ * @param   {string}  str String representing a class selector
+ * @returns {integer}     Number of BEM elements in str
+ */
+var bemDepth = function (str) {
+  var elements = str.split('__').length;
+
+  if (elements >= 2) {
+    return elements - 1;
+  }
+
+  return 0;
+};
+
+module.exports = {
+  'name': 'bem-depth',
+  'defaults': {
+    'max-depth': 1
+  },
+  'detect': function (ast, parser) {
+    var result = [];
+
+    ast.traverseByTypes(['ruleset', 'placeholder'], function (node) {
+      var name,
+          depth,
+          selectorAndExtensions,
+          maxDepth = parser.options['max-depth'];
+
+      if (node.is('placeholder')) {
+        name = node.first('ident') && node.first('ident').content;
+        depth = bemDepth(name);
+
+        if (name && depth > maxDepth) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': node.start.line,
+            'column': node.start.column,
+            'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ',
+              depth, ' were found.'].join(''),
+            'severity': parser.severity
+          });
+        }
+      }
+      else {
+        selectorAndExtensions = helpers.collectSuffixExtensions(node, 'class');
+
+        selectorAndExtensions.forEach(function (selector) {
+          name = selector.content;
+          depth = bemDepth(name);
+
+          if (depth > maxDepth) {
+            result = helpers.addUnique(result, {
+              'ruleId': parser.rule.name,
+              'line': selector.start.line,
+              'column': selector.start.column,
+              'message': ['Selector \'.', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ',
+                depth, ' were found.'].join(''),
+              'severity': parser.severity
+            });
+          }
+        });
+      }
+    });
+
+    return result;
+  }
+};
diff --git a/lib/rules/class-name-format.js b/lib/rules/class-name-format.js
new file mode 100644
index 0000000..8ecfe2f
--- /dev/null
+++ b/lib/rules/class-name-format.js
@@ -0,0 +1,85 @@
+'use strict';
+
+var helpers = require('../helpers');
+
+module.exports = {
+  'name': 'class-name-format',
+  'defaults': {
+    'allow-leading-underscore': true,
+    'convention': 'hyphenatedlowercase',
+    'convention-explanation': false,
+    'ignore': []
+  },
+  'detect': function (ast, parser) {
+    var result = [];
+
+    ast.traverseByType('ruleset', function (ruleset) {
+      var selectorAndExtensions = helpers.collectSuffixExtensions(ruleset, 'class');
+
+      selectorAndExtensions.forEach(function (node) {
+        var name = node.content,
+            strippedName,
+            violationMessage = false;
+
+        if (parser.options.ignore.indexOf(name) !== -1) {
+          return;
+        }
+
+        strippedName = name;
+
+        if (parser.options['allow-leading-underscore'] && name[0] === '_') {
+          strippedName = name.slice(1);
+        }
+
+        switch (parser.options.convention) {
+        case 'hyphenatedlowercase':
+          if (!helpers.isHyphenatedLowercase(strippedName)) {
+            violationMessage = 'Class \'.' + name + '\' should be written in lowercase with hyphens';
+          }
+          break;
+        case 'camelcase':
+          if (!helpers.isCamelCase(strippedName)) {
+            violationMessage = 'Class \'.' + name + '\' should be written in camelCase';
+          }
+          break;
+        case 'snakecase':
+          if (!helpers.isSnakeCase(strippedName)) {
+            violationMessage = 'Class \'.' + name + '\' should be written in snake_case';
+          }
+          break;
+        case 'strictbem':
+          if (!helpers.isStrictBEM(strippedName)) {
+            violationMessage = 'Class \'.' + name + '\' should be written in BEM (Block Element Modifier) format';
+          }
+          break;
+        case 'hyphenatedbem':
+          if (!helpers.isHyphenatedBEM(strippedName)) {
+            violationMessage = 'Class \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format';
+          }
+          break;
+        default:
+          if (!(new RegExp(parser.options.convention).test(strippedName))) {
+            violationMessage = 'Class \'.' + name + '\' should match regular expression /' + parser.options.convention + '/';
+
+            // convention-message overrides violationMessage
+            if (parser.options['convention-explanation']) {
+              violationMessage = parser.options['convention-explanation'];
+            }
+          }
+        }
+
+        if (violationMessage) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': node.start.line,
+            'column': node.start.column,
+            'message': violationMessage,
+            'severity': parser.severity
+          });
+        }
+      });
+    });
+
+    return result;
+  }
+};
diff --git a/lib/rules/force-attribute-nesting.js b/lib/rules/force-attribute-nesting.js
index e18d4e8..861e752 100644
--- a/lib/rules/force-attribute-nesting.js
+++ b/lib/rules/force-attribute-nesting.js
@@ -1,147 +1,40 @@
 'use strict';
 
-var helpers = require('../helpers');
+var helpers = require('../helpers'),
+    capitalize = require('lodash.capitalize'),
+    kebabcase = require('lodash.kebabcase');
 
 // Our nestable selector types, separated by type for ease of use with rules
 // we replace ident with 'selector' for readability'
 var nestableElements = ['selector', 'class', 'id'],
     nestableAttributes = ['attribute'],
-    nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector'],
-    // we need to keep track of the current and previous selector type
-    prevVal = '',
-    curVal = '',
-
-    /**
-     * constructSelector
-     * Constructs a syntax complete selector for our selector matching and warning output
-     * @param {object} val - the current node / part of our selector
-     * @returns {string} content - The current node with correct syntax e.g. class my-class = '.my-class'
-     */
-
-    constructSelector = function (val) {
-
-      var content = val.content;
-
-      if (val.is('id')) {
-        content = '#' + val.content;
-        curVal = 'id';
-      }
-
-      else if (val.is('class')) {
-        content = '.' + val.content;
-        curVal = 'class';
-      }
-
-      else if (val.is('ident')) {
-        content = val.content;
-        curVal = 'selector';
-      }
-
-      else if (val.is('attribute')) {
-        var selector = '[';
-        val.forEach( function (attrib) {
-          selector += constructSelector(attrib);
-        });
-        content = selector + ']';
-        curVal = 'attribute';
-      }
-
-      else if (val.is('pseudoClass')) {
-        content = ':' + val.content;
-        curVal = 'pseudoClass';
-      }
-
-      else if (val.is('pseudoElement')) {
-        content = '::' + val.content;
-        curVal = 'pseudoElement';
-      }
-
-      else if (val.is('nth')) {
-        content = '(' + val.content + ')';
-        curVal = 'nth';
-      }
-
-      else if (val.is('nthSelector')) {
-        var nthSelector = ':';
-        val.forEach( function (attrib) {
-          nthSelector += constructSelector(attrib);
-        });
-        content = nthSelector;
-        curVal = 'nthSelector';
-      }
-
-      else if (val.is('space')) {
-        content = ' ';
-      }
-
-      else if (val.is('parentSelector')) {
-        content = val.content;
-        curVal = 'parentSelector';
-      }
-
-      else if (val.is('combinator')) {
-        content = val.content;
-        curVal = 'combinator';
-      }
-
-      return content;
-
-    },
-
-    /**
-     * resets the current value and previous value markers to default
-     */
-
-    resetNesting = function () {
-      curVal = '';
-      prevVal = '';
-    },
-
-    /**
-     * checkNesting
-     * Checks the current selector value against the previous selector value and assesses whether they are
-     * a) currently an enforced selector type for nesting (user specified - all true by default)
-     * b) whether they should be nested
-     * @param {object} val - the current node / part of our selector
-     * @param {string} curSelector - the current selector string
-     * @param {object} parser - and instance of our parser
-     * @param {array} result - an array of result objects
-     */
-
-    checkNesting = function (val, parser, result) {
-      var elements = nestableElements.concat(nestableAttributes, nestablePseudo);
-      // check if they are nestable by checking the previous element against one of the user specified
-      // selector types
-      if (elements.indexOf(prevVal) !== -1 && nestableAttributes.indexOf(curVal) !== -1) {
-        helpers.addUnique(result, {
-          'ruleId': parser.rule.name,
-          'line': val.start.line,
-          'column': val.start.column,
-          'message': curVal + ' `' + constructSelector(val) + '` should be nested within its parent ' + prevVal,
-          'severity': parser.severity
-        });
-      }
-
-      if (curVal !== '') {
-        prevVal = curVal;
-        curVal = '';
-      }
-    };
+    nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector'];
+
+/**
+ * Formats a string from camelCase to hyphens and capitalizes
+ * @param {string} str - The string to be formatted
+ * @returns {string} A hyphenated and capitalized string
+ */
+var formatOutput = function (str) {
+  return capitalize(kebabcase(str));
+};
 
 module.exports = {
   'name': 'force-attribute-nesting',
   'defaults': {},
   'detect': function (ast, parser) {
-    var result = [];
+    var result = [],
+        elements = nestableElements.concat(nestableAttributes, nestablePseudo);
 
     ast.traverseByType('ruleset', function (ruleset) {
       var delimOrder = [];
 
       ruleset.forEach('selector', function (selector) {
+        // Where we'll store the previous value
+        var previousVal;
 
-        resetNesting();
         // Keep track of the order of elements & delimeters
-        selector.forEach( function (el) {
+        selector.forEach(function (el) {
           var curNode = helpers.mapDelims(el);
 
           if (curNode !== false) {
@@ -150,35 +43,49 @@ module.exports = {
         });
 
         selector.forEach('simpleSelector', function (simpleSelector) {
-
           // check if the next selector is proceeded by a delimiter
           // if it is add it to the curSelector output
-
           var nextType = delimOrder[0];
 
           if (nextType === 'd') {
-            resetNesting();
+            // Empty the previous value store
+            previousVal = null;
 
-            // remove the next delim and selector from our order as we are only looping over selectors
+            // remove the next delim and selector from our order as we are only
+            // looping over selectors
             delimOrder.splice(0, 2);
           }
           else {
-
-            // if no delim then just remove the current selectors marker in the order array
+            // if no delim then just remove the current selectors marker in the
+            // order array
             delimOrder.splice(0, 1);
           }
 
-          simpleSelector.forEach(function (val) {
-
-            // construct our selector from its content parts
-            constructSelector(val);
-
-            checkNesting(val, parser, result);
-
+          // Loop each selector while checking if it should be nested
+          simpleSelector.forEach(function (node) {
+            // Construct the selector and store the current selector value
+            var constructedSelector = helpers.constructSelector(node),
+                currentVal = constructedSelector.type;
+
+            if (helpers.isNestable(currentVal, previousVal, elements, nestableAttributes)) {
+              helpers.addUnique(result, {
+                'ruleId': parser.rule.name,
+                'line': node.start.line,
+                'column': node.start.column,
+                'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal,
+                'severity': parser.severity
+              });
+            }
+
+            if (currentVal) {
+              // Store current value as previous and continue with the loop
+              previousVal = currentVal;
+            }
           });
         });
       });
     });
+
     return result;
   }
 };
diff --git a/lib/rules/force-element-nesting.js b/lib/rules/force-element-nesting.js
index eae7972..7e9692b 100644
--- a/lib/rules/force-element-nesting.js
+++ b/lib/rules/force-element-nesting.js
@@ -1,147 +1,40 @@
 'use strict';
 
-var helpers = require('../helpers');
+var helpers = require('../helpers'),
+    capitalize = require('lodash.capitalize'),
+    kebabcase = require('lodash.kebabcase');
 
 // Our nestable selector types, separated by type for ease of use with rules
 // we replace ident with 'selector' for readability'
 var nestableElements = ['selector', 'class', 'id'],
     nestableAttributes = ['attribute'],
-    nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector'],
-    // we need to keep track of the current and previous selector type
-    prevVal = '',
-    curVal = '',
-
-    /**
-     * constructSelector
-     * Constructs a syntax complete selector for our selector matching and warning output
-     * @param {object} val - the current node / part of our selector
-     * @returns {string} content - The current node with correct syntax e.g. class my-class = '.my-class'
-     */
-
-    constructSelector = function (val) {
-
-      var content = val.content;
-
-      if (val.is('id')) {
-        content = '#' + val.content;
-        curVal = 'id';
-      }
-
-      else if (val.is('class')) {
-        content = '.' + val.content;
-        curVal = 'class';
-      }
-
-      else if (val.is('ident')) {
-        content = val.content;
-        curVal = 'selector';
-      }
-
-      else if (val.is('attribute')) {
-        var selector = '[';
-        val.forEach( function (attrib) {
-          selector += constructSelector(attrib);
-        });
-        content = selector + ']';
-        curVal = 'attribute';
-      }
-
-      else if (val.is('pseudoClass')) {
-        content = ':' + val.content;
-        curVal = 'pseudoClass';
-      }
-
-      else if (val.is('pseudoElement')) {
-        content = '::' + val.content;
-        curVal = 'pseudoElement';
-      }
-
-      else if (val.is('nth')) {
-        content = '(' + val.content + ')';
-        curVal = 'nth';
-      }
-
-      else if (val.is('nthSelector')) {
-        var nthSelector = ':';
-        val.forEach( function (attrib) {
-          nthSelector += constructSelector(attrib);
-        });
-        content = nthSelector;
-        curVal = 'nthSelector';
-      }
-
-      else if (val.is('space')) {
-        content = ' ';
-      }
-
-      else if (val.is('parentSelector')) {
-        content = val.content;
-        curVal = 'parentSelector';
-      }
-
-      else if (val.is('combinator')) {
-        content = val.content;
-        curVal = 'combinator';
-      }
-
-      return content;
-
-    },
-
-    /**
-     * resets the current value and previous value markers to default
-     */
-
-    resetNesting = function () {
-      curVal = '';
-      prevVal = '';
-    },
-
-    /**
-     * checkNesting
-     * Checks the current selector value against the previous selector value and assesses whether they are
-     * a) currently an enforced selector type for nesting (user specified - all true by default)
-     * b) whether they should be nested
-     * @param {object} val - the current node / part of our selector
-     * @param {string} curSelector - the current selector string
-     * @param {object} parser - and instance of our parser
-     * @param {array} result - an array of result objects
-     */
-
-    checkNesting = function (val, parser, result) {
-      var elements = nestableElements.concat(nestableAttributes, nestablePseudo);
-      // check if they are nestable by checking the previous element against one of the user specified
-      // selector types
-      if (elements.indexOf(prevVal) !== -1 && nestableElements.indexOf(curVal) !== -1) {
-        helpers.addUnique(result, {
-          'ruleId': parser.rule.name,
-          'line': val.start.line,
-          'column': val.start.column,
-          'message': curVal + ' `' + constructSelector(val) + '` should be nested within its parent ' + prevVal,
-          'severity': parser.severity
-        });
-      }
-
-      if (curVal !== '') {
-        prevVal = curVal;
-        curVal = '';
-      }
-    };
+    nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector'];
+
+/**
+ * Formats a string from camelCase to hyphens and capitalizes
+ * @param {string} str - The string to be formatted
+ * @returns {string} A hyphenated and capitalized string
+ */
+var formatOutput = function (str) {
+  return capitalize(kebabcase(str));
+};
 
 module.exports = {
   'name': 'force-element-nesting',
   'defaults': {},
   'detect': function (ast, parser) {
-    var result = [];
+    var result = [],
+        elements = nestableElements.concat(nestableAttributes, nestablePseudo);
 
     ast.traverseByType('ruleset', function (ruleset) {
       var delimOrder = [];
 
       ruleset.forEach('selector', function (selector) {
+        // Where we'll store the previous value
+        var previousVal;
 
-        resetNesting();
         // Keep track of the order of elements & delimeters
-        selector.forEach( function (el) {
+        selector.forEach(function (el) {
           var curNode = helpers.mapDelims(el);
 
           if (curNode !== false) {
@@ -150,35 +43,49 @@ module.exports = {
         });
 
         selector.forEach('simpleSelector', function (simpleSelector) {
-
           // check if the next selector is proceeded by a delimiter
           // if it is add it to the curSelector output
-
           var nextType = delimOrder[0];
 
           if (nextType === 'd') {
-            resetNesting();
+            // Empty the previous value store
+            previousVal = null;
 
-            // remove the next delim and selector from our order as we are only looping over selectors
+            // remove the next delim and selector from our order as we are only
+            // looping over selectors
             delimOrder.splice(0, 2);
           }
           else {
-
-            // if no delim then just remove the current selectors marker in the order array
+            // if no delim then just remove the current selectors marker in the
+            // order array
             delimOrder.splice(0, 1);
           }
 
-          simpleSelector.forEach(function (val) {
-
-            // construct our selector from its content parts
-            constructSelector(val);
-
-            checkNesting(val, parser, result);
-
+          // Loop each selector while checking if it should be nested
+          simpleSelector.forEach(function (node) {
+            // Construct the selector and store the current selector value
+            var constructedSelector = helpers.constructSelector(node),
+                currentVal = constructedSelector.type;
+
+            if (helpers.isNestable(currentVal, previousVal, elements, nestableElements)) {
+              helpers.addUnique(result, {
+                'ruleId': parser.rule.name,
+                'line': node.start.line,
+                'column': node.start.column,
+                'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal,
+                'severity': parser.severity
+              });
+            }
+
+            if (currentVal) {
+              // Store current value as previous and continue with the loop
+              previousVal = currentVal;
+            }
           });
         });
       });
     });
+
     return result;
   }
 };
diff --git a/lib/rules/force-pseudo-nesting.js b/lib/rules/force-pseudo-nesting.js
index 5832a46..3f1b2d7 100644
--- a/lib/rules/force-pseudo-nesting.js
+++ b/lib/rules/force-pseudo-nesting.js
@@ -1,147 +1,47 @@
 'use strict';
 
-var helpers = require('../helpers');
+var helpers = require('../helpers'),
+    capitalize = require('lodash.capitalize'),
+    kebabcase = require('lodash.kebabcase');
 
 // Our nestable selector types, separated by type for ease of use with rules
 // we replace ident with 'selector' for readability'
 var nestableElements = ['selector', 'class', 'id'],
     nestableAttributes = ['attribute'],
-    nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector'],
-    // we need to keep track of the current and previous selector type
-    prevVal = '',
-    curVal = '',
-
-    /**
-     * constructSelector
-     * Constructs a syntax complete selector for our selector matching and warning output
-     * @param {object} val - the current node / part of our selector
-     * @returns {string} content - The current node with correct syntax e.g. class my-class = '.my-class'
-     */
-
-    constructSelector = function (val) {
-
-      var content = val.content;
-
-      if (val.is('id')) {
-        content = '#' + val.content;
-        curVal = 'id';
-      }
-
-      else if (val.is('class')) {
-        content = '.' + val.content;
-        curVal = 'class';
-      }
-
-      else if (val.is('ident')) {
-        content = val.content;
-        curVal = 'selector';
-      }
-
-      else if (val.is('attribute')) {
-        var selector = '[';
-        val.forEach( function (attrib) {
-          selector += constructSelector(attrib);
-        });
-        content = selector + ']';
-        curVal = 'attribute';
-      }
-
-      else if (val.is('pseudoClass')) {
-        content = ':' + val.content;
-        curVal = 'pseudoClass';
-      }
-
-      else if (val.is('pseudoElement')) {
-        content = '::' + val.content;
-        curVal = 'pseudoElement';
-      }
-
-      else if (val.is('nth')) {
-        content = '(' + val.content + ')';
-        curVal = 'nth';
-      }
-
-      else if (val.is('nthSelector')) {
-        var nthSelector = ':';
-        val.forEach( function (attrib) {
-          nthSelector += constructSelector(attrib);
-        });
-        content = nthSelector;
-        curVal = 'nthSelector';
-      }
-
-      else if (val.is('space')) {
-        content = ' ';
-      }
-
-      else if (val.is('parentSelector')) {
-        content = val.content;
-        curVal = 'parentSelector';
-      }
-
-      else if (val.is('combinator')) {
-        content = val.content;
-        curVal = 'combinator';
-      }
-
-      return content;
-
-    },
-
-    /**
-     * resets the current value and previous value markers to default
-     */
-
-    resetNesting = function () {
-      curVal = '';
-      prevVal = '';
-    },
-
-    /**
-     * checkNesting
-     * Checks the current selector value against the previous selector value and assesses whether they are
-     * a) currently an enforced selector type for nesting (user specified - all true by default)
-     * b) whether they should be nested
-     * @param {object} val - the current node / part of our selector
-     * @param {string} curSelector - the current selector string
-     * @param {object} parser - and instance of our parser
-     * @param {array} result - an array of result objects
-     */
-
-    checkNesting = function (val, parser, result) {
-      var elements = nestableElements.concat(nestableAttributes, nestablePseudo);
-      // check if they are nestable by checking the previous element against one of the user specified
-      // selector types
-      if (elements.indexOf(prevVal) !== -1 && nestablePseudo.indexOf(curVal) !== -1) {
-        helpers.addUnique(result, {
-          'ruleId': parser.rule.name,
-          'line': val.start.line,
-          'column': val.start.column,
-          'message': curVal + ' `' + constructSelector(val) + '` should be nested within its parent ' + prevVal,
-          'severity': parser.severity
-        });
-      }
+    nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector'];
+
+/**
+ * Formats a string from camelCase to hyphens and capitalizes when not
+ * nth related
+ * @param {string} str - The string to be formatted
+ * @returns {string} A hyphenated (and possibly capitalized) string
+ */
+var formatOutput = function (str) {
+  str = kebabcase(str);
+
+  if (str.indexOf('nth') === -1) {
+    str = capitalize(str);
+  }
 
-      if (curVal !== '') {
-        prevVal = curVal;
-        curVal = '';
-      }
-    };
+  return str;
+};
 
 module.exports = {
   'name': 'force-pseudo-nesting',
   'defaults': {},
   'detect': function (ast, parser) {
-    var result = [];
+    var result = [],
+        elements = nestableElements.concat(nestableAttributes, nestablePseudo);
 
     ast.traverseByType('ruleset', function (ruleset) {
       var delimOrder = [];
 
       ruleset.forEach('selector', function (selector) {
+        // Where we'll store the previous value
+        var previousVal;
 
-        resetNesting();
         // Keep track of the order of elements & delimeters
-        selector.forEach( function (el) {
+        selector.forEach(function (el) {
           var curNode = helpers.mapDelims(el);
 
           if (curNode !== false) {
@@ -150,35 +50,49 @@ module.exports = {
         });
 
         selector.forEach('simpleSelector', function (simpleSelector) {
-
           // check if the next selector is proceeded by a delimiter
           // if it is add it to the curSelector output
-
           var nextType = delimOrder[0];
 
           if (nextType === 'd') {
-            resetNesting();
+            // Empty the previous value store
+            previousVal = null;
 
-            // remove the next delim and selector from our order as we are only looping over selectors
+            // remove the next delim and selector from our order as we are only
+            // looping over selectors
             delimOrder.splice(0, 2);
           }
           else {
-
-            // if no delim then just remove the current selectors marker in the order array
+            // if no delim then just remove the current selectors marker in the
+            // order array
             delimOrder.splice(0, 1);
           }
 
-          simpleSelector.forEach(function (val) {
-
-            // construct our selector from its content parts
-            constructSelector(val);
-
-            checkNesting(val, parser, result);
-
+          // Loop each selector while checking if it should be nested
+          simpleSelector.forEach(function (node) {
+            // Construct the selector and store the current selector value
+            var constructedSelector = helpers.constructSelector(node),
+                currentVal = constructedSelector.type;
+
+            if (helpers.isNestable(currentVal, previousVal, elements, nestablePseudo)) {
+              helpers.addUnique(result, {
+                'ruleId': parser.rule.name,
+                'line': node.start.line,
+                'column': node.start.column,
+                'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal,
+                'severity': parser.severity
+              });
+            }
+
+            if (currentVal) {
+              // Store current value as previous and continue with the loop
+              previousVal = currentVal;
+            }
           });
         });
       });
     });
+
     return result;
   }
 };
diff --git a/lib/rules/function-name-format.js b/lib/rules/function-name-format.js
index 56b1e37..163993d 100644
--- a/lib/rules/function-name-format.js
+++ b/lib/rules/function-name-format.js
@@ -1,20 +1,12 @@
 // Note that this file is nearly identical to mixin-name-format.js, placeholder-name-format.js, and variable-name-format.js
 'use strict';
 
-var helpers = require('../helpers');
-var whitelist = [
-  'rotateX',
-  'rotateY',
-  'rotateZ',
-  'scaleX',
-  'scaleY',
-  'scaleZ',
-  'skewX',
-  'skewY',
-  'translateX',
-  'translateY',
-  'translateZ'
-];
+var helpers = require('../helpers'),
+    yaml = require('js-yaml'),
+    fs = require('fs'),
+    path = require('path');
+
+var whitelist = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '../../data', 'functions.yml'), 'utf8')).split(' ');
 
 module.exports = {
   'name': 'function-name-format',
diff --git a/lib/rules/id-name-format.js b/lib/rules/id-name-format.js
new file mode 100644
index 0000000..6a7801f
--- /dev/null
+++ b/lib/rules/id-name-format.js
@@ -0,0 +1,75 @@
+'use strict';
+
+var helpers = require('../helpers');
+
+module.exports = {
+  'name': 'id-name-format',
+  'defaults': {
+    'allow-leading-underscore': true,
+    'convention': 'hyphenatedlowercase',
+    'convention-explanation': false,
+    'ignore': []
+  },
+  'detect': function (ast, parser) {
+    var result = [];
+
+    ast.traverseByType('ruleset', function (ruleset) {
+      var selectorAndExtensions = helpers.collectSuffixExtensions(ruleset, 'id');
+
+      selectorAndExtensions.forEach(function (node) {
+        var name = node.content,
+            strippedName,
+            violationMessage = false;
+
+        if (parser.options.ignore.indexOf(name) !== -1) {
+          return;
+        }
+
+        strippedName = name;
+
+        if (parser.options['allow-leading-underscore'] && name[0] === '_') {
+          strippedName = name.slice(1);
+        }
+
+        switch (parser.options.convention) {
+        case 'hyphenatedlowercase':
+          if (!helpers.isHyphenatedLowercase(strippedName)) {
+            violationMessage = 'ID \'#' + name + '\' should be written in lowercase with hyphens';
+          }
+          break;
+        case 'camelcase':
+          if (!helpers.isCamelCase(strippedName)) {
+            violationMessage = 'ID \'#' + name + '\' should be written in camelCase';
+          }
+          break;
+        case 'snakecase':
+          if (!helpers.isSnakeCase(strippedName)) {
+            violationMessage = 'ID \'#' + name + '\' should be written in snake_case';
+          }
+          break;
+        default:
+          if (!(new RegExp(parser.options.convention).test(strippedName))) {
+            violationMessage = 'ID \'#' + name + '\' should match regular expression /' + parser.options.convention + '/';
+
+            // convention-message overrides violationMessage
+            if (parser.options['convention-explanation']) {
+              violationMessage = parser.options['convention-explanation'];
+            }
+          }
+        }
+
+        if (violationMessage) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': node.start.line,
+            'column': node.start.column,
+            'message': violationMessage,
+            'severity': parser.severity
+          });
+        }
+      });
+    });
+
+    return result;
+  }
+};
diff --git a/lib/rules/no-trailing-zero.js b/lib/rules/no-trailing-zero.js
index f959d56..98e1205 100644
--- a/lib/rules/no-trailing-zero.js
+++ b/lib/rules/no-trailing-zero.js
@@ -2,7 +2,7 @@
 
 var helpers = require('../helpers');
 
-var trailingZeroRegex = /^(\d+\.|\.)+(\d*?[1-9])0+$/;
+var trailingZeroRegex = /^(\d+\.|\.)+(\d*?)0+$/;
 
 module.exports = {
   'name': 'no-trailing-zero',
diff --git a/lib/rules/property-sort-order.js b/lib/rules/property-sort-order.js
index 1df6fbf..adbecbf 100644
--- a/lib/rules/property-sort-order.js
+++ b/lib/rules/property-sort-order.js
@@ -1,6 +1,11 @@
 'use strict';
 
-var helpers = require('../helpers');
+var helpers = require('../helpers'),
+    yaml = require('js-yaml'),
+    fs = require('fs'),
+    path = require('path');
+
+var propertyCheckList = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '../../data', 'properties.yml'), 'utf8')).split(' ');
 
 var orderPresets = {
   'recess': 'recess.yml',
@@ -84,7 +89,8 @@ var sortProperties = function (obj, order) {
 module.exports = {
   'name': 'property-sort-order',
   'defaults': {
-    'order': 'alphabetical'
+    'order': 'alphabetical',
+    'ignore-custom-properties': false
   },
   'detect': function (ast, parser) {
     var result = [],
@@ -102,7 +108,14 @@ module.exports = {
               name = prop.first('ident');
 
           if (name) {
-            properties[name.content] = prop;
+            if (parser.options['ignore-custom-properties']) {
+              if (propertyCheckList.indexOf(name.content) !== -1) {
+                properties[name.content] = prop;
+              }
+            }
+            else {
+              properties[name.content] = prop;
+            }
           }
         });
 
diff --git a/lib/rules/property-units.js b/lib/rules/property-units.js
new file mode 100644
index 0000000..9f8b28b
--- /dev/null
+++ b/lib/rules/property-units.js
@@ -0,0 +1,56 @@
+'use strict';
+
+var helpers = require('../helpers');
+
+module.exports = {
+  'name': 'property-units',
+  'defaults': {
+    'per-property': {},
+    'global': []
+  },
+  'detect': function (ast, parser) {
+    var result = [],
+        unitsAllowedGlobally = parser.options.global,
+        unitsAllowedPerProperty = parser.options['per-property'];
+
+    ast.traverseByType('declaration', function (declaration) {
+      var property = declaration.first('property'),
+          ident = property ? property.first('ident') : null,
+          propertyName = ident ? ident.content : null,
+          valueNode = declaration.first('value'),
+          hasDimension = valueNode ? !!valueNode.first('dimension') : null;
+      if (propertyName && hasDimension) {
+        // properties such as box-shadow may have multiple dimensions defined so enumerate through them
+        valueNode.forEach('dimension', function (dimension) {
+          var dimensionIdent = dimension ? dimension.first('ident') : null,
+              unitType = dimensionIdent ? dimensionIdent.content : null,
+              unitsAllowed = unitsAllowedPerProperty[propertyName];
+          // If a property is defined in unitsAllowed, then it will only validate those unit types
+          if (unitType && unitsAllowed) {
+            if (unitsAllowed.indexOf(unitType) === -1) {
+              result = helpers.addUnique(result, {
+                'ruleId': parser.rule.name,
+                'severity': parser.severity,
+                'line': dimension.start.line,
+                'column': dimension.start.column,
+                'message': 'Values for property \'' + propertyName + '\' may not use ' + unitType + ' units'
+              });
+            }
+          }
+          // If no units are defined in unitsAllowedGlobally, then allow all of them
+          // Otherwise, verify the given unit is in the unitsAllowedGlobally list.
+          else if (unitType && unitsAllowedGlobally.length && unitsAllowedGlobally.indexOf(unitType) === -1) {
+            result = helpers.addUnique(result, {
+              'ruleId': parser.rule.name,
+              'severity': parser.severity,
+              'line': dimension.start.line,
+              'column': dimension.start.column,
+              'message': 'Values for property \'' + propertyName + '\' may not use ' + unitType + ' units'
+            });
+          }
+        });
+      }
+    });
+    return result;
+  }
+};
diff --git a/lib/rules/shorthand-values.js b/lib/rules/shorthand-values.js
index 3f6c405..25fd704 100644
--- a/lib/rules/shorthand-values.js
+++ b/lib/rules/shorthand-values.js
@@ -83,6 +83,12 @@ module.exports = {
                 });
               }
 
+              else if (val.is('percentage')) {
+                val.forEach(function (el) {
+                  fullVal += el.content + '%';
+                });
+              }
+
               else if (val.is('operator') || val.is('ident') || val.is('number') || val.is('unaryOperator')) {
                 fullVal += val.content;
               }
diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js
new file mode 100644
index 0000000..57cf682
--- /dev/null
+++ b/lib/rules/space-around-operator.js
@@ -0,0 +1,148 @@
+'use strict';
+
+var helpers = require('../helpers');
+
+var operators = ['+', '-', '/', '*', '%', '<', '>', '==', '!=', '<=', '>='];
+
+var getRelationalOperator = function (node) {
+  if (node.content === '<') {
+    return '<=';
+  }
+
+  if (node.content === '>') {
+    return '>=';
+  }
+};
+
+var checkSpacing = function (node, i, parent, parser, result) {
+  if (node.is('operator') || node.is('unaryOperator')) {
+    var previous = parent.content[i - 1] || false,
+        next = parent.content[i + 1] || false,
+        operator = node.content;
+
+    //////////////////////////
+    // Multi-part operators
+    //////////////////////////
+
+    // If second part of relational operator move on
+    if (node.content === '=' && previous) {
+      if (previous.content === '<' || previous.content === '>') {
+        return false;
+      }
+    }
+
+    // If first part of relational operator, carry on and build it
+    if ((node.content === '<' || node.content === '>') && next) {
+      if (next.content === '=') {
+        operator = getRelationalOperator(node);
+        next = parent.content[i + 2] || false;
+      }
+    }
+
+    //////////////////////////
+    // Exceptions
+    //////////////////////////
+
+    if (parent && operator && next) {
+      // The parent ofthe exceptions are always going to be of type value
+      if (parent.is('value')) {
+        // 1. We should allow valid CSS use of leading - operator when
+        // defining negative values (margin, top, etc)
+        if (parent.is('value') && operator === '-' && (next.is('dimension') || next.is('percentage'))) {
+          return false;
+        }
+
+        // 2. We should allow valid CSS use of / operators when defining
+        // font size/line-height
+        if (previous) {
+
+          // The first value is the font-size and must have a unit, therefore
+          // a node of type dimension
+          if (operator === '/' && previous.is('dimension')
+            // Line-height - can be a number with our without units
+            && (next.is('dimension') || next.is('number'))
+          ) {
+            return false;
+          }
+        }
+      }
+    }
+
+    // If the operator checks out in our valid operator list
+    if (operators.indexOf(operator) !== -1) {
+
+      if (parser.options.include) {
+        if (
+          (previous && !previous.is('space'))
+          || (next && !next.is('space'))
+        ) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': node.start.line,
+            'column': node.start.column,
+            'message': 'Space expected around operator',
+            'severity': parser.severity
+          });
+        }
+        else {
+          if (
+              ((previous.end.line >= previous.start.line) && (previous.end.column > previous.start.column))
+              || (next.end.line >= next.start.line) && (next.end.column > next.start.column)
+            ) {
+            result = helpers.addUnique(result, {
+              'ruleId': parser.rule.name,
+              'line': node.start.line,
+              'column': node.start.column,
+              'message': 'Multiple spaces not allowed around operator',
+              'severity': parser.severity
+            });
+          }
+        }
+      }
+      else {
+        if (
+          (previous && previous.is('space'))
+          || (next && next.is('space'))
+        ) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': node.start.line,
+            'column': node.start.column,
+            'message': 'No spaces allowed around operator',
+            'severity': parser.severity
+          });
+        }
+      }
+    }
+  }
+};
+
+module.exports = {
+  'name': 'space-around-operator',
+  'defaults': {
+    'include': true
+  },
+  'detect': function (ast, parser) {
+    var result = [];
+
+    // FIXME: Gonzales v3 - No longer need atrulerq (for Sass)
+    ast.traverseByTypes(['condition', 'atruleb', 'value', 'atrulerq'], function (node) {
+      node.forEach(function (item, i, parent) {
+        // Perform another loop of the children if we come across a parenthesis
+        // parent node
+        if (item.is('parentheses')) {
+          item.forEach(function (child, j, childParent) {
+            // Do the spacing checks
+            checkSpacing(child, j, childParent, parser, result);
+          });
+        }
+        else {
+          // Do the spacing checks
+          checkSpacing(item, i, parent, parser, result);
+        }
+      });
+    });
+
+    return result;
+  }
+};
diff --git a/package.json b/package.json
index a53ced9..9241efb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "sass-lint",
-  "version": "1.3.3",
+  "version": "1.4.0",
   "description": "All Node Sass linter!",
   "main": "index.js",
   "scripts": {
@@ -30,10 +30,12 @@
   "dependencies": {
     "commander": "^2.8.1",
     "eslint": "^1.1.0",
-    "fs-extra": "^0.24.0",
-    "glob": "^5.0.15",
+    "fs-extra": "^0.26.0",
+    "glob": "^6.0.0",
     "gonzales-pe": "3.0.0-31",
     "js-yaml": "^3.2.6",
+    "lodash.capitalize": "^3.0.0",
+    "lodash.kebabcase": "^3.0.1",
     "merge": "^1.2.0",
     "util": "^0.10.3"
   },


Test patch:
diff --git a/tests/cli.js b/tests/cli.js
index 7a55e60..5775ce1 100644
--- a/tests/cli.js
+++ b/tests/cli.js
@@ -280,4 +280,31 @@ describe('cli', function () {
       done();
     });
   });
+
+  it('should override filename convention if a valid --syntax is provided', function (done) {
+
+    var sassTestsPath = path.join(__dirname, '/sass/'),
+        files = [];
+
+    files.push(sassTestsPath + fs.readdirSync(sassTestsPath));
+
+    var command = 'sass-lint --syntax scss tests/sass/cli.txt --verbose';
+
+    childProcess.exec(command, function (err, stdout) {
+
+      var result = 0;
+
+      if (err) {
+        return done(err);
+      }
+      result = stdout.length;
+
+      if (result !== 0) {
+        return done(new Error('warnings/errors were returned'));
+      }
+
+      return done();
+    });
+
+  });
 });
diff --git a/tests/config.js b/tests/config.js
index 70e3b9d..0ac61fa 100644
--- a/tests/config.js
+++ b/tests/config.js
@@ -1,102 +1,192 @@
 'use strict';
 
 var equal = require('deep-equal'),
+    assert = require('assert'),
     yaml = require('js-yaml'),
     fs = require('fs'),
     path = require('path'),
     merge = require('merge'),
     config = require('../lib/config');
 
-var defaults = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '..', 'lib', 'config', 'sass-lint.yml'), 'utf8'));
-
-var options = {
-  'options': {
-    'formatter': 'stylish'
-  },
-  'files': {
-    'ignore': [
-      'foo',
-      'bar'
-    ]
-  },
-  'rules': {
-    'no-duplicate-property': 0,
-    'indentation': [
-      2,
-        {
-          'size': 4
-        }
-    ]
-  }
+var custOptions = function () {
+  return {
+    'options': {
+      'formatter': 'stylish',
+      'cache-config': false
+    },
+    'files': {
+      'ignore': [
+        'foo',
+        'bar'
+      ]
+    },
+    'rules': {
+      'no-duplicate-properties': 0,
+      'indentation': [
+        2,
+          {
+            'size': 4
+          }
+      ]
+    }
+  };
 };
 
 describe('config', function () {
   it('should return the defaults if no config is passed in', function (done) {
-    var conf = config();
-
-    equal(
-      conf,
-      defaults,
-      {
-        'strict': true
-      }
+    var defaultConfig = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '..', 'lib', 'config', 'sass-lint.yml'), 'utf8')),
+        conf = config();
+
+    assert(
+      equal(
+        conf,
+        defaultConfig,
+        {
+          'strict': true
+        }
+      )
     );
 
     done();
   });
 
   it('should merge options when passed in', function (done) {
-    var conf = config(options),
-        merged = merge.recursive(defaults, options);
-
-    equal(
-      conf,
-      merged,
-      {
-        'strict': true
-      }
+
+    var defaultConfig = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '..', 'lib', 'config', 'sass-lint.yml'), 'utf8')),
+        tempOptions = custOptions(),
+        conf = config(tempOptions),
+        merged = merge.recursive(defaultConfig, tempOptions);
+
+    assert(
+      equal(
+        conf,
+        merged,
+        {
+          'strict': true
+        }
+      )
     );
 
     done();
   });
 
   it('should use a config file if one is provided', function (done) {
-    var conf = config({
+    var defaultConfig = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '..', 'lib', 'config', 'sass-lint.yml'), 'utf8')),
+        tempOptions = {
           'options': {
-            'config-file': 'tests/yml/.stylish-output.yml'
+            'config-file': 'tests/yml/.stylish-output.yml',
+            'cache-config': false
           }
-        }),
-        merged = merge.recursive(defaults, fs.readFileSync(path.join(__dirname, 'yml', '.stylish-output.yml')));
-
-    equal(
-      conf,
-      merged,
-      {
-        'strict': true
-      }
+        },
+        conf = config(tempOptions),
+        merged = merge.recursive(defaultConfig, tempOptions, yaml.safeLoad(fs.readFileSync(path.join(__dirname, 'yml', '.stylish-output.yml'), 'utf8')));
+
+    assert(
+      equal(
+        conf,
+        merged,
+        {
+          'strict': true
+        }
+      )
     );
 
     done();
   });
 
-  it('should not merge rules when `merge-default-rules` is false', function (done) {
-    var opts = options,
+  it('should not merge default rules when `merge-default-rules` is false in config file [check unique]', function (done) {
+    var defaultConfig = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '..', 'lib', 'config', 'sass-lint.yml'), 'utf8')),
+        tempOptions = custOptions(),
         conf,
         merged;
 
-    opts.options['merge-default-rules'] = false;
+    conf = config(tempOptions, path.join(__dirname, 'yml', '.no-merge-default.yml'));
+    merged = merge.recursive(defaultConfig, tempOptions);
+
+    assert(
+      !equal(
+        conf,
+        merged,
+        {
+          'strict': true
+        }
+      )
+    );
+
+    done();
+  });
+
+  it('should not merge default rules when `merge-default-rules` is false in config file [check equal]', function (done) {
+    var customConfig = yaml.safeLoad(fs.readFileSync(path.join(__dirname, 'yml', '.no-merge-default.yml'), 'utf8')),
+        tempOptions = custOptions(),
+        conf,
+        merged;
 
-    merged = merge.recursive(defaults, opts);
-    merged.rules = opts.rules;
 
-    conf = config(opts);
+    conf = config(tempOptions, path.join(__dirname, 'yml', '.no-merge-default.yml'));
+    merged = merge.recursive(customConfig, tempOptions);
 
-    equal(
-      conf,
-      merged,
-      {
-        'strict': true
-      }
+    assert(
+      equal(
+        conf,
+        merged,
+        {
+          'strict': true
+        }
+      )
+    );
+
+    done();
+  });
+
+  it('should not merge custom option rules when `merge-default-rules` is false', function (done) {
+    var defaultConfig = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '..', 'lib', 'config', 'sass-lint.yml'), 'utf8')),
+        tempOptions = custOptions(),
+        conf,
+        merged;
+
+    tempOptions.options['merge-default-rules'] = false;
+    conf = config(tempOptions);
+    merged = merge.recursive(tempOptions, defaultConfig);
+    merged.rules = tempOptions.rules;
+
+    assert(
+      equal(
+        conf,
+        merged,
+        {
+          'strict': true
+        }
+      )
+    );
+
+    done();
+  });
+
+  it('should load a cached version of the config', function (done) {
+    var tempOptions = {
+          'options': {}
+        },
+        confFirst,
+        confSecond;
+
+    tempOptions.options['cache-config'] = true;
+
+    // first pass at config to set the cache
+    confFirst = config(tempOptions);
+
+    // second pass to check if cache loads adding in a config file that shouldn't be loaded
+    tempOptions.options['config-file'] = 'tests/yml/.stylish-output.yml';
+    confSecond = config(tempOptions);
+
+    assert(
+      equal(
+        confFirst,
+        confSecond,
+        {
+          'strict': true
+        }
+      )
     );
 
     done();
diff --git a/tests/helpers.js b/tests/helpers.js
index 1ed628b..885ba04 100644
--- a/tests/helpers.js
+++ b/tests/helpers.js
@@ -1,7 +1,8 @@
 'use strict';
 
 var assert = require('assert'),
-    helpers = require('../lib/helpers');
+    helpers = require('../lib/helpers'),
+    gonzales = require('gonzales-pe');
 
 var haystack = [
     {
@@ -14,6 +15,281 @@ var haystack = [
     }
 ];
 
+var idNode = gonzales.createNode({
+  type: 'id',
+  content: [
+    gonzales.createNode({
+      type: 'ident',
+      content: 'header',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 2
+      },
+      end: {
+        line: 1,
+        column: 7
+      }
+    })
+  ],
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 1
+  },
+  end: {
+    line: 1,
+    column: 7
+  }
+});
+
+
+var identNode = gonzales.createNode({
+  type: 'ident',
+  content: 'input',
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 1
+  },
+  end: {
+    line: 1,
+    column: 5
+  }
+});
+
+
+var classNode = gonzales.createNode({
+  type: 'class',
+  content: [
+    gonzales.createNode({
+      type: 'ident',
+      content: 'header',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 2
+      },
+      end: {
+        line: 1,
+        column: 7
+      }
+    })
+  ],
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 1
+  },
+  end: {
+    line: 1,
+    column: 7
+  }
+});
+
+
+var attributeNode = gonzales.createNode({
+  type: 'attribute',
+  content: [
+    gonzales.createNode({
+      type: 'ident',
+      content: 'type',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 7
+      },
+      end: {
+        line: 1,
+        column: 10
+      }
+    }),
+    gonzales.createNode({
+      type: 'attributeSelector',
+      content: '=',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 11
+      },
+      end: {
+        line: 1,
+        column: 11
+      }
+    }),
+    gonzales.createNode({
+      type: 'ident',
+      content: 'radio',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 12
+      },
+      end: {
+        line: 1,
+        column: 16
+      }
+    })
+  ],
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 6
+  },
+  end: {
+    line: 1,
+    column: 17
+  }
+});
+
+
+var pseudoNode = gonzales.createNode({
+  type: 'pseudoClass',
+  content: [
+    gonzales.createNode({
+      type: 'ident',
+      content: 'last-child',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 3
+      },
+      end: {
+        line: 1,
+        column: 12
+      }
+    })
+  ],
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 2
+  },
+  end: {
+    line: 1,
+    column: 12
+  }
+});
+
+
+var pseudoElementNode = gonzales.createNode({
+  type: 'pseudoElement',
+  content: [
+    gonzales.createNode({
+      type: 'ident',
+      content: 'first-line',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 3
+      },
+      end: {
+        line: 1,
+        column: 12
+      }
+    })
+  ],
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 2
+  },
+  end: {
+    line: 1,
+    column: 12
+  }
+});
+
+
+
+var nthSelectorNode = gonzales.createNode({
+  type: 'nthSelector',
+  content: [
+    gonzales.createNode({
+      type: 'ident',
+      content: 'nth-of-type',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 3
+      },
+      end: {
+        line: 1,
+        column: 13
+      }
+    }),
+    gonzales.createNode({
+      type: 'nth',
+      content: '2',
+      syntax: 'scss',
+      start: {
+        line: 1,
+        column: 15
+      },
+      end: {
+        line: 1,
+        column: 15
+      }
+    })
+  ],
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 2
+  },
+  end: {
+    line: 1,
+    column: 16
+  }
+});
+
+
+var spaceNode = gonzales.createNode({
+  type: 'space',
+  content: ' ',
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 1
+  },
+  end: {
+    line: 1,
+    column: 1
+  }
+});
+
+
+var parentSelectorNode = gonzales.createNode({
+  type: 'parentSelector',
+  content: '&',
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 1
+  },
+  end: {
+    line: 1,
+    column: 1
+  }
+});
+
+
+var combinatorNode = gonzales.createNode({
+  type: 'combinator',
+  content: '+',
+  syntax: 'scss',
+  start: {
+    line: 1,
+    column: 6
+  },
+  end: {
+    line: 1,
+    column: 6
+  }
+});
+
+
+
 var classBlock =
     {
       type: 'class',
@@ -547,6 +823,22 @@ describe('helpers', function () {
     done();
   });
 
+  it('isHyphenatedLowercase - [\'abc-\\31\\32\\33\' - false]', function (done) {
+
+    var result = helpers.isHyphenatedLowercase('abc-\\31\\32\\33');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isHyphenatedLowercase - [\'abc-\\+\\*\' - false]', function (done) {
+
+    var result = helpers.isHyphenatedLowercase('abc-\\+\\*');
+
+    assert.equal(false, result);
+    done();
+  });
+
   //////////////////////////////
   // isSnakeCase
   //////////////////////////////
@@ -599,6 +891,198 @@ describe('helpers', function () {
     done();
   });
 
+  //////////////////////////////
+  // isHyphenatedBEM
+  //////////////////////////////
+
+  it('isHyphenatedBEM - [\'TEST\' - false]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('TEST');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'test\' - true]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('test');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [abcDEF - false]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('abcDEF');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'abc---def\' - false]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('abc---def');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'abc___def\' - false]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('abc___def');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'ab__cd__ef\' - true]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('ab__cd__ef');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'ab__cd--ef\' - true]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('ab__cd--ef');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'abc_def\' - false]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('abc_def');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'abc-def\' - true]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('abc-def');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isHyphenatedBEM - [\'ab-cd__ef\' - true]', function (done) {
+
+    var result = helpers.isHyphenatedBEM('ab-cd__ef');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  //////////////////////////////
+  // isStrictBEM
+  //////////////////////////////
+
+  it('isStrictBEM - [\'TEST\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('TEST');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'test\' - true]', function (done) {
+
+    var result = helpers.isStrictBEM('test');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isStrictBEM - [abcDEF - false]', function (done) {
+
+    var result = helpers.isStrictBEM('abcDEF');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'abc---def\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('abc---def');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'abc___def\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('abc___def');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'ab__cd__ef\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('ab__cd__ef');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'ab__cd--ef\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('ab__cd--ef');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'ab__cd_ef_gh\' - true]', function (done) {
+
+    var result = helpers.isStrictBEM('ab__cd_ef_gh');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'ab-cd-ef__gh-ij-kl_mn-op-qr_st-uv-wx\' - true]', function (done) {
+
+    var result = helpers.isStrictBEM('ab-cd-ef__gh-ij-kl_mn-op-qr_st-uv-wx');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'ab__cd_ef_gh_ij\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('ab__cd_ef_gh_ij');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'abc_def\' - false]', function (done) {
+
+    var result = helpers.isStrictBEM('abc_def');
+
+    assert.equal(false, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'abc-def\' - true]', function (done) {
+
+    var result = helpers.isStrictBEM('abc-def');
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isStrictBEM - [\'ab-cd__ef\' - true]', function (done) {
+
+    var result = helpers.isStrictBEM('ab-cd__ef');
+
+    assert.equal(true, result);
+    done();
+  });
+
   //////////////////////////////
   // isValidHex
   //////////////////////////////
@@ -888,4 +1372,305 @@ describe('helpers', function () {
     done();
   });
 
+  //////////////////////////////
+  // isNestable
+  //////////////////////////////
+
+  it('isNestable - nest attribute in selector', function (done) {
+    var elements = ['selector', 'class', 'id', 'attribute'],
+        nestable = ['class', 'selector', 'attribute'],
+        previous = 'selector',
+        current = 'attribute';
+
+    var result = helpers.isNestable(current, previous, elements, nestable);
+
+    assert.equal(true, result);
+    done();
+  });
+
+  it('isNestable - nest id in class', function (done) {
+    var elements = ['selector', 'class', 'id', 'attribute'],
+        nestable = ['class', 'selector', 'attribute'],
+        previous = 'class',
+        current = 'id';
+
+    var result = helpers.isNestable(current, previous, elements, nestable);
+
+    assert.equal(false, result);
+    done();
+  });
+
+  //////////////////////////////
+  // constructSelector
+  //////////////////////////////
+
+  it('constructSelector - id node - [#header]', function (done) {
+    var result = helpers.constructSelector(idNode),
+        expect = '#header';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - id node - [id]', function (done) {
+    var result = helpers.constructSelector(idNode),
+        expect = 'id';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - class node - [.header]', function (done) {
+    var result = helpers.constructSelector(classNode),
+        expect = '.header';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - class node - [class]', function (done) {
+    var result = helpers.constructSelector(classNode),
+        expect = 'class';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - ident node - [input]', function (done) {
+    var result = helpers.constructSelector(identNode),
+        expect = 'input';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - ident node - [selector]', function (done) {
+    var result = helpers.constructSelector(identNode),
+        expect = 'selector';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - attribute node - [type=radio]', function (done) {
+    var result = helpers.constructSelector(attributeNode),
+        expect = '[type=radio]';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - attribute node - [attribute]', function (done) {
+    var result = helpers.constructSelector(attributeNode),
+        expect = 'attribute';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - pseudo node - [:last-child]', function (done) {
+    var result = helpers.constructSelector(pseudoNode),
+        expect = ':last-child';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - pseudo node - [pseudoClass]', function (done) {
+    var result = helpers.constructSelector(pseudoNode),
+        expect = 'pseudoClass';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - pseudo element node - [::first-line]', function (done) {
+    var result = helpers.constructSelector(pseudoElementNode),
+        expect = '::first-line';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - pseudo element node - [pseudoElement]', function (done) {
+    var result = helpers.constructSelector(pseudoElementNode),
+        expect = 'pseudoElement';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - nthSelector node - [:nth-of-type(2)]', function (done) {
+    var result = helpers.constructSelector(nthSelectorNode),
+        expect = ':nth-of-type(2)';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - nthSelector node - [nthSelector]', function (done) {
+    var result = helpers.constructSelector(nthSelectorNode),
+        expect = 'nthSelector';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - space node - [ ]', function (done) {
+    var result = helpers.constructSelector(spaceNode),
+        expect = ' ';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - space node - []', function (done) {
+    var result = helpers.constructSelector(spaceNode),
+        expect = '';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - parent selector node - [&]', function (done) {
+    var result = helpers.constructSelector(parentSelectorNode),
+        expect = '&';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - parent selector node - [parentSelector]', function (done) {
+    var result = helpers.constructSelector(parentSelectorNode),
+        expect = 'parentSelector';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+
+  it('constructSelector - combinator node - [+]', function (done) {
+    var result = helpers.constructSelector(combinatorNode),
+        expect = '+';
+
+    assert.equal(expect, result.content);
+    done();
+  });
+
+  it('constructSelector - combinator node - [combinator]', function (done) {
+    var result = helpers.constructSelector(combinatorNode),
+        expect = 'combinator';
+
+    assert.equal(expect, result.type);
+    done();
+  });
+
+  //////////////////////////////
+  // attemptTraversal
+  //////////////////////////////
+  it('attemptTraversal - collect all nodes', function () {
+    var stylesheet = gonzales.parse(['',
+      '.a {',
+      '  .b {',
+      '    color: red;',
+      '  }',
+      '  .c {',
+      '    color: blue;',
+      '  }',
+      '  .d {',
+      '    color: green;',
+      '  }',
+      '}'].join('\n'), { syntax: 'scss' });
+
+    assert.deepEqual(
+      helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block', 'declaration', 'property', 'ident'])
+        .map(function (node) {
+          return node.content;
+        }),
+      ['color', 'color', 'color']
+    );
+  });
+
+  it('attemptTraversal - empty array when traversal fails', function () {
+    var stylesheet = gonzales.parse(['',
+      '.a {',
+      '  color: red;',
+      '}'].join('\n'), { syntax: 'scss' });
+
+    assert.equal(
+      helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block']).length,
+      0
+    );
+  });
+
+  //////////////////////////////
+  // collectSuffixExtensions
+  //////////////////////////////
+  it('collectSuffixExtensions - no extensions', function () {
+    var ruleset = gonzales.parse(['',
+      '.a {',
+      '  .b {',
+      '    .c {',
+      '      width: 2px;',
+      '    }',
+      '  }',
+      '}'].join('\n'), { syntax: 'scss' })
+      .first('ruleset');
+
+    assert.deepEqual(
+      helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) {
+        return node.content;
+      }),
+      ['a']
+    );
+  });
+
+  it('collectSuffixExtensions - BEM example', function () {
+    var ruleset = gonzales.parse(['',
+      '.block {',
+      '  &__element {',
+      '    &--modifier {',
+      '      width: 2px;',
+      '    }',
+      '  }',
+      '}'].join('\n'), { syntax: 'scss' })
+      .first('ruleset');
+
+    assert.deepEqual(
+      helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) {
+        return node.content;
+      }),
+      ['block', 'block__element', 'block__element--modifier']
+    );
+  });
+
+  it('collectSuffixExtensions - many parents and children', function () {
+    var ruleset = gonzales.parse(['',
+      '.a,',
+      '.b {',
+      '  &c,',
+      '  &d {',
+      '    &e,',
+      '    &f {',
+      '      width: 2px;',
+      '    }',
+      '  }',
+      '}'].join('\n'), { syntax: 'scss' })
+      .first('ruleset');
+
+    assert.deepEqual(
+      helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) {
+        return node.content;
+      }),
+      ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf']
+    );
+  });
 });
diff --git a/tests/output.js b/tests/output.js
index ef76a70..0268970 100644
--- a/tests/output.js
+++ b/tests/output.js
@@ -46,7 +46,8 @@ describe('output', function () {
   it('should have configurable formatters', function (done) {
     var formatted = lint.format(results, {
       'options': {
-        'formatter': 'json'
+        'formatter': 'json',
+        'cache-config': false
       }
     });
 
@@ -59,7 +60,8 @@ describe('output', function () {
     var options = {
       'options': {
         'formatter': 'json',
-        'output-file': 'tests/output.json'
+        'output-file': 'tests/output.json',
+        'cache-config': false
       }
     };
 
diff --git a/tests/rules/_lint.js b/tests/rules/_lint.js
index a9cb160..1d34d94 100644
--- a/tests/rules/_lint.js
+++ b/tests/rules/_lint.js
@@ -23,7 +23,8 @@ module.exports.test = function (text, options, cb) {
 
   options = {
     'options': {
-      'merge-default-rules': false
+      'merge-default-rules': false,
+      'cache-config': false
     },
     'rules': options
   };
diff --git a/tests/rules/bem-depth.js b/tests/rules/bem-depth.js
new file mode 100644
index 0000000..9eb0e13
--- /dev/null
+++ b/tests/rules/bem-depth.js
@@ -0,0 +1,91 @@
+'use strict';
+
+var lint = require('./_lint');
+
+//////////////////////////////
+// SCSS syntax tests
+//////////////////////////////
+describe('bem depth - scss', function () {
+  var file = lint.file('bem-depth.scss');
+
+  // silly case to raise a warning for every selector/placeholder
+  it('[max-depth: 0]', function (done) {
+    lint.test(file, {
+      'bem-depth': [1, { 'max-depth': 0 }]
+    }, function (data) {
+      lint.assert.equal(12, data.warningCount);
+      done();
+    });
+  });
+
+  it('[max-depth: 1]', function (done) {
+    lint.test(file, {
+      'bem-depth': 1
+    }, function (data) {
+      lint.assert.equal(6, data.warningCount);
+      done();
+    });
+  });
+
+  it('[max-depth: 2]', function (done) {
+    lint.test(file, {
+      'bem-depth': [1, { 'max-depth': 2 }]
+    }, function (data) {
+      lint.assert.equal(1, data.warningCount);
+      done();
+    });
+  });
+
+  it('[max-depth: 3]', function (done) {
+    lint.test(file, {
+      'bem-depth': [1, { 'max-depth': 3 }]
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+});
+
+//////////////////////////////
+// Sass syntax tests
+//////////////////////////////
+describe('bem depth - sass', function () {
+  var file = lint.file('bem-depth.sass');
+
+  // silly case to raise a warning for every selector/placeholder
+  it('[max-depth: 0]', function (done) {
+    lint.test(file, {
+      'bem-depth': [1, { 'max-depth': 0 }]
+    }, function (data) {
+      lint.assert.equal(12, data.warningCount);
+      done();
+    });
+  });
+
+  it('[max-depth: 1]', function (done) {
+    lint.test(file, {
+      'bem-depth': 1
+    }, function (data) {
+      lint.assert.equal(6, data.warningCount);
+      done();
+    });
+  });
+
+  it('[max-depth: 2]', function (done) {
+    lint.test(file, {
+      'bem-depth': [1, { 'max-depth': 2 }]
+    }, function (data) {
+      lint.assert.equal(1, data.warningCount);
+      done();
+    });
+  });
+
+  it('[max-depth: 3]', function (done) {
+    lint.test(file, {
+      'bem-depth': [1, { 'max-depth': 3 }]
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+});
diff --git a/tests/rules/class-name-format.js b/tests/rules/class-name-format.js
new file mode 100644
index 0000000..c7bd7c9
--- /dev/null
+++ b/tests/rules/class-name-format.js
@@ -0,0 +1,237 @@
+'use strict';
+
+var lint = require('./_lint');
+
+//////////////////////////////
+// SCSS syntax tests
+//////////////////////////////
+describe('class name format - scss', function () {
+  var file = lint.file('class-name-format.scss');
+
+  it('[convention: hyphenatedlowercase]', function (done) {
+    lint.test(file, {
+      'class-name-format': 1
+    }, function (data) {
+      lint.assert.equal(24, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: hyphenatedlowercase with ignore]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'ignore': ['block__element--modifier']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(23, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: camelcase]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'camelcase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(34, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: snakecase]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'snakecase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(29, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: strictbem]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'strictbem'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(19, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: hyphenatedbem]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'hyphenatedbem'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(17, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(35, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$], with convention-explanation', function (done) {
+    var message = 'Its bad and you should feel bad.';
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$',
+          'convention-explanation': message
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(31, data.warningCount);
+      lint.assert.equal(data.messages[0].message, message);
+      done();
+    });
+  });
+});
+
+//////////////////////////////
+// Sass syntax tests
+//////////////////////////////
+describe('class name format - sass', function () {
+  var file = lint.file('class-name-format.sass');
+
+  it('[convention: hyphenatedlowercase]', function (done) {
+    lint.test(file, {
+      'class-name-format': 1
+    }, function (data) {
+      lint.assert.equal(24, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: hyphenatedlowercase with ignore]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'ignore': ['block__element--modifier']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(23, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: camelcase]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'camelcase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(34, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: snakecase]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'snakecase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(29, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: strictbem]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'strictbem'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(19, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: hyphenatedbem]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': 'hyphenatedbem'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(17, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$]', function (done) {
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(35, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$], with convention-explanation', function (done) {
+    var message = 'Its bad and you should feel bad.';
+    lint.test(file, {
+      'class-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$',
+          'convention-explanation': message
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(31, data.warningCount);
+      lint.assert.equal(data.messages[0].message, message);
+      done();
+    });
+  });
+});
diff --git a/tests/rules/id-name-format.js b/tests/rules/id-name-format.js
new file mode 100644
index 0000000..0518279
--- /dev/null
+++ b/tests/rules/id-name-format.js
@@ -0,0 +1,181 @@
+'use strict';
+
+var lint = require('./_lint');
+
+//////////////////////////////
+// SCSS syntax tests
+//////////////////////////////
+describe('id name format - scss', function () {
+  var file = lint.file('id-name-format.scss');
+
+  it('[convention: hyphenatedlowercase]', function (done) {
+    lint.test(file, {
+      'id-name-format': 1
+    }, function (data) {
+      lint.assert.equal(14, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: hyphenatedlowercase with ignore]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'ignore': ['one_parent_valid_child']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(13, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: camelcase]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': 'camelcase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(20, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: snakecase]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': 'snakecase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(16, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(20, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$], with convention-explanation', function (done) {
+    var message = 'Its bad and you should feel bad.';
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$',
+          'convention-explanation': message
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(16, data.warningCount);
+      lint.assert.equal(data.messages[0].message, message);
+      done();
+    });
+  });
+});
+
+//////////////////////////////
+// Sass syntax tests
+//////////////////////////////
+describe('id name format - sass', function () {
+  var file = lint.file('id-name-format.sass');
+
+  it('[convention: hyphenatedlowercase]', function (done) {
+    lint.test(file, {
+      'id-name-format': 1
+    }, function (data) {
+      lint.assert.equal(14, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: hyphenatedlowercase with ignore]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'ignore': ['one_parent_valid_child']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(13, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: camelcase]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': 'camelcase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(20, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: snakecase]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': 'snakecase'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(16, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$]', function (done) {
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$'
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(20, data.warningCount);
+      done();
+    });
+  });
+
+  it('[convention: RegExp ^[_A-Z]+$], with convention-explanation', function (done) {
+    var message = 'Its bad and you should feel bad.';
+    lint.test(file, {
+      'id-name-format': [
+        1,
+        {
+          'convention': '^[_A-Z]+$',
+          'convention-explanation': message
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(16, data.warningCount);
+      lint.assert.equal(data.messages[0].message, message);
+      done();
+    });
+  });
+});
diff --git a/tests/rules/property-spelling.js b/tests/rules/no-misspelled-properties.js
similarity index 100%
rename from tests/rules/property-spelling.js
rename to tests/rules/no-misspelled-properties.js
diff --git a/tests/rules/no-trailing-zero.js b/tests/rules/no-trailing-zero.js
index f6f1277..0da6797 100644
--- a/tests/rules/no-trailing-zero.js
+++ b/tests/rules/no-trailing-zero.js
@@ -12,7 +12,7 @@ describe('no trailing zero - scss', function () {
     lint.test(file, {
       'trailing-zero': 1
     }, function (data) {
-      lint.assert.equal(3, data.warningCount);
+      lint.assert.equal(8, data.warningCount);
       done();
     });
   });
@@ -28,7 +28,7 @@ describe('no trailing zero - sass', function () {
     lint.test(file, {
       'trailing-zero': 1
     }, function (data) {
-      lint.assert.equal(3, data.warningCount);
+      lint.assert.equal(8, data.warningCount);
       done();
     });
   });
diff --git a/tests/rules/property-sort-order.js b/tests/rules/property-sort-order.js
index d59a3c8..865565d 100644
--- a/tests/rules/property-sort-order.js
+++ b/tests/rules/property-sort-order.js
@@ -12,7 +12,21 @@ describe('property sort order - scss', function () {
     lint.test(file, {
       'property-sort-order': 1
     }, function (data) {
-      lint.assert.equal(6, data.warningCount);
+      lint.assert.equal(15, data.warningCount);
+      done();
+    });
+  });
+
+  it('[order: alphabetical, ignore-custom-properties: true]', function (done) {
+    lint.test(file, {
+      'property-sort-order': [
+        1,
+        {
+          'ignore-custom-properties': true
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(12, data.warningCount);
       done();
     });
   });
@@ -31,7 +45,48 @@ describe('property sort order - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(4, data.warningCount);
+      lint.assert.equal(8, data.warningCount);
+      done();
+    });
+  });
+
+  it('[order: custom + composes, ignore-custom-properties: false]', function (done) {
+    lint.test(file, {
+      'property-sort-order': [
+        1,
+        {
+          'order': [
+            'height',
+            'composes',
+            'width',
+            'display',
+            'color'
+          ],
+          'ignore-custom-properties': false
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(10, data.warningCount);
+      done();
+    });
+  });
+
+  it('[order: custom + composes, ignore-custom-properties: true]', function (done) {
+    lint.test(file, {
+      'property-sort-order': [
+        1,
+        {
+          'order': [
+            'height',
+            'width',
+            'display',
+            'color'
+          ],
+          'ignore-custom-properties': true
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(8, data.warningCount);
       done();
     });
   });
@@ -45,7 +100,7 @@ describe('property sort order - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(5, data.warningCount);
+      lint.assert.equal(12, data.warningCount);
       done();
     });
   });
@@ -59,7 +114,7 @@ describe('property sort order - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(5, data.warningCount);
+      lint.assert.equal(12, data.warningCount);
       done();
     });
   });
@@ -73,7 +128,7 @@ describe('property sort order - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(7, data.warningCount);
+      lint.assert.equal(14, data.warningCount);
       done();
     });
   });
@@ -89,7 +144,21 @@ describe('property sort order - sass', function () {
     lint.test(file, {
       'property-sort-order': 1
     }, function (data) {
-      lint.assert.equal(6, data.warningCount);
+      lint.assert.equal(15, data.warningCount);
+      done();
+    });
+  });
+
+  it('[order: alphabetical, ignore-custom-properties: true]', function (done) {
+    lint.test(file, {
+      'property-sort-order': [
+        1,
+        {
+          'ignore-custom-properties': true
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(12, data.warningCount);
       done();
     });
   });
@@ -108,7 +177,48 @@ describe('property sort order - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(4, data.warningCount);
+      lint.assert.equal(8, data.warningCount);
+      done();
+    });
+  });
+
+  it('[order: custom + composes, ignore-custom-properties: false]', function (done) {
+    lint.test(file, {
+      'property-sort-order': [
+        1,
+        {
+          'order': [
+            'height',
+            'composes',
+            'width',
+            'display',
+            'color'
+          ],
+          'ignore-custom-properties': false
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(10, data.warningCount);
+      done();
+    });
+  });
+
+  it('[order: custom + composes, ignore-custom-properties: true]', function (done) {
+    lint.test(file, {
+      'property-sort-order': [
+        1,
+        {
+          'order': [
+            'height',
+            'width',
+            'display',
+            'color'
+          ],
+          'ignore-custom-properties': true
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(8, data.warningCount);
       done();
     });
   });
@@ -122,7 +232,7 @@ describe('property sort order - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(5, data.warningCount);
+      lint.assert.equal(12, data.warningCount);
       done();
     });
   });
@@ -136,7 +246,7 @@ describe('property sort order - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(5, data.warningCount);
+      lint.assert.equal(12, data.warningCount);
       done();
     });
   });
@@ -150,7 +260,7 @@ describe('property sort order - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(7, data.warningCount);
+      lint.assert.equal(14, data.warningCount);
       done();
     });
   });
diff --git a/tests/rules/property-units.js b/tests/rules/property-units.js
new file mode 100644
index 0000000..40db4fc
--- /dev/null
+++ b/tests/rules/property-units.js
@@ -0,0 +1,205 @@
+'use strict';
+
+var lint = require('./_lint');
+
+//////////////////////////////
+// SCSS syntax tests
+//////////////////////////////
+describe('property-units - scss', function () {
+  var file = lint.file('property-units.scss');
+
+  it('global: [], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': 1
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'px\'], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['px']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(6, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'em\'], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['em']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(3, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'em\', \'px\'], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['em', 'px']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [], per-property: { height: [\'px\'] }', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'per-property': { height: ['px'] }
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(1, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [], per-property: { height: [\'px\', \'em\'] }', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'per-property': { height: ['px', 'em'] }
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'px\'], per-property: { height: [\'em\'] }', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['px'],
+          'per-property': { height: ['em'] }
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(6, data.warningCount);
+      done();
+    });
+  });
+});
+
+//////////////////////////////
+// Sass syntax tests
+//////////////////////////////
+describe('property-units - sass', function () {
+  var file = lint.file('property-units.sass');
+
+  it('global: [], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': 1
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'px\'], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['px']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(6, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'em\'], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['em']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(3, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'em\', \'px\'], per-property: {}', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['em', 'px']
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [], per-property: { height: [\'px\'] }', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'per-property': { height: ['px'] }
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(1, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [], per-property: { height: [\'px\', \'em\'] }', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'per-property': { height: ['px', 'em'] }
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(0, data.warningCount);
+      done();
+    });
+  });
+
+  it('global: [\'px\'], per-property: { height: [\'em\'] }', function (done) {
+    lint.test(file, {
+      'property-units': [
+        1,
+        {
+          'global': ['px'],
+          'per-property': { height: ['em'] }
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(6, data.warningCount);
+      done();
+    });
+  });
+});
diff --git a/tests/rules/shorthand-values.js b/tests/rules/shorthand-values.js
index 1181518..7188106 100644
--- a/tests/rules/shorthand-values.js
+++ b/tests/rules/shorthand-values.js
@@ -12,7 +12,7 @@ describe('shorthand values - scss', function () {
     lint.test(file, {
       'shorthand-values': 1
     }, function (data) {
-      lint.assert.equal(49, data.warningCount);
+      lint.assert.equal(54, data.warningCount);
       done();
     });
   });
@@ -28,7 +28,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(21, data.warningCount);
+      lint.assert.equal(23, data.warningCount);
       done();
     });
   });
@@ -44,7 +44,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(23, data.warningCount);
+      lint.assert.equal(26, data.warningCount);
       done();
     });
   });
@@ -60,7 +60,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(29, data.warningCount);
+      lint.assert.equal(32, data.warningCount);
       done();
     });
   });
@@ -92,7 +92,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(36, data.warningCount);
+      lint.assert.equal(40, data.warningCount);
       done();
     });
   });
@@ -109,7 +109,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(42, data.warningCount);
+      lint.assert.equal(46, data.warningCount);
       done();
     });
   });
@@ -126,7 +126,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(36, data.warningCount);
+      lint.assert.equal(40, data.warningCount);
       done();
     });
   });
@@ -144,7 +144,7 @@ describe('shorthand values - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(49, data.warningCount);
+      lint.assert.equal(54, data.warningCount);
       done();
     });
   });
@@ -161,7 +161,7 @@ describe('shorthand values - sass', function () {
     lint.test(file, {
       'shorthand-values': 1
     }, function (data) {
-      lint.assert.equal(49, data.warningCount);
+      lint.assert.equal(54, data.warningCount);
       done();
     });
   });
@@ -177,7 +177,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(21, data.warningCount);
+      lint.assert.equal(23, data.warningCount);
       done();
     });
   });
@@ -193,7 +193,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(23, data.warningCount);
+      lint.assert.equal(26, data.warningCount);
       done();
     });
   });
@@ -209,7 +209,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(29, data.warningCount);
+      lint.assert.equal(32, data.warningCount);
       done();
     });
   });
@@ -241,7 +241,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(36, data.warningCount);
+      lint.assert.equal(40, data.warningCount);
       done();
     });
   });
@@ -258,7 +258,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(42, data.warningCount);
+      lint.assert.equal(46, data.warningCount);
       done();
     });
   });
@@ -275,7 +275,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(36, data.warningCount);
+      lint.assert.equal(40, data.warningCount);
       done();
     });
   });
@@ -293,7 +293,7 @@ describe('shorthand values - sass', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(49, data.warningCount);
+      lint.assert.equal(54, data.warningCount);
       done();
     });
   });
diff --git a/tests/rules/space-around-operator.js b/tests/rules/space-around-operator.js
new file mode 100644
index 0000000..ca2f5f9
--- /dev/null
+++ b/tests/rules/space-around-operator.js
@@ -0,0 +1,63 @@
+'use strict';
+
+var lint = require('./_lint');
+
+//////////////////////////////
+// SCSS syntax tests
+//////////////////////////////
+describe('space around operator - scss', function () {
+  var file = lint.file('space-around-operator.scss');
+
+  it('[include: true]', function (done) {
+    lint.test(file, {
+      'space-around-operator': 1
+    }, function (data) {
+      lint.assert.equal(83, data.warningCount);
+      done();
+    });
+  });
+
+  it('[include: false]', function (done) {
+    lint.test(file, {
+      'space-around-operator': [
+        1,
+        {
+          'include': false
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(77, data.warningCount);
+      done();
+    });
+  });
+});
+
+//////////////////////////////
+// Sass syntax tests
+//////////////////////////////
+describe('space around operator - sass', function () {
+  var file = lint.file('space-around-operator.sass');
+
+  it('[include: true]', function (done) {
+    lint.test(file, {
+      'space-around-operator': 1
+    }, function (data) {
+      lint.assert.equal(83, data.warningCount);
+      done();
+    });
+  });
+
+  it('[include: false]', function (done) {
+    lint.test(file, {
+      'space-around-operator': [
+        1,
+        {
+          'include': false
+        }
+      ]
+    }, function (data) {
+      lint.assert.equal(77, data.warningCount);
+      done();
+    });
+  });
+});
diff --git a/tests/sass/bem-depth.sass b/tests/sass/bem-depth.sass
new file mode 100644
index 0000000..13e78c9
--- /dev/null
+++ b/tests/sass/bem-depth.sass
@@ -0,0 +1,35 @@
+.one
+  color: red
+  &__two
+    color: blue
+    &__three
+      color: green
+  &__two-again
+    color: pink
+
+.one__two
+  color: red
+  &--modifier
+    color: blue
+  &__three
+    color: green
+  &--modifier__three
+    color: white
+
+.block
+  color: red
+
+.block__element-one
+  color: red
+
+.block__element-one__element-two
+  color: red
+
+%one__two
+  color: red
+
+%one__two__three
+  color: blue
+
+%one__two__three__four
+  color: blue
diff --git a/tests/sass/bem-depth.scss b/tests/sass/bem-depth.scss
new file mode 100644
index 0000000..6a8f2e9
--- /dev/null
+++ b/tests/sass/bem-depth.scss
@@ -0,0 +1,55 @@
+.one {
+    color: red;
+
+    &__two {
+      color: blue;
+
+      &__three {
+        color: green;
+      }
+    }
+
+    &__two-again {
+      color: pink;
+    }
+}
+
+.one__two {
+  color: red;
+
+  &--modifier {
+    color: blue;
+  }
+
+  &__three {
+    color: green;
+  }
+
+  &--modifier__three {
+    color: white;
+  }
+}
+
+.block {
+  color: red;
+}
+
+.block__element-one {
+  color: red;
+}
+
+.block__element-one__element-two {
+  color: red;
+}
+
+%one__two {
+  color: red;
+}
+
+%one__two__three {
+  color: blue;
+}
+
+%one__two__three__four {
+  color: blue;
+}
diff --git a/tests/sass/class-name-format.sass b/tests/sass/class-name-format.sass
new file mode 100644
index 0000000..ecc6fb8
--- /dev/null
+++ b/tests/sass/class-name-format.sass
@@ -0,0 +1,78 @@
+.hyphenated-lowercase
+  content: ''
+
+.snake_case
+  content: ''
+
+.camelCase
+  content: ''
+
+.PascalCase
+  content: ''
+
+  .Camel_Snake_Case
+    content: ''
+
+    .SCREAMING_SNAKE_CASE
+      content: ''
+
+._with-leading-underscore
+  content: ''
+
+._does_NOT-fitSTANDARD
+  @extend .snake_case
+
+.block-name__elem-name
+  content: ''
+
+.block
+  &__element
+    &--modifier
+      color: green
+  &__another-element
+    &--modifier
+      color: red
+    &--another-modifier
+      color: yellow
+  &--non-element-modifier
+    color: blue
+
+.owner-name_mod-name_mod-val
+  content: ''
+
+.owner-name
+  &_mod-name
+    &_mod-val
+      color: blue
+
+.site-search
+  color: blue
+  width: 50px
+  height: 100%
+
+.site-search__field
+  text-decoration: underline
+
+.site-search--full
+  color: green
+
+.hyphentated-lowercase
+  color: blue
+
+  &-with-suffix-extension
+    color: green
+
+    &-and-another
+      color: red
+
+  &-INVALID
+    color: pink
+
+.one_parent,
+.two_parents,
+.third-invalid-parent
+  width: 10px
+
+  &_valid_child,
+  &-invalid-child
+    height: 10px
diff --git a/tests/sass/class-name-format.scss b/tests/sass/class-name-format.scss
new file mode 100644
index 0000000..019e00a
--- /dev/null
+++ b/tests/sass/class-name-format.scss
@@ -0,0 +1,110 @@
+.hyphenated-lowercase {
+  content: '';
+}
+
+.snake_case {
+  content: '';
+}
+
+.camelCase {
+  content: '';
+}
+
+.PascalCase {
+  content: '';
+
+  .Camel_Snake_Case {
+    content: '';
+
+    .SCREAMING_SNAKE_CASE {
+      content: '';
+    }
+  }
+}
+
+._with-leading-underscore {
+  content: '';
+}
+
+._does_NOT-fitSTANDARD {
+  @extend .snake_case;
+}
+
+.block-name__elem-name {
+  content: '';
+}
+
+.block {
+  &__element {
+    &--modifier {
+      color: green;
+    }
+  }
+
+  &__another-element {
+    &--modifier {
+      color: red;
+    }
+
+    &--another-modifier {
+      color: yellow;
+    }
+  }
+
+  &--non-element-modifier {
+    color: blue;
+  }
+}
+
+.owner-name_mod-name_mod-val {
+  content: '';
+}
+
+.owner-name {
+  &_mod-name {
+    &_mod-val {
+      color: blue;
+    }
+  }
+}
+
+.site-search {
+  color: blue;
+  width: 50px;
+  height: 100%;
+}
+
+.site-search__field {
+  text-decoration: underline;
+}
+
+.site-search--full {
+  color: green;
+}
+
+.hyphentated-lowercase {
+  color: blue;
+
+  &-with-suffix-extension {
+    color: green;
+
+    &-and-another {
+      color: red;
+    }
+  }
+
+  &-INVALID {
+    color: pink;
+  }
+}
+
+.one_parent,
+.two_parents,
+.third-invalid-parent {
+  width: 10px;
+
+  &_valid_child,
+  &-invalid-child {
+    height: 10px;
+  }
+}
diff --git a/tests/sass/cli.txt b/tests/sass/cli.txt
new file mode 100644
index 0000000..dcffa43
--- /dev/null
+++ b/tests/sass/cli.txt
@@ -0,0 +1,3 @@
+.cli {
+  font-size: 100%;
+}
diff --git a/tests/sass/function-name-format.sass b/tests/sass/function-name-format.sass
index e35ee26..d5ceca1 100644
--- a/tests/sass/function-name-format.sass
+++ b/tests/sass/function-name-format.sass
@@ -25,7 +25,12 @@
 $foo: SCREAMING_SNAKE_CASE()
 
 .class
+  background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%)
   content: snake_case()
   color: _does_NOT-fitSTANDARD('bar')
   border-color: $foo
   transform: translateX(1px) translateY(1px) translateZ(1px) scaleX(0.5) scaleY(0.5) scaleZ(0.5) skewX(1deg) skewY(1deg) rotateX(1deg) rotateY(1deg) rotateZ(1deg)
+
+.test
+  background: repeating-radial-gradient(black, black 5px, white 5px, white 10px)
+  color: rgb(25, 25, 25)
diff --git a/tests/sass/function-name-format.scss b/tests/sass/function-name-format.scss
index 12eb417..e060994 100644
--- a/tests/sass/function-name-format.scss
+++ b/tests/sass/function-name-format.scss
@@ -33,8 +33,14 @@
 $foo: SCREAMING_SNAKE_CASE();
 
 .class {
+  background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
   content: snake_case();
   color: _does_NOT-fitSTANDARD('bar');
   border-color: $foo;
   transform: translateX(1px) translateY(1px) translateZ(1px) scaleX(0.5) scaleY(0.5) scaleZ(0.5) skewX(1deg) skewY(1deg) rotateX(1deg) rotateY(1deg) rotateZ(1deg);
 }
+
+.test {
+  background: repeating-radial-gradient(black, black 5px, white 5px, white 10px);
+  color: rgb(25, 25, 25);
+}
diff --git a/tests/sass/id-name-format.sass b/tests/sass/id-name-format.sass
new file mode 100644
index 0000000..f4d199c
--- /dev/null
+++ b/tests/sass/id-name-format.sass
@@ -0,0 +1,44 @@
+#hyphenated-lowercase
+  content: ''
+
+#snake_case
+  content: ''
+
+#camelCase
+  content: ''
+
+#PascalCase
+  content: ''
+
+  #Camel_Snake_Case
+    content: ''
+
+    #SCREAMING_SNAKE_CASE
+      content: ''
+
+#_with-leading-underscore
+  content: ''
+
+#_does_NOT-fitSTANDARD
+  @extend #snake_case
+
+#hyphentated-lowercase
+  color: blue
+
+  &-with-suffix-extension
+    color: green
+
+    &-and-another
+      color: red
+
+  &-INVALID
+    color: pink
+
+#one_parent,
+#two_parents,
+#third-invalid-parent
+  width: 10px
+
+  &_valid_child,
+  &-invalid-child
+    height: 10px
diff --git a/tests/sass/id-name-format.scss b/tests/sass/id-name-format.scss
new file mode 100644
index 0000000..13d9f3e
--- /dev/null
+++ b/tests/sass/id-name-format.scss
@@ -0,0 +1,58 @@
+#hyphenated-lowercase {
+  content: '';
+}
+
+#snake_case {
+  content: '';
+}
+
+#camelCase {
+  content: '';
+}
+
+#PascalCase {
+  content: '';
+
+  #Camel_Snake_Case {
+    content: '';
+
+    #SCREAMING_SNAKE_CASE {
+      content: '';
+    }
+  }
+}
+
+#_with-leading-underscore {
+  content: '';
+}
+
+#_does_NOT-fitSTANDARD {
+  @extend #snake_case;
+}
+
+#hyphentated-lowercase {
+  color: blue;
+
+  &-with-suffix-extension {
+    color: green;
+
+    &-and-another {
+      color: red;
+    }
+  }
+
+  &-INVALID {
+    color: pink;
+  }
+}
+
+#one_parent,
+#two_parents,
+#third-invalid-parent {
+  width: 10px;
+
+  &_valid_child,
+  &-invalid-child {
+    height: 10px;
+  }
+}
diff --git a/tests/sass/no-trailing-zero.sass b/tests/sass/no-trailing-zero.sass
index 352c621..389bd75 100644
--- a/tests/sass/no-trailing-zero.sass
+++ b/tests/sass/no-trailing-zero.sass
@@ -22,5 +22,20 @@
   margin: 0.2500rem
 
 
+.foo
+  margin: 5.0rem
+
+.foo
+  margin: 5.00rem
+
+.foo
+  margin: .000rem
+
+.foo
+  margin: 0.0rem
+
+.foo
+  margin: .0rem
+
 .foo
   margin: 0
diff --git a/tests/sass/no-trailing-zero.scss b/tests/sass/no-trailing-zero.scss
index f476fa2..027d9cf 100644
--- a/tests/sass/no-trailing-zero.scss
+++ b/tests/sass/no-trailing-zero.scss
@@ -22,6 +22,26 @@
   margin: 0.2500rem;
 }
 
+.foo {
+  margin: 5.0rem;
+}
+
+.foo {
+  margin: 5.00rem;
+}
+
+.foo {
+  margin: .000rem;
+}
+
+.foo {
+  margin: 0.0rem;
+}
+
+.foo {
+  margin: .0rem;
+}
+
 .foo {
   margin: 0;
 }
diff --git a/tests/sass/property-sort-order.sass b/tests/sass/property-sort-order.sass
index ed7903b..c69a6fa 100644
--- a/tests/sass/property-sort-order.sass
+++ b/tests/sass/property-sort-order.sass
@@ -11,6 +11,19 @@
     height: 100vw
 
 
+.other-property
+  composes: heading
+  height: 100vh
+  display: block
+  width: 100vw
+  border: 1px
+
+.other-property-new
+  height: 100vh
+  display: block
+  width: 100vw
+  border: 1px
+  composes: heading
 
 @function test($foo)
   // The line below causes the problem
@@ -34,5 +47,5 @@
   @if not unitless($context)
     $context: strip-units($context)
 
-  @if $target == 0 @return 0 
+  @if $target == 0 @return 0
   @return $target / $context + 0em
diff --git a/tests/sass/property-sort-order.scss b/tests/sass/property-sort-order.scss
index 92eae71..f0945c5 100644
--- a/tests/sass/property-sort-order.scss
+++ b/tests/sass/property-sort-order.scss
@@ -12,6 +12,22 @@
   }
 }
 
+.other-property {
+  composes: heading;
+  height: 100vh;
+  display: block;
+  width: 100vw;
+  border: 1px;
+}
+
+.other-property-new {
+  height: 100vh;
+  display: block;
+  width: 100vw;
+  border: 1px;
+  composes: heading;
+}
+
 @function test($foo) {
   // The line below causes the problem
   $bar: $foo;
diff --git a/tests/sass/property-units.sass b/tests/sass/property-units.sass
new file mode 100644
index 0000000..91533ae
--- /dev/null
+++ b/tests/sass/property-units.sass
@@ -0,0 +1,24 @@
+.literal
+  height: 3em
+
+.literal-property
+  width: 3px
+
+.box-shadow
+  box-shadow: 1em 1em black, 1em 1em black
+
+.background
+  background: 1em solid white
+
+.function
+  color: test(2em)
+
+// using literals as property names
+$sizes: 2em
+
+.literal-property
+  width: 3px
+  height: 0
+  .red
+    width: 4
+    height: 5px
diff --git a/tests/sass/property-units.scss b/tests/sass/property-units.scss
new file mode 100644
index 0000000..2d780d6
--- /dev/null
+++ b/tests/sass/property-units.scss
@@ -0,0 +1,37 @@
+.literal {
+    height: 3em;
+}
+
+.literal-property {
+    width: 3px;
+}
+
+.box-shadow {
+  box-shadow: 1em 1em black, 1em 1em black;
+}
+
+.background {
+  background: 1em solid white;
+}
+
+.function {
+  color: test(2em);
+}
+
+//  using literals as property names
+$sizes: (
+  small: 2em,
+  big : (
+    important: 2rem
+  )
+);
+
+.literal-property {
+    width: 3px;
+    height: 0;
+
+    .red {
+      width: 4;
+      height: 5px;
+    }
+}
diff --git a/tests/sass/shorthand-values.sass b/tests/sass/shorthand-values.sass
index 6e3a4c0..4b75496 100644
--- a/tests/sass/shorthand-values.sass
+++ b/tests/sass/shorthand-values.sass
@@ -170,3 +170,36 @@
 
 .value-four-diff-four-negative-mixed
   margin: -1px 1px -1rem -1rem
+
+.value-percentage
+  margin: 1%
+
+.value-two-percentage
+  margin: 1% 1%
+
+.value-two-diff-percentage
+  margin: 1% -1%
+
+.value-three-diff-one-percentage
+  margin: 1% 2% 1%
+
+.value-three-diff-two-percentage
+  margin: 1% 2% -1%
+
+.value-four-percentage
+  margin: 1% 1% 1% 1%
+
+.value-four-diff-one-percentage
+  margin: 1% 1% 1% -1%
+
+.value-four-diff-two-percentage
+  margin: 1% -1% 1% -1%
+
+.value-four-diff-three-percentage
+  margin: 1% -1% -1% -1%
+
+.value-four-diff-four-percentage
+  margin: 1% -2% -3% 4%
+
+.value-four-diff-four-percentage-mixed
+  margin: -1% -1% -1rem -1rem
diff --git a/tests/sass/shorthand-values.scss b/tests/sass/shorthand-values.scss
index b163149..bf1bc69 100644
--- a/tests/sass/shorthand-values.scss
+++ b/tests/sass/shorthand-values.scss
@@ -199,3 +199,47 @@
 .value-four-diff-four-negative-mixed {
   margin: -1px 1px -1rem -1rem;
 }
+
+.value-percentage {
+  margin: 1%;
+}
+
+.value-two-percentage {
+  margin: 1% 1%;
+}
+
+.value-two-diff-percentage {
+  margin: 1% -1%;
+}
+
+.value-three-diff-one-percentage {
+  margin: 1% 2% 1%;
+}
+
+.value-three-diff-two-percentage {
+  margin: 1% 2% -1%;
+}
+
+.value-four-percentage {
+  margin: 1% 1% 1% 1%;
+}
+
+.value-four-diff-one-percentage {
+  margin: 1% 1% 1% -1%;
+}
+
+.value-four-diff-two-percentage {
+  margin: 1% -1% 1% -1%;
+}
+
+.value-four-diff-three-percentage {
+  margin: 1% -1% -1% -1%;
+}
+
+.value-four-diff-four-percentage {
+  margin: 1% -2% -3% 4%;
+}
+
+.value-four-diff-four-percentage-mixed {
+  margin: -1% -1% -1rem -1rem;
+}
diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass
new file mode 100644
index 0000000..b9da58e
--- /dev/null
+++ b/tests/sass/space-around-operator.sass
@@ -0,0 +1,449 @@
+//====================
+// Should fail
+//====================
+
+// FIXME: Gonzales issue
+// https://github.com/tonyganch/gonzales-pe/issues/85
+// .foo
+//   background: url('image'  +  '/foo.png')
+//
+
+.foo
+  margin: 5px+15px
+
+.foo
+  margin: 5px+ 15px
+
+
+$foo: 1
+$bar: 3
+
+.foo
+  margin: $foo+$bar+'px'
+
+
+.foo
+  margin: #{$foo}* #{$bar}+'px'
+
+
+// No space
+
+$foo: 1+1
+$bar: 2-1
+$baz: 1/2
+$qux: 2*1
+// $norf: 5%2 Not valid SCSS
+
+
+// No space after
+
+$foo: 1 +1
+$bar: 2 -1
+$baz: 1 /2
+$qux: 2 *1
+// $norf: 5 %2 FIXME: Gonzales - FIXED IN BETA
+
+
+// No space before
+
+$foo: 1+ 1
+$bar: 2- 1
+$baz: 1/ 2
+$qux: 2* 1
+// $norf: 5% 2  Not valid SCSS. Parses as 5 percent.
+
+
+// Multiple space
+
+$foo: 1     +    1
+$bar: 2 +   1
+$baz: 1     + 2
+$qux: 2  +1
+// $norf: 5  %    2 FIXME: Gonzales - FIXED IN BETA
+
+// No space with parens
+
+$foo: (1+1)
+$bar: (2-1)
+$baz: (1/2)
+// $qux: (2*1) FIXME: Gonzales - FIXED IN BETA
+// $norf: (5%2) Not valid SCSS.
+
+
+// No space after with parens
+
+$foo: (1 +1)
+$bar: (2 -1)
+$baz: (1 /2)
+// $qux: (2 *1) FIXME: Gonzales - FIXED IN BETA
+// $norf: (5 %2) FIXME: Gonzales - FIXED IN BETA
+
+
+// No space before with parens
+
+$foo: (1+ 1)
+$bar: (2- 1)
+$baz: (1/ 2)
+// $qux: (2* 1) FIXME: Gonzales - FIXED IN BETA
+// $norf: (5% 2)  Not valid SCSS. Parses as 5 percent.
+
+
+// Multiple space with parens
+
+$foo: (1     +    1)
+$bar: (2 +   1)
+$baz: (1     + 2)
+$qux: (2  +1)
+// $norf: (5   %   2) FIXME: Gonzales - FIXED IN BETA
+
+
+
+
+// No space
+
+@if $foo==$bar
+  $baz: 1
+
+
+@if $foo!=$bar
+  $baz: 1
+
+
+@if $foo>=$bar
+  $baz: 1
+
+
+@if $foo<=$bar
+  $baz: 1
+
+
+@if $foo>$bar
+  $baz: 1
+
+
+@if $foo<$bar
+  $baz: 1
+
+
+
+// No space after
+
+@if $foo ==$bar
+  $baz: 1
+
+
+@if $foo !=$bar
+  $baz: 1
+
+
+@if $foo >=$bar
+  $baz: 1
+
+
+@if $foo <=$bar
+  $baz: 1
+
+
+@if $foo >$bar
+  $baz: 1
+
+
+@if $foo <$bar
+  $baz: 1
+
+
+
+// No space before
+
+@if $foo== $bar
+  $baz: 1
+
+
+@if $foo!= $bar
+  $baz: 1
+
+
+@if $foo>= $bar
+  $baz: 1
+
+
+@if $foo<= $bar
+  $baz: 1
+
+
+@if $foo> $bar
+  $baz: 1
+
+
+@if $foo< $bar
+  $baz: 1
+
+
+
+// Multiple spaces
+
+@if $foo  ==  $bar
+  $baz: 1
+
+
+@if $foo !=   $bar
+  $baz: 1
+
+
+@if $foo  >= $bar
+  $baz: 1
+
+
+@if $foo      <= $bar
+  $baz: 1
+
+
+@if $foo    >   $bar
+  $baz: 1
+
+
+@if $foo  <  $bar
+  $baz: 1
+
+
+
+// No space with parens
+
+@if ($foo==$bar)
+  $baz: 1
+
+
+@if ($foo!=$bar)
+  $baz: 1
+
+
+@if ($foo>=$bar)
+  $baz: 1
+
+
+@if ($foo<=$bar)
+  $baz: 1
+
+
+@if ($foo>$bar)
+  $baz: 1
+
+
+@if ($foo<$bar)
+  $baz: 1
+
+
+
+// No space after
+
+@if ($foo ==$bar)
+  $baz: 1
+
+
+@if ($foo !=$bar)
+  $baz: 1
+
+
+@if ($foo >=$bar)
+  $baz: 1
+
+
+@if ($foo <=$bar)
+  $baz: 1
+
+
+@if ($foo >$bar)
+  $baz: 1
+
+
+@if ($foo <$bar)
+  $baz: 1
+
+
+
+// No space before
+
+@if ($foo== $bar)
+  $baz: 1
+
+
+@if ($foo!= $bar)
+  $baz: 1
+
+
+@if ($foo>= $bar)
+  $baz: 1
+
+
+@if ($foo<= $bar)
+  $baz: 1
+
+
+@if ($foo> $bar)
+  $baz: 1
+
+
+@if ($foo< $bar)
+  $baz: 1
+
+
+
+// Multiple spaces
+
+@if ($foo  ==  $bar)
+  $baz: 1
+
+
+@if ($foo !=   $bar)
+  $baz: 1
+
+
+@if ($foo  >= $bar)
+  $baz: 1
+
+
+@if ($foo      <= $bar)
+  $baz: 1
+
+
+@if ($foo    >   $bar)
+  $baz: 1
+
+
+@if ($foo  <  $bar)
+  $baz: 1
+
+
+
+//====================
+// Should be ok
+//====================
+
+$foo: scale-color($foo, $lightness: -14%)
+
+.foo
+  top: -10px
+
+.foo
+  margin: 5px -15px
+
+.foo
+  margin: -10px -15px
+
+.foo
+  margin: -10px 10px -15px
+
+.foo
+  margin: -10px -15px -15px
+
+.foo
+  margin: -10px -15px -15px -15px
+
+.foo
+  margin: -10rem -15em -15px 15px
+
+.foo
+  margin-bottom: -20px
+
+.foo
+  width: 100%
+
+.foo
+  width: calc(100% - 20px)
+
+.foo
+  width: calc(100% / 2)
+
+.foo
+  margin-bottom: -20px
+
+.foo
+  font: italic small-caps bolder 16px/3 cursive
+
+.foo
+  font: italic small-caps bolder 16px/8px cursive
+
+ul
+  li:nth-child(2n+1)
+    content: ''
+
+
+
+// Invalid CSS
+
+.foo
+  font: italic small-caps bolder 16px / 3 cursive
+  font: italic small-caps bolder 16px / 8px cursive
+
+
+ul
+  li:nth-child(2n + 1)
+    content: ''
+
+
+
+
+// Values no parens
+
+$foo: 1 + 1
+$bar: 2 - 1
+$baz: 1 / 2
+$qux: 2 * 1
+// $norf: 5 % 2 FIXME: Gonzales - FIXED IN BETA
+
+// Values with parens
+
+$foo: (1 + 1)
+$bar: (2 - 1)
+$baz: (1 / 2)
+// Include: false will ignore this, so count will be one down on what it should
+// be
+$qux: (2 * 1) // FIXME: Gonzales - FIXED IN BETA
+// $norf: (5 % 2) FIXME: Gonzales - FIXED IN BETA
+
+// Space with no parens
+
+@if $foo == $bar
+  $baz: 1
+
+
+@if $foo != $bar
+  $baz: 1
+
+
+@if $foo >= $bar
+  $baz: 1
+
+
+@if $foo <= $bar
+  $baz: 1
+
+
+// Space with parens
+
+@if ($foo == $bar)
+  $baz: 1
+
+
+@if ($foo != $bar)
+  $baz: 1
+
+
+@if ($foo >= $bar)
+  $baz: 1
+
+
+@if ($foo <= $bar)
+  $baz: 1
+
+// Mixins
+
+=large-text
+  font:
+    family: Arial
+    size: 20px
+    weight: bold
+  color: #ff0000
+
+h1
+  +large-text
diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss
new file mode 100644
index 0000000..27287c9
--- /dev/null
+++ b/tests/sass/space-around-operator.scss
@@ -0,0 +1,454 @@
+//====================
+// Should fail
+//====================
+
+// FIXME: Gonzales issue
+// https://github.com/tonyganch/gonzales-pe/issues/85
+// .foo {
+//   background: url('image'  +  '/foo.png');
+// }
+
+.foo {
+  margin: 5px+15px;
+}
+
+.foo {
+  margin: 5px -15px;
+}
+
+.foo {
+  margin: 5px+ 15px;
+}
+
+$foo: 1;
+$bar: 3;
+
+.foo {
+  margin: $foo+$bar+'px';
+}
+
+.foo {
+  margin: #{$foo}* #{$bar}+'px';
+}
+
+
+
+// No space
+
+$foo: 1+1;
+$bar: 2-1;
+$baz: 1/2;
+$qux: 2*1;
+// $norf: 5%2; Not valid SCSS
+
+
+// No space after
+
+$foo: 1 +1;
+$bar: 2 -1;
+$baz: 1 /2;
+$qux: 2 *1;
+// $norf: 5 %2; FIXME: Gonzales - FIXED IN BETA
+
+
+// No space before
+
+$foo: 1+ 1;
+$bar: 2- 1;
+$baz: 1/ 2;
+$qux: 2* 1;
+// $norf: 5% 2;  Not valid SCSS. Parses as 5 percent.
+
+
+// Multiple space
+
+$foo: 1     +    1;
+$bar: 2 +   1;
+$baz: 1     + 2;
+$qux: 2  +1;
+// $norf: 5  %    2; FIXME: Gonzales - FIXED IN BETA
+
+// No space with parens
+
+$foo: (1+1);
+$bar: (2-1);
+$baz: (1/2);
+// $qux: (2*1); FIXME: Gonzales - FIXED IN BETA
+// $norf: (5%2); Not valid SCSS.
+
+
+// No space after with parens
+
+$foo: (1 +1);
+$bar: (2 -1);
+$baz: (1 /2);
+// $qux: (2 *1); FIXME: Gonzales - FIXED IN BETA
+// $norf: (5 %2); FIXME: Gonzales - FIXED IN BETA
+
+
+// No space before with parens
+
+$foo: (1+ 1);
+$bar: (2- 1);
+$baz: (1/ 2);
+// $qux: (2* 1); FIXME: Gonzales - FIXED IN BETA
+// $norf: (5% 2);  Not valid SCSS. Parses as 5 percent.
+
+
+// Multiple space with parens
+
+$foo: (1     +    1);
+$bar: (2 +   1);
+$baz: (1     + 2);
+$qux: (2  +1);
+// $norf: (5   %   2); FIXME: Gonzales - FIXED IN BETA
+
+
+
+
+// No space
+
+@if $foo==$bar {
+  $baz: 1;
+}
+
+@if $foo!=$bar {
+  $baz: 1;
+}
+
+@if $foo>=$bar {
+  $baz: 1;
+}
+
+@if $foo<=$bar {
+  $baz: 1;
+}
+
+@if $foo>$bar {
+  $baz: 1;
+}
+
+@if $foo<$bar {
+  $baz: 1;
+}
+
+
+// No space after
+
+@if $foo ==$bar {
+  $baz: 1;
+}
+
+@if $foo !=$bar {
+  $baz: 1;
+}
+
+@if $foo >=$bar {
+  $baz: 1;
+}
+
+@if $foo <=$bar {
+  $baz: 1;
+}
+
+@if $foo >$bar {
+  $baz: 1;
+}
+
+@if $foo <$bar {
+  $baz: 1;
+}
+
+
+// No space before
+
+@if $foo== $bar {
+  $baz: 1;
+}
+
+@if $foo!= $bar {
+  $baz: 1;
+}
+
+@if $foo>= $bar {
+  $baz: 1;
+}
+
+@if $foo<= $bar {
+  $baz: 1;
+}
+
+@if $foo> $bar {
+  $baz: 1;
+}
+
+@if $foo< $bar {
+  $baz: 1;
+}
+
+
+// Multiple spaces
+
+@if $foo  ==  $bar {
+  $baz: 1;
+}
+
+@if $foo !=   $bar {
+  $baz: 1;
+}
+
+@if $foo  >= $bar {
+  $baz: 1;
+}
+
+@if $foo      <= $bar {
+  $baz: 1;
+}
+
+@if $foo    >   $bar {
+  $baz: 1;
+}
+
+@if $foo  <  $bar {
+  $baz: 1;
+}
+
+
+// No space with parens
+
+@if ($foo==$bar) {
+  $baz: 1;
+}
+
+@if ($foo!=$bar) {
+  $baz: 1;
+}
+
+@if ($foo>=$bar) {
+  $baz: 1;
+}
+
+@if ($foo<=$bar) {
+  $baz: 1;
+}
+
+@if ($foo>$bar) {
+  $baz: 1;
+}
+
+@if ($foo<$bar) {
+  $baz: 1;
+}
+
+
+// No space after
+
+@if ($foo ==$bar) {
+  $baz: 1;
+}
+
+@if ($foo !=$bar) {
+  $baz: 1;
+}
+
+@if ($foo >=$bar) {
+  $baz: 1;
+}
+
+@if ($foo <=$bar) {
+  $baz: 1;
+}
+
+@if ($foo >$bar) {
+  $baz: 1;
+}
+
+@if ($foo <$bar) {
+  $baz: 1;
+}
+
+
+// No space before
+
+@if ($foo== $bar) {
+  $baz: 1;
+}
+
+@if ($foo!= $bar) {
+  $baz: 1;
+}
+
+@if ($foo>= $bar) {
+  $baz: 1;
+}
+
+@if ($foo<= $bar) {
+  $baz: 1;
+}
+
+@if ($foo> $bar) {
+  $baz: 1;
+}
+
+@if ($foo< $bar) {
+  $baz: 1;
+}
+
+
+// Multiple spaces
+
+@if ($foo  ==  $bar) {
+  $baz: 1;
+}
+
+@if ($foo !=   $bar) {
+  $baz: 1;
+}
+
+@if ($foo  >= $bar) {
+  $baz: 1;
+}
+
+@if ($foo      <= $bar) {
+  $baz: 1;
+}
+
+@if ($foo    >   $bar) {
+  $baz: 1;
+}
+
+@if ($foo  <  $bar) {
+  $baz: 1;
+}
+
+
+//====================
+// Should be ok
+//====================
+
+// Valid CSS
+
+$foo: scale-color($foo, $lightness: -14%);
+
+.foo {
+  top: -10px;
+}
+
+.foo {
+  margin: 5px -15px;
+}
+
+.foo {
+  margin: -10px -15px;
+}
+
+.foo {
+  margin: -10px 10px -15px;
+}
+
+.foo {
+  margin: -10px -15px -15px;
+}
+
+.foo {
+  margin: -10px -15px -15px -15px;
+}
+
+.foo {
+  margin: -10rem -15em -15px 15px;
+}
+
+.foo {
+  margin-bottom: -20px;
+}
+
+.foo {
+  width: 100%;
+}
+
+.foo {
+  width: calc(100% - 20px);
+}
+
+.foo {
+  width: calc(100% / 2);
+}
+
+.foo {
+  font: italic small-caps bolder 16px/3 cursive;
+  font: italic small-caps bolder 16px/8px cursive;
+}
+
+ul {
+  li:nth-child(2n+1) {
+    content: '';
+  }
+}
+
+// Invalid CSS
+
+.foo {
+  font: italic small-caps bolder 16px / 3 cursive;
+  font: italic small-caps bolder 16px / 8px cursive;
+}
+
+ul {
+  li:nth-child(2n + 1) {
+    content: '';
+  }
+}
+
+
+// Values no parens
+
+$foo: 1 + 1;
+$bar: 2 - 1;
+$baz: 1 / 2;
+$qux: 2 * 1;
+// $norf: 5 % 2; FIXME: Gonzales - FIXED IN BETA
+
+// Values with parens
+
+$foo: (1 + 1);
+$bar: (2 - 1);
+$baz: (1 / 2);
+// Include: false will ignore this, so count will be one down on what it should
+// be
+$qux: (2 * 1); // FIXME: Gonzales - FIXED IN BETA
+// $norf: (5 % 2); FIXME: Gonzales - FIXED IN BETA
+
+// Space with no parens
+
+@if $foo == $bar {
+  $baz: 1;
+}
+
+@if $foo != $bar {
+  $baz: 1;
+}
+
+@if $foo >= $bar {
+  $baz: 1;
+}
+
+@if $foo <= $bar {
+  $baz: 1;
+}
+
+// Space with parens
+
+@if ($foo == $bar) {
+  $baz: 1;
+}
+
+@if ($foo != $bar) {
+  $baz: 1;
+}
+
+@if ($foo >= $bar) {
+  $baz: 1;
+}
+
+@if ($foo <= $bar) {
+  $baz: 1;
+}
diff --git a/tests/yml/.color-keyword-errors.yml b/tests/yml/.color-keyword-errors.yml
index 0943855..fc4fb96 100644
--- a/tests/yml/.color-keyword-errors.yml
+++ b/tests/yml/.color-keyword-errors.yml
@@ -1,5 +1,6 @@
 options:
   formatter: json
+  cache-config: false
 files:
   include: '**/*.s+(a|c)ss'
 rules:
diff --git a/tests/yml/.json-lint.yml b/tests/yml/.json-lint.yml
index 90c3410..b5ccbfb 100644
--- a/tests/yml/.json-lint.yml
+++ b/tests/yml/.json-lint.yml
@@ -1,5 +1,6 @@
 options:
   formatter: json
+  cache-config: false
 files:
   include: '**/*.s+(a|c)ss'
 rules:
diff --git a/tests/yml/.no-merge-default.yml b/tests/yml/.no-merge-default.yml
new file mode 100644
index 0000000..a8a4706
--- /dev/null
+++ b/tests/yml/.no-merge-default.yml
@@ -0,0 +1,59 @@
+options:
+  formatter: stylish
+  cache-config: false
+  merge-default-rules: false
+files:
+  include: '**/*.s+(a|c)ss'
+rules:
+  # Extends
+  extends-before-mixins: 0
+  extends-before-declarations: 0
+  placeholder-in-extend: 0
+
+  # Mixins
+  mixins-before-declarations: 0
+
+  # Line Spacing
+  one-declaration-per-line: 0
+  empty-line-between-blocks: 0
+  single-line-per-selector: 0
+
+  # Disallows
+  no-debug: 0
+  no-duplicate-properties: 0
+  no-empty-rulesets: 0
+  no-extends: 0
+  no-ids: 0
+  no-important: 0
+  no-warn: 0
+  no-color-keywords: 0
+  no-invalid-hex: 0
+  no-css-comments: 0
+  no-color-literals: 1
+
+  # Style Guide
+  border-zero: 0
+  clean-import-paths: 0
+  empty-args: 0
+  hex-length: 0
+  hex-notation: 0
+  indentation: 0
+  leading-zero: 0
+  nesting-depth: 0
+  property-sort-order: 0
+  quotes: 0
+  variable-for-property: 0
+  zero-unit: 0
+
+  # Inner Spacing
+  space-after-comma: 0
+  space-before-colon: 0
+  space-after-colon: 0
+  space-before-brace: 0
+  space-before-bang: 0
+  space-after-bang: 0
+  space-between-parens: 0
+
+  # Final Items
+  trailing-semicolon: 0
+  final-newline: 0
diff --git a/tests/yml/.stylish-errors.yml b/tests/yml/.stylish-errors.yml
index 6fa3e58..d36e45b 100644
--- a/tests/yml/.stylish-errors.yml
+++ b/tests/yml/.stylish-errors.yml
@@ -1,5 +1,6 @@
 options:
   formatter: stylish
+  cache-config: false
 files:
   include: '**/*.s+(a|c)ss'
 rules:
diff --git a/tests/yml/.stylish-output.yml b/tests/yml/.stylish-output.yml
index 9f38c10..48329e1 100644
--- a/tests/yml/.stylish-output.yml
+++ b/tests/yml/.stylish-output.yml
@@ -1,5 +1,6 @@
 options:
   formatter: stylish
+  cache-config: false
 files:
   include: '**/*.s+(a|c)ss'
 rules: