mirror of https://github.com/perkeep/perkeep.git
Merge "Add jstests.go, a unit test harness for JavaScript files."
This commit is contained in:
commit
8a970d23f0
2
Makefile
2
Makefile
|
@ -17,7 +17,7 @@ forcefull:
|
|||
go install -a --tags=with_sqlite ./pkg/... ./server/... ./cmd/... ./third_party/... ./dev/...
|
||||
|
||||
presubmit:
|
||||
SKIP_DEP_TESTS=1 go test `pkg-config --libs sqlite3 1>/dev/null 2>/dev/null && echo "--tags=with_sqlite"` -short ./pkg/... ./server/camlistored ./server/appengine ./cmd/... ./dev/... && echo PASS
|
||||
SKIP_DEP_TESTS=1 go test `pkg-config --libs sqlite3 1>/dev/null 2>/dev/null && echo "--tags=with_sqlite"` -short ./pkg/... ./server/camlistored/... ./server/appengine ./cmd/... ./dev/... && echo PASS
|
||||
|
||||
embeds:
|
||||
go install ./pkg/fileembed/genfileembed/ && genfileembed ./server/camlistored/ui && genfileembed ./pkg/server
|
||||
|
|
|
@ -33,12 +33,18 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// GenDeps returns the namespace dependencies between the
|
||||
// closure javascript files in root. It does not descend
|
||||
// in directories.
|
||||
// GenDeps returns the namespace dependencies between the closure javascript files in root. It does not descend in directories.
|
||||
// Each of the files listed in the output is prepended with the path "../../", which is assumed to be the location where these files can be found, relative to Closure's base.js.
|
||||
//
|
||||
// The format for each relevant javascript file is:
|
||||
// goog.addDependency("filepath", ["namespace provided"], ["required namespace 1", "required namespace 2", ...]);
|
||||
func GenDeps(root http.FileSystem) ([]byte, error) {
|
||||
// In the typical configuration, Closure is served at 'closure/goog/...''
|
||||
return GenDepsWithPath("../../", root)
|
||||
}
|
||||
|
||||
// GenDepsWithPath is like GenDeps, but you can specify a path where the files are to be found at runtime relative to Closure's base.js.
|
||||
func GenDepsWithPath(pathPrefix string, root http.FileSystem) ([]byte, error) {
|
||||
d, err := root.Open("/")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to open root of %v: %v", root, err)
|
||||
|
@ -70,7 +76,7 @@ func GenDeps(root http.FileSystem) ([]byte, error) {
|
|||
return nil, fmt.Errorf("Could not parse deps for %v: %v", name, err)
|
||||
}
|
||||
if len(prov) > 0 {
|
||||
fmt.Fprintf(&buf, "goog.addDependency(%q, %v, %v);\n", "../../"+name, jsList(prov), jsList(req))
|
||||
fmt.Fprintf(&buf, "goog.addDependency(%q, %v, %v);\n", pathPrefix+name, jsList(prov), jsList(req))
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright 2014 The Camlistore Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// Package jstest uses the Go testing package to test JavaScript code using Node and Mocha.
|
||||
package jstest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"camlistore.org/pkg/misc/closure"
|
||||
)
|
||||
|
||||
// checkSystemRequirements checks whether system dependencies such as node and npm are present.
|
||||
func checkSystemRequirements() error {
|
||||
binaries := []string{"mocha", "node", "npm"}
|
||||
for _, b := range binaries {
|
||||
if _, err := exec.LookPath(b); err != nil {
|
||||
return fmt.Errorf("Required dependency %q not present", b)
|
||||
}
|
||||
}
|
||||
|
||||
c := exec.Command("npm", "list", "--depth=0")
|
||||
b, _ := c.Output()
|
||||
s := string(b)
|
||||
modules := []string{"mocha", "assert"}
|
||||
for _, m := range modules {
|
||||
if !strings.Contains(s, fmt.Sprintf(" %s@", m)) {
|
||||
return fmt.Errorf("Required npm module %v not present", m)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRepoRoot(target string) (string, error) {
|
||||
dir, err := filepath.Abs(filepath.Dir(target))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not get working directory: %v", err)
|
||||
}
|
||||
for ; dir != "" && filepath.Base(dir) != "camlistore.org"; dir = filepath.Dir(dir) {
|
||||
}
|
||||
if dir == "" {
|
||||
return "", fmt.Errorf("Could not find Camlistore repo in ancestors of %q", target)
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// writeDeps runs closure.GenDeps() on targetDir and writes the resulting dependencies to a temporary file which will be used during the test run. The entries in the deps files are generated with paths relative to baseJS, which should be Closure's base.js file.
|
||||
func writeDeps(baseJS, targetDir string) (string, error) {
|
||||
closureBaseDir := filepath.Dir(baseJS)
|
||||
depPrefix, err := filepath.Rel(closureBaseDir, targetDir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not compute relative path from %q to %q: %v", baseJS, targetDir, err)
|
||||
}
|
||||
|
||||
depPrefix += string(os.PathSeparator)
|
||||
b, err := closure.GenDepsWithPath(depPrefix, http.Dir(targetDir))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GenDepsWithPath failed: %v", err)
|
||||
}
|
||||
depsFile, err := ioutil.TempFile("", "camlistore_closure_test_runner")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not create temp js deps file: %v", err)
|
||||
}
|
||||
err = ioutil.WriteFile(depsFile.Name(), b, 0644)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not write js deps file: %v", err)
|
||||
}
|
||||
return depsFile.Name(), nil
|
||||
}
|
||||
|
||||
// TestCwd runs all the tests in the current working directory.
|
||||
func TestCwd(t *testing.T) {
|
||||
err := checkSystemRequirements()
|
||||
if err != nil {
|
||||
t.Logf("WARNING: JavaScript unit tests could not be run due to a missing system dependency: %v.\nIf you are doing something that might affect JavaScript, you might want to fix this.", err)
|
||||
t.Log(err)
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not determine current directory: %v.", err)
|
||||
}
|
||||
|
||||
repoRoot, err := getRepoRoot(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not find repository root: %v", err)
|
||||
}
|
||||
baseJS := filepath.Join(repoRoot, "third_party", "closure", "lib", "closure", "goog", "base.js")
|
||||
bootstrap := filepath.Join(filepath.Dir(baseJS), "bootstrap", "nodejs.js")
|
||||
depsFile, err := writeDeps(baseJS, path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c := exec.Command("mocha", "-r", bootstrap, "-r", depsFile, filepath.Join(path, "*test.js"))
|
||||
b, err := c.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf(string(b))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright 2014 Google 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.
|
||||
*/
|
||||
|
||||
goog.require('goog.crypt.Hash');
|
||||
goog.require('goog.crypt.Sha1');
|
||||
goog.require('goog.string');
|
||||
var assert = require('assert');
|
||||
var fs = require('fs');
|
||||
|
||||
goog.require('camlistore.blob');
|
||||
|
||||
|
||||
var MockDOMBlob = function(buffer, start, end) {
|
||||
this.buffer_ = buffer;
|
||||
this.start_ = start;
|
||||
this.size = end - start;
|
||||
};
|
||||
|
||||
MockDOMBlob.fromSize = function(size, chr) {
|
||||
var arr = new Uint8Array(size);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = chr.charCodeAt(0);
|
||||
}
|
||||
return new MockDOMBlob(arr.buffer, 0, arr.length);
|
||||
};
|
||||
|
||||
MockDOMBlob.prototype.slice = function(start, end) {
|
||||
if (start < 0 || start >= this.size) {
|
||||
throw new Error(goog.strings.subs("start '%s' out of range [0,%s)", start, this.size));
|
||||
}
|
||||
if (end < this.start_ || end > this.size) {
|
||||
throw new Error(goog.string.subs("end '%s' out of range [0,%s)", end, this.size));
|
||||
}
|
||||
if (end < start) {
|
||||
throw new Error(goog.string.subs("end '%s' is less than start '%s'", start, end));
|
||||
}
|
||||
|
||||
return new MockDOMBlob(this.buffer_, this.start_ + start, this.start_ + end);
|
||||
};
|
||||
|
||||
MockDOMBlob.prototype.getArrayBuffer = function() {
|
||||
return new Uint8Array(this.buffer_, this.start_, this.size);
|
||||
};
|
||||
|
||||
|
||||
var MockFileReaderSync = function() {
|
||||
};
|
||||
|
||||
MockFileReaderSync.prototype.readAsArrayBuffer = function(blob) {
|
||||
return blob.getArrayBuffer();
|
||||
};
|
||||
|
||||
|
||||
describe('camlistore.blob', function() {
|
||||
describe('#refFromHash', function() {
|
||||
it('should calculate the right hash', function() {
|
||||
var hash = new goog.crypt.Sha1();
|
||||
assert.equal(camlistore.blob.refFromHash(hash), 'sha1-da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||
|
||||
hash.reset();
|
||||
hash.update('The quick brown fox jumps over the lazy dog');
|
||||
assert.equal(camlistore.blob.refFromHash(hash), 'sha1-2fd4e1c67a2d28fced849ee1bb76e7391b93eb12');
|
||||
});
|
||||
|
||||
it('should complain about wrong hash function', function() {
|
||||
function FooHash() {};
|
||||
goog.inherits(FooHash, goog.crypt.Hash);
|
||||
assert.throws(camlistore.blob.refFromHash.bind(null, new FooHash()), /Unsupported hash function type/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#refFromString', function() {
|
||||
it('should calculate the right hash', function() {
|
||||
assert.equal(camlistore.blob.refFromString(''), 'sha1-da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||
assert.equal(camlistore.blob.refFromString('The quick brown fox jumps over the lazy dog'), 'sha1-2fd4e1c67a2d28fced849ee1bb76e7391b93eb12');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#refFromDOMBlob', function() {
|
||||
it('should calculate the right hash', function() {
|
||||
blob = MockDOMBlob.fromSize(1000001, 'a');
|
||||
goog.global.FileReaderSync = MockFileReaderSync;
|
||||
try {
|
||||
// Verified with openssl.
|
||||
assert.equal(camlistore.blob.refFromDOMBlob(blob), 'sha1-432e7e01de7086c5246b6ac57f5f435b58f13752');
|
||||
} finally {
|
||||
delete goog.global.FileReaderSync;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright 2014 The Camlistore Authors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"camlistore.org/pkg/misc/closure/jstest"
|
||||
)
|
||||
|
||||
func TestJS(t *testing.T) {
|
||||
jstest.TestCwd(t)
|
||||
}
|
Loading…
Reference in New Issue