<commit_before>// Copyright (c) 2018, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

const utils = require('./utils');

class LlvmIrParser {
    constructor(compilerProps) {
        // @todo More settings/compiler properties

        this.maxIrLines = 500;
        if (compilerProps) {
            this.maxIrLines = compilerProps('maxLinesOfAsm', this.maxIrLines);
        }

        this.debugReference = /!dbg (!\d+)/;
    }

    getFileName(debugInfo, scope) {
        const stdInLooking = /.*<stdin>|^-$|example\.[^/]+$|<source>/;

        if (!debugInfo[scope]) {
            // No such meta info.
            return null;
        }
        // MetaInfo is a file node
        if (debugInfo[scope].filename) {
            const filename = debugInfo[scope].filename;
            return !filename.match(stdInLooking) ? filename : null;
        }
        // MetaInfo has a file reference.
        if (debugInfo[scope].file) {
            return this.getFileName(debugInfo, debugInfo[scope].file);
        }
        if (!debugInfo[scope].scope) {
            // No higher scope => can't find file.
            return null;
        }
        // "Bubbling" up.
        return this.getFileName(debugInfo, debugInfo[scope].scope);
    }

    processIr(ir, filters) {
        const result = [];
        let irLines = utils.splitLines(ir);
        let debugInfo = {};

        // Filters
        const commentOnly = /^\s*(;.*)$/;

        irLines.forEach(line => {
            if (result.length >= this.maxIrLines) {
                if (result.length === this.maxIrLines) {
                    result.push({text: "[truncated; too many lines]", source: null});
                }
                return;
            }

            let source = null;
            let match;

            if (!line.trim().length) {
                result.push({text: "", source: null});
                return;
            }

            if (filters.commentOnly && line.match(commentOnly)) {
                return;
            }

            // Non-Meta IR line. Metadata is attached to it using "!dbg !123"
            match = line.match(this.debugReference);
            if (match) {
                result.push({text: (filters.trim ? utils.trimLine(line) : line), source: source, scope: match[1]});
                return;
            }

            // Metadata Nodes
            // See: https://llvm.org/docs/LangRef.html#metadata
            // @todo: Multiline Metadata?
            // @todo: Named Metadata?
            match = line.match(/^(!\d+) = (distinct )?!DI([A-Za-z]+)\(([^)]+?)\)/);
            if (match) {
                const metaId = match[1];
                const metaName = match[3];

                let options = {};
                const optionWithValue = match[4].split(',');
                for (let i = 0; i < optionWithValue.length; ++i) {
                    const pair = optionWithValue[i].split(':');
                    options[pair[0].trim()] = pair[1].trim();
                }
                options.metaId = match[1];
                debugInfo[metaId] = options;

                if (metaName === "File" && options.filename) {
                    // Remove quotes
                    debugInfo[metaId].filename = options.filename.substr(1, options.filename.length - 2);
                }
            }

            if (filters.directives && this.isLineLlvmDirective(line)) {
                return;
            }

            result.push({text: (filters.trim ? utils.trimLine(line) : line), source: source});
        });

        result.forEach(line => {
            if (!line.scope) return;
            // @todo: Nested Scopes?
            line.source = {
                file: this.getFileName(debugInfo, line.scope),
                line: debugInfo[line.scope] ? debugInfo[line.scope].line : null
            };
        });

        return result;
    }

    process(ir, filters) {
        return this.processIr(ir, filters);
    }

    isLineLlvmDirective(line) {
        return line.match(/^!\d+ = !{/)
            || line.match(/^!\d+ = (distinct )?!DI/)
            || line.match(/^!llvm./)
            || line.match(/^source_filename = /)
            || line.match(/^target datalayout = /)
            || line.match(/^target triple = /);
    }

    isLlvmIr(code) {
        return code.includes("@llvm") && code.includes("!DI") && code.includes("!dbg");
    }
}

module.exports = {
    LlvmIrParser: LlvmIrParser
};
<commit_msg>[LLVM] Better handling of max lines<commit_after>// Copyright (c) 2018, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

const utils = require('./utils');

class LlvmIrParser {
    constructor(compilerProps) {
        // @todo More settings/compiler properties

        this.maxIrLines = 500;
        if (compilerProps) {
            this.maxIrLines = compilerProps('maxLinesOfAsm', this.maxIrLines);
        }

        this.debugReference = /!dbg (!\d+)/;
    }

    getFileName(debugInfo, scope) {
        const stdInLooking = /.*<stdin>|^-$|example\.[^/]+$|<source>/;

        if (!debugInfo[scope]) {
            // No such meta info.
            return null;
        }
        // MetaInfo is a file node
        if (debugInfo[scope].filename) {
            const filename = debugInfo[scope].filename;
            return !filename.match(stdInLooking) ? filename : null;
        }
        // MetaInfo has a file reference.
        if (debugInfo[scope].file) {
            return this.getFileName(debugInfo, debugInfo[scope].file);
        }
        if (!debugInfo[scope].scope) {
            // No higher scope => can't find file.
            return null;
        }
        // "Bubbling" up.
        return this.getFileName(debugInfo, debugInfo[scope].scope);
    }

    processIr(ir, filters) {
        const result = [];
        let irLines = utils.splitLines(ir);
        let debugInfo = {};

        // Filters
        const commentOnly = /^\s*(;.*)$/;

        irLines.forEach(line => {
            let source = null;
            let match;

            if (!line.trim().length) {
                result.push({text: "", source: null});
                return;
            }

            if (filters.commentOnly && line.match(commentOnly)) {
                return;
            }

            // Non-Meta IR line. Metadata is attached to it using "!dbg !123"
            match = line.match(this.debugReference);
            if (match) {
                result.push({text: (filters.trim ? utils.trimLine(line) : line), source: source, scope: match[1]});
                return;
            }

            // Metadata Nodes
            // See: https://llvm.org/docs/LangRef.html#metadata
            // @todo: Multiline Metadata?
            // @todo: Named Metadata?
            match = line.match(/^(!\d+) = (distinct )?!DI([A-Za-z]+)\(([^)]+?)\)/);
            if (match) {
                const metaId = match[1];
                const metaName = match[3];

                let options = {};
                const optionWithValue = match[4].split(',');
                for (let i = 0; i < optionWithValue.length; ++i) {
                    const pair = optionWithValue[i].split(':');
                    options[pair[0].trim()] = pair[1].trim();
                }
                options.metaId = match[1];
                debugInfo[metaId] = options;

                if (metaName === "File" && options.filename) {
                    // Remove quotes
                    debugInfo[metaId].filename = options.filename.substr(1, options.filename.length - 2);
                }
            }

            if (filters.directives && this.isLineLlvmDirective(line)) {
                return;
            }

            result.push({text: (filters.trim ? utils.trimLine(line) : line), source: source});
        });

        if (result.length >= this.maxIrLines) {
            result.length = this.maxIrLines + 1;
            result[this.maxIrLines] = {text: "[truncated; too many lines]", source: null};
        }

        result.forEach(line => {
            if (!line.scope) return;
            // @todo: Nested Scopes?
            line.source = {
                file: this.getFileName(debugInfo, line.scope),
                line: debugInfo[line.scope] ? debugInfo[line.scope].line : null
            };
        });

        return result;
    }

    process(ir, filters) {
        return this.processIr(ir, filters);
    }

    isLineLlvmDirective(line) {
        return line.match(/^!\d+ = !{/)
            || line.match(/^!\d+ = (distinct )?!DI/)
            || line.match(/^!llvm./)
            || line.match(/^source_filename = /)
            || line.match(/^target datalayout = /)
            || line.match(/^target triple = /);
    }

    isLlvmIr(code) {
        return code.includes("@llvm") && code.includes("!DI") && code.includes("!dbg");
    }
}

module.exports = {
    LlvmIrParser: LlvmIrParser
};
<|endoftext|>


<commit_before>import reducer from '../src/redux/reducer';
import initialState from '../src/redux/initial-state';
import { addTodo, deleteTodo, toggleTodo, changeInput } from '../src/redux/actions';

describe('reducer tests', () => {
  test('ADD_TODO adds a todo element and resets the input field', () => {
    const state = { ...initialState, input: 'test todo' };
    const result = reducer(state, addTodo());
    expect(result.todos.length).toBe(2);
    expect(result.todos[1].task).toBe('test todo');
    expect(result.input).toBe('');
  });

  test('DELETE_TODO deletes a todo element.', () => {
    const state = {
      ...initialState,
      todos: [{ task: 'test todo', id: 1, done: false }]
    };
    const id = state.todos[0].id;
    const result = reducer(state, deleteTodo(id));
    expect(result.todos.length).toBe(0);
  });

  test('TOGGLE_TODO toggles a todo element.', () => {
    const state = {
      ...initialState,
      todos: [{ task: 'test todo', id: 1, done: false }]
    };
    const id = state.todos[0].id;
    const result = reducer(state, toggleTodo(id));
    expect(result.todos[0].done).toBe(true);
  });

  test('CHANGE_INPUT changes the input field.', () => {
    const value = 'this is a test input value';
    const state = { ...initialState, input: '' };
    const result = reducer(state, changeInput(value));
    expect(result.input).toBe(value);
  });
});
<commit_msg>chore: formatting<commit_after>import reducer from '../src/redux/reducer';
import initialState from '../src/redux/initial-state';
import {
  addTodo,
  deleteTodo,
  toggleTodo,
  changeInput
} from '../src/redux/actions';

