blob: 178e9ca0bb7355cc65c67481fec446f5e872cdd7 [file] [log] [blame]
package btts
import (
"context"
"encoding/json"
"math"
"net/url"
"testing"
"time"
"cloud.google.com/go/bigtable"
"github.com/stretchr/testify/assert"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/query"
"go.skia.org/infra/go/testutils"
"go.skia.org/infra/go/vec32"
"go.skia.org/infra/perf/go/btts_testutils"
"go.skia.org/infra/perf/go/config"
)
var (
cfg = &config.PerfBigTableConfig{
TileSize: 256,
Project: "test",
Instance: "test",
Table: "test",
Topic: "",
GitUrl: "",
Shards: 8,
}
)
func TestBasic(t *testing.T) {
testutils.LargeTest(t)
ctx := context.Background()
btts_testutils.CreateTestTable(t)
defer btts_testutils.CleanUpTestTable(t)
b, err := NewBigTableTraceStoreFromConfig(ctx, cfg, &btts_testutils.MockTS{}, true)
assert.NoError(t, err)
// Create an OPS in a fresh tile.
tileKey := TileKeyFromOffset(1)
op, err := b.UpdateOrderedParamSet(tileKey, paramtools.ParamSet{
"cpu": []string{"x86", "arm"},
"config": []string{"8888", "565"},
})
assert.NoError(t, err)
assert.Len(t, op.KeyOrder, 2)
// Then update that OPS.
op, err = b.UpdateOrderedParamSet(tileKey, paramtools.ParamSet{
"os": []string{"linux", "win"},
})
assert.NoError(t, err)
assert.Len(t, op.KeyOrder, 3)
// Do we calculate LatestTile correctly?
latest, err := b.GetLatestTile()
assert.NoError(t, err)
assert.Equal(t, int32(1), latest.Offset())
// Add an OPS for a new tile.
tileKey2 := TileKeyFromOffset(4)
op, err = b.UpdateOrderedParamSet(tileKey2, paramtools.ParamSet{
"os": []string{"win", "linux"},
})
// Do we calculate LatestTile correctly?
latest, err = b.GetLatestTile()
assert.NoError(t, err)
assert.Equal(t, int32(4), latest.Offset())
// Create another instance, so it has no cache.
b2, err := NewBigTableTraceStoreFromConfig(ctx, cfg, &btts_testutils.MockTS{}, false)
assert.NoError(t, err)
// OPS for tile 4 should be a no-op since it's already in BT.
op2, err := b2.UpdateOrderedParamSet(tileKey2, paramtools.ParamSet{
// Note we reverse "linux", "win" order, but still get the same
// result as op.
"os": []string{"linux", "win"},
})
assert.Equal(t, op, op2)
}
func encodeParams(t *testing.T, op *paramtools.OrderedParamSet, p paramtools.Params) string {
key, err := op.EncodeParamsAsString(p)
assert.NoError(t, err)
return key
}
func TestTraces(t *testing.T) {
testutils.LargeTest(t)
ctx := context.Background()
btts_testutils.CreateTestTable(t)
defer btts_testutils.CleanUpTestTable(t)
now := time.Now()
b, err := NewBigTableTraceStoreFromConfig(ctx, cfg, &btts_testutils.MockTS{}, true)
assert.NoError(t, err)
tileKey := TileKeyFromOffset(1)
op, err := b.UpdateOrderedParamSet(tileKey, paramtools.ParamSet{
"cpu": []string{"x86", "arm"},
})
assert.NoError(t, err)
op, err = b.UpdateOrderedParamSet(tileKey, paramtools.ParamSet{
"config": []string{"8888", "565"},
})
assert.NoError(t, err)
values := map[string]float32{
encodeParams(t, op, paramtools.Params{"cpu": "x86", "config": "8888"}): 1.0,
encodeParams(t, op, paramtools.Params{"cpu": "x86", "config": "565"}): 1.1,
encodeParams(t, op, paramtools.Params{"cpu": "arm", "config": "8888"}): 1.2,
encodeParams(t, op, paramtools.Params{"cpu": "arm", "config": "565"}): 1.3,
}
err = b.WriteTraces(257, values, "gs://some/test/location", now)
assert.NoError(t, err)
q, err := query.New(url.Values{"config": []string{"8888"}})
assert.NoError(t, err)
r, err := q.Regexp(op)
assert.NoError(t, err)
results, err := b.QueryTraces(tileKey, r)
assert.NoError(t, err)
vec1 := vec32.New(256)
vec1[1] = 1.0
vec2 := vec32.New(256)
vec2[1] = 1.2
expected := map[string][]float32{
",0=0,1=0,": vec1,
",0=1,1=0,": vec2,
}
assert.Equal(t, expected, results)
// Now overwrite a value.
values = map[string]float32{
encodeParams(t, op, paramtools.Params{"cpu": "x86", "config": "8888"}): 2.0,
}
err = b.WriteTraces(257, values, "gs://some/other/test/location", now)
assert.NoError(t, err)
// Query again to get the updated value.
results, err = b.QueryTraces(tileKey, r)
assert.NoError(t, err)
vec1 = vec32.New(256)
vec1[1] = 2.0
vec2 = vec32.New(256)
vec2[1] = 1.2
expected = map[string][]float32{
",0=0,1=0,": vec1,
",0=1,1=0,": vec2,
}
assert.Equal(t, expected, results)
// Write in the next column.
values = map[string]float32{
encodeParams(t, op, paramtools.Params{"cpu": "x86", "config": "8888"}): 3.0,
}
err = b.WriteTraces(258, values, "gs://some/other/test/location", now)
assert.NoError(t, err)
// Query again to get the updated value.
results, err = b.QueryTraces(tileKey, r)
assert.NoError(t, err)
vec1 = vec32.New(256)
vec1[1] = 2.0
vec1[2] = 3.0
vec2 = vec32.New(256)
vec2[1] = 1.2
expected = map[string][]float32{
",0=0,1=0,": vec1,
",0=1,1=0,": vec2,
}
assert.Equal(t, expected, results)
// Source
s, err := b.GetSource(258, encodeParams(t, op, paramtools.Params{"cpu": "x86", "config": "8888"}))
assert.NoError(t, err)
assert.Equal(t, "gs://some/other/test/location", s)
s, err = b.GetSource(259, encodeParams(t, op, paramtools.Params{"cpu": "x86", "config": "8888"}))
assert.Error(t, err)
assert.Equal(t, "", s)
}
func TestTileKey(t *testing.T) {
testutils.SmallTest(t)
numShards := int32(3)
tileKey := TileKeyFromOffset(0)
assert.Equal(t, int32(math.MaxInt32), int32(tileKey))
assert.Equal(t, int32(0), tileKey.Offset())
assert.Equal(t, "@2147483647", tileKey.OpsRowName())
assert.Equal(t, "2:2147483647:", tileKey.TraceRowPrefix(2))
assert.Equal(t, "1:2147483647:,0=1,", tileKey.TraceRowName(",0=1,", numShards))
tileKey = TileKeyFromOffset(1)
assert.Equal(t, int32(math.MaxInt32-1), int32(tileKey))
assert.Equal(t, "@2147483646", tileKey.OpsRowName())
assert.Equal(t, "3:2147483646:", tileKey.TraceRowPrefix(3))
assert.Equal(t, "1:2147483646:,0=1,", tileKey.TraceRowName(",0=1,", numShards))
tileKey = TileKeyFromOffset(-1)
assert.Equal(t, BadTileKey, tileKey)
var err error
tileKey, err = TileKeyFromOpsRowName("2147483646")
assert.Error(t, err)
assert.Equal(t, BadTileKey, tileKey)
tileKey, err = TileKeyFromOpsRowName("@2147483637")
assert.NoError(t, err)
assert.Equal(t, "@2147483637", tileKey.OpsRowName())
}
const rowJson = `{
"D": [
{
"Row": "@2147483643",
"Column": "D:H",
"Timestamp": 1536145696388000,
"Value": "NWY2MDQ5ZTk3ODdiMDcxMGFhY2U2MTYzNDU3NzRiNTk="
},
{
"Row": "@2147483643",
"Column": "D:OPS",
"Timestamp": 1536145696388000,
"Value": "Of+BAwEBD09yZGVyZWRQYXJhbVNldAH/ggABAgEIS2V5T3JkZXIB/4QAAQhQYXJhbVNldAH/hgAAABb/gwIBAQhbXXN0cmluZwH/hAABDAAAGf+FBAEBCFBhcmFtU2V0Af+GAAEMAf+EAAAY/4IBAQJvcwEBAm9zAgN3aW4FbGludXgA"
}
]
}`
func TestOpsCacheEntry(t *testing.T) {
testutils.SmallTest(t)
// Entry for an empty OPS.
o, err := NewOpsCacheEntry()
assert.NoError(t, err)
assert.Equal(t, 0, len(o.ops.KeyOrder))
assert.Equal(t, "c011636276b346664a4d3a473ff07fc5", o.hash)
// Entry for an OPS with just one key-value pair:
ops := o.ops.Copy()
ops.Update(paramtools.ParamSet{"config": []string{"8888"}})
o2, err := opsCacheEntryFromOPS(ops)
assert.NoError(t, err)
assert.Equal(t, []string{"config"}, o2.ops.KeyOrder)
assert.Equal(t, "7a59e4600a8f20900c933037d8e0011a", o2.hash)
// From a BigTable row.
goodRow := bigtable.Row{}
err = json.Unmarshal([]byte(rowJson), &goodRow)
assert.NoError(t, err)
o3, err := NewOpsCacheEntryFromRow(goodRow)
assert.NoError(t, err)
assert.Equal(t, []string{"os"}, o3.ops.KeyOrder)
assert.Equal(t, "5f6049e9787b0710aace616345774b59", o3.hash)
// Empty BT row.
row := bigtable.Row{}
_, err = NewOpsCacheEntryFromRow(row)
assert.Error(t, err)
// Nothing missing.
row = bigtable.Row{
"D": []bigtable.ReadItem{
goodRow["D"][0],
goodRow["D"][1],
},
}
_, err = NewOpsCacheEntryFromRow(row)
assert.NoError(t, err)
// Missing H.
row = bigtable.Row{
"D": []bigtable.ReadItem{
goodRow["D"][0],
},
}
_, err = NewOpsCacheEntryFromRow(row)
assert.Error(t, err)
// Missing OPS.
row = bigtable.Row{
"D": []bigtable.ReadItem{
goodRow["D"][1],
},
}
_, err = NewOpsCacheEntryFromRow(row)
assert.Error(t, err)
}