-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Joshua Krstic
committed
Sep 6, 2023
1 parent
d86a047
commit c8d59dc
Showing
9 changed files
with
431 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Package experiments contains functionalities to retrieve synced experiments | ||
package experiments | ||
|
||
import ( | ||
"encoding/json" | ||
"io" | ||
"os" | ||
) | ||
|
||
// GetBooleanExperiment attempts to read the key from the map provided and does type checking on the | ||
// returned value, if any. Value is false on failure. | ||
func GetBooleanExperiment(e map[string]any, key string) (bool, bool) { | ||
v, err := e[key] | ||
if !err { | ||
return false, false | ||
} | ||
|
||
f, ok := v.(bool) | ||
if !ok { | ||
return false, false | ||
} | ||
|
||
return f, true | ||
} | ||
|
||
// GetStringExperiment attempts to read the key from the map provided and does type checking on the | ||
// returned value, if any. Value is an empty string on failure. | ||
func GetStringExperiment(e map[string]any, key string) (string, bool) { | ||
v, err := e[key] | ||
if !err { | ||
return "", false | ||
} | ||
|
||
f, ok := v.(string) | ||
if !ok { | ||
return "", false | ||
} | ||
|
||
return f, true | ||
} | ||
|
||
// GetIntExperiment attempts to read the key from the map provided and does type checking on the | ||
// returned value, if any. Value is -1 on failure. | ||
// encoding/JSON doesn't differentiate between ints and floats. This method casts the float from | ||
// the unmarshalled json to an int. | ||
func GetIntExperiment(e map[string]any, key string) (int, bool) { | ||
v, err := e[key] | ||
if !err { | ||
return -1, false | ||
} | ||
|
||
f, ok := v.(float64) | ||
if !ok { | ||
return -1, false | ||
} | ||
|
||
return int(f), true | ||
} | ||
|
||
// GetFloatExperiment attempts to read the key from the map provided and does type checking on the | ||
// returned value, if any. Value is -1 on failure. | ||
func GetFloatExperiment(e map[string]any, key string) (float64, bool) { | ||
v, err := e[key] | ||
if !err { | ||
return -1, false | ||
} | ||
|
||
f, ok := v.(float64) | ||
if !ok { | ||
return -1, false | ||
} | ||
|
||
return f, true | ||
} | ||
|
||
// ReadExperimentsFile takes a filepath, opens the file, and calls ReadJsonInput with the contents | ||
// of the file. | ||
// If the file cannot be opened, the experiments map is set to an empty map. | ||
func ReadExperimentsFile(fpath string) (map[string]any, error) { | ||
f, err := os.Open(fpath) | ||
if err != nil { | ||
return map[string]any{}, err | ||
} | ||
|
||
return readJSONInput(f) | ||
} | ||
|
||
// ReadJSONInput takes a reader and unmarshalls the contents into the experiments map. | ||
// If the unmarsahlling fails, the experiments map is set to an empty map. | ||
func readJSONInput(read io.Reader) (map[string]any, error) { | ||
jsondata := make(map[string]any) | ||
if err := json.NewDecoder(read).Decode(&jsondata); err != nil { | ||
return map[string]any{}, err | ||
} | ||
|
||
return jsondata, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
package experiments | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
) | ||
|
||
func TestGetBooleanExperiment(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"TestFeatureForImage\":true}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetBooleanExperiment(e, "TestFeatureForImage") | ||
if !ok { | ||
t.Error("failed to get key 'TestFeatureForImage'") | ||
} | ||
if val != true { | ||
t.Errorf("expected 'TestFeatureForImage' to be true, got: %v", val) | ||
} | ||
} | ||
|
||
func TestGetBooleanExperimentNoKey(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetBooleanExperiment(e, "InvalidKey") | ||
if ok { | ||
t.Errorf("expected to fail to get boolean experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetBooleanExperimentInvalidType(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"StringFeature\":\"string_value\"}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetBooleanExperiment(e, "StringFeature") | ||
if ok { | ||
t.Errorf("expected to fail to get boolean experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetStringExperiment(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"StringFeature\":\"string_value\"}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetStringExperiment(e, "StringFeature") | ||
if !ok { | ||
t.Error("failed to get key 'StringFeature'") | ||
} | ||
if val != "string_value" { | ||
t.Errorf("expected 'StringFeature' to be 'string_value', got: %v", val) | ||
} | ||
} | ||
|
||
func TestGetStringExperimentNoKey(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetBooleanExperiment(e, "InvalidKey") | ||
if ok { | ||
t.Errorf("expected to fail to get string experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetStringExperimentInvalidType(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"FloatFeature\":-5.6}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetStringExperiment(e, "FloatFeature") | ||
if ok { | ||
t.Errorf("expected to fail to get string experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetFloatExperiment(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"FloatFeature\":-5.6}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetFloatExperiment(e, "FloatFeature") | ||
if !ok { | ||
t.Error("failed to get key 'FloatFeature'") | ||
} | ||
if val != -5.6 { | ||
t.Errorf("expected 'FloatFeature' to be -5.6, got: %v", val) | ||
} | ||
} | ||
|
||
func TestGetFloatExperimentNoKey(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetFloatExperiment(e, "InvalidKey") | ||
if ok { | ||
t.Errorf("expected to fail to get string experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetFloatExperimentInvalidType(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"TestFeatureForImage\":true}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetFloatExperiment(e, "TestFeatureForImage") | ||
if ok { | ||
t.Errorf("expected to fail to get float experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetIntExperiment(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"IntFeature\":10293812}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetIntExperiment(e, "IntFeature") | ||
if !ok { | ||
t.Error("failed to get key 'IntFeature'") | ||
} | ||
if val != 10293812 { | ||
t.Errorf("expected 'IntFeature' to be 10293812, got: %v", val) | ||
} | ||
} | ||
|
||
func TestGetIntExperimentNoKey(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetIntExperiment(e, "InvalidKey") | ||
if ok { | ||
t.Errorf("expected to fail to get int experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetIntExperimentInvalidType(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"StringFeature\":\"string_value\"}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetIntExperiment(e, "StringFeature") | ||
if ok { | ||
t.Errorf("expected to fail to get int experiment, got %v", val) | ||
} | ||
} | ||
|
||
func TestGetSeveralExperiments(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"TestFeatureForImage\":true,\"StringFeature\":\"string_value\",\"FloatFeature\":-5.6,\"OtherTestFeatureForImage\":false}") | ||
|
||
e, err := readJSONInput(&buffer) | ||
if err != nil { | ||
t.Errorf("Failed to parse input json, err: %v", err) | ||
} | ||
|
||
val, ok := GetBooleanExperiment(e, "TestFeatureForImage") | ||
if !ok { | ||
t.Error("failed to get key 'TestFeatureForImage'") | ||
} | ||
if val != true { | ||
t.Errorf("expected 'TestFeatureForImage' to be true, got: %v", val) | ||
} | ||
|
||
val, ok = GetBooleanExperiment(e, "OtherTestFeatureForImage") | ||
if !ok { | ||
t.Error("failed to get key 'TestFeatureForImage'") | ||
} | ||
if val != false { | ||
t.Errorf("expected 'OtherTestFeatureForImage' to be false, got: %v", val) | ||
} | ||
} | ||
|
||
func TestReadExperimentsFileReturnsEmptyOnNoFile(t *testing.T) { | ||
e, err := ReadExperimentsFile("$!bad path//") | ||
|
||
if err == nil { | ||
t.Errorf("expected err to be non nil, got nil") | ||
} | ||
|
||
if e == nil || len(e) > 0 { | ||
t.Errorf("expected empty experiments object, got %v", e) | ||
} | ||
} | ||
|
||
func TestReadExperimentsFileReturnsEmptyOnBadJsonFormat(t *testing.T) { | ||
var buffer bytes.Buffer | ||
buffer.WriteString("{\"this string isnt json") | ||
|
||
e, err := readJSONInput(&buffer) | ||
|
||
if err == nil { | ||
t.Errorf("expected err to be non nil, got nil") | ||
} | ||
|
||
if e == nil || len(e) > 0 { | ||
t.Errorf("expected empty experiments object, got %v", e) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.