describe('reducer tests', () => {
  test('ADD_TODO adds a todo element and resets the input field', () => {
    const state = { ...initialState, input: 'test todo' };
    const result = reducer(state, addTodo());
    expect(result.todos.length).toBe(2);
    expect(result.todos[1].task).toBe('test todo');
    expect(result.input).toBe('');
  });

  test('DELETE_TODO deletes a todo element.', () => {
    const state = {
      ...initialState,
      todos: [{ task: 'test todo', id: 1, done: false }]
    };
    const id = state.todos[0].id;
    const result = reducer(state, deleteTodo(id));
    expect(result.todos.length).toBe(0);
  });

  test('TOGGLE_TODO toggles a todo element.', () => {
    const state = {
      ...initialState,
      todos: [{ task: 'test todo', id: 1, done: false }]
    };
    const id = state.todos[0].id;
    const result = reducer(state, toggleTodo(id));
    expect(result.todos[0].done).toBe(true);
  });

  test('CHANGE_INPUT changes the input field.', () => {
    const value = 'this is a test input value';
    const state = { ...initialState, input: '' };
    const result = reducer(state, changeInput(value));
    expect(result.input).toBe(value);
  });
});
<|endoftext|>


<commit_before>// replies-test.js
//
// Test adding and removing replies
//
// Copyright 2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var assert = require("assert"),
    vows = require("vows"),
    databank = require("databank"),
    _ = require("underscore"),
    Step = require("step"),
    schema = require("../lib/schema").schema,
    URLMaker = require("../lib/urlmaker").URLMaker,
    Databank = databank.Databank,
    DatabankObject = databank.DatabankObject;

var suite = vows.describe("replies interface");

suite.addBatch({
    "When we initialize the environment": {
        topic: function() {

            var cb = this.callback;
            // Need this to make IDs

            URLMaker.hostname = "example.net";

            // Dummy databank

            var params = {schema: schema};

            var db = Databank.get("memory", params);

            db.connect({}, function(err) {
                if (err) {
                    cb(err);
                } else {
                    DatabankObject.bank = db;
                    cb(null);
                }
            });
        },
        "it works": function(err) {
            assert.ifError(err);
        },
        "and we create a new object": {
            topic: function() {
                var Note = require("../lib/model/note").Note;
                Note.create({content: "This sucks."}, this.callback);
            },
            "it works": function(err, note) {
                assert.ifError(err);
                assert.isObject(note);
            },
            "it has a getReplies() method": function(err, note) {
                assert.isFunction(note.getReplies);
            },
            "and we check its replies list": {
                topic: function(note) {
                    note.getReplies(0, 20, this.callback);
                },
                "it works": function(err, replies) {
                    assert.ifError(err);
                },
                "it is empty": function(err, replies) {
                    assert.isArray(replies);
                    assert.lengthOf(replies, 0);
                }
            }
        },
        "and we create a new object and post a reply": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback;

                Step(
                    function() {
                        Note.create({content: "Testing testing 123."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "Whatever.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, comment, note);
                        }
                    }
                );
            },
            "it works": function(err, comment, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment);
                    });
                },
                "it works": function(err, list, comment) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment) {
                    assert.isArray(list);
                    assert.lengthOf(list, 1);
                    assert.deepEqual(list[0], comment);
                }
            }
        },
        "and we create a new object and post a reply and remove the reply": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback;

                Step(
                    function() {
                        Note.create({content: "Another test note."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "Still bad.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) throw err;
                        comment.del(this);
                    },
                    function(err) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, note);
                        }
                    }
                );
            },
            "it works": function(err, comment, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment);
                    });
                },
                "it works": function(err, list, comment) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment) {
                    assert.isArray(list);
                    assert.lengthOf(list, 0);
                }
            }
        },
        "and we create a new object and post a reply and post a reply to that": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comment1 = null;

                Step(
                    function() {
                        Note.create({content: "Test again."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "PLBBBBTTTTBBTT.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) throw err;
                        comment1 = comment;
                        Comment.create({content: "Uncalled for!", inReplyTo: comment}, this);
                    },
                    function(err, comment2) {
                        if (err) {
                            cb(err, null, null, null);
                        } else {
                            cb(null, comment2, comment1, note);
                        }
                    }
                );
            },
            "it works": function(err, comment2, comment1, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment2, comment1, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment1, comment2);
                    });
                },
                "it works": function(err, list, comment1, comment2) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment1, comment2) {
                    assert.isArray(list);
                    assert.lengthOf(list, 1);
                    assert.equal(list[0].id, comment1.id);
                    assert.include(list[0], 'replies');
                    assert.include(list[0].replies, 'items');
                    assert.lengthOf(list[0].replies.items, 1);
                    assert.equal(list[0].replies.items[0].id, comment2.id);
                }
            }
        },
        "and we create a new object and post a reply and favour the reply": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comment1 = null;

                Step(
                    function() {
                        Note.create({content: "Test again."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "PLBBBBTTTTBBTT.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) throw err;
                        comment1 = comment;
                    },
                    function(err, comment2) {
                        if (err) {
                            cb(err, null, null, null);
                        } else {
                            cb(null, comment2, comment1, note);
                        }
                    }
                );
            },
            "it works": function(err, comment2, comment1, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment2, comment1, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment1, comment2);
                    });
                },
                "it works": function(err, list, comment1, comment2) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment1, comment2) {
                    assert.isArray(list);
                    assert.lengthOf(list, 1);
                    assert.equal(list[0].id, comment1.id);
                    assert.include(list[0], 'replies');
                    assert.include(list[0].replies, 'items');
                    assert.lengthOf(list[0].replies.items, 1);
                    assert.equal(list[0].replies.items[0].id, comment2.id);
                }
            }
        },
        "and we create a new object and post a lot of replies": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comments = null;

                Step(
                    function() {
                        Note.create({content: "More testing."}, this);
                    },
                    function(err, result) {
                        var i, group = this.group();
                        if (err) throw err;
                        note = result;
                        for (var i = 0; i < 100; i++) {
                            Comment.create({content: "YOU LIE.", inReplyTo: note}, group());
                        }
                    },
                    function(err, comments) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, comments, note);
                        }
                    }
                );
            },
            "it works": function(err, comments, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comments, note) {
                    var cb = this.callback;
                    note.getReplies(0, 200, function(err, list) {
                        cb(err, list, comments, note);
                    });
                },
                "it works": function(err, list, comments, note) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comments, note) {
                    var i, listIDs = new Array(100), commentIDs = new Array(100);
                    assert.isArray(list);
                    assert.lengthOf(list, 100);
                    for (i = 0; i < 100; i++) {
                        listIDs[i] = list.items[i].id;
                        commentIDs[i] = comments[i].id;
                    }
                    for (i = 0; i < 100; i++) {
                        assert.include(listIDs, comments[i].id);
                        assert.include(commentIDs, list.items[i].id);
                    }
                }
            }
        },
        "and we create a new object and expand it": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    cb = this.callback;

                Step(
                    function() {
                        Note.create({content: "Blow face."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        note.expand(this);
                    },
                    function(err) {
                        if (err) {
                            cb(err, null);
                        } else {
                            cb(null, note);
                        }
                    }
                );
            },
            "it works": function(err, note) {
                assert.ifError(err);
            },
            "its replies element looks right": function(err, comment, note) {
                assert.include(note, "replies");
                assert.isObject(note.replies);
                assert.include(note.replies, "totalItems");
                assert.equal(note.replies.totalItems, 0);
                assert.include(note.replies, "url");
                assert.isString(note.replies.url);
                assert.include(note.replies, "items");
                assert.isArray(note.replies.items);
                assert.lengthOf(note.replies.items, 0);
            }
        },
        "and we create a new object and post a reply and expand the object": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comment = null;

                Step(
                    function() {
                        Note.create({content: "Test your face."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "UR FACE", inReplyTo: note}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        comment = result;
                        note.expand(this);
                    },
                    function(err) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, comment, note);
                        }
                    }
                );
            },
            "it works": function(err, comment, note) {
                assert.ifError(err);
            },
            "its replies element looks right": function(err, comment, note) {
                assert.include(note, "replies");
                assert.isObject(note.replies);
                assert.include(note.replies, "totalItems");
                assert.equal(note.replies.totalItems, 1);
                assert.include(note.replies, "url");
                assert.isString(note.replies.url);
                assert.include(note.replies, "items");
                assert.isArray(note.replies.items);
                assert.lengthOf(note.replies.items, 1);
                assert.equal(note.replies.items[0].id, comment.id);
            }
        }
    }
});

