Unverified Commit e3ca0757 by Michael Mifsud Committed by GitHub

Merge pull request #2128 from stefanpenner/leaks

Fix Some Leaks
parents 0a0f5c64 f6b0c722
"use strict";
module.exports = function iterateAndMeasure(fn, mod = 1000000) {
let count = 0;
while (true) {
count++;
fn();
if (count % mod === 0) {
console.log(process.memoryUsage().rss / 1000000);
}
}
}
'use strict';
var types = require('../').types;
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() { return types.Boolean(true).getValue(); });
"use strict";
var sass = require("../");
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() {
sass.renderSync({
data: '#{headings()} { color: #08c; }',
functions: {
'headings()': function() {
return new sass.types.String('hi');
}
}
});
}, 10000);
'use strict';
var types = require('../').types;
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() {
var key = new types.String('the-key');
var value = new types.String('the-value');
var map = new types.Map(1);
map.setKey(0, key);
map.setValue(0, value);
map.getKey(0);
}, 100000);
'use strict';
var types = require('../').types;
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() { return new types.String('hi'); });
......@@ -158,7 +158,9 @@ int ExtractOptions(v8::Local<v8::Object> options, void* cptr, sass_context_wrapp
CustomFunctionBridge *bridge = new CustomFunctionBridge(callback, ctx_w->is_sync);
ctx_w->function_bridges.push_back(bridge);
Sass_Function_Entry fn = sass_make_function(create_string(signature), sass_custom_function, bridge);
char* sig = create_string(signature);
Sass_Function_Entry fn = sass_make_function(sig, sass_custom_function, bridge);
free(sig);
sass_function_set_list_entry(fn_list, i, fn);
}
......@@ -290,6 +292,7 @@ NAN_METHOD(render_sync) {
}
sass_free_context_wrapper(ctx_w);
info.GetReturnValue().Set(result == 0);
}
......
......@@ -4,10 +4,10 @@
#include "sass_types/factory.h"
#include "sass_types/value.h"
Sass_Value* CustomFunctionBridge::post_process_return_value(v8::Local<v8::Value> val) const {
SassTypes::Value *v_;
if ((v_ = SassTypes::Factory::unwrap(val))) {
return v_->get_sass_value();
Sass_Value* CustomFunctionBridge::post_process_return_value(v8::Local<v8::Value> _val) const {
SassTypes::Value *value = SassTypes::Factory::unwrap(_val);
if (value) {
return value->get_sass_value();
} else {
return sass_make_error("A SassValue object was expected.");
}
......@@ -17,7 +17,10 @@ std::vector<v8::Local<v8::Value>> CustomFunctionBridge::pre_process_args(std::ve
std::vector<v8::Local<v8::Value>> argv = std::vector<v8::Local<v8::Value>>();
for (void* value : in) {
argv.push_back(SassTypes::Factory::create(static_cast<Sass_Value*>(value))->get_js_object());
Sass_Value* x = static_cast<Sass_Value*>(value);
SassTypes::Value* y = SassTypes::Factory::create(x);
argv.push_back(y->get_js_object());
}
return argv;
......
......@@ -29,6 +29,7 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val
imports[i] = sass_make_import_entry(0, 0, 0);
sass_import_set_error(imports[i], message, -1, -1);
free(message);
}
else {
imports[i] = get_importer_entry(object);
......@@ -43,6 +44,7 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val
imports[0] = sass_make_import_entry(0, 0, 0);
sass_import_set_error(imports[0], message, -1, -1);
free(message);
}
else if (returned_value->IsObject()) {
imports = sass_make_import_list(1);
......
......@@ -6,7 +6,9 @@ namespace SassTypes
Nan::Persistent<v8::Function> Boolean::constructor;
bool Boolean::constructor_locked = false;
Boolean::Boolean(bool v) : value(v) {}
Boolean::Boolean(bool _value) {
value = sass_make_boolean(_value);
}
Boolean& Boolean::get_singleton(bool v) {
static Boolean instance_false(false), instance_true(true);
......@@ -42,16 +44,15 @@ namespace SassTypes
return scope.Escape(conslocal);
}
Sass_Value* Boolean::get_sass_value() {
return sass_make_boolean(value);
}
v8::Local<v8::Object> Boolean::get_js_object() {
return Nan::New(this->js_object);
}
NAN_METHOD(Boolean::New) {
v8::Local<v8::Boolean> Boolean::get_js_boolean() {
return sass_boolean_get_value(this->value) ? Nan::True() : Nan::False();
}
NAN_METHOD(Boolean::New) {
if (info.IsConstructCall()) {
if (constructor_locked) {
return Nan::ThrowTypeError("Cannot instantiate SassBoolean");
......@@ -67,9 +68,6 @@ namespace SassTypes
}
NAN_METHOD(Boolean::GetValue) {
Boolean *out;
if ((out = static_cast<Boolean*>(Factory::unwrap(info.This())))) {
info.GetReturnValue().Set(Nan::New(out->value));
}
info.GetReturnValue().Set(Boolean::Unwrap<Boolean>(info.This())->get_js_boolean());
}
}
......@@ -12,7 +12,6 @@ namespace SassTypes
static Boolean& get_singleton(bool);
static v8::Local<v8::Function> get_constructor();
Sass_Value* get_sass_value();
v8::Local<v8::Object> get_js_object();
static NAN_METHOD(New);
......@@ -21,11 +20,11 @@ namespace SassTypes
private:
Boolean(bool);
bool value;
Nan::Persistent<v8::Object> js_object;
static Nan::Persistent<v8::Function> constructor;
static bool constructor_locked;
v8::Local<v8::Boolean> get_js_boolean();
};
}
......
......@@ -62,19 +62,19 @@ namespace SassTypes
}
NAN_METHOD(Color::GetR) {
info.GetReturnValue().Set(sass_color_get_r(unwrap(info.This())->value));
info.GetReturnValue().Set(sass_color_get_r(Color::Unwrap<Color>(info.This())->value));
}
NAN_METHOD(Color::GetG) {
info.GetReturnValue().Set(sass_color_get_g(unwrap(info.This())->value));
info.GetReturnValue().Set(sass_color_get_g(Color::Unwrap<Color>(info.This())->value));
}
NAN_METHOD(Color::GetB) {
info.GetReturnValue().Set(sass_color_get_b(unwrap(info.This())->value));
info.GetReturnValue().Set(sass_color_get_b(Color::Unwrap<Color>(info.This())->value));
}
NAN_METHOD(Color::GetA) {
info.GetReturnValue().Set(sass_color_get_a(unwrap(info.This())->value));
info.GetReturnValue().Set(sass_color_get_a(Color::Unwrap<Color>(info.This())->value));
}
NAN_METHOD(Color::SetR) {
......@@ -86,7 +86,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number");
}
sass_color_set_r(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust());
sass_color_set_r(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
}
NAN_METHOD(Color::SetG) {
......@@ -98,7 +98,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number");
}
sass_color_set_g(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust());
sass_color_set_g(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
}
NAN_METHOD(Color::SetB) {
......@@ -110,7 +110,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number");
}
sass_color_set_b(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust());
sass_color_set_b(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
}
NAN_METHOD(Color::SetA) {
......@@ -122,6 +122,6 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number");
}
sass_color_set_a(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust());
sass_color_set_a(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
}
}
......@@ -61,11 +61,12 @@ namespace SassTypes
}
Value* Factory::unwrap(v8::Local<v8::Value> obj) {
// Todo: non-SassValue objects could easily fall under that condition, need to be more specific.
if (!obj->IsObject() || obj.As<v8::Object>()->InternalFieldCount() != 1) {
return NULL;
if (obj->IsObject()) {
v8::Local<v8::Object> v8_obj = obj.As<v8::Object>();
if (v8_obj->InternalFieldCount() == 1) {
return SassTypes::Value::Unwrap<Value>(v8_obj);
}
return static_cast<Value*>(Nan::GetInternalFieldPointer(obj.As<v8::Object>(), 0));
}
return NULL;
}
}
......@@ -47,7 +47,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied index should be an integer");
}
Sass_Value* list = unwrap(info.This())->value;
Sass_Value* list = List::Unwrap<List>(info.This())->value;
size_t index = Nan::To<uint32_t>(info[0]).FromJust();
......@@ -73,14 +73,14 @@ namespace SassTypes
Value* sass_value = Factory::unwrap(info[1]);
if (sass_value) {
sass_list_set_value(unwrap(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
sass_list_set_value(List::Unwrap<List>(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
} else {
Nan::ThrowTypeError("A SassValue is expected as the list item");
}
}
NAN_METHOD(List::GetSeparator) {
info.GetReturnValue().Set(sass_list_get_separator(unwrap(info.This())->value) == SASS_COMMA);
info.GetReturnValue().Set(sass_list_get_separator(List::Unwrap<List>(info.This())->value) == SASS_COMMA);
}
NAN_METHOD(List::SetSeparator) {
......@@ -92,10 +92,10 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a boolean");
}
sass_list_set_separator(unwrap(info.This())->value, Nan::To<bool>(info[0]).FromJust() ? SASS_COMMA : SASS_SPACE);
sass_list_set_separator(List::Unwrap<List>(info.This())->value, Nan::To<bool>(info[0]).FromJust() ? SASS_COMMA : SASS_SPACE);
}
NAN_METHOD(List::GetLength) {
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_list_get_length(unwrap(info.This())->value)));
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_list_get_length(List::Unwrap<List>(info.This())->value)));
}
}
......@@ -37,7 +37,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied index should be an integer");
}
Sass_Value* map = unwrap(info.This())->value;
Sass_Value* map = Map::Unwrap<Map>(info.This())->value;
size_t index = Nan::To<uint32_t>(info[0]).FromJust();
......@@ -63,14 +63,13 @@ namespace SassTypes
Value* sass_value = Factory::unwrap(info[1]);
if (sass_value) {
sass_map_set_value(unwrap(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
sass_map_set_value(Map::Unwrap<Map>(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
} else {
Nan::ThrowTypeError("A SassValue is expected as a map value");
}
}
NAN_METHOD(Map::GetKey) {
if (info.Length() != 1) {
return Nan::ThrowTypeError("Expected just one argument");
}
......@@ -79,7 +78,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied index should be an integer");
}
Sass_Value* map = unwrap(info.This())->value;
Sass_Value* map = Map::Unwrap<Map>(info.This())->value;
size_t index = Nan::To<uint32_t>(info[0]).FromJust();
......@@ -87,7 +86,9 @@ namespace SassTypes
return Nan::ThrowRangeError(Nan::New("Out of bound index").ToLocalChecked());
}
info.GetReturnValue().Set(Factory::create(sass_map_get_key(map, Nan::To<uint32_t>(info[0]).FromJust()))->get_js_object());
SassTypes::Value* obj = Factory::create(sass_map_get_key(map, Nan::To<uint32_t>(info[0]).FromJust()));
v8::Local<v8::Object> js_obj = obj->get_js_object();
info.GetReturnValue().Set(js_obj);
}
NAN_METHOD(Map::SetKey) {
......@@ -105,13 +106,13 @@ namespace SassTypes
Value* sass_value = Factory::unwrap(info[1]);
if (sass_value) {
sass_map_set_key(unwrap(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
sass_map_set_key(Map::Unwrap<Map>(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
} else {
Nan::ThrowTypeError("A SassValue is expected as a map key");
}
}
NAN_METHOD(Map::GetLength) {
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_map_get_length(unwrap(info.This())->value)));
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_map_get_length(Map::Unwrap<Map>(info.This())->value)));
}
}
......@@ -6,7 +6,9 @@ namespace SassTypes
Nan::Persistent<v8::Function> Null::constructor;
bool Null::constructor_locked = false;
Null::Null() {}
Null::Null() {
value = sass_make_null();
}
Null& Null::get_singleton() {
static Null singleton_instance;
......@@ -37,10 +39,6 @@ namespace SassTypes
return scope.Escape(conslocal);
}
Sass_Value* Null::get_sass_value() {
return sass_make_null();
}
v8::Local<v8::Object> Null::get_js_object() {
return Nan::New(this->js_object);
}
......
......@@ -23,6 +23,10 @@ namespace SassTypes
}
unit = create_string(raw_val[1]);
*out = sass_make_number(value, unit);
delete unit;
return *out;
}
}
......@@ -37,11 +41,11 @@ namespace SassTypes
}
NAN_METHOD(Number::GetValue) {
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_number_get_value(unwrap(info.This())->value)));
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_number_get_value(Number::Unwrap<Number>(info.This())->value)));
}
NAN_METHOD(Number::GetUnit) {
info.GetReturnValue().Set(Nan::New<v8::String>(sass_number_get_unit(unwrap(info.This())->value)).ToLocalChecked());
info.GetReturnValue().Set(Nan::New<v8::String>(sass_number_get_unit(Number::Unwrap<Number>(info.This())->value)).ToLocalChecked());
}
NAN_METHOD(Number::SetValue) {
......@@ -54,7 +58,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number");
}
sass_number_set_value(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust());
sass_number_set_value(Number::Unwrap<Number>(info.This())->value, Nan::To<double>(info[0]).FromJust());
}
NAN_METHOD(Number::SetUnit) {
......@@ -66,6 +70,6 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a string");
}
sass_number_set_unit(unwrap(info.This())->value, create_string(info[0]));
sass_number_set_unit(Number::Unwrap<Number>(info.This())->value, create_string(info[0]));
}
}
......@@ -12,14 +12,12 @@ namespace SassTypes
// Include this in any SassTypes::Value subclasses to handle all the heavy lifting of constructing JS
// objects and wrapping sass values inside them
template <class T>
/* class SassValueWrapper : public SassTypes::Value { */
class SassValueWrapper : public SassTypes::Value {
public:
static char const* get_constructor_name() { return "SassValue"; }
SassValueWrapper(Sass_Value*);
virtual ~SassValueWrapper();
Sass_Value* get_sass_value();
SassValueWrapper(Sass_Value* v) : Value(v) { }
v8::Local<v8::Object> get_js_object();
static v8::Local<v8::Function> get_constructor();
......@@ -27,44 +25,21 @@ namespace SassTypes
static NAN_METHOD(New);
static Sass_Value *fail(const char *, Sass_Value **);
protected:
Sass_Value* value;
static T* unwrap(v8::Local<v8::Object>);
private:
/* private: */
static Nan::Persistent<v8::Function> constructor;
Nan::Persistent<v8::Object> js_object;
};
template <class T>
Nan::Persistent<v8::Function> SassValueWrapper<T>::constructor;
template <class T>
SassValueWrapper<T>::SassValueWrapper(Sass_Value* v) {
this->value = sass_clone_value(v);
}
template <class T>
SassValueWrapper<T>::~SassValueWrapper() {
this->js_object.Reset();
sass_delete_value(this->value);
}
template <class T>
Sass_Value* SassValueWrapper<T>::get_sass_value() {
return sass_clone_value(this->value);
}
template <class T>
v8::Local<v8::Object> SassValueWrapper<T>::get_js_object() {
if (this->js_object.IsEmpty()) {
if (this->persistent().IsEmpty()) {
v8::Local<v8::Object> wrapper = Nan::NewInstance(T::get_constructor()).ToLocalChecked();
delete static_cast<T*>(Nan::GetInternalFieldPointer(wrapper, 0));
Nan::SetInternalFieldPointer(wrapper, 0, this);
this->js_object.Reset(wrapper);
this->Wrap(wrapper);
}
return Nan::New(this->js_object);
return this->handle();
}
template <class T>
......@@ -100,8 +75,8 @@ namespace SassTypes
T* obj = new T(value);
sass_delete_value(value);
Nan::SetInternalFieldPointer(info.This(), 0, obj);
obj->js_object.Reset(info.This());
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
} else {
return Nan::ThrowError(Nan::New<v8::String>(sass_error_get_message(value)).ToLocalChecked());
}
......@@ -117,17 +92,10 @@ namespace SassTypes
}
template <class T>
T* SassValueWrapper<T>::unwrap(v8::Local<v8::Object> obj) {
/* This maybe NULL */
return static_cast<T*>(Factory::unwrap(obj));
}
template <class T>
Sass_Value *SassValueWrapper<T>::fail(const char *reason, Sass_Value **out) {
*out = sass_make_error(reason);
return NULL;
}
}
#endif
......@@ -15,18 +15,23 @@ namespace SassTypes
}
value = create_string(raw_val[0]);
}
*out = sass_make_string(value);
delete value;
return *out;
} else {
return *out = sass_make_string(value);
}
}
void String::initPrototype(v8::Local<v8::FunctionTemplate> proto) {
Nan::SetPrototypeMethod(proto, "getValue", GetValue);
Nan::SetPrototypeMethod(proto, "setValue", SetValue);
}
NAN_METHOD(String::GetValue) {
info.GetReturnValue().Set(Nan::New<v8::String>(sass_string_get_value(unwrap(info.This())->value)).ToLocalChecked());
info.GetReturnValue().Set(Nan::New<v8::String>(sass_string_get_value(String::Unwrap<String>(info.This())->value)).ToLocalChecked());
}
NAN_METHOD(String::SetValue) {
......@@ -38,6 +43,6 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a string");
}
sass_string_set_value(unwrap(info.This())->value, create_string(info[0]));
sass_string_set_value(String::Unwrap<String>(info.This())->value, create_string(info[0]));
}
}
......@@ -7,10 +7,35 @@
namespace SassTypes
{
// This is the interface that all sass values must comply with
class Value {
class Value : public Nan::ObjectWrap {
public:
virtual Sass_Value* get_sass_value() =0;
virtual v8::Local<v8::Object> get_js_object() =0;
Value() {
}
Sass_Value* get_sass_value() {
return sass_clone_value(this->value);
}
protected:
Sass_Value* value;
Value(Sass_Value* v) {
this->value = sass_clone_value(v);
}
~Value() {
sass_delete_value(this->value);
}
static Sass_Value* fail(const char *reason, Sass_Value **out) {
*out = sass_make_error(reason);
return NULL;
}
};
}
......
......@@ -310,7 +310,6 @@ describe('api', function() {
contents: '@import "b"'
});
} else {
console.log(prev);
assert.equal(prev, '/Users/me/sass/lib/a.scss');
done({
file: '/Users/me/sass/lib/b.scss',
......
/*eslint new-cap: ["error", { "capIsNew": false }]*/
'use strict';
var assert = require('assert');
var sass = require('../');
describe('sass.types', function() {
describe('Boolean', function() {
it('exists', function() {
assert(sass.types.Boolean);
});
it('names the constructor correctly', function() {
assert.equal(sass.types.Boolean.name, 'SassBoolean');
});
it('supports call constructor', function() {
var t = sass.types.Boolean(true);
assert.equal(t.toString(), '[object SassBoolean]');
var f = sass.types.Boolean(false);
assert.equal(f.toString(), '[object SassBoolean]');
});
it('has true and false singletons', function() {
assert.equal(sass.types.Boolean(true), sass.types.Boolean(true));
assert.equal(sass.types.Boolean(false), sass.types.Boolean(false));
assert.notEqual(sass.types.Boolean(false), sass.types.Boolean(true));
assert.equal(sass.types.Boolean(true), sass.types.Boolean.TRUE);
assert.equal(sass.types.Boolean(false), sass.types.Boolean.FALSE);
});
it('supports DOES NOT support new constructor', function() {
assert.throws(function() {
new sass.types.Boolean(true);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Cannot instantiate SassBoolean');
return true;
});
});
it('throws with incorrect constructor args', function() {
assert.throws(function() {
sass.types.Boolean();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected one boolean argument');
return true;
});
[1, 2, '', 'hi', {}, []].forEach(function(arg) {
assert.throws(function() {
sass.types.Boolean(arg);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected one boolean argument');
return true;
});
});
assert.throws(function() {
sass.types.Boolean(true, false);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected one boolean argument');
return true;
});
});
it('implements getValue', function() {
var t = sass.types.Boolean(true);
assert.equal(typeof t.getValue, 'function');
assert.equal(t.getValue(), true);
var f = sass.types.Boolean(false);
assert.equal(typeof f.getValue, 'function');
assert.equal(f.getValue(), false);
});
});
describe('Color', function() {
it('exists', function() {
assert(sass.types.Color);
});
it('names the constructor correctly', function() {
assert.equal(sass.types.Color.name, 'SassColor');
});
it('supports call constructor', function() {
var t = sass.types.Color();
assert.equal(t.toString(), '[object SassColor]');
});
it('supports new constructor', function() {
var t = new sass.types.Color(1);
assert.equal(t.toString(), '[object SassColor]');
});
it('supports variadic constructor args', function() {
var a = new sass.types.Color();
assert.equal(a.getR(), 0);
assert.equal(a.getG(), 0);
assert.equal(a.getB(), 0);
assert.equal(a.getA(), 1);
var b = new sass.types.Color(1);
assert.equal(b.getR(), 0);
assert.equal(b.getG(), 0);
assert.equal(b.getB(), 1);
assert.equal(b.getA(), 0); // why ?
assert.throws(function() {
new sass.types.Color(1, 2);
}, function(error) {
// assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Constructor should be invoked with either 0, 1, 3 or 4 arguments.');
return true;
});
var c = new sass.types.Color(1, 2, 3);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 2);
assert.equal(c.getB(), 3);
assert.equal(c.getA(), 1);
var d = new sass.types.Color(1, 2, 3, 4);
assert.equal(d.getR(), 1);
assert.equal(d.getG(), 2);
assert.equal(d.getB(), 3);
assert.equal(d.getA(), 4);
assert.throws(function() {
new sass.types.Color(1, 2, 3, 4, 5);
}, function(error) {
// assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Constructor should be invoked with either 0, 1, 3 or 4 arguments.');
return true;
});
});
it('supports get{R,G,B,A} and set{R,G,B,A}', function() {
var c = new sass.types.Color();
assert.equal(c.getR(), 0);
assert.equal(c.getG(), 0);
assert.equal(c.getB(), 0);
assert.equal(c.getA(), 1);
assert.equal(c.setR(1), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 0);
assert.equal(c.getB(), 0);
assert.equal(c.getA(), 1);
assert.equal(c.setG(1), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 1);
assert.equal(c.getB(), 0);
assert.equal(c.getA(), 1);
assert.equal(c.setB(1), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 1);
assert.equal(c.getB(), 1);
assert.equal(c.getA(), 1);
assert.equal(c.setA(0), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 1);
assert.equal(c.getB(), 1);
assert.equal(c.getA(), 0);
});
it('throws with incorrect set{R,G,B,A} arguments', function() {
var c = new sass.types.Color();
function assertJustOneArgument(cb) {
assert.throws(function() {
cb();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
}
function assertNumberArgument(arg, cb) {
assert.throws(function() {
cb();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a number');
return true;
}, 'argument was: ' + arg);
}
assertJustOneArgument(function() { c.setR(); });
assertJustOneArgument(function() { c.setG(); });
assertJustOneArgument(function() { c.setB(); });
assertJustOneArgument(function() { c.setA(); });
assertJustOneArgument(function() { c.setR(1, 2); });
assertJustOneArgument(function() { c.setG(1, 2); });
assertJustOneArgument(function() { c.setB(1, 2); });
assertJustOneArgument(function() { c.setA(1, 2); });
[true, false, '0', '1', '', 'omg', {}, []].forEach(function(arg) {
assertNumberArgument(arg, function() { c.setR(arg); });
assertNumberArgument(arg, function() { c.setG(arg); });
assertNumberArgument(arg, function() { c.setB(arg); });
assertNumberArgument(arg, function() { c.setA(arg); });
});
});
});
describe('Error', function() {
it('exists', function() {
assert(sass.types.Error);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Error.name, 'SassError');
});
it('supports call constructor', function() {
var e = sass.types.Error('Such Error');
assert.ok(e instanceof sass.types.Error);
assert.equal(e.toString(), '[object SassError]');
// TODO: I'm not sure this object works well, it likely needs to be fleshed out more...
});
it('supports new constructor', function() {
var e = new sass.types.Error('Such Error');
assert.ok(e instanceof sass.types.Error);
assert.equal(e.toString(), '[object SassError]');
// TODO: I'm not sure this object works well, it likely needs to be fleshed out more...
});
});
describe('List', function() {
it('exists', function() {
assert(sass.types.List);
});
it('has a corectly named constructor', function() {
assert.equal(sass.types.List.name, 'SassList');
});
it('support call constructor', function() {
var list = sass.types.List();
assert.ok(list instanceof sass.types.List);
assert.equal(list.toString(), '[object SassList]');
});
it('support new constructor', function() {
var list = new sass.types.List();
assert.ok(list instanceof sass.types.List);
assert.equal(list.toString(), '[object SassList]');
});
it('support variadic constructor', function() {
var a = new sass.types.List();
assert.equal(a.getLength(), 0);
assert.equal(a.getSeparator(), true);
var b = new sass.types.List(1);
assert.equal(b.getSeparator(), true);
assert.equal(b.getLength(), 1);
var c = new sass.types.List(1, true);
assert.equal(b.getLength(), 1);
assert.equal(c.getSeparator(), true);
var d = new sass.types.List(1, false);
assert.equal(b.getLength(), 1);
assert.equal(d.getSeparator(), false);
var e = new sass.types.List(1, true, 2);
assert.equal(b.getLength(), 1);
assert.equal(e.getSeparator(), true);
assert.throws(function() {
new sass.types.List('not-a-number');
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'First argument should be an integer.');
return true;
});
assert.throws(function() {
new sass.types.List(1, 'not-a-boolean');
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'Second argument should be a boolean.');
return true;
});
});
it('supports {get,set}Separator', function() {
var a = new sass.types.List();
assert.equal(a.getSeparator(), true);
assert.equal(a.setSeparator(true), undefined);
assert.equal(a.getSeparator(), true);
assert.equal(a.setSeparator(false), undefined);
assert.equal(a.getSeparator(), false);
assert.throws(function() {
a.setSeparator();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
[1, '', [], {}].forEach(function(arg) {
assert.throws(function() {
a.setSeparator(arg);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a boolean');
return true;
}, 'setSeparator(' + arg + ')');
});
});
it('supports setValue and getValue', function() {
var a = new sass.types.List();
assert.throws(function() {
a.getValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
['hi', [], {}].forEach(function(arg) {
assert.throws(function() {
a.getValue(arg);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied index should be an integer');
return true;
}, 'getValue(' + arg + ')');
});
assert.throws(function() {
a.getValue(0);
}, function(error) {
assert.ok(error instanceof RangeError);
assert.equal(error.message, 'Out of bound index');
return true;
});
assert.throws(function() {
a.getValue(-1);
}, function(error) {
assert.ok(error instanceof RangeError);
assert.equal(error.message, 'Out of bound index');
return true;
});
assert.throws(function() {
a.setValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected two arguments');
return true;
});
assert.throws(function() {
a.setValue(1);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected two arguments');
return true;
});
assert.throws(function() {
a.setValue(0, 'no-a-sass-value');
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a SassValue object');
return true;
});
});
// TODO: more complex set/get value scenarios
});
describe('Map', function() {
it('exists', function() {
assert(sass.types.Map);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Map.name, 'SassMap');
});
it('supports call constructor', function() {
var x = sass.types.Map();
assert.equal(x.toString(), '[object SassMap]');
});
it('supports new constructor', function() {
var x = new sass.types.Map();
assert.equal(x.toString(), '[object SassMap]');
});
it('supports an optional constructor argument', function() {
var x = new sass.types.Map();
var y = new sass.types.Map(1);
var z = new sass.types.Map(2, 3);
assert.throws(function() {
new sass.types.Map('OMG');
}, function(error) {
assert.equal(error.message, 'First argument should be an integer.');
// TODO: TypeError
return true;
});
assert.equal(x.getLength(), 0);
assert.equal(y.getLength(), 1);
assert.equal(z.getLength(), 2);
});
it('supports length', function() {
var y = new sass.types.Map(1);
var z = new sass.types.Map(2);
assert.equal(y.getLength(), 1);
assert.equal(z.getLength(), 2);
});
it('supports {get,set}Value {get,set}Key', function() {
var y = new sass.types.Map(1);
var omg = new sass.types.String('OMG');
y.setValue(0, omg);
console.log(y.getValue(0));
});
});
describe('Null', function() {
it('exists', function() {
assert(sass.types.Null);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Null.name, 'SassNull');
});
it('does not support new constructor', function() {
assert.throws(function() {
new sass.types.Null();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Cannot instantiate SassNull');
return true;
});
});
it('supports call constructor (and is a singleton)', function() {
assert.equal(sass.types.Null(), sass.types.Null());
assert.equal(sass.types.Null(), sass.types.Null.NULL);
});
});
describe('Number', function() {
it('exists', function() {
assert(sass.types.Number);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Number.name, 'SassNumber');
});
it('supports new constructor', function() {
var number = new sass.types.Number();
assert.equal(number.toString(), '[object SassNumber]');
});
it('supports call constructor', function() {
var number = sass.types.Number();
assert.equal(number.toString(), '[object SassNumber]');
});
it('supports multiple constructor arguments', function() {
var a = new sass.types.Number();
var b = new sass.types.Number(1);
var c = new sass.types.Number(2, 'px');
assert.throws(function() {
new sass.types.Number('OMG');
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'First argument should be a number.');
return true;
});
assert.throws(function() {
new sass.types.Number(1, 2);
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'Second argument should be a string.');
return true;
});
assert.equal(a.getValue(), 0);
assert.equal(a.getUnit(), '');
assert.equal(b.getValue(), 1);
assert.equal(b.getUnit(), '');
assert.equal(c.getValue(), 2);
assert.equal(c.getUnit(), 'px');
});
it('supports get{Unit,Value}, set{Unit,Value}', function() {
var number = new sass.types.Number(1, 'px');
assert.equal(number.getValue(), 1);
assert.equal(number.getUnit(), 'px');
number.setValue(2);
assert.equal(number.getValue(), 2);
assert.equal(number.getUnit(), 'px');
number.setUnit('em');
assert.equal(number.getValue(), 2);
assert.equal(number.getUnit(), 'em');
assert.throws(function() {
number.setValue('OMG');
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a number');
return true;
});
assert.throws(function() {
number.setValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
assert.throws(function() {
number.setUnit();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
assert.throws(function() {
number.setUnit(1);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a string');
return true;
});
});
});
describe('String', function() {
it('exists', function() {
assert(sass.types.String);
});
it('has a properly named constructor', function() {
assert.equal(sass.types.String.name, 'SassString');
});
it('supports call constructor', function() {
var x = sass.types.String('OMG');
assert.equal(x.toString(), '[object SassString]');
assert.equal(x.getValue(), 'OMG');
});
it('supports new constructor', function() {
var x = new sass.types.String('OMG');
assert.equal(x.toString(), '[object SassString]');
assert.equal(x.getValue(), 'OMG');
});
it('supports multiple constructor arg combinations', function() {
new sass.types.String();
new sass.types.String('OMG');
new sass.types.String('OMG', 'NOPE');
[null, undefined, [], {}, function() { }].forEach(function(arg) {
assert.throws(function() {
new sass.types.String(arg);
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'Argument should be a string.');
return true;
});
});
});
it('supports {get,set}Value', function() {
var x = new sass.types.String();
assert.equal(x.getValue(), '');
assert.equal(x.setValue('hi'), undefined);
assert.equal(x.getValue(), 'hi');
assert.equal(x.setValue('bye'), undefined);
assert.equal(x.getValue(), 'bye');
assert.throws(function() {
x.setValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
assert.throws(function() {
x.setValue('hi', 'hi');
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment