|  | package chrome_branch | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "fmt" | 
|  | "strconv" | 
|  | "strings" | 
|  | "testing" | 
|  |  | 
|  | "github.com/stretchr/testify/require" | 
|  | "go.skia.org/infra/go/deepequal/assertdeep" | 
|  | "go.skia.org/infra/go/mockhttpclient" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | fakeData = `[ | 
|  | { | 
|  | "angle_branch": "4577", | 
|  | "bling_ldap": "govind", | 
|  | "bling_owner": "Krishna Govind", | 
|  | "chromium_branch": "4577", | 
|  | "chromium_main_branch_hash": "761ddde228655e313424edec06497d0c56b0f3c4", | 
|  | "chromium_main_branch_position": 902210, | 
|  | "clank_ldap": "benmason", | 
|  | "clank_owner": "Ben Mason", | 
|  | "cros_ldap": "geohsu", | 
|  | "cros_owner": "Geo Hsu", | 
|  | "dawn_branch": "4577", | 
|  | "desktop_ldap": "pbommana", | 
|  | "desktop_owner": "Prudhvi Bommana", | 
|  | "devtools_branch": "4577", | 
|  | "milestone": 93, | 
|  | "pdfium_branch": "4577", | 
|  | "schedule_active": true, | 
|  | "schedule_phase": "beta", | 
|  | "skia_branch": "m93", | 
|  | "v8_branch": "9.3-lkgr", | 
|  | "webrtc_branch": "4577" | 
|  | }, | 
|  | { | 
|  | "angle_branch": "4515", | 
|  | "bling_ldap": "benmason", | 
|  | "bling_owner": "Ben Mason", | 
|  | "chromium_branch": "4515", | 
|  | "chromium_main_branch_hash": "488fc70865ddaa05324ac00a54a6eb783b4bc41c", | 
|  | "chromium_main_branch_position": 885287, | 
|  | "clank_ldap": "govind", | 
|  | "clank_owner": "Krishna Govind", | 
|  | "cros_ldap": "dgagnon", | 
|  | "cros_owner": "Daniel Gagnon", | 
|  | "dawn_branch": "4515", | 
|  | "desktop_ldap": "srinivassista", | 
|  | "desktop_owner": "Srinivas Sista", | 
|  | "devtools_branch": "4515", | 
|  | "milestone": 92, | 
|  | "pdfium_branch": "4515", | 
|  | "schedule_active": true, | 
|  | "schedule_phase": "stable", | 
|  | "skia_branch": "m92", | 
|  | "v8_branch": "9.2-lkgr", | 
|  | "webrtc_branch": "4515" | 
|  | }, | 
|  | { | 
|  | "angle_branch": "4472", | 
|  | "bling_ldap": "bindusuvarna", | 
|  | "bling_owner": "Bindu Suvarna", | 
|  | "chromium_branch": "4472", | 
|  | "chromium_main_branch_hash": "3d60439cfb36485e76a1c5bb7f513d3721b20da1", | 
|  | "chromium_main_branch_position": 870763, | 
|  | "clank_ldap": "benmason", | 
|  | "clank_owner": "Ben Mason", | 
|  | "cros_ldap": "marinakz", | 
|  | "cros_owner": "Marina Kazatcker", | 
|  | "dawn_branch": null, | 
|  | "desktop_ldap": "pbommana", | 
|  | "desktop_owner": "Prudhvi Bommana", | 
|  | "devtools_branch": "4472", | 
|  | "milestone": 91, | 
|  | "pdfium_branch": "4472", | 
|  | "schedule_active": false, | 
|  | "skia_branch": "m91", | 
|  | "v8_branch": "9.1-lkgr", | 
|  | "webrtc_branch": "4472" | 
|  | } | 
|  | ]` | 
|  |  | 
|  | fakeData2 = `[ | 
|  | { | 
|  | "angle_branch": "4606", | 
|  | "bling_ldap": "harrysouders", | 
|  | "bling_owner": "Harry Souders", | 
|  | "chromium_branch": "4606", | 
|  | "chromium_main_branch_hash": "35b0d5a9dc8362adfd44e2614f0d5b7402ef63d0", | 
|  | "chromium_main_branch_position": 911515, | 
|  | "clank_ldap": "govind", | 
|  | "clank_owner": "Krishna Govind", | 
|  | "cros_ldap": "matthewjoseph", | 
|  | "cros_owner": "Matt Nelson", | 
|  | "dawn_branch": "4606", | 
|  | "desktop_ldap": "srinivassista", | 
|  | "desktop_owner": "Srinivas Sista", | 
|  | "devtools_branch": "4606", | 
|  | "milestone": 94, | 
|  | "pdfium_branch": "4606", | 
|  | "schedule_active": true, | 
|  | "schedule_phase": "branch", | 
|  | "skia_branch": "m94", | 
|  | "v8_branch": "9.4-lkgr", | 
|  | "webrtc_branch": "4606" | 
|  | }, | 
|  | { | 
|  | "angle_branch": "4577", | 
|  | "bling_ldap": "govind", | 
|  | "bling_owner": "Krishna Govind", | 
|  | "chromium_branch": "4577", | 
|  | "chromium_main_branch_hash": "761ddde228655e313424edec06497d0c56b0f3c4", | 
|  | "chromium_main_branch_position": 902210, | 
|  | "clank_ldap": "benmason", | 
|  | "clank_owner": "Ben Mason", | 
|  | "cros_ldap": "geohsu", | 
|  | "cros_owner": "Geo Hsu", | 
|  | "dawn_branch": "4577", | 
|  | "desktop_ldap": "pbommana", | 
|  | "desktop_owner": "Prudhvi Bommana", | 
|  | "devtools_branch": "4577", | 
|  | "milestone": 93, | 
|  | "pdfium_branch": "4577", | 
|  | "schedule_active": true, | 
|  | "schedule_phase": "stable_cut", | 
|  | "skia_branch": "m93", | 
|  | "v8_branch": "9.3-lkgr", | 
|  | "webrtc_branch": "4577" | 
|  | }, | 
|  | { | 
|  | "angle_branch": "4515", | 
|  | "bling_ldap": "benmason", | 
|  | "bling_owner": "Ben Mason", | 
|  | "chromium_branch": "4515", | 
|  | "chromium_main_branch_hash": "488fc70865ddaa05324ac00a54a6eb783b4bc41c", | 
|  | "chromium_main_branch_position": 885287, | 
|  | "clank_ldap": "govind", | 
|  | "clank_owner": "Krishna Govind", | 
|  | "cros_ldap": "dgagnon", | 
|  | "cros_owner": "Daniel Gagnon", | 
|  | "dawn_branch": "4515", | 
|  | "desktop_ldap": "srinivassista", | 
|  | "desktop_owner": "Srinivas Sista", | 
|  | "devtools_branch": "4515", | 
|  | "milestone": 92, | 
|  | "pdfium_branch": "4515", | 
|  | "schedule_active": true, | 
|  | "schedule_phase": "stable", | 
|  | "skia_branch": "m92", | 
|  | "v8_branch": "9.2-lkgr", | 
|  | "webrtc_branch": "4515" | 
|  | }, | 
|  | { | 
|  | "angle_branch": "4472", | 
|  | "bling_ldap": "bindusuvarna", | 
|  | "bling_owner": "Bindu Suvarna", | 
|  | "chromium_branch": "4472", | 
|  | "chromium_main_branch_hash": "3d60439cfb36485e76a1c5bb7f513d3721b20da1", | 
|  | "chromium_main_branch_position": 870763, | 
|  | "clank_ldap": "benmason", | 
|  | "clank_owner": "Ben Mason", | 
|  | "cros_ldap": "marinakz", | 
|  | "cros_owner": "Marina Kazatcker", | 
|  | "dawn_branch": null, | 
|  | "desktop_ldap": "pbommana", | 
|  | "desktop_owner": "Prudhvi Bommana", | 
|  | "devtools_branch": "4472", | 
|  | "milestone": 91, | 
|  | "pdfium_branch": "4472", | 
|  | "schedule_active": false, | 
|  | "skia_branch": "m91", | 
|  | "v8_branch": "9.1-lkgr", | 
|  | "webrtc_branch": "4472" | 
|  | } | 
|  | ]` | 
|  | ) | 
|  |  | 
|  | func fakeBranches() *Branches { | 
|  | m := fakeMilestones() | 
|  | return &Branches{ | 
|  | Main: &Branch{ | 
|  | Milestone: 94, | 
|  | Number:    0, | 
|  | Ref:       RefMain, | 
|  | V8Branch:  RefMain, | 
|  | }, | 
|  | Beta:   m[0], | 
|  | Stable: m[1], | 
|  | } | 
|  | } | 
|  |  | 
|  | func fakeMilestones() []*Branch { | 
|  | return []*Branch{ | 
|  | { | 
|  | Milestone: 93, | 
|  | Number:    4577, | 
|  | Ref:       fmt.Sprintf(refTmplRelease, 4577), | 
|  | V8Branch:  "9.3", | 
|  | }, | 
|  | { | 
|  | Milestone: 92, | 
|  | Number:    4515, | 
|  | Ref:       fmt.Sprintf(refTmplRelease, 4515), | 
|  | V8Branch:  "9.2", | 
|  | }, | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestBranchCopy(t *testing.T) { | 
|  |  | 
|  | b := fakeBranches() | 
|  | assertdeep.Copy(t, b.Beta, b.Beta.Copy()) | 
|  | } | 
|  |  | 
|  | func TestBranchesCopy(t *testing.T) { | 
|  |  | 
|  | b := fakeBranches() | 
|  | b.Main.Milestone = 95 | 
|  | b.Dev = &Branch{ | 
|  | Milestone: 94, | 
|  | Number:    4606, | 
|  | Ref:       fmt.Sprintf(refTmplRelease, 4606), | 
|  | V8Branch:  "9.4", | 
|  | } | 
|  | assertdeep.Copy(t, b, b.Copy()) | 
|  | } | 
|  |  | 
|  | func TestBranchValidate(t *testing.T) { | 
|  |  | 
|  | test := func(fn func(*Branch), expectErr string) { | 
|  | b := fakeBranches().Beta | 
|  | fn(b) | 
|  | err := b.Validate() | 
|  | if expectErr == "" { | 
|  | require.NoError(t, err) | 
|  | } else { | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), expectErr)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // OK. | 
|  | test(func(b *Branch) {}, "") | 
|  | test(func(b *Branch) { | 
|  | b.Ref = RefMain | 
|  | b.Number = 0 | 
|  | }, "") | 
|  |  | 
|  | // Not OK. | 
|  | test(func(b *Branch) { | 
|  | b.Milestone = 0 | 
|  | }, "Milestone is required") | 
|  | test(func(b *Branch) { | 
|  | b.Number = 0 | 
|  | }, "Number is required") | 
|  | test(func(b *Branch) { | 
|  | b.Ref = RefMain | 
|  | }, "Number must be zero for main branch") | 
|  | } | 
|  |  | 
|  | func TestBranchesValidate(t *testing.T) { | 
|  |  | 
|  | test := func(fn func(*Branches), expectErr string) { | 
|  | b := fakeBranches() | 
|  | fn(b) | 
|  | err := b.Validate() | 
|  | if expectErr == "" { | 
|  | require.NoError(t, err) | 
|  | } else { | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), expectErr), err) | 
|  | } | 
|  | } | 
|  |  | 
|  | // OK. | 
|  | test(func(b *Branches) {}, "") | 
|  |  | 
|  | // Missing branch. | 
|  | test(func(b *Branches) { | 
|  | b.Beta = nil | 
|  | }, "Beta branch is missing") | 
|  | test(func(b *Branches) { | 
|  | b.Stable = nil | 
|  | }, "Stable branch is missing") | 
|  | test(func(b *Branches) { | 
|  | b.Main = nil | 
|  | }, "Main branch is missing") | 
|  |  | 
|  | // Each Branch should be validated. | 
|  | test(func(b *Branches) { | 
|  | b.Beta.Milestone = 0 | 
|  | }, "Milestone is required") | 
|  | test(func(b *Branches) { | 
|  | b.Stable.Number = 0 | 
|  | }, "Number is required") | 
|  | test(func(b *Branches) { | 
|  | b.Main.Number = 42 | 
|  | }, "Number must be zero for main branch.") | 
|  | } | 
|  |  | 
|  | func TestGet(t *testing.T) { | 
|  |  | 
|  | ctx := context.Background() | 
|  | urlmock := mockhttpclient.NewURLMock() | 
|  | c := urlmock.Client() | 
|  |  | 
|  | // Everything okay. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(fakeData))) | 
|  | b, m, err := Get(ctx, c) | 
|  | require.NoError(t, err) | 
|  | require.Equal(t, fakeBranches(), b) | 
|  | require.Equal(t, fakeMilestones(), m) | 
|  |  | 
|  | // Beta channel is missing, we retrieve the branch via milestone number. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(strings.ReplaceAll(fakeData, schedulePhaseBeta, "dev")))) | 
|  | b, m, err = Get(ctx, c) | 
|  | require.NoError(t, err) | 
|  | require.Equal(t, fakeBranches(), b) | 
|  | require.Equal(t, fakeMilestones(), m) | 
|  |  | 
|  | // Beta channel is actually missing. | 
|  | noBeta := strings.ReplaceAll(strings.ReplaceAll(fakeData, schedulePhaseBeta, "dev"), strconv.Itoa(fakeBranches().Beta.Milestone), "9999") | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(noBeta))) | 
|  | b, m, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "Beta branch is missing"), err) | 
|  |  | 
|  | // Stable channel is missing. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(strings.ReplaceAll(fakeData, schedulePhaseStable, "dev")))) | 
|  | b, m, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "Stable branch is missing"), err) | 
|  |  | 
|  | // Invalid branch number. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(strings.ReplaceAll(fakeData, "4577", "nope")))) | 
|  | b, m, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "invalid branch number \"nope\" for channel \"beta\""), err) | 
|  |  | 
|  | // Missing milestone. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(strings.ReplaceAll(fakeData, "93", "null")))) | 
|  | b, m, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "Beta branch is invalid: Milestone is required"), err) | 
|  | } | 
|  |  | 
|  | func TestGetSecondDataSet(t *testing.T) { | 
|  |  | 
|  | ctx := context.Background() | 
|  | urlmock := mockhttpclient.NewURLMock() | 
|  | c := urlmock.Client() | 
|  |  | 
|  | // Everything okay. This data set is missing the "beta" branch in | 
|  | // schedule_phase, so we fall back to using "stable_cut". | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(fakeData2))) | 
|  | b, _, err := Get(ctx, c) | 
|  | require.NoError(t, err) | 
|  | expect := fakeBranches() | 
|  | expect.Main.Milestone = 95 | 
|  | expect.Dev = &Branch{ | 
|  | Milestone: 94, | 
|  | Number:    4606, | 
|  | Ref:       fmt.Sprintf(refTmplRelease, 4606), | 
|  | V8Branch:  "9.4", | 
|  | } | 
|  | require.Equal(t, expect, b) | 
|  |  | 
|  | // Beta channel is actually missing. | 
|  | noBeta := strings.ReplaceAll(strings.ReplaceAll(fakeData2, schedulePhaseStableCut, "dev"), strconv.Itoa(fakeBranches().Beta.Milestone), "9999") | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(noBeta))) | 
|  | b, _, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "Beta branch is missing"), err) | 
|  |  | 
|  | // Stable channel is missing. | 
|  | noStable := strings.ReplaceAll(fakeData2, fmt.Sprintf("\"%s\"", schedulePhaseStable), "\"dev\"") | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(noStable))) | 
|  | b, _, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "Stable branch is missing"), err) | 
|  |  | 
|  | // Invalid branch number. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(strings.ReplaceAll(fakeData2, "4577", "nope")))) | 
|  | b, _, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "invalid branch number \"nope\" for channel \"stable_cut\""), err) | 
|  |  | 
|  | // Missing milestone. | 
|  | urlmock.MockOnce(jsonURL, mockhttpclient.MockGetDialogue([]byte(strings.ReplaceAll(fakeData2, "93", "null")))) | 
|  | b, _, err = Get(ctx, c) | 
|  | require.Nil(t, b) | 
|  | require.NotNil(t, err) | 
|  | require.True(t, strings.Contains(err.Error(), "Beta branch is invalid: Milestone is required"), err) | 
|  | } |