suite["export"](module);
<commit_msg>Remove the favour test<commit_after>// replies-test.js
//
// Test adding and removing replies
//
// Copyright 2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var assert = require("assert"),
    vows = require("vows"),
    databank = require("databank"),
    _ = require("underscore"),
    Step = require("step"),
    schema = require("../lib/schema").schema,
    URLMaker = require("../lib/urlmaker").URLMaker,
    Databank = databank.Databank,
    DatabankObject = databank.DatabankObject;

var suite = vows.describe("replies interface");

suite.addBatch({
    "When we initialize the environment": {
        topic: function() {

            var cb = this.callback;
            // Need this to make IDs

            URLMaker.hostname = "example.net";

            // Dummy databank

            var params = {schema: schema};

            var db = Databank.get("memory", params);

            db.connect({}, function(err) {
                if (err) {
                    cb(err);
                } else {
                    DatabankObject.bank = db;
                    cb(null);
                }
            });
        },
        "it works": function(err) {
            assert.ifError(err);
        },
        "and we create a new object": {
            topic: function() {
                var Note = require("../lib/model/note").Note;
                Note.create({content: "This sucks."}, this.callback);
            },
            "it works": function(err, note) {
                assert.ifError(err);
                assert.isObject(note);
            },
            "it has a getReplies() method": function(err, note) {
                assert.isFunction(note.getReplies);
            },
            "and we check its replies list": {
                topic: function(note) {
                    note.getReplies(0, 20, this.callback);
                },
                "it works": function(err, replies) {
                    assert.ifError(err);
                },
                "it is empty": function(err, replies) {
                    assert.isArray(replies);
                    assert.lengthOf(replies, 0);
                }
            }
        },
        "and we create a new object and post a reply": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback;

                Step(
                    function() {
                        Note.create({content: "Testing testing 123."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "Whatever.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, comment, note);
                        }
                    }
                );
            },
            "it works": function(err, comment, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment);
                    });
                },
                "it works": function(err, list, comment) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment) {
                    assert.isArray(list);
                    assert.lengthOf(list, 1);
                    assert.deepEqual(list[0], comment);
                }
            }
        },
        "and we create a new object and post a reply and remove the reply": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback;

                Step(
                    function() {
                        Note.create({content: "Another test note."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "Still bad.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) throw err;
                        comment.del(this);
                    },
                    function(err) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, note);
                        }
                    }
                );
            },
            "it works": function(err, comment, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment);
                    });
                },
                "it works": function(err, list, comment) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment) {
                    assert.isArray(list);
                    assert.lengthOf(list, 0);
                }
            }
        },
        "and we create a new object and post a reply and post a reply to that": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comment1 = null;

                Step(
                    function() {
                        Note.create({content: "Test again."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "PLBBBBTTTTBBTT.", inReplyTo: note}, this);
                    },
                    function(err, comment) {
                        if (err) throw err;
                        comment1 = comment;
                        Comment.create({content: "Uncalled for!", inReplyTo: comment}, this);
                    },
                    function(err, comment2) {
                        if (err) {
                            cb(err, null, null, null);
                        } else {
                            cb(null, comment2, comment1, note);
                        }
                    }
                );
            },
            "it works": function(err, comment2, comment1, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comment2, comment1, note) {
                    var cb = this.callback;
                    note.getReplies(0, 20, function(err, list) {
                        cb(err, list, comment1, comment2);
                    });
                },
                "it works": function(err, list, comment1, comment2) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comment1, comment2) {
                    assert.isArray(list);
                    assert.lengthOf(list, 1);
                    assert.equal(list[0].id, comment1.id);
                    assert.include(list[0], 'replies');
                    assert.include(list[0].replies, 'items');
                    assert.lengthOf(list[0].replies.items, 1);
                    assert.equal(list[0].replies.items[0].id, comment2.id);
                }
            }
        },
        "and we create a new object and post a lot of replies": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comments = null;

                Step(
                    function() {
                        Note.create({content: "More testing."}, this);
                    },
                    function(err, result) {
                        var i, group = this.group();
                        if (err) throw err;
                        note = result;
                        for (var i = 0; i < 100; i++) {
                            Comment.create({content: "YOU LIE.", inReplyTo: note}, group());
                        }
                    },
                    function(err, comments) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, comments, note);
                        }
                    }
                );
            },
            "it works": function(err, comments, note) {
                assert.ifError(err);
            },
            "and we check the replies of the first object": {
                topic: function(comments, note) {
                    var cb = this.callback;
                    note.getReplies(0, 200, function(err, list) {
                        cb(err, list, comments, note);
                    });
                },
                "it works": function(err, list, comments, note) {
                    assert.ifError(err);
                },
                "it looks correct": function(err, list, comments, note) {
                    var i, listIDs = new Array(100), commentIDs = new Array(100);
                    assert.isArray(list);
                    assert.lengthOf(list, 100);
                    for (i = 0; i < 100; i++) {
                        listIDs[i] = list.items[i].id;
                        commentIDs[i] = comments[i].id;
                    }
                    for (i = 0; i < 100; i++) {
                        assert.include(listIDs, comments[i].id);
                        assert.include(commentIDs, list.items[i].id);
                    }
                }
            }
        },
        "and we create a new object and expand it": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    cb = this.callback;

                Step(
                    function() {
                        Note.create({content: "Blow face."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        note.expand(this);
                    },
                    function(err) {
                        if (err) {
                            cb(err, null);
                        } else {
                            cb(null, note);
                        }
                    }
                );
            },
            "it works": function(err, note) {
                assert.ifError(err);
            },
            "its replies element looks right": function(err, comment, note) {
                assert.include(note, "replies");
                assert.isObject(note.replies);
                assert.include(note.replies, "totalItems");
                assert.equal(note.replies.totalItems, 0);
                assert.include(note.replies, "url");
                assert.isString(note.replies.url);
                assert.include(note.replies, "items");
                assert.isArray(note.replies.items);
                assert.lengthOf(note.replies.items, 0);
            }
        },
        "and we create a new object and post a reply and expand the object": {
            topic: function() {
                var Note = require("../lib/model/note").Note,
                    note = null,
                    Comment = require("../lib/model/comment").Comment,
                    cb = this.callback,
                    comment = null;

                Step(
                    function() {
                        Note.create({content: "Test your face."}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        note = result;
                        Comment.create({content: "UR FACE", inReplyTo: note}, this);
                    },
                    function(err, result) {
                        if (err) throw err;
                        comment = result;
                        note.expand(this);
                    },
                    function(err) {
                        if (err) {
                            cb(err, null, null);
                        } else {
                            cb(null, comment, note);
                        }
                    }
                );
            },
            "it works": function(err, comment, note) {
                assert.ifError(err);
            },
            "its replies element looks right": function(err, comment, note) {
                assert.include(note, "replies");
                assert.isObject(note.replies);
                assert.include(note.replies, "totalItems");
                assert.equal(note.replies.totalItems, 1);
                assert.include(note.replies, "url");
                assert.isString(note.replies.url);
                assert.include(note.replies, "items");
                assert.isArray(note.replies.items);
                assert.lengthOf(note.replies.items, 1);
                assert.equal(note.replies.items[0].id, comment.id);
            }
        }
    }
});

suite["export"](module);
<|endoftext|>


<commit_before>var assert = require('assert');
var url = require('url');
var debug = require('debug')('rets.js:session.test.js');

