diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2016-02-25 00:52:11 +0100 |
---|---|---|
committer | Cameron Moore <moorereason@gmail.com> | 2016-03-11 15:51:37 -0600 |
commit | cafb784799e2e09df7345ca1d7cfcfae4d1b7aa2 (patch) | |
tree | 1201fcb64fb2dd74a2f846cd2d008bba5a48a298 /helpers/emoji_test.go | |
parent | 5926c6c8d5ae950a0ea2ef6492b1e03095b60574 (diff) |
Add emoji support
This uses the Emoji map from https://github.com/kyokomi/emoji -- but with a custom replacement implementation.
The built-in are fine for most use cases, but in Hugo we do care about pure speed.
The benchmarks below are skewed in Hugo's direction as the source and result is a byte slice,
Kyokomi's implementation works best with strings.
Curious: The easy-to-use `strings.Replacer` is also plenty fast.
```
BenchmarkEmojiKyokomiFprint-4 20000 86038 ns/op 33960 B/op 117 allocs/op
BenchmarkEmojiKyokomiSprint-4 20000 83252 ns/op 38232 B/op 122 allocs/op
BenchmarkEmojiStringsReplacer-4 100000 21092 ns/op 17248 B/op 25 allocs/op
BenchmarkHugoEmoji-4 500000 5728 ns/op 624 B/op 13 allocs/op
```
Fixes #1891
Diffstat (limited to 'helpers/emoji_test.go')
-rw-r--r-- | helpers/emoji_test.go | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/helpers/emoji_test.go b/helpers/emoji_test.go new file mode 100644 index 000000000..70d02e93d --- /dev/null +++ b/helpers/emoji_test.go @@ -0,0 +1,128 @@ +// Copyright 2016 The Hugo Authors. All rights reserved. +// +// 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 helpers + +import ( + "github.com/kyokomi/emoji" + "github.com/spf13/hugo/bufferpool" + "reflect" + "strings" + "testing" +) + +func TestEmojiCustom(t *testing.T) { + for i, this := range []struct { + input string + expect []byte + }{ + {"A :smile: a day", []byte(emoji.Sprint("A :smile: a day"))}, + {"A few :smile:s a day", []byte(emoji.Sprint("A few :smile:s a day"))}, + {"A :smile: and a :beer: makes the day for sure.", []byte(emoji.Sprint("A :smile: and a :beer: makes the day for sure."))}, + {"A :smile: and: a :beer:", []byte(emoji.Sprint("A :smile: and: a :beer:"))}, + {"A :diamond_shape_with_a_dot_inside: and then some.", []byte(emoji.Sprint("A :diamond_shape_with_a_dot_inside: and then some."))}, + {":smile:", []byte(emoji.Sprint(":smile:"))}, + {":smi", []byte(":smi")}, + {"A :smile:", []byte(emoji.Sprint("A :smile:"))}, + {":beer:!", []byte(emoji.Sprint(":beer:!"))}, + {"::smile:", []byte(emoji.Sprint("::smile:"))}, + {":beer::", []byte(emoji.Sprint(":beer::"))}, + {" :beer: :", []byte(emoji.Sprint(" :beer: :"))}, + {":beer: and :smile: and another :beer:!", []byte(emoji.Sprint(":beer: and :smile: and another :beer:!"))}, + {" :beer: : ", []byte(emoji.Sprint(" :beer: : "))}, + {"No smilies for you!", []byte("No smilies for you!")}, + {" The motto: no smiles! ", []byte(" The motto: no smiles! ")}, + {":hugo_is_the_best_static_gen:", []byte(":hugo_is_the_best_static_gen:")}, + {"은행 :smile: 은행", []byte(emoji.Sprint("은행 :smile: 은행"))}, + } { + result := Emojify([]byte(this.input)) + + if !reflect.DeepEqual(result, this.expect) { + t.Errorf("[%d] got '%q' but expected %q", i, result, this.expect) + } + + } +} + +// The Emoji benchmarks below are heavily skewed in Hugo's direction: +// +// Hugo have a byte slice, wants a byte slice and doesn't mind if the original is modified. + +func BenchmarkEmojiKyokomiFprint(b *testing.B) { + + f := func(in []byte) []byte { + buff := bufferpool.GetBuffer() + defer bufferpool.PutBuffer(buff) + emoji.Fprint(buff, string(in)) + + bc := make([]byte, buff.Len(), buff.Len()) + copy(bc, buff.Bytes()) + return bc + } + + doBenchmarkEmoji(b, f) +} + +func BenchmarkEmojiKyokomiSprint(b *testing.B) { + + f := func(in []byte) []byte { + return []byte(emoji.Sprint(string(in))) + } + + doBenchmarkEmoji(b, f) +} + +func BenchmarkHugoEmoji(b *testing.B) { + doBenchmarkEmoji(b, Emojify) +} + +func doBenchmarkEmoji(b *testing.B, f func(in []byte) []byte) { + + type input struct { + in []byte + expect []byte + } + + data := []struct { + input string + expect string + }{ + {"A :smile: a day", emoji.Sprint("A :smile: a day")}, + {"A :smile: and a :beer: day keeps the doctor away", emoji.Sprint("A :smile: and a :beer: day keeps the doctor away")}, + {"A :smile: a day and 10 " + strings.Repeat(":beer: ", 10), emoji.Sprint("A :smile: a day and 10 " + strings.Repeat(":beer: ", 10))}, + {"No smiles today.", "No smiles today."}, + {"No smiles for you or " + strings.Repeat("you ", 1000), "No smiles for you or " + strings.Repeat("you ", 1000)}, + } + + var in []input = make([]input, b.N*len(data)) + var cnt = 0 + for i := 0; i < b.N; i++ { + for _, this := range data { + in[cnt] = input{[]byte(this.input), []byte(this.expect)} + cnt++ + } + } + + b.ResetTimer() + cnt = 0 + for i := 0; i < b.N; i++ { + for j := range data { + currIn := in[cnt] + cnt++ + result := f(currIn.in) + if len(result) != len(currIn.expect) { + b.Fatalf("[%d] emoji std, got \n%q but expected \n%q", j, result, currIn.expect) + } + } + + } +} |