Rename random to shuffle. Remove count parameteter to simplify its role. Add tests...
authorAntti Järvinen <antti.jarvinen@alupark.fi>
Mon, 7 Dec 2015 05:27:37 +0000 (07:27 +0200)
committerSteve Francia <steve.francia@gmail.com>
Mon, 4 Jan 2016 16:47:37 +0000 (11:47 -0500)
tpl/template_funcs.go
tpl/template_funcs_test.go

index e933c0bb3b3e5a9df7a5c90d8749090c9e77f80d..3ef36d69457e5e1b1bcf983b6664508981800d6e 100644 (file)
@@ -496,24 +496,14 @@ func After(index interface{}, seq interface{}) (interface{}, error) {
        return seqv.Slice(indexv, seqv.Len()).Interface(), nil
 }
 
-// Random is exposed to templates, to iterate over N random items in a
-// rangeable list.
-func Random(count interface{}, seq interface{}) (interface{}, error) {
+// Shuffle is exposed to templates, to iterate over items in rangeable list in
+// a randomised order.
+func Shuffle(seq interface{}) (interface{}, error) {
 
-       if count == nil || seq == nil {
+       if seq == nil {
                return nil, errors.New("both count and seq must be provided")
        }
 
-       countv, err := cast.ToIntE(count)
-
-       if err != nil {
-               return nil, err
-       }
-
-       if countv < 1 {
-               return nil, errors.New("can't return negative/empty count of items from sequence")
-       }
-
        seqv := reflect.ValueOf(seq)
        seqv, isNil := indirect(seqv)
        if isNil {
@@ -527,20 +517,16 @@ func Random(count interface{}, seq interface{}) (interface{}, error) {
                return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
        }
 
-       if countv >= seqv.Len() {
-               countv = seqv.Len()
-       }
-
-       suffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())
+       shuffled := reflect.MakeSlice(reflect.TypeOf(seq), seqv.Len(), seqv.Len())
 
        rand.Seed(time.Now().UTC().UnixNano())
        randomIndices := rand.Perm(seqv.Len())
 
        for index, value := range randomIndices {
-               suffled.Index(value).Set(seqv.Index(index))
+               shuffled.Index(value).Set(seqv.Index(index))
        }
 
-       return suffled.Slice(0, countv).Interface(), nil
+       return shuffled.Interface(), nil
 }
 
 var (
@@ -1501,7 +1487,7 @@ func init() {
                "first":        First,
                "last":         Last,
                "after":        After,
-               "random":       Random,
+               "shuffle":      Shuffle,
                "where":        Where,
                "delimit":      Delimit,
                "sort":         Sort,
index 91d3cbd1c3e86f5104df24a0725e0b2a7e68b755..95e84bf3685b13db6d2331029f191ff26cbd43be 100644 (file)
@@ -25,6 +25,7 @@ import (
        "runtime"
        "testing"
        "time"
+       "math/rand"
 
        "github.com/stretchr/testify/assert"
 )
@@ -341,43 +342,71 @@ func TestAfter(t *testing.T) {
        }
 }
 
-func TestRandom(t *testing.T) {
+func TestShuffleInputAndOutputFormat(t *testing.T) {
        for i, this := range []struct {
-               count    interface{}
                sequence interface{}
-               expect   interface{}
+               success bool
        }{
-               {int(2), []string{"a", "b", "c", "d"}, 2},
-               {int64(2), []int{100, 200, 300}, 2},
-               {"1", []int{100, 200, 300}, 1},
-               {100, []int{100, 200}, 2},
-               {int32(3), []string{"a", "b"}, 2},
-               {int64(-1), []int{100, 200, 300}, false},
-               {"noint", []int{100, 200, 300}, false},
-               {1, nil, false},
-               {nil, []int{100}, false},
-               {1, t, false},
+               {[]string{"a", "b", "c", "d"}, true},
+               {[]int{100, 200, 300}, true},
+               {[]int{100, 200, 300}, true},
+               {[]int{100, 200}, true},
+               {[]string{"a", "b"}, true},
+               {[]int{100, 200, 300}, true},
+               {[]int{100, 200, 300}, true},
+               {[]int{100}, true},
+               {nil, false},
+               {t, false},
        } {
-               results, err := Random(this.count, this.sequence)
-               if b, ok := this.expect.(bool); ok && !b {
+               results, err := Shuffle(this.sequence)
+               if !this.success {
                        if err == nil {
                                t.Errorf("[%d] First didn't return an expected error", i)
                        }
                } else {
                        resultsv := reflect.ValueOf(results)
+                       sequencev := reflect.ValueOf(this.sequence)
+
                        if err != nil {
                                t.Errorf("[%d] failed: %s", i, err)
                                continue
                        }
 
-                       if resultsv.Len() != this.expect {
-                               t.Errorf("[%d] requested %d random items, got %v but expected %v",
-                                       i, this.count, resultsv.Len(), this.expect)
+                       if resultsv.Len() != sequencev.Len() {
+                               t.Errorf("Expected %d items, got %d items", sequencev.Len(), resultsv.Len())
                        }
                }
        }
 }
 
+func TestShuffleRandomising(t *testing.T) {
+       // Note that this test can fail with false negative result if the shuffle
+       // of the sequence happens to be the same as the original sequence. However
+       // the propability of the event is 10^-158 which is negligible.
+       sequenceLength := 100
+       rand.Seed(time.Now().UTC().UnixNano())
+
+       for _, this := range []struct {
+               sequence []int
+       }{
+               {rand.Perm(sequenceLength)},
+       } {
+               results, _ := Shuffle(this.sequence)
+
+               resultsv := reflect.ValueOf(results)
+
+               allSame := true
+               for index, value := range this.sequence {
+                       allSame = allSame && (resultsv.Index(index).Interface() == value)
+               }
+
+               if allSame {
+                       t.Error("Expected sequence to be shuffled but was in the same order")
+               }
+       }
+}
+
+
 func TestDictionary(t *testing.T) {
        for i, this := range []struct {
                v1            []interface{}