module.exports = describe('Session', function(){

    var Session = null;
    var instance = null;
    var config = require('./servers/config.json')[0];
    var expectedDefaults = {
        url: null,
        ua: {
            name: 'RETS-Connector1/2',
            pass: ''
        },
        version: 'RETS/1.7.2'
    };

    before('Load RETS', function() {
        Session = require('../lib/session');
    });

    after('after description', function(){
        Session = null;
    });

    beforeEach('beforeEach description', function(){
        instance = new Session(config);
    });

    afterEach('afterEach description', function(){
        instance = null;
    });

    it('Loads correctly.', function(){
        assert.notEqual(typeof Session, 'undefined');
    });

    it('Is a function.', function(){
        assert.equal(typeof Session, 'function');
    });

    it('Instantiates correctly.', function(){
        assert(instance instanceof Session);
    });

    it('Is an object.', function(){
        assert.equal(typeof instance, 'object');
    });

    it('Has the correct set of expected defaults.', function(){
        var instance = new Session(config);
        assert.deepEqual(expectedDefaults, instance.defaults);
    });

    it('Requires a url to instantiate',function(){
        // assert.throws(function(){
        //     new Session();
        // }, Error);
        try {
            new Session({});
        } catch(e) {
            assert.equal(e.message, 'options.url is required.');
        }
    });

    it('Requires a url to be a string or an object',function(){
        try {
            new Session({ url: true });
        } catch(e) {
            assert.equal(e.message, 'options.url is not a string or an object.');
        }
    });

    it('Requires string urls to be valid',function(){
        try {
            new Session({ url: 'htp:/rets.org' });
        } catch(e) {
            assert.equal(e.message, 'invalid options.url.host.');
        }
    });

    it('Requires string urls to contain auth credentials',function(){
        try {
            new Session({ url: 'http://localhost:9160/mock/Login' });
        } catch(e) {
            assert.equal(e.message, 'invalid options.url.auth.');
        }
    });

    it('instance.url is an object.', function(){
        assert.equal(typeof instance.url, 'object');
    });

    it('Instance.url.auth set to expected value.', function(){
        var parsed = url.parse(config.url);
        assert.equal(instance.url.auth, parsed.auth);
    });

    it('Instance.url.host set to expected value.', function(){
        var parsed = url.parse(config.url);
        assert.equal(instance.url.host, parsed.host);
    });

    it('Instance.url.path set to expected value.', function(){
        var parsed = url.parse(config.url);
        assert.equal(instance.url.path, parsed.path);
    });

    it('Has a .headers object.', function(){
        assert.equal(typeof instance.headers, 'object');
    });

    it('Has a .capabilities object.', function(){
        assert.equal(typeof instance.capabilities, 'object');
    });

    it('Has a .settings object.', function(){
        assert.equal(typeof instance.settings, 'object');
    });

    it('Has a .hash() method.', function(){
        assert.equal(typeof instance.hash, 'function');
    });

    it('.hash returns the expected hash.', function(){
        var md5 = require('MD5');
        var hash = md5([md5([config.ua.name, config.ua.pass].join(':').trim()),'','',instance.version].join(':'));
        assert.equal(instance.hash(), hash);
    });

});
<commit_msg>JSHint passing<commit_after>var assert = require('assert');
var url = require('url');
// var debug = require('debug')('rets.js:session.test.js');

module.exports = describe('Session', function(){

    var Session = null;
    var instance = null;
    var config = require('./servers/config.json')[0];
    var expectedDefaults = {
        url: null,
        ua: {
            name: 'RETS-Connector1/2',
            pass: ''
        },
        version: 'RETS/1.7.2'
    };

    before('Load RETS', function() {
        Session = require('../lib/session');
    });

    after('after description', function(){
        Session = null;
    });

    beforeEach('beforeEach description', function(){
        instance = new Session(config);
    });

    afterEach('afterEach description', function(){
        instance = null;
    });

    it('Loads correctly.', function(){
        assert.notEqual(typeof Session, 'undefined');
    });

    it('Is a function.', function(){
        assert.equal(typeof Session, 'function');
    });

    it('Instantiates correctly.', function(){
        assert(instance instanceof Session);
    });

    it('Is an object.', function(){
        assert.equal(typeof instance, 'object');
    });

    it('Has the correct set of expected defaults.', function(){
        var instance = new Session(config);
        assert.deepEqual(expectedDefaults, instance.defaults);
    });

    it('Requires a url to instantiate',function(){
        // assert.throws(function(){
        //     new Session();
        // }, Error);
        try {
            new Session({});
        } catch(e) {
            assert.equal(e.message, 'options.url is required.');
        }
    });

    it('Requires a url to be a string or an object',function(){
        try {
            new Session({ url: true });
        } catch(e) {
            assert.equal(e.message, 'options.url is not a string or an object.');
        }
    });

    it('Requires string urls to be valid',function(){
        try {
            new Session({ url: 'htp:/rets.org' });
        } catch(e) {
            assert.equal(e.message, 'invalid options.url.host.');
        }
    });

    it('Requires string urls to contain auth credentials',function(){
        try {
            new Session({ url: 'http://localhost:9160/mock/Login' });
        } catch(e) {
            assert.equal(e.message, 'invalid options.url.auth.');
        }
    });

    it('instance.url is an object.', function(){
        assert.equal(typeof instance.url, 'object');
    });

    it('Instance.url.auth set to expected value.', function(){
        var parsed = url.parse(config.url);
        assert.equal(instance.url.auth, parsed.auth);
    });

    it('Instance.url.host set to expected value.', function(){
        var parsed = url.parse(config.url);
        assert.equal(instance.url.host, parsed.host);
    });

    it('Instance.url.path set to expected value.', function(){
        var parsed = url.parse(config.url);
        assert.equal(instance.url.path, parsed.path);
    });

    it('Has a .headers object.', function(){
        assert.equal(typeof instance.headers, 'object');
    });

    it('Has a .capabilities object.', function(){
        assert.equal(typeof instance.capabilities, 'object');
    });

    it('Has a .settings object.', function(){
        assert.equal(typeof instance.settings, 'object');
    });

    it('Has a .hash() method.', function(){
        assert.equal(typeof instance.hash, 'function');
    });

    it('.hash returns the expected hash.', function(){
        var md5 = require('MD5');
        var hash = md5([md5([config.ua.name, config.ua.pass].join(':').trim()),'','',instance.version].join(':'));
        assert.equal(instance.hash(), hash);
    });

});
<|endoftext|>


<commit_before>/**
 * import(s)
 */

var Vue = require('../../node_modules/vue/dist/vue')
var validator = require('../../index')
var createInstance = require('./helper').createInstance

describe('minLength', function () {
  var vm, target

  before(function () {
    Vue.config.async = false
    Vue.use(validator)
  })

  after(function () {
    Vue.config.async = true
  })

  beforeEach(function () {
    vm = createInstance(
      '<input type="text" v-model="comment" v-validate="minLength: 4">',
      { msg: null }
    )
    target = vm._children[0]
  })

  describe('when input 3 length string', function () {
    beforeEach(function () {
      vm.comment = 'aaa'
      vm._digest() // force update
    })

    it('should be true', function () {
      expect(target.validation.comment.minLength).to.be(true)
    })
  })

  describe('when input 4 length string', function () {
    beforeEach(function () {
      vm.comment = 'aaaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.minLength).to.be(false)
    })
  })

  describe('when input 5 length string', function () {
    beforeEach(function () {
      vm.comment = 'aaaaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.minLength).to.be(false)
    })
  })
})

describe('maxLength', function () {
  var vm, target

  before(function () {
    Vue.config.async = false
    Vue.use(validator)
  })

  after(function () {
    Vue.config.async = true
  })

  beforeEach(function () {
    vm = createInstance(
      '<input type="text" v-model="comment" v-validate="maxLength: 4">',
      { msg: null }
    )
    target = vm._children[0]
  })

  describe('when input 3 length string', function () {
    beforeEach(function () {
      vm.comment = 'aaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.maxLength).to.be(false)
    })
  })

  describe('when input 4 length string', function () {
    beforeEach(function () {
      vm.comment = 'aaaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.maxLength).to.be(false)
    })
  })

  describe('when input 5 length string', function () {
    beforeEach(function () {
      vm.comment = 'aaaaa'
      vm._digest() // force update
    })

    it('should be true', function () {
      expect(target.validation.comment.maxLength).to.be(true)
    })
  })
})
<commit_msg>Refactor length spec<commit_after>/**
 * import(s)
 */

var Vue = require('../../node_modules/vue/dist/vue')
var validator = require('../../index')
var createInstance = require('./helper').createInstance

describe('minLength', function () {
  var vm, target

  before(function () {
    Vue.config.async = false
    Vue.use(validator)
  })

  after(function () {
    Vue.config.async = true
  })

  beforeEach(function () {
    vm = createInstance(
      '<input type="text" v-model="comment" v-validate="minLength: 4">',
      { comment: null }
    )
    target = vm._children[0]
  })

  describe('boundary value - 1', function () {
    beforeEach(function () {
      vm.comment = 'aaa'
      vm._digest() // force update
    })

    it('should be true', function () {
      expect(target.validation.comment.minLength).to.be(true)
    })
  })

  describe('boundary value', function () {
    beforeEach(function () {
      vm.comment = 'aaaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.minLength).to.be(false)
    })
  })

  describe('boundary value + 1', function () {
    beforeEach(function () {
      vm.comment = 'aaaaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.minLength).to.be(false)
    })
  })
})

describe('maxLength', function () {
  var vm, target

  before(function () {
    Vue.config.async = false
    Vue.use(validator)
  })

  after(function () {
    Vue.config.async = true
  })

  beforeEach(function () {
    vm = createInstance(
      '<input type="text" v-model="comment" v-validate="maxLength: 4">',
      { comment: null }
    )
    target = vm._children[0]
  })

  describe('boundary value - 1', function () {
    beforeEach(function () {
      vm.comment = 'aaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.maxLength).to.be(false)
    })
  })

  describe('boundary value', function () {
    beforeEach(function () {
      vm.comment = 'aaaa'
      vm._digest() // force update
    })

    it('should be false', function () {
      expect(target.validation.comment.maxLength).to.be(false)
    })
  })

  describe('boundary value + 1', function () {
    beforeEach(function () {
      vm.comment = 'aaaaa'
      vm._digest() // force update
    })

    it('should be true', function () {
      expect(target.validation.comment.maxLength).to.be(true)
    })
  })
})
<|endoftext|>


