This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch test-autorun
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git


The following commit(s) were added to refs/heads/test-autorun by this push:
     new 54ed04f  test: support multi threads
54ed04f is described below

commit 54ed04ffa16071165b52205898995f9ffff85c38
Author: pissang <bm2736...@gmail.com>
AuthorDate: Sun Sep 8 15:05:10 2019 +0800

    test: support multi threads
---
 test/runTest/client/client.css |  21 ++++
 test/runTest/client/client.js  | 241 ++++++++++++++++++++++-------------------
 test/runTest/client/index.html |  12 +-
 test/runTest/server.js         | 102 +++++++++++++----
 4 files changed, 242 insertions(+), 134 deletions(-)

diff --git a/test/runTest/client/client.css b/test/runTest/client/client.css
index 05d3ed9..1b2b025 100644
--- a/test/runTest/client/client.css
+++ b/test/runTest/client/client.css
@@ -47,6 +47,27 @@
 .nav-toolbar .controls {
     margin-top: 10px;
 }
+.nav-toolbar .controls>* {
+    display: inline-block;
+    vertical-align: middle;
+}
+.nav-toolbar .controls .el-checkbox {
+    margin-right: 2px;
+}
+.nav-toolbar .el-icon-setting {
+    color: #f3f3f3;
+    font-size: 20px;
+    margin-left: 5px;
+}
+.run-config-item {
+    margin: 5px 0;
+}
+.run-config-item>* {
+    display: inline-block;
+    vertical-align: middle;
+    margin-right: 10px;
+}
+
 
 .test-list {
     overflow-x: hidden;
diff --git a/test/runTest/client/client.js b/test/runTest/client/client.js
index 38f4a75..6ac28a1 100644
--- a/test/runTest/client/client.js
+++ b/test/runTest/client/client.js
@@ -32,121 +32,141 @@ function processTestsData(tests, oldTestsData) {
     return tests;
 }
 
-socket.on('connect', () => {
-    console.log('Connected');
-    const app = new Vue({
-        el: '#app',
-        data: {
-            fullTests: [],
-            currentTestName: '',
-            sortBy: 'name',
-            searchString: '',
-            running: false,
-
-            allSelected: false,
-            lastSelectedIndex: -1,
-
+const app = new Vue({
+    el: '#app',
+    data: {
+        fullTests: [],
+        currentTestName: '',
+        sortBy: 'name',
+        searchString: '',
+        running: false,
+
+        allSelected: false,
+        lastSelectedIndex: -1,
+
+        runConfig: {
             noHeadless: false,
-        },
-        computed: {
-            tests() {
-                let sortFunc = this.sortBy === 'name'
-                    ? (a, b) => a.name.localeCompare(b.name)
-                    : (a, b) => {
-                        if (a.percentage === b.percentage) {
-                            return a.name.localeCompare(b.name);
-                        }
-                        return a.percentage - b.percentage;
-                    };
-
-                if (!this.searchString) {
-                    // Not modify the original tests data.
-                    return this.fullTests.slice().sort(sortFunc);
-                }
+            threads: 1
+        }
+    },
+    computed: {
+        tests() {
+            let sortFunc = this.sortBy === 'name'
+                ? (a, b) => a.name.localeCompare(b.name)
+                : (a, b) => {
+                    if (a.percentage === b.percentage) {
+                        return a.name.localeCompare(b.name);
+                    }
+                    return a.percentage - b.percentage;
+                };
 
-                return this.fullTests.filter(test => {
-                    return test.name.match(this.searchString);
-                }).sort(sortFunc);
-            },
+            if (!this.searchString) {
+                // Not modify the original tests data.
+                return this.fullTests.slice().sort(sortFunc);
+            }
 
-            currentTest() {
-                let currentTest = this.fullTests.find(item => item.name === 
this.currentTestName);
-                if (!currentTest) {
-                    currentTest = this.fullTests[0];
-                }
-                return currentTest;
-            },
+            return this.fullTests.filter(test => {
+                return test.name.match(this.searchString);
+            }).sort(sortFunc);
+        },
 
-            currentTestUrl() {
-                return window.location.origin + '/test/' + 
this.currentTestName + '.html';
-            },
+        currentTest() {
+            let currentTest = this.fullTests.find(item => item.name === 
this.currentTestName);
+            if (!currentTest) {
+                currentTest = this.fullTests[0];
+            }
+            return currentTest;
+        },
 
-            currentTestRecordUrl() {
-                return window.location.origin + 
'/test/runTest/recorder/index.html#' + this.currentTestName;
-            },
+        currentTestUrl() {
+            return window.location.origin + '/test/' + this.currentTestName + 
'.html';
+        },
 
-            isSelectAllIndeterminate: {
-                get() {
-                    if (!this.tests.length) {
-                        return true;
-                    }
-                    return this.tests.some(test => {
-                        return test.selected !== this.tests[0].selected;
-                    });
-                },
-                set() {}
-            }
+        currentTestRecordUrl() {
+            return window.location.origin + 
'/test/runTest/recorder/index.html#' + this.currentTestName;
         },
-        methods: {
-            goto(url) {
-                window.location.hash = '#' + url;
-            },
-            toggleSort() {
-                this.sortBy = this.sortBy === 'name' ? 'percentage' : 'name';
-            },
-            handleSelectAllChange(val) {
-                // Only select filtered tests.
-                this.tests.forEach(test => {
-                    test.selected = val;
-                });
-                this.isSelectAllIndeterminate = false;
-            },
-            handleSelect(idx) {
-                Vue.nextTick(() => {
-                    this.lastSelectedIndex = idx;
-                });
-            },
-            handleShiftSelect(idx) {
-                if (this.lastSelectedIndex < 0) {
-                    return;
-                }
-                let start = Math.min(this.lastSelectedIndex, idx);
-                let end = Math.max(this.lastSelectedIndex, idx);
-                let selected = !this.tests[idx].selected;   // Will change
-                for (let i = start; i < end; i++) {
-                    this.tests[i].selected = selected;
-                }
-            },
-            refreshList() {
 
-            },
-            runSelectedTests() {
-                const tests = this.fullTests.filter(test => {
-                    return test.selected;
-                }).map(test => {
-                    return test.name;
-                });
-                if (tests.length > 0) {
-                    this.running = true;
-                    socket.emit('run', {tests, noHeadless: this.noHeadless});
+        isSelectAllIndeterminate: {
+            get() {
+                if (!this.tests.length) {
+                    return true;
                 }
+                return this.tests.some(test => {
+                    return test.selected !== this.tests[0].selected;
+                });
             },
-            stopTests() {
-                this.running = false;
-                socket.emit('stop');
+            set() {}
+        }
+    },
+    methods: {
+        goto(url) {
+            window.location.hash = '#' + url;
+        },
+        toggleSort() {
+            this.sortBy = this.sortBy === 'name' ? 'percentage' : 'name';
+        },
+        handleSelectAllChange(val) {
+            // Only select filtered tests.
+            this.tests.forEach(test => {
+                test.selected = val;
+            });
+            this.isSelectAllIndeterminate = false;
+        },
+        handleSelect(idx) {
+            Vue.nextTick(() => {
+                this.lastSelectedIndex = idx;
+            });
+        },
+        handleShiftSelect(idx) {
+            if (this.lastSelectedIndex < 0) {
+                return;
             }
+            let start = Math.min(this.lastSelectedIndex, idx);
+            let end = Math.max(this.lastSelectedIndex, idx);
+            let selected = !this.tests[idx].selected;   // Will change
+            for (let i = start; i < end; i++) {
+                this.tests[i].selected = selected;
+            }
+        },
+        refreshList() {
+
+        },
+        runSelectedTests() {
+            const tests = this.fullTests.filter(test => {
+                return test.selected;
+            }).map(test => {
+                return test.name;
+            });
+            runTests(tests);
+        },
+        stopTests() {
+            this.running = false;
+            socket.emit('stop');
         }
-    });
+    }
+});
+
+function runTests(tests) {
+    if (tests.length > 0) {
+        app.running = true;
+        socket.emit('run', {
+            tests,
+            threads: app.runConfig.threads,
+            noHeadless: app.runConfig.noHeadless
+        });
+    }
+    else {
+        app.$notify({
+            title: 'No test selected.',
+            position: 'bottom-right'
+        });
+    }
+}
+
+
+socket.on('connect', () => {
+    console.log('Connected');
+
     app.$el.style.display = 'block';
 
     let firstUpdate = true;
@@ -159,11 +179,7 @@ socket.on('connect', () => {
                 dangerouslyUseHTMLString: true,
                 center: true
             }).then(value => {
-                app.running = true;
-                socket.emit('run', {
-                    tests: msg.tests.map(test => test.name),
-                    noHeadless: this.noHeadless
-                });
+                runTests(msg.tests.map(test => test.name));
             }).catch(() => {});
         }
         // TODO
@@ -172,10 +188,13 @@ socket.on('connect', () => {
 
         firstUpdate = false;
     });
-    socket.on('finish', () => {
+    socket.on('finish', res => {
         app.$notify({
-            title: 'Test Complete',
-            position: 'bottom-right'
+            type: 'success',
+            title: `${res.count} test complete`,
+            message: `Cost: ${(res.time / 1000).toFixed(1)} s. Threads: 
${res.threads}`,
+            position: 'top-right',
+            duration: 8000
         });
         app.running = false;
     });
diff --git a/test/runTest/client/index.html b/test/runTest/client/index.html
index 7f0ccd0..aad6d83 100644
--- a/test/runTest/client/index.html
+++ b/test/runTest/client/index.html
@@ -29,7 +29,17 @@
                             <el-button v-if="running" title="Run Selected" 
@click="stopTests" circle size="mini" type="primary" 
icon="el-icon-close"></el-button>
                         </el-button-group>
 
-                        <el-checkbox v-model="noHeadless" 
label="Playback"></el-checkbox>
+
+                        <el-popover title="Configuration" 
class="run-configuration">
+                            <div class="config-item">
+                                    <el-checkbox 
v-model="runConfig.noHeadless">Replay</el-checkbox>
+                            </div>
+                            <div class="run-config-item">
+                                <span>Threads</span>
+                                <el-slider style="width: 140px;" 
v-model="runConfig.threads" :step="1" :min="1" :max="8" show-stops></el-slider>
+                            </div>
+                            <i slot="reference" class="el-icon-setting"></i>
+                        </el-popover>
 
 
                         <!-- <el-button-group>
diff --git a/test/runTest/server.js b/test/runTest/server.js
index 05472a3..1cf628c 100644
--- a/test/runTest/server.js
+++ b/test/runTest/server.js
@@ -30,13 +30,13 @@ function serve() {
     };
 };
 
-let testProcess;
+let runningThreads = [];
 let pendingTests;
 
 function stopRunningTests() {
-    if (testProcess) {
-        testProcess.kill();
-        testProcess = null;
+    if (runningThreads) {
+        runningThreads.forEach(thread => thread.kill());
+        runningThreads = [];
     }
     if (pendingTests) {
         pendingTests.forEach(testOpt => {
@@ -48,9 +48,48 @@ function stopRunningTests() {
     }
 }
 
-function startTests(testsNameList, socket, noHeadless) {
+class Thread {
+    constructor() {
+        this.tests = [];
+
+        this.onExit;
+        this.onUpdate;
+    }
+
+    fork(noHeadless) {
+        let p = fork(path.join(__dirname, 'cli.js'), [
+            '--tests',
+            this.tests.map(testOpt => testOpt.name).join(','),
+            '--speed',
+            5,
+            ...(noHeadless ? ['--no-headless'] : [])
+        ]);
+        this.p = p;
+
+        // Finished one test
+        p.on('message', testOpt => {
+            mergeTestsResults([testOpt]);
+            saveTestsList();
+            this.onUpdate();
+        });
+        // Finished all
+        p.on('exit', () => {
+            this.p = null;
+            setTimeout(this.onExit);
+        });
+    }
+
+    kill() {
+        if (this.p) {
+            this.p.kill();
+        }
+    }
+}
+
+function startTests(testsNameList, socket, {noHeadless, threadsCount}) {
     console.log(testsNameList.join(','));
 
+    threadsCount = threadsCount || 1;
     stopRunningTests();
 
     return new Promise(resolve => {
@@ -65,24 +104,34 @@ function startTests(testsNameList, socket, noHeadless) {
 
         socket.emit('update', {tests: getTestsList()});
 
-        testProcess = fork(path.join(__dirname, 'cli.js'), [
-            '--tests',
-            pendingTests.map(testOpt => testOpt.name).join(','),
-            '--speed',
-            5,
-            ...(noHeadless ? ['--no-headless'] : [])
-        ]);
-        // Finished one test
-        testProcess.on('message', testOpt => {
-            mergeTestsResults([testOpt]);
+        let runningCount = 0;
+        function onExit() {
+            runningCount--;
+            if (runningCount === 0) {
+                resolve();
+            }
+        }
+        function onUpdate() {
             // Merge tests.
             socket.emit('update', {tests: getTestsList(), running: true});
-            saveTestsList();
-        });
-        // Finished all
-        testProcess.on('exit', () => {
+        }
+
+        threadsCount = Math.min(threadsCount, pendingTests.length);
+        // Assigning tests to threads
+        runningThreads = new Array(threadsCount).fill(0).map(a => new Thread() 
);
+        for (let i = 0; i < pendingTests.length; i++) {
+            runningThreads[i % threadsCount].tests.push(pendingTests[i]);
+        }
+        for (let i = 0; i < threadsCount; i++) {
+            runningThreads[i].onExit = onExit;
+            runningThreads[i].onUpdate = onUpdate;
+            runningThreads[i].fork(noHeadless, onExit);
+            runningCount++;
+        }
+        // If something bad happens and no proccess are started successfully
+        if (runningCount === 0) {
             resolve();
-        });
+        }
     });
 }
 
@@ -118,13 +167,22 @@ async function start() {
         socket.emit('update', {tests: getTestsList()});
 
         socket.on('run', async data => {
+            let startTime = Date.now();
             // TODO Should broadcast to all sockets.
             try {
-                await startTests(data.tests, socket, data.noHeadless);
+                await startTests(
+                    data.tests,
+                    socket,
+                    { noHeadless: data.noHeadless, threadsCount: data.threads }
+                );
             }
             catch (e) { console.error(e); }
             console.log('Finished');
-            socket.emit('finish');
+            socket.emit('finish', {
+                time: Date.now() - startTime,
+                count: data.tests.length,
+                threads: data.threads
+            });
         });
         socket.on('stop', () => {
             stopRunningTests();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org
For additional commands, e-mail: commits-h...@echarts.apache.org

Reply via email to