From aa860937dfcc6529faba0b470e344e14ccbb813a Mon Sep 17 00:00:00 2001 From: Leonardo Silveira Date: Sun, 30 Jun 2024 16:31:46 -0300 Subject: [PATCH] finally covered issue #4 --- package-lock.json | 88 +++++++++++++++++++ package.json | 5 +- src/chip.cc | 16 ++-- src/line.cc | 20 +++-- src/misc.cc | 4 +- test/{00-misc.js => 00-misc.spec.js} | 11 ++- test/{01-chip.js => 01-chip.spec.js} | 15 ++-- test/{02-line.js => 02-line.spec.js} | 34 +++---- test/{03-pin.js => 03-pin.spec.js} | 4 +- ...04-line-flags.js => 04-line-flags.spec.js} | 2 +- test/issues/issue-4.spec.js | 49 +++++++++++ 11 files changed, 199 insertions(+), 49 deletions(-) rename test/{00-misc.js => 00-misc.spec.js} (72%) rename test/{01-chip.js => 01-chip.spec.js} (65%) rename test/{02-line.js => 02-line.spec.js} (79%) rename test/{03-pin.js => 03-pin.spec.js} (75%) rename test/{04-line-flags.js => 04-line-flags.spec.js} (97%) create mode 100644 test/issues/issue-4.spec.js diff --git a/package-lock.json b/package-lock.json index 7cd1c59..d519dd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "nan": "^2.11.1" }, "devDependencies": { + "chai": "^4.4.1", "mocha": "10.1.0", "node-gyp": "^10.0.1", "nyc": "^15.1.0", @@ -1815,6 +1816,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2075,6 +2085,24 @@ } ] }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2103,6 +2131,18 @@ "node": ">=8" } }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2420,6 +2460,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4181,6 +4233,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -5561,6 +5622,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -6686,6 +6756,15 @@ "node": ">=8" } }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -8048,6 +8127,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", diff --git a/package.json b/package.json index abd90a0..92dc269 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,12 @@ "build": "node-gyp build", "clean": "node-gyp clean", "configure-gpio-sim": "sh test/prepare-gpio-sim.sh", - "test": "mocha", + "test": "mocha test/*.spec.js test/**/*.spec.js", "test:coverage": "nyc npm run test", "lint": "xo --fix" }, "devDependencies": { + "chai": "^4.4.1", "mocha": "10.1.0", "node-gyp": "^10.0.1", "nyc": "^15.1.0", @@ -53,4 +54,4 @@ "unicorn/prefer-module": 0 } } -} \ No newline at end of file +} diff --git a/src/chip.cc b/src/chip.cc index d8fd2e6..381bd6b 100644 --- a/src/chip.cc +++ b/src/chip.cc @@ -17,7 +17,9 @@ NAN_MODULE_INIT(Chip::Init) { Chip::Chip(const char *device) { chip = gpiod_chip_open_lookup(device); - if (!chip) Nan::ThrowError("Unable to open device"); + std::string msg = "Chip::new - Unable to open device "; + msg = msg + device; + if (!chip) Nan::ThrowError(Nan::ErrnoException(errno, msg.c_str())); } Chip::~Chip() { @@ -44,12 +46,12 @@ NAN_METHOD(Chip::New) { NAN_METHOD(Chip::getNumberOfLines) { Chip *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->chip) { - Nan::ThrowError("::getNumberOfLines() for chip==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getNumberOfLines() for chip==NULL")); return; } int ret = gpiod_chip_num_lines(obj->getNativeChip()); if (-1 == ret) { - Nan::ThrowError("::getNumberOfLines() failed"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getNumberOfLines() failed")); } else info.GetReturnValue().Set(ret); } @@ -57,12 +59,12 @@ NAN_METHOD(Chip::getNumberOfLines) { NAN_METHOD(Chip::getChipName) { Chip *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->chip) { - Nan::ThrowError("::getChipName() for chip==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getChipName() for chip==NULL")); return; } const char *name = gpiod_chip_name(obj->getNativeChip()); if (!name) { - Nan::ThrowError("::getChipName() failed"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getChipName() failed")); } else info.GetReturnValue().Set(Nan::New(name).ToLocalChecked()); } @@ -70,12 +72,12 @@ NAN_METHOD(Chip::getChipName) { NAN_METHOD(Chip::getChipLabel) { Chip *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->chip) { - Nan::ThrowError("::getChipLabel() for chip==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getChipLabel() for chip==NULL")); return; } const char *label = gpiod_chip_label(obj->getNativeChip()); if (!label) { - Nan::ThrowError("::getChipLabel() failed"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getChipLabel() failed")); } else info.GetReturnValue().Set(Nan::New(label).ToLocalChecked()); } diff --git a/src/line.cc b/src/line.cc index 5632c6b..31aed11 100644 --- a/src/line.cc +++ b/src/line.cc @@ -23,7 +23,9 @@ NAN_MODULE_INIT(Line::Init) { Line::Line(Chip *chip, unsigned int pin) { line = gpiod_chip_get_line(chip->getNativeChip(), pin); - if (!line) Nan::ThrowError("Unable to open GPIO line "); + std::string msg = "Line::new - Unable to open GPIO line "; + msg = msg + std::to_string(pin); + if (!line) Nan::ThrowError(Nan::ErrnoException(errno, msg.c_str())); } Line::~Line() { @@ -51,7 +53,7 @@ NAN_METHOD(Line::New) { NAN_METHOD(Line::getLineOffset) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::getLineOffset() for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getLineOffset() for line==NULL")); return; } int ret = gpiod_line_offset(obj->getNativeLine()); @@ -64,7 +66,7 @@ NAN_METHOD(Line::getLineOffset) { NAN_METHOD(Line::getLineName) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::getLineName() for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getLineName() for line==NULL")); return; } const char *name = gpiod_line_name(obj->getNativeLine()); @@ -77,7 +79,7 @@ NAN_METHOD(Line::getLineName) { NAN_METHOD(Line::getLineConsumer) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::getLineConsumer() for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getLineConsumer() for line==NULL")); return; } const char *name = gpiod_line_consumer(obj->getNativeLine()); @@ -90,7 +92,7 @@ NAN_METHOD(Line::getLineConsumer) { NAN_METHOD(Line::getValue) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::getValue() for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getValue() for line==NULL")); return; } int ret = gpiod_line_get_value(obj->getNativeLine()); @@ -112,7 +114,7 @@ NAN_METHOD(Line::setValue) { NAN_METHOD(Line::requestInputMode) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::requestInputMode() for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::requestInputMode() for line==NULL")); return; } Nan::Utf8String consumer(info[0]); @@ -123,7 +125,7 @@ NAN_METHOD(Line::requestInputMode) { NAN_METHOD(Line::requestInputModeFlags) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::requestInputModeFlags for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::requestInputModeFlags for line==NULL")); return; } Nan::Utf8String consumer(info[0]); @@ -135,7 +137,7 @@ NAN_METHOD(Line::requestInputModeFlags) { NAN_METHOD(Line::requestOutputMode) { Line *obj = Nan::ObjectWrap::Unwrap(info.This()); if (!obj->line) { - Nan::ThrowError("::requestOutputMode() for line==NULL"); + Nan::ThrowError(Nan::ErrnoException(errno, "::requestOutputMode() for line==NULL")); return; } unsigned int value = 0; @@ -143,7 +145,7 @@ NAN_METHOD(Line::requestOutputMode) { if (!defaultValue->IsUndefined() && defaultValue->IsNumber()) { unsigned int val = Nan::To(defaultValue).FromJust(); if (val > 1) { - Nan::ThrowError("::requestOutputMode() value is not in {0,1} range"); + Nan::ThrowError(Nan::ErrnoException(errno, "::requestOutputMode() value is not in {0,1} range")); return; } value = val; diff --git a/src/misc.cc b/src/misc.cc index 0f8367f..bc80068 100644 --- a/src/misc.cc +++ b/src/misc.cc @@ -14,7 +14,7 @@ NAN_METHOD(getInstantLineValue) { int value = -1; if (-1 == (value = gpiod_ctxless_get_value(*device, offset, active_low, *consumer))) { - Nan::ThrowError("Unable to get instant value"); + Nan::ThrowError(Nan::ErrnoException(errno, "::getInstantLineValue - Unable to get instant value")); return; } @@ -29,7 +29,7 @@ NAN_METHOD(setInstantLineValue) { Nan::Utf8String consumer(info[4]); if (-1 == gpiod_ctxless_set_value(*device, offset, value, active_low, *consumer, NULL, NULL)) { - Nan::ThrowError("Unable to get instant value"); + Nan::ThrowError(Nan::ErrnoException(errno, "::setInstantLineValue - Unable to get instant value")); return; } diff --git a/test/00-misc.js b/test/00-misc.spec.js similarity index 72% rename from test/00-misc.js rename to test/00-misc.spec.js index 42e6a78..5f9c077 100644 --- a/test/00-misc.js +++ b/test/00-misc.spec.js @@ -1,22 +1,25 @@ -const assert = require('node:assert'); +const { expect } = require('chai'); const gpiod = require('..'); describe('libgpiod miscellaneous bindings', () => { it('should get libgpiod version', done => { - assert.ok(gpiod.version()); + expect(gpiod.version()).to.be.ok; done(); }); it('should get line instant value', done => { const value = gpiod.getInstantLineValue(0, 17); - assert.equal(0, value); + expect(value).to.eq(0); done(); }); it('should NOT get line instant value due wrong chip name', done => { try { gpiod.getInstantLineValue('/dev/gpiochipZero', 17); - } catch { + } catch (e) { + expect(e.errno).eq(2) + expect(e.code).eq('ENOENT') + expect(e.syscall).eq('::getInstantLineValue - Unable to get instant value') done(); } }); diff --git a/test/01-chip.js b/test/01-chip.spec.js similarity index 65% rename from test/01-chip.js rename to test/01-chip.spec.js index e20f0fa..f207ed2 100644 --- a/test/01-chip.js +++ b/test/01-chip.spec.js @@ -1,4 +1,4 @@ -const assert = require('node:assert'); +const { expect } = require('chai'); const gpiod = require('..'); describe('libgpiod chip bindings', () => { @@ -8,27 +8,30 @@ describe('libgpiod chip bindings', () => { it('should \'create\' a new chip by number', done => { const chip0 = new gpiod.Chip('0'); - assert.equal(numLines, chip0.getNumberOfLines()); + expect(chip0.getNumberOfLines()).eq(numLines); done(); }); it('should \'create\' a new chip by name', done => { const chip0 = new gpiod.Chip('gpiochip0'); - assert.equal(numLines, chip0.getNumberOfLines()); + expect(chip0.getNumberOfLines()).eq(numLines); done(); }); it('should \'create\' a new chip by path', done => { const chip0 = new gpiod.Chip('/dev/gpiochip0'); - assert.equal(numLines, chip0.getNumberOfLines()); + expect(chip0.getNumberOfLines()).eq(numLines); done(); }); it('should NOT \'create\' a chip because it does not exists', done => { try { const chip0 = new gpiod.Chip('/dev/gpiochippuden'); - assert.equal(numLines, chip0.getNumberOfLines()); - } catch { + chip0.getNumberOfLines() + } catch (e) { + expect(e.errno).eq(2) + expect(e.code).eq("ENOENT") + expect(e.syscall).eq("Chip::new - Unable to open device /dev/gpiochippuden") done(); } }); diff --git a/test/02-line.js b/test/02-line.spec.js similarity index 79% rename from test/02-line.js rename to test/02-line.spec.js index f30933e..a59daa5 100644 --- a/test/02-line.js +++ b/test/02-line.spec.js @@ -1,5 +1,5 @@ -const assert = require('node:assert'); -const gpiod = require('../'); +const { expect } = require('chai'); +const gpiod = require('..'); describe('libgpiod line bindings', () => { @@ -8,7 +8,7 @@ describe('libgpiod line bindings', () => { it('should get a line from the chip', done => { const chip0 = new gpiod.Chip('gpiochip0'); - assert(chip0.getLine(17)); + expect(chip0.getLine(17)).ok; done(); }); @@ -16,7 +16,10 @@ describe('libgpiod line bindings', () => { const chip0 = new gpiod.Chip('gpiochip0'); try { new gpiod.Line(chip0, 1700); - } catch { + } catch (e) { + expect(e.syscall).eq("Line::new - Unable to open GPIO line 1700") + expect(e.code).eq("EINVAL") + expect(e.errno).eq(22) done(); } }); @@ -29,14 +32,14 @@ describe('libgpiod line bindings', () => { setTimeout(() => { line17.release(); done(); - }, 500); + }, 500); // will blink for a half second on the real deal }); it('should get line value', done => { const chip0 = new gpiod.Chip('gpiochip0'); const line17 = chip0.getLine(17); line17.requestInputMode(); - assert.equal(0, line17.getValue()); + expect(line17.getValue()).eq(0); line17.release(); done(); }); @@ -46,9 +49,9 @@ describe('libgpiod line bindings', () => { const line17 = chip0.getLine(17); const line13 = chip0.getLine(13); let offset = line17.getLineOffset(); - assert.equal(17, offset); + expect(17).eq(offset); offset = line13.getLineOffset(); - assert.equal(13, offset); + expect(13).eq(offset); line17.release(); line13.release(); done(); @@ -58,7 +61,7 @@ describe('libgpiod line bindings', () => { const chip0 = new gpiod.Chip('gpiochip0'); const line17 = chip0.getLine(17); const name = line17.getLineName(); - assert.equal(lineName, name); + expect(lineName).eq(name); line17.release(); done(); }); @@ -67,7 +70,7 @@ describe('libgpiod line bindings', () => { const chip0 = new gpiod.Chip('gpiochip0'); const line17 = new gpiod.Line(chip0, 17); line17.requestOutputMode(); - let count = 7; + let count = 4; const interval = setInterval(() => { line17.setValue(count-- % 2); if (count == 0) { @@ -75,24 +78,23 @@ describe('libgpiod line bindings', () => { line17.release(); clearInterval(interval); } - }, 200); + }, 300); }); it('should get line consumer', done => { const chip0 = new gpiod.Chip('gpiochip0'); let line13 = chip0.getLine(13); - assert.equal(undefined, line13.getLineConsumer()); + expect(undefined).eq(line13.getLineConsumer()); line13.requestInputMode("foobar"); - let consumer = line13.getLineConsumer(); - assert.equal("foobar", consumer); + expect("foobar").eq(line13.getLineConsumer()); line13.release(); line13 = chip0.getLine(13); line13.requestInputMode("quix"); consumer = line13.getLineConsumer(); - assert.equal("quix", consumer); + expect("quix").eq(consumer); line13.release(); done(); @@ -105,7 +107,7 @@ describe('libgpiod line bindings', () => { line13.requestInputModeFlags("foobar", gpiod.LineFlags.GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN); consumer = line13.getLineConsumer(); - assert.equal("foobar", consumer); + expect("foobar").eq(consumer); line13.release(); done(); diff --git a/test/03-pin.js b/test/03-pin.spec.js similarity index 75% rename from test/03-pin.js rename to test/03-pin.spec.js index dd36ff2..c205329 100644 --- a/test/03-pin.js +++ b/test/03-pin.spec.js @@ -1,5 +1,5 @@ -const assert = require('assert'); -const gpiod = require('../'); +const { assert } = require('chai'); +const gpiod = require('..'); describe('libgpiod Pin sugar', () => { it('should create a Pin for line 10', done => { diff --git a/test/04-line-flags.js b/test/04-line-flags.spec.js similarity index 97% rename from test/04-line-flags.js rename to test/04-line-flags.spec.js index 3bac5fd..a54c4b7 100644 --- a/test/04-line-flags.js +++ b/test/04-line-flags.spec.js @@ -1,5 +1,5 @@ const assert = require('node:assert'); -const gpiod = require('../'); +const gpiod = require('..'); describe('libgpiod LineFlags sugar', () => { it('GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN', done => { diff --git a/test/issues/issue-4.spec.js b/test/issues/issue-4.spec.js new file mode 100644 index 0000000..4a3c8e9 --- /dev/null +++ b/test/issues/issue-4.spec.js @@ -0,0 +1,49 @@ +const { expect } = require("chai") + +const { Chip, Line } = require("../../") + +describe("Line double-definition issue (#4)", () => { + + it("Should fail on double definition", done => { + + const chip0 = new Chip(0) // see prepare-gpio-sim.sh or real hardware + + expect(chip0).to.be.ok + let l17 = new Line(chip0, 17) + + try { + l17.requestOutputMode() + l17.setValue(1) + + l17 = new Line(chip0, 17) // we didn't release the previous one + l17.requestOutputMode() + l17.setValue(1) + } catch (e) { + expect(e).to.be.ok + expect(e.code).to.eq('EBUSY') + expect(e.syscall).to.eq('::requestOutputMode') + done() + } finally { + l17.release() + } + }) + + it("Should work fine on double definition", done => { + + const chip0 = new Chip(0) // see prepare-gpio-sim.sh or real hardware + + expect(chip0).to.be.ok + + let l17 = new Line(chip0, 17) + l17.requestOutputMode() + l17.setValue(1) + l17.release() + + l17 = new Line(chip0, 17) // we didn't release the previous one + l17.requestOutputMode() + l17.setValue(1) + l17.release() + + done() + }) +}) \ No newline at end of file