<commit_before>'use strict';

const EventEmitter = require('events');
const child_process = require('child_process');

// Default options
const defaults = {
	waitformsg: false,	// Wait for first message from child before considering launched
	starttime: 10000,	// Timeout for above
	waitforexit: false,	// Wait for exit event from child before considering shutdown
	exittime: 10000,	// Timeout for above
	relaunch: false,	// Relaunch child on unexpected exit
	forkopts: null,		// Additional options to fork()
	addtoenv: null,		// Values to add to child environment (supercedes forkopts.env)
};

// Process checker
function checkproc (proc) {
	return Object.prototype.hasOwnProperty(proc, 'name') && Object.prototype.hasOwnProperty(this.children, proc.name);
}

class Manager extends EventEmitter {

	constructor (options) {
		super();
		this.options = Object.assign({}, defaults, options);
		// Tracked child processes
		this.children = {};
		// Logger
		this.log = this.options.log || console.log.bind(console);
		// TODO: add SIGTERM listener here, or leave to calling code?
	}

	// Handler for child message recipt
	// TODO: emit child process name instead, or leave with whole object?
	recvmsg (proc, msg, handle) {
		// Emit heartbeat
		if (msg === 'heartbeat') {
			// TODO: add heartbeat tracking per-child
			this.emit('heartbeat', proc, handle);
		}
		// Emit raw message if string
		else if (typeof msg === 'string') {
			this.emit('message', proc, msg, handle);
		}
		// Emit typed message if available
		else if (msg && msg.type && msg.data) {
			this.emit(msg.type, proc, msg.data, handle);
		}
	}

	sendmsg (proc, msg, handle) {
		// Get process if only given name
		if (typeof proc === 'string') {
			proc = this.children[proc];
		}
		// Sanity check
		if (!checkproc(proc)) {
			throw new Error(`Can't send message to invalid process '${proc}'`);
		}
		proc.send(msg, handle);
	}

	launch (procfile, ...args) {
		// Startup function
		let started = proc => {
			// Add child to managed list
			this.children[proc.name] = proc;

			// Log startup
			this.log(`Started: '${proc.name}'...`);

			// Add unexpected exit handler
			let exitfn = (code, signal) => {
				this.log(`Child process '${proc.name}' exited unexpectedly with ${
					(code !== null) ? 'code: ' + code : 'signal: ' + signal}`);
				// TODO: add relaunch check
				// TODO: anything else?
			};
			proc.exitfn = exitfn;
			proc.once('exit', exitfn);

			// Add message handler
			let msgfn = (msg, handle) => {
				this.recvmsg(proc, msg, handle);
			};
			proc.msgfn = msgfn;
			proc.on('message', msgfn);

			// Emit started event
			this.emit('start', proc);
		}

		// Options to use
		var opts = Object.assign({}, this.options);
		// Child args to use
		var useargs = [];
		// Process name (default to file/module)
		var usename = procfile;

		// Parse options
		for (let i = 0, len_i = args.length; i < len_i; i++) {
			let opt = args[i];
			// String args will overwrite child name
			if (typeof opt === 'string') {
				usename = opt;
			}
			// Array args will extend supplied args (no dup check)
			else if (Array.isArray(opt)) {
				useargs = useargs.concat(opt);
			}
			// Object args will overwrite defaults
			else if (typeof opt === 'object') {
				opts = Object.assign(opts, opt);
			}
			// TODO: add function check for callbacks?
		}

		// Process environment values
		var env = Object.assign({}, opts.forkopts.env || process.env);
		// Add extra environment values if given
		if (typeof opts.addtoenv === 'object'){
			env = Object.assign(env, opts.addtoenv)
		}
		// Update fork options
		if (typeof opts.forkopts === 'object') {
			opts.forkopts.env = env;
		}
		else {
			opts.forkopts = { env: env };
		}

		// Launch process
		var child = child_process.fork(procfile, useargs, opts.forkopts);

		// Name process
		child.name = usename;
		// Store launch options
		child.options = opts;

		// Return promise
		var promise;
		if (opts.waitformsg) {
			promise = new Promise((resolve, reject) => {
				let errfn = err => {
					reject(new Error(`Error while starting process '${child.name}': ${err}`));
				};

				// Reject promise if timeout expires before first message
				if (opts.starttime > 0) {
					setTimeout(() => {
						reject(new Error(`Timed out waiting for process '${child.name}' to send first message`));
					}, opts.starttime);
				}

				// Reject promise if error occurs during spawn
				child.once('error', errfn);

				// Resolve promise once child sends first message
				child.once('message', (msg, handle) => {
					// Remove error handler
					child.removeListener('error', errfn);

					// Resolve promise
					resolve([child, msg, handle]);
				});
			});
		}
		else {
			promise = Promise.resolve([child]);
		}

		return promise.then(([proc, initmsg, inithand]) => {
			// Initial startup
			started(proc);

			// Deal with initial message if rec'd
			if (initmsg) {
				this.recvmsg(proc, initmsg, inithand);
			}

			return proc;
		});
	}

	shutdown (proc) {
		// Shut down specific process
		if (proc) {
			// Get process if only given name
			if (typeof proc === 'string') {
				proc = this.children[proc];
			}

			// Sanity check
			if (!checkproc(proc)) {
				throw new Error(`Can't shutdown invalid process: '${proc.name || proc}'`);
			}

			return new Promise(function (resolve, reject) {
				// Remove unexpected-exit handler
				proc.removeListener('exit', proc.exitfn);
				delete proc.exitfn;

				// Remove message handler
				proc.removeListener('message', proc.msgfn);
				delete proc.msgfn;

				// Add exit waiter
				if (proc.options.waitforexit) {
					let errfn = err => {
						reject(new Error(`Error while shutting down process '${proc.name}': ${err}`));
					};

					// Reject promise if timeout expires before first message
					if (proc.options.starttime > 0) {
						setTimeout(() => {
							reject(new Error(`Timed out waiting for process '${proc.name}' to shut down`));
						}, proc.options.starttime);
					}

					// Reject promise if error occurs during spawn
					proc.once('error', errfn);

					// Resolve promise once child sends exit event
					proc.once('exit', (code, signal) => {
						// Remove error handler
						proc.removeListener('error', errfn);

						// Resolve promise
						resolve([proc, code, signal]);
					});
				}

				// Log stoppage attempt
				this.log(`Stopping: '${proc.name}', PID: ${proc.pid}`);

				// Send signal
				proc.kill('SIGTERM');

				// Resolve immediately if not waiting
				if (!proc.options.waitforexit) {
					resolve([proc, null, null]);
				}

			}).then(([child, code, signal]) => {
				// Log successful shutdown
				if (code !== null || signal !== null) {
					this.log(`Stopped: '${proc.name}', exited with: ${(code !== null) ? 'code: ' + code : 'signal: ' + signal}`);
				}

				// Remove child from manager
				delete this.children[child.name];

				// Emit exit event
				this.emit('exit', child);

				return child;
			});
		}
		// Call recursively on each child process, return object of shutdown promises
		// Note: does NOT return Promise.all(), since that might reject with only one
		// error, and thus lose information on the rest of the processes; if the end
		// user wants to go that route, they're welcome to...
		else {
			let ret = {};
			for (let name of Object.keys(this.children)) {
				ret[name] = this.shutdown(this.children[name]);
			}
			return ret;
		}
	}
}

module.exports = Manager;
<commit_msg>Couple more tweaks to manager.js<commit_after>'use strict';

const EventEmitter = require('events');
const child_process = require('child_process');

// Default options
const defaults = {
	autokill: true,		// Automatically terminate children when master exits
	waitformsg: false,	// Wait for first message from child before considering launched
	starttime: 10000,	// Timeout for above
	waitforexit: false,	// Wait for exit event from child before considering shutdown
	exittime: 10000,	// Timeout for above
	relaunch: false,	// Relaunch child on unexpected exit
	forkopts: null,		// Additional options to fork()
	addtoenv: null,		// Values to add to child environment (supercedes forkopts.env)
};

// Process checker
function checkproc (proc) {
	return Object.prototype.hasOwnProperty(proc, 'name') && Object.prototype.hasOwnProperty(this.children, proc.name);
}

class Manager extends EventEmitter {

	constructor (options, logger) {
		super();
		this.options = Object.assign({}, defaults, options);
		// Tracked child processes
		this.children = {};
		// Logger
		this.log = logger || console.log.bind(console);
		// Add exit listener for main process
		if (this.options.autokill) {
			process.once('exit', code => {
				this.shutdown();
			});
		}
	}

	// Handler for child message recipt
	// TODO: emit child process name instead, or leave with whole object?
	recvmsg (proc, msg, handle) {
		// Emit heartbeat
		if (msg === 'heartbeat') {
			// TODO: add heartbeat tracking per-child
			this.emit('heartbeat', proc, handle);
		}
		// Emit raw message if string
		else if (typeof msg === 'string') {
			this.emit('message', proc, msg, handle);
		}
		// Emit typed message if available
		else if (msg && msg.type && msg.data) {
			this.emit(msg.type, proc, msg.data, handle);
		}
	}

	sendmsg (proc, msg, handle) {
		// Get process if only given name
		if (typeof proc === 'string') {
			proc = this.children[proc];
		}
		// Sanity check
		if (!checkproc(proc)) {
			throw new Error(`Can't send message to invalid process '${proc}'`);
		}
		proc.send(msg, handle);
	}

	launch (procfile, ...args) {
		// Startup function
		let started = proc => {
			// Add child to managed list
			this.children[proc.name] = proc;

			// Log startup
			this.log(`Started: '${proc.name}'...`);

			// Add unexpected exit handler
			let exitfn = (code, signal) => {
				this.log(`Child process '${proc.name}' exited unexpectedly with ${
					(code !== null) ? 'code: ' + code : 'signal: ' + signal}`);
				// TODO: add relaunch check
				// TODO: anything else?
			};
			proc.exitfn = exitfn;
			proc.once('exit', exitfn);

			// Add message handler
			let msgfn = (msg, handle) => {
				this.recvmsg(proc, msg, handle);
			};
			proc.msgfn = msgfn;
			proc.on('message', msgfn);

			// Emit started event
			this.emit('start', proc);
		}

		// Options to use
		var opts = Object.assign({}, this.options);
		// Child args to use
		var useargs = [];
		// Process name (default to file/module)
		var usename = procfile;

		// Parse options
		for (let i = 0, len_i = args.length; i < len_i; i++) {
			let opt = args[i];
			// String args will overwrite child name
			if (typeof opt === 'string') {
				usename = opt;
			}
			// Array args will extend supplied args (no dup check)
			else if (Array.isArray(opt)) {
				useargs = useargs.concat(opt);
			}
			// Object args will overwrite defaults
			else if (typeof opt === 'object') {
				opts = Object.assign(opts, opt);
			}
			// TODO: add function check for callbacks?
		}

		// Process environment values
		var env = Object.assign({}, opts.forkopts.env || process.env);
		// Add extra environment values if given
		if (typeof opts.addtoenv === 'object'){
			env = Object.assign(env, opts.addtoenv)
		}
		// Update fork options
		if (typeof opts.forkopts === 'object') {
			opts.forkopts.env = env;
		}
		else {
			opts.forkopts = { env: env };
		}

		// Launch process
		var child = child_process.fork(procfile, useargs, opts.forkopts);

		// Name process
		child.name = usename;
		// Store launch options
		child.options = opts;

		// Return promise
		var promise;
		if (opts.waitformsg) {
			promise = new Promise((resolve, reject) => {
				let errfn = err => {
					reject(new Error(`Error while starting process '${child.name}': ${err}`));
				};

				// Reject promise if timeout expires before first message
				if (opts.starttime > 0) {
					setTimeout(() => {
						reject(new Error(`Timed out waiting for process '${child.name}' to send first message`));
					}, opts.starttime);
				}

				// Reject promise if error occurs during spawn
				child.once('error', errfn);

				// Resolve promise once child sends first message
				child.once('message', (msg, handle) => {
					// Remove error handler
					child.removeListener('error', errfn);

					// Resolve promise
					resolve([child, msg, handle]);
				});
			});
		}
		else {
			promise = Promise.resolve([child]);
		}

		return promise.then(([proc, initmsg, inithand]) => {
			// Initial startup
			started(proc);

			// Deal with initial message if rec'd
			if (initmsg) {
				this.recvmsg(proc, initmsg, inithand);
			}

			return proc;
		});
	}

	shutdown (proc) {
		// Shut down specific process
		if (proc) {
			// Get process if only given name
			if (typeof proc === 'string') {
				proc = this.children[proc];
			}

			// Sanity check
			if (!checkproc(proc)) {
				throw new Error(`Can't shutdown invalid process: '${proc.name || proc}'`);
			}

			return new Promise(function (resolve, reject) {
				// Remove unexpected-exit handler
				proc.removeListener('exit', proc.exitfn);
				delete proc.exitfn;

				// Remove message handler
				proc.removeListener('message', proc.msgfn);
				delete proc.msgfn;

				// Add exit waiter
				if (proc.options.waitforexit) {
					let errfn = err => {
						reject(new Error(`Error while shutting down process '${proc.name}': ${err}`));
					};

					// Reject promise if timeout expires before first message
					if (proc.options.starttime > 0) {
						setTimeout(() => {
							reject(new Error(`Timed out waiting for process '${proc.name}' to shut down`));
						}, proc.options.starttime);
					}

					// Reject promise if error occurs during spawn
					proc.once('error', errfn);

					// Resolve promise once child sends exit event
					proc.once('exit', (code, signal) => {
						// Remove error handler
						proc.removeListener('error', errfn);

						// Resolve promise
						resolve([proc, code, signal]);
					});
				}

				// Log stoppage attempt
				this.log(`Stopping: '${proc.name}', PID: ${proc.pid}`);

				// Send signal
				proc.kill('SIGTERM');

				// Resolve immediately if not waiting
				if (!proc.options.waitforexit) {
					resolve([proc, null, null]);
				}

			}).then(([child, code, signal]) => {
				// Log successful shutdown
				if (code !== null || signal !== null) {
					this.log(`Stopped: '${proc.name}', exited with: ${(code !== null) ? 'code: ' + code : 'signal: ' + signal}`);
				}

				// Remove child from manager
				delete this.children[child.name];

				// Emit exit event
				this.emit('exit', child);

				return child;
			});
		}
		// Call recursively on each child process, return array of shutdown promises
		// Note: does NOT return Promise.all(), since that might reject with only one
		// error, and thus lose information on the rest of the processes (if the end
		// user wants to go that route, they're welcome to...)
		else {
			let ret = [];
			for (let name of Object.keys(this.children)) {
				ret.push = this.shutdown(this.children[name]);
			}
			return ret;
		}
	}
}

module.exports = Manager;
<|endoftext|>


<commit_before>var _ = require('lodash');
var EventEmitter = require('events').EventEmitter;

/**
 * Constructor for a Connex Manager
 * @class ConnexManager
 * @augments require('events').EventEmitter
 * @param {Object} [connections] Connections to add
 * @param {Object} [options] Options for adding
 * @return ConnexManager
 */
var Manager = module.exports = function(connections, options) {

  if(!(this instanceof Manager)) {
    return new Manager(connections, options);
  }

  var self = this;

  connections = connections || {};

  _.bindAll(this, 'add', 'remove', 'get', 'getHandle');

  this._instances = {};
  this._handlers = {};

  _.each(connections, function(instance, name) {
    self.add(name, instance, options);
  });

};
require('util').inherits(Manager, EventEmitter);

/**
 * Add a connection to manage
 * @param {String} name Name of connection
 * @param {ConnexConnection} instance Instance of connection
 * @param {Object} [options] Options for adding
 * @return {void}
 */
Manager.prototype.add = function(name, instance, options) {
  options = options || {};

  var self = this;

  if(name in this._instances) {
    throw new Error('Instance with name "' + name + '" already exists');
  }

  this._instances[name] = instance;
  this._handlers[name] = {};

  this._addInstanceEvent(instance, name, 'connect', function() {
    self.emit(self._getProxyEventName('connect', name));
    self.emit('connect', name);
  });

  this._addInstanceEvent(instance, name, 'disconnect', function() {
    self.emit(self._getProxyEventName('disconnect', name));
    self.emit('disconnect', name);
  });

  this._addInstanceEvent(instance, name, 'error', function(err) {
    self.emit(self._getProxyEventName('error', name), err);
    self.emit('error', err);
  });

  this._addInstanceEvent(instance, name, 'reconnect', function(ms) {
    self.emit(self._getProxyEventName('reconnect', name), ms);
    self.emit('reconnect', name, ms);
  });

  this._addInstanceEvent(instance, name, 'state', function(to) {
    self.emit(self._getProxyEventName('state', name), to);
    self.emit(self._getProxyEventName('state', name, to));
    self.emit('state', name, to);
  });

  var defineOptions = {
    enumerable: true,
    configurable: true,
    get: function() {
      return self.getHandle(name);
    }
  };

  // define some magic getters
  Object.defineProperty(this, name, defineOptions);
  Object.defineProperty(this, name.toLowerCase(), defineOptions);

  if(!options.delayConnect) {
    instance.connect();
  }

};

/**
 * Disconnect from a connection and remove it from the manager
 * @param {String} name Name of connection to remove
 * @return void
 */
Manager.prototype.remove = function(name) {

  var self = this;
  var instance = this.get(name);

  _.each(this._handlers[name], function(handler, eventName) {
    instance.removeListener(eventName, handler);
  });

  delete this._handlers[name];
  delete this._instances[name];

  // delete magic getters
  delete this[name];
  delete this[name.toLowerCase()];

  instance.disconnect();

  return instance;

};

/**
 * Get a managed connection object
 * @param {String} name Name of connection to get
 * @return {ConnexConnection}
 */
Manager.prototype.get = function(name) {

  if(!(name in this._instances)) {
    throw new Error('Instance with name "' + name + '" does not exist');
  }

  return this._instances[name];
};

/**
 * Determine if a connection exists
 * @param {String} name Name of connection to check
 * @return {Boolean}
 */
Manager.prototype.has = function(name) {
  return name in this._instances;
};

/**
 * Get a managed connection handle
 * @param {String} name Name of connection to get handle for
 * @return {Object}
 */
Manager.prototype.getHandle = function(name) {
  var instance = this.get(name);
  return instance.getHandle();
};

/**
 * Returns a object with magic lowercase-getHandlers for all added connections
 * @return {Object}
 */
Manager.prototype.getMagicHandleObject = function() {
  return Object.defineProperties({}, _.reduce(this._handles, function(obj, instance, name) {

    obj[name] = obj[name.toLowerCase()] = {
      configurable: true,
      enumerable: true,
      get: function() { return instance.getHandle(); }
    };

    return obj;

  }, {}));
};

Manager.prototype._addInstanceEvent = function(instance, instanceName, eventName, handler) {

  if(eventName in this._handlers[instanceName]) {
    instance.removeListener(eventName, this._handlers[instanceName][eventName]);
    delete this._handlers[instanceName][eventName];
  }

  instance.on(eventName, handler);
  this._handlers[instanceName][eventName] = handler;

  return handler;
};

Manager.prototype._getProxyEventName = function() {
  return _.toArray(arguments).join(':');
};

<commit_msg>New method in lib/manager<commit_after>var _ = require('lodash');
var EventEmitter = require('events').EventEmitter;

/**
 * Constructor for a Connex Manager
 * @class ConnexManager
 * @augments require('events').EventEmitter
 * @param {Object} [connections] Connections to add
 * @param {Object} [options] Options for adding
 * @return ConnexManager
 */
var Manager = module.exports = function(connections, options) {

  if(!(this instanceof Manager)) {
    return new Manager(connections, options);
  }

  var self = this;

  connections = connections || {};

  _.bindAll(this, 'add', 'remove', 'get', 'getHandle');

  this._instances = {};
  this._handlers = {};

  _.each(connections, function(instance, name) {
    self.add(name, instance, options);
  });

};
require('util').inherits(Manager, EventEmitter);

/**
 * Add a connection to manage
 * @param {String} name Name of connection
 * @param {ConnexConnection} instance Instance of connection
 * @param {Object} [options] Options for adding
 * @return {void}
 */
Manager.prototype.add = function(name, instance, options) {
  options = options || {};

  var self = this;

  if(name in this._instances) {
    throw new Error('Instance with name "' + name + '" already exists');
  }

  this._instances[name] = instance;
  this._handlers[name] = {};

  this._addInstanceEvent(instance, name, 'connect', function() {
    self.emit(self._getProxyEventName('connect', name));
    self.emit('connect', name);
  });

  this._addInstanceEvent(instance, name, 'disconnect', function() {
    self.emit(self._getProxyEventName('disconnect', name));
    self.emit('disconnect', name);
  });

  this._addInstanceEvent(instance, name, 'error', function(err) {
    self.emit(self._getProxyEventName('error', name), err);
    self.emit('error', err);
  });

  this._addInstanceEvent(instance, name, 'reconnect', function(ms) {
    self.emit(self._getProxyEventName('reconnect', name), ms);
    self.emit('reconnect', name, ms);
  });

  this._addInstanceEvent(instance, name, 'state', function(to) {
    self.emit(self._getProxyEventName('state', name), to);
    self.emit(self._getProxyEventName('state', name, to));
    self.emit('state', name, to);
  });

  var defineOptions = {
    enumerable: true,
    configurable: true,
    get: function() {
      return self.getHandle(name);
    }
  };

  // define some magic getters
  Object.defineProperty(this, name, defineOptions);
  Object.defineProperty(this, name.toLowerCase(), defineOptions);

  if(!options.delayConnect) {
    instance.connect();
  }

};

/**
 * Disconnect from a connection and remove it from the manager
 * @param {String} name Name of connection to remove
 * @return void
 */
Manager.prototype.remove = function(name) {

  var self = this;
  var instance = this.get(name);

  _.each(this._handlers[name], function(handler, eventName) {
    instance.removeListener(eventName, handler);
  });

  delete this._handlers[name];
  delete this._instances[name];

  // delete magic getters
  delete this[name];
  delete this[name.toLowerCase()];

  instance.disconnect();

  return instance;

};

/**
 * Get a managed connection object
 * @param {String} name Name of connection to get
 * @return {ConnexConnection}
 */
Manager.prototype.get = function(name) {

  if(!(name in this._instances)) {
    throw new Error('Instance with name "' + name + '" does not exist');
  }

  return this._instances[name];
};

/**
 * Determine if a connection exists
 * @param {String} name Name of connection to check
 * @return {Boolean}
 */
Manager.prototype.has = function(name) {
  return name in this._instances;
};

/**
 * Get a managed connection handle
 * @param {String} name Name of connection to get handle for
 * @return {Object}
 */
Manager.prototype.getHandle = function(name) {
  var instance = this.get(name);
  return instance.getHandle();
};

/**
 * Returns a object with magic lowercase-getHandlers for all added connections
 * @return {Object}
 */
Manager.prototype.getMagicHandleObject = function() {

  var self = this;
  var magic = {};

  return Object.defineProperties({}, _.reduce(this._handles, function(obj, instance, name) {
    obj[name] = obj[name.toLowerCase()] = self.getMagicHandleDefinition(name);
    return obj;
  }, {}));

};

/**
 * Returns a setting for Object.defineProperty for getting the handle of a connection
 * @param {String} name Name of connection
 * @return {Object}
 */
Manager.prototype.getMagicHandleDefinition = function(name) {
  var instance = this.get(name);
  return {
    configurable: true,
    enumerable: true,
    get: function() {
      return instance.getHandle();
    }
  }
};

Manager.prototype._addInstanceEvent = function(instance, instanceName, eventName, handler) {

  if(eventName in this._handlers[instanceName]) {
    instance.removeListener(eventName, this._handlers[instanceName][eventName]);
    delete this._handlers[instanceName][eventName];
  }

  instance.on(eventName, handler);
  this._handlers[instanceName][eventName] = handler;

  return handler;
};

Manager.prototype._getProxyEventName = function() {
  return _.toArray(arguments).join(':');
};

<|endoftext|>


<commit_before>// Remember to include harness.js before this file.

var sab = new SharedArrayBuffer(1024);

// TODO: These are not the official view names, harness.js corrects
// for that but we should rename here once Firefox has been fixed.

var int_views = [SharedInt8Array, SharedUint8Array, SharedInt16Array, SharedUint16Array, SharedInt32Array, SharedUint32Array];

// TODO: SharedUint8ClampedArray in Firefox allows atomics
var other_views = [/*SharedUint8ClampedArray,*/ SharedFloat32Array, SharedFloat64Array];

// Test allowed atomic operations on integer views

for ( var View of int_views ) {
    var view = new View(sab);

    view[0] = 0;
    view[1] = 0;

    assertEq(Atomics.store(view, 0, 10), 10);
    assertEq(Atomics.load(view, 0), 10);
    assertEq(Atomics.exchange(view, 0, 5), 10);
    assertEq(Atomics.load(view, 0), 5);
    assertEq(Atomics.compareExchange(view, 0, 5, 8), 5);
    assertEq(Atomics.load(view, 0), 8);
    assertEq(Atomics.compareExchange(view, 0, 5, 16), 8);
    assertEq(Atomics.load(view, 0), 8);

    assertEq(Atomics.add(view, 1, 10), 0);  // 0 + 10 -> 10
    assertEq(Atomics.sub(view, 1, 5), 10);  // 10 - 5 -> 5
    assertEq(Atomics.and(view, 1, 3), 5);   // 5 & 3 -> 1
    assertEq(Atomics.or(view, 1, 6), 1);    // 1 | 6 -> 7
    assertEq(Atomics.xor(view, 1, 2), 7);   // 7 ^ 2 -> 5
    assertEq(Atomics.load(view, 1), 5);     // 5

    // TODO: tests for truncation and proper sign extension
}

// Test disallowed atomic operations on non-integer views

for ( var View of other_views ) {
    var view = new View(sab);

    expectException((() => Atomics.load(view, 0)), TypeError);
    expectException((() => Atomics.store(view, 0, 0)), TypeError);
    expectException((() => Atomics.exchange(view, 0, 0)), TypeError);
    expectException((() => Atomics.compareExchange(view, 0, 0, 0)), TypeError);
    expectException((() => Atomics.add(view, 0, 0)), TypeError);
    expectException((() => Atomics.sub(view, 0, 0)), TypeError);
    expectException((() => Atomics.and(view, 0, 0)), TypeError);
    expectException((() => Atomics.or(view, 0, 0)), TypeError);
    expectException((() => Atomics.xor(view, 0, 0)), TypeError);
}

// TODO: test that futex operations are disallowed on non-int32 arrays
// (but can't test in a shell test whether futex operations work)

// TODO: test isLockFree
<commit_msg>More test cases<commit_after>// Remember to include harness.js before this file.

var sab = new SharedArrayBuffer(1024);
var ab = new ArrayBuffer(16);

// TODO: These are not the official view names, harness.js corrects
// for that but we should rename here once Firefox has been fixed.

var int_views = [SharedInt8Array, SharedUint8Array, SharedInt16Array, SharedUint16Array, SharedInt32Array, SharedUint32Array];
var unshared_int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array];

// TODO: SharedUint8ClampedArray in Firefox allows atomics
var other_views = [/*SharedUint8ClampedArray,*/ SharedFloat32Array, SharedFloat64Array];

// Test allowed atomic operations on integer views

for ( var idx=0 ; idx < int_views.length ; idx++ ) {
    var View = int_views[idx];
    var Control = unshared_int_views[idx];

    // Make it interesting - use non-zero byteOffsets and non-zero indexes

    var view = new View(sab, 32, 20);

    assertEq(Atomics.store(view, 3, 10), 10);
    assertEq(Atomics.load(view, 3), 10);
    assertEq(Atomics.exchange(view, 3, 5), 10);
    assertEq(Atomics.load(view, 3), 5);
    assertEq(Atomics.compareExchange(view, 3, 5, 8), 5);
    assertEq(Atomics.load(view, 3), 8);
    assertEq(Atomics.compareExchange(view, 3, 5, 16), 8);
    assertEq(Atomics.load(view, 3), 8);

    assertEq(Atomics.add(view, 8, 10), 0);  // 0 + 10 -> 10
    assertEq(Atomics.sub(view, 8, 5), 10);  // 10 - 5 -> 5
    assertEq(Atomics.and(view, 8, 3), 5);   // 5 & 3 -> 1
    assertEq(Atomics.or(view, 8, 6), 1);    // 1 | 6 -> 7
    assertEq(Atomics.xor(view, 8, 2), 7);   // 7 ^ 2 -> 5
    assertEq(Atomics.load(view, 8), 5);     // 5

    // Rudimentary tests for sign extension and chopping.

    var control = new Control(ab, 0, 2);

    Atomics.store(view, 3, -5); control[0] = -5;
    assertEq(Atomics.load(view, 3), control[0]);
    assertEq(Atomics.exchange(view, 3, 0), control[0]);
    view[3] = -5;
    assertEq(Atomics.add(view, 3, 0), control[0]);
    assertEq(Atomics.sub(view, 3, 0), control[0]);
    assertEq(Atomics.and(view, 3, -1), control[0]);
    assertEq(Atomics.or(view, 3, 0), control[0]);
    assertEq(Atomics.xor(view, 3, 0), control[0]);
    control[1] = -7;
    assertEq(Atomics.compareExchange(view, 3, control[0], -7), control[0]);
    assertEq(Atomics.compareExchange(view, 3, control[0], -9), control[1]);
    assertEq(view[3], control[1]);

    Atomics.store(view, 3, 12345); control[0] = 12345;
    assertEq(Atomics.load(view, 3), control[0]);
    assertEq(Atomics.exchange(view, 3, 0), control[0]);
    view[3] = 12345;
    assertEq(Atomics.add(view, 3, 0), control[0]);
    assertEq(Atomics.sub(view, 3, 0), control[0]);
    assertEq(Atomics.and(view, 3, -1), control[0]);
    assertEq(Atomics.or(view, 3, 0), control[0]);
    assertEq(Atomics.xor(view, 3, 0), control[0]);
    control[1] = 23456;
    assertEq(Atomics.compareExchange(view, 3, control[0], 23456), control[0]);
    assertEq(Atomics.compareExchange(view, 3, control[0], 34567), control[1]);
    assertEq(view[3], control[1]);

    Atomics.store(view, 3, 123456789); control[0] = 123456789;
    assertEq(Atomics.load(view, 3), control[0]);
    assertEq(Atomics.exchange(view, 3, 0), control[0]);
    view[3] = 123456789;
    assertEq(Atomics.add(view, 3, 0), control[0]);
    assertEq(Atomics.sub(view, 3, 0), control[0]);
    assertEq(Atomics.and(view, 3, -1), control[0]);
    assertEq(Atomics.or(view, 3, 0), control[0]);
    assertEq(Atomics.xor(view, 3, 0), control[0]);
    control[1] = 234567890;
    assertEq(Atomics.compareExchange(view, 3, control[0], 234567890), control[0]);
    assertEq(Atomics.compareExchange(view, 3, control[0], 345678901), control[1]);
    assertEq(view[3], control[1]);

    Atomics.store(view, 3, Math.PI); control[0] = Math.PI;
    assertEq(Atomics.load(view, 3), control[0]);
    assertEq(Atomics.exchange(view, 3, 0), control[0]);

    // Do not affect memory outside the elements written.

    for ( var i=0 ; i < view.length ; i++ ) {
	if (i == 3 || i == 8)
	    continue;
	assertEq(view[i], 0);
    }

    view[3] = 0;
    view[8] = 0;
}

// Test disallowed atomic operations on non-integer shared views - these should throw.

for ( var View of other_views ) {
    var view = new View(sab);

    expectException((() => Atomics.load(view, 0)), TypeError);
    expectException((() => Atomics.store(view, 0, 0)), TypeError);
    expectException((() => Atomics.exchange(view, 0, 0)), TypeError);
    expectException((() => Atomics.compareExchange(view, 0, 0, 0)), TypeError);
    expectException((() => Atomics.add(view, 0, 0)), TypeError);
    expectException((() => Atomics.sub(view, 0, 0)), TypeError);
    expectException((() => Atomics.and(view, 0, 0)), TypeError);
    expectException((() => Atomics.or(view, 0, 0)), TypeError);
    expectException((() => Atomics.xor(view, 0, 0)), TypeError);
}

// Test disallowed atomic operations on non-shared integer views - these should throw.

for ( var View of unshared_int_views ) {
    var view = new View(ab);

    expectException((() => Atomics.load(view, 0)), TypeError);
    expectException((() => Atomics.store(view, 0, 0)), TypeError);
    expectException((() => Atomics.exchange(view, 0, 0)), TypeError);
    expectException((() => Atomics.compareExchange(view, 0, 0, 0)), TypeError);
    expectException((() => Atomics.add(view, 0, 0)), TypeError);
    expectException((() => Atomics.sub(view, 0, 0)), TypeError);
    expectException((() => Atomics.and(view, 0, 0)), TypeError);
    expectException((() => Atomics.or(view, 0, 0)), TypeError);
    expectException((() => Atomics.xor(view, 0, 0)), TypeError);
}

// Test that futex operations are disallowed on non-int32 shared arrays.

for ( var View of int_views ) {
    if (View == SharedInt32Array)
	continue;
    testFutexDisallowed(View, sab);
}

for ( var View of other_views )
    testFutexDisallowed(View, sab);

for ( var View of unshared_int_views )
    testFutexDisallowed(View, ab);

function testFutexDisallowed(View, ab) {
    var view = new View(ab);
    expectException((() => Atomics.futexWait(view, 0, 0)), TypeError);
    expectException((() => Atomics.futexWake(view, 0, 1)), TypeError);
    expectException((() => Atomics.futexWakeOrRequeue(view, 0, 1, 1, 0)), TypeError);
}

// Test isLockFree.

var sizes   = [    1,     2,     3,     4,     5,     6,     7,  8,
                   9,    10,    11,    12];
var answers = [   {},    {}, false,    {}, false, false, false, {},
	       false, false, false, false];

function testIsLockFree() {
    var saved = {};

    // This should defeat most optimizations.

    for ( var i=0 ; i < sizes.length ; i++ ) {
	var v = Atomics.isLockFree(sizes[i]);
	var a = answers[i];
	assertEq(typeof v, 'boolean');
	if (typeof a == 'boolean')
	    assertEq(v, a);
	else
	    saved[sizes[i]] = v;
    }

    // This ought to be optimizable.  Make sure the answers are the same
    // as for the unoptimized case.

    assertEq(Atomics.isLockFree(1), saved[1]);
    assertEq(Atomics.isLockFree(2), saved[2]);
    assertEq(Atomics.isLockFree(3), false);
    assertEq(Atomics.isLockFree(4), saved[4]);
    assertEq(Atomics.isLockFree(5), false);
    assertEq(Atomics.isLockFree(6), false);
    assertEq(Atomics.isLockFree(7), false);
    assertEq(Atomics.isLockFree(8), saved[8]);
    assertEq(Atomics.isLockFree(9), false);
    assertEq(Atomics.isLockFree(10), false);
    assertEq(Atomics.isLockFree(11), false);
    assertEq(Atomics.isLockFree(12), false);
}

testIsLockFree();

<|endoftext|>