Best Syzkaller code snippet using prog.insertBefore
mutation.go
Source:mutation.go
...138 c = p.Calls[idx]139 }140 s := analyze(ctx.ct, p, c)141 calls := r.generateCall(s, p)142 p.insertBefore(c, calls)143 return true144}145func (ctx *mutator) removeCall() bool {146 p, r := ctx.p, ctx.r147 if len(p.Calls) == 0 {148 return false149 }150 idx := r.Intn(len(p.Calls))151 p.RemoveCall(idx)152 return true153}154func (ctx *mutator) mutateArg() bool {155 p, r := ctx.p, ctx.r156 if len(p.Calls) == 0 {157 return false158 }159 c := p.Calls[r.Intn(len(p.Calls))]160 if len(c.Args) == 0 {161 return false162 }163 s := analyze(ctx.ct, p, c)164 updateSizes := true165 for stop, ok := false, false; !stop; stop = ok && r.oneOf(3) {166 ok = true167 ma := &mutationArgs{target: p.Target}168 ForeachArg(c, ma.collectArg)169 if len(ma.args) == 0 {170 return false171 }172 idx := r.Intn(len(ma.args))173 arg, ctx := ma.args[idx], ma.ctxes[idx]174 calls, ok1 := p.Target.mutateArg(r, s, arg, ctx, &updateSizes)175 if !ok1 {176 ok = false177 continue178 }179 p.insertBefore(c, calls)180 if updateSizes {181 p.Target.assignSizesCall(c)182 }183 p.Target.SanitizeCall(c)184 }185 return true186}187func (target *Target) mutateArgForDependencyResource(r *randGen, s *state, arg Arg, ctx ArgCtx, updateSizes *bool) ([]*Call, bool) {188 var baseSize uint64189 if ctx.Base != nil {190 baseSize = ctx.Base.Res.Size()191 }192 calls, retry, preserve := arg.Type().mutate(r, s, arg, ctx)193 if retry {194 return nil, false195 }196 if preserve {197 *updateSizes = false198 }199 // Update base pointer if size has increased.200 if base := ctx.Base; base != nil && baseSize < base.Res.Size() {201 newArg := r.allocAddr(s, base.Type(), base.Res.Size(), base.Res)202 replaceArg(base, newArg)203 }204 for _, c := range calls {205 target.SanitizeCall(c)206 }207 return calls, true208}209func (target *Target) mutateArg(r *randGen, s *state, arg Arg, ctx ArgCtx, updateSizes *bool) ([]*Call, bool) {210 var baseSize uint64211 if ctx.Base != nil {212 baseSize = ctx.Base.Res.Size()213 }214 calls, retry, preserve := arg.Type().mutate(r, s, arg, ctx)215 if retry {216 return nil, false217 }218 if preserve {219 *updateSizes = false220 }221 // Update base pointer if size has increased.222 if base := ctx.Base; base != nil && baseSize < base.Res.Size() {223 newArg := r.allocAddr(s, base.Type(), base.Res.Size(), base.Res)224 replaceArg(base, newArg)225 }226 for _, c := range calls {227 target.SanitizeCall(c)228 }229 return calls, true230}231func regenerate(r *randGen, s *state, arg Arg) (calls []*Call, retry, preserve bool) {232 var newArg Arg233 newArg, calls = r.generateArg(s, arg.Type())234 replaceArg(arg, newArg)235 return236}237func mutateInt(r *randGen, s *state, arg Arg) (calls []*Call, retry, preserve bool) {238 if r.bin() {239 return regenerate(r, s, arg)240 }241 a := arg.(*ConstArg)242 switch {243 case r.nOutOf(1, 3):244 a.Val += uint64(r.Intn(4)) + 1245 case r.nOutOf(1, 2):246 a.Val -= uint64(r.Intn(4)) + 1247 default:248 a.Val ^= 1 << uint64(r.Intn(64))249 }250 return251}252func (t *IntType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {253 return mutateInt(r, s, arg)254}255func (t *FlagsType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {256 return mutateInt(r, s, arg)257}258func (t *LenType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {259 if !r.mutateSize(arg.(*ConstArg), *ctx.Parent) {260 retry = true261 return262 }263 preserve = true264 return265}266func (t *ResourceType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {267 return regenerate(r, s, arg)268}269func (t *VmaType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {270 return regenerate(r, s, arg)271}272func (t *ProcType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {273 return regenerate(r, s, arg)274}275func (t *BufferType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {276 a := arg.(*DataArg)277 switch t.Kind {278 case BufferBlobRand, BufferBlobRange:279 data := append([]byte{}, a.Data()...)280 minLen, maxLen := uint64(0), maxBlobLen281 if t.Kind == BufferBlobRange {282 minLen, maxLen = t.RangeBegin, t.RangeEnd283 }284 a.data = mutateData(r, data, minLen, maxLen)285 case BufferString:286 data := append([]byte{}, a.Data()...)287 if r.bin() {288 minLen, maxLen := uint64(0), maxBlobLen289 if t.TypeSize != 0 {290 minLen, maxLen = t.TypeSize, t.TypeSize291 }292 a.data = mutateData(r, data, minLen, maxLen)293 } else {294 a.data = r.randString(s, t)295 }296 case BufferFilename:297 a.data = []byte(r.filename(s, t))298 case BufferText:299 data := append([]byte{}, a.Data()...)300 a.data = r.mutateText(t.Text, data)301 default:302 panic("unknown buffer kind")303 }304 return305}306func (t *ArrayType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {307 // TODO: swap elements of the array308 a := arg.(*GroupArg)309 count := uint64(0)310 switch t.Kind {311 case ArrayRandLen:312 for count == uint64(len(a.Inner)) {313 count = r.randArrayLen()314 }315 case ArrayRangeLen:316 if t.RangeBegin == t.RangeEnd {317 panic("trying to mutate fixed length array")318 }319 for count == uint64(len(a.Inner)) {320 count = r.randRange(t.RangeBegin, t.RangeEnd)321 }322 }323 if count > uint64(len(a.Inner)) {324 for count > uint64(len(a.Inner)) {325 newArg, newCalls := r.generateArg(s, t.Type)326 a.Inner = append(a.Inner, newArg)327 calls = append(calls, newCalls...)328 for _, c := range newCalls {329 s.analyze(c)330 }331 }332 } else if count < uint64(len(a.Inner)) {333 for _, arg := range a.Inner[count:] {334 removeArg(arg)335 }336 a.Inner = a.Inner[:count]337 }338 return339}340func (t *PtrType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {341 a := arg.(*PointerArg)342 if r.oneOf(1000) {343 removeArg(a.Res)344 index := r.rand(len(r.target.SpecialPointers))345 newArg := MakeSpecialPointerArg(t, index)346 replaceArg(arg, newArg)347 return348 }349 newArg := r.allocAddr(s, t, a.Res.Size(), a.Res)350 replaceArg(arg, newArg)351 return352}353func (t *StructType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {354 gen := r.target.SpecialTypes[t.Name()]355 if gen == nil {356 panic("bad arg returned by mutationArgs: StructType")357 }358 var newArg Arg359 newArg, calls = gen(&Gen{r, s}, t, arg)360 a := arg.(*GroupArg)361 for i, f := range newArg.(*GroupArg).Inner {362 replaceArg(a.Inner[i], f)363 }364 return365}366func (t *UnionType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {367 if gen := r.target.SpecialTypes[t.Name()]; gen != nil {368 var newArg Arg369 newArg, calls = gen(&Gen{r, s}, t, arg)370 replaceArg(arg, newArg)371 } else {372 a := arg.(*UnionArg)373 current := -1374 for i, option := range t.Fields {375 if a.Option.Type().FieldName() == option.FieldName() {376 current = i377 break378 }379 }380 if current == -1 {381 panic("can't find current option in union")382 }383 newIdx := r.Intn(len(t.Fields) - 1)384 if newIdx >= current {385 newIdx++386 }387 optType := t.Fields[newIdx]388 removeArg(a.Option)389 var newOpt Arg390 newOpt, calls = r.generateArg(s, optType)391 replaceArg(arg, MakeUnionArg(t, newOpt))392 }393 return394}395func (t *CsumType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {396 panic("CsumType can't be mutated")397}398func (t *ConstType) mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool) {399 panic("ConstType can't be mutated")400}401type mutationArgs struct {402 target *Target403 args []Arg404 ctxes []ArgCtx405 ignoreSpecial bool406}407func (ma *mutationArgs) collectArg(arg Arg, ctx *ArgCtx) {408 ignoreSpecial := ma.ignoreSpecial409 ma.ignoreSpecial = false410 switch typ := arg.Type().(type) {411 case *StructType:412 if ma.target.SpecialTypes[typ.Name()] == nil || ignoreSpecial {413 return // For structs only individual fields are updated.414 }415 // These special structs are mutated as a whole.416 ctx.Stop = true417 case *UnionType:418 if ma.target.SpecialTypes[typ.Name()] == nil && len(typ.Fields) == 1 || ignoreSpecial {419 return420 }421 ctx.Stop = true422 case *ArrayType:423 // Don't mutate fixed-size arrays.424 if typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd {425 return426 }427 case *CsumType:428 return // Checksum is updated when the checksummed data changes.429 case *ConstType:430 return // Well, this is const.431 case *BufferType:432 if typ.Kind == BufferString && len(typ.Values) == 1 {433 return // string const434 }435 case *PtrType:436 if arg.(*PointerArg).IsSpecial() {437 // TODO: we ought to mutate this, but we don't have code for this yet.438 return439 }440 }441 typ := arg.Type()442 if typ == nil || typ.Dir() == DirOut || !typ.Varlen() && typ.Size() == 0 {443 return444 }445 ma.args = append(ma.args, arg)446 ma.ctxes = append(ma.ctxes, *ctx)447}448func mutateData(r *randGen, data []byte, minLen, maxLen uint64) []byte {449 for stop := false; !stop; stop = stop && r.oneOf(3) {450 f := mutateDataFuncs[r.Intn(len(mutateDataFuncs))]451 data, stop = f(r, data, minLen, maxLen)452 }453 return data454}455const maxInc = 35456var mutateDataFuncs = [...]func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool){457 // TODO(dvyukov): duplicate part of data.458 // Flip bit in byte.459 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {460 if len(data) == 0 {461 return data, false462 }463 byt := r.Intn(len(data))464 bit := r.Intn(8)465 data[byt] ^= 1 << uint(bit)466 return data, true467 },468 // Insert random bytes.469 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {470 if len(data) == 0 {471 return data, false472 }473 n := r.Intn(16) + 1474 if r := int(maxLen) - len(data); n > r {475 n = r476 }477 pos := r.Intn(len(data))478 for i := 0; i < n; i++ {479 data = append(data, 0)480 }481 copy(data[pos+n:], data[pos:])482 for i := 0; i < n; i++ {483 data[pos+i] = byte(r.Int31())484 }485 if uint64(len(data)) > maxLen || r.bin() {486 data = data[:len(data)-n] // preserve original length487 }488 return data, true489 },490 // Remove bytes.491 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {492 if len(data) == 0 {493 return data, false494 }495 n := r.Intn(16) + 1496 if n > len(data) {497 n = len(data)498 }499 pos := 0500 if n < len(data) {501 pos = r.Intn(len(data) - n)502 }503 copy(data[pos:], data[pos+n:])504 data = data[:len(data)-n]505 if uint64(len(data)) < minLen || r.bin() {506 for i := 0; i < n; i++ {507 data = append(data, 0) // preserve original length508 }509 }510 return data, true511 },512 // Append a bunch of bytes.513 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {514 if uint64(len(data)) >= maxLen {515 return data, false516 }517 const max = 256518 n := max - r.biasedRand(max, 10)519 if r := int(maxLen) - len(data); n > r {520 n = r521 }522 for i := 0; i < n; i++ {523 data = append(data, byte(r.rand(256)))524 }525 return data, true526 },527 // Replace int8/int16/int32/int64 with a random value.528 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {529 width := 1 << uint(r.Intn(4))530 if len(data) < width {531 return data, false532 }533 i := r.Intn(len(data) - width + 1)534 storeInt(data[i:], r.Uint64(), width)535 return data, true536 },537 // Add/subtract from an int8/int16/int32/int64.538 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {539 width := 1 << uint(r.Intn(4))540 if len(data) < width {541 return data, false542 }543 i := r.Intn(len(data) - width + 1)544 v := loadInt(data[i:], width)545 delta := r.rand(2*maxInc+1) - maxInc546 if delta == 0 {547 delta = 1548 }549 if r.oneOf(10) {550 v = swapInt(v, width)551 v += delta552 v = swapInt(v, width)553 } else {554 v += delta555 }556 storeInt(data[i:], v, width)557 return data, true558 },559 // Set int8/int16/int32/int64 to an interesting value.560 func(r *randGen, data []byte, minLen, maxLen uint64) ([]byte, bool) {561 width := 1 << uint(r.Intn(4))562 if len(data) < width {563 return data, false564 }565 i := r.Intn(len(data) - width + 1)566 value := r.randInt()567 if r.oneOf(10) {568 value = swap64(value)569 }570 storeInt(data[i:], value, width)571 return data, true572 },573}574func swap16(v uint16) uint16 {575 v0 := byte(v >> 0)576 v1 := byte(v >> 8)577 v = 0578 v |= uint16(v1) << 0579 v |= uint16(v0) << 8580 return v581}582func swap32(v uint32) uint32 {583 v0 := byte(v >> 0)584 v1 := byte(v >> 8)585 v2 := byte(v >> 16)586 v3 := byte(v >> 24)587 v = 0588 v |= uint32(v3) << 0589 v |= uint32(v2) << 8590 v |= uint32(v1) << 16591 v |= uint32(v0) << 24592 return v593}594func swap64(v uint64) uint64 {595 v0 := byte(v >> 0)596 v1 := byte(v >> 8)597 v2 := byte(v >> 16)598 v3 := byte(v >> 24)599 v4 := byte(v >> 32)600 v5 := byte(v >> 40)601 v6 := byte(v >> 48)602 v7 := byte(v >> 56)603 v = 0604 v |= uint64(v7) << 0605 v |= uint64(v6) << 8606 v |= uint64(v5) << 16607 v |= uint64(v4) << 24608 v |= uint64(v3) << 32609 v |= uint64(v2) << 40610 v |= uint64(v1) << 48611 v |= uint64(v0) << 56612 return v613}614func swapInt(v uint64, size int) uint64 {615 switch size {616 case 1:617 return v618 case 2:619 return uint64(swap16(uint16(v)))620 case 4:621 return uint64(swap32(uint32(v)))622 case 8:623 return swap64(v)624 default:625 panic(fmt.Sprintf("swapInt: bad size %v", size))626 }627}628func loadInt(data []byte, size int) uint64 {629 p := unsafe.Pointer(&data[0])630 switch size {631 case 1:632 return uint64(*(*uint8)(p))633 case 2:634 return uint64(*(*uint16)(p))635 case 4:636 return uint64(*(*uint32)(p))637 case 8:638 return *(*uint64)(p)639 default:640 panic(fmt.Sprintf("loadInt: bad size %v", size))641 }642}643func storeInt(data []byte, v uint64, size int) {644 p := unsafe.Pointer(&data[0])645 switch size {646 case 1:647 *(*uint8)(p) = uint8(v)648 case 2:649 *(*uint16)(p) = uint16(v)650 case 4:651 *(*uint32)(p) = uint32(v)652 case 8:653 *(*uint64)(p) = v654 default:655 panic(fmt.Sprintf("storeInt: bad size %v", size))656 }657}658func (p *Prog) Splice(rp *Prog, idx uint32, ncalls int) bool {659 p0c := rp.Clone()660 p.Calls = append(p.Calls[:idx], append(p0c.Calls, p.Calls[idx:]...)...)661 for i := len(p.Calls) - 1; i >= ncalls; i-- {662 p.RemoveCall(i)663 }664 return true665}666func (p *Prog) MutateIoctl1Arg(rs rand.Source, idx int, ct *ChoiceTable) bool {667 r := newRand(p.Target, rs)668 c := p.Calls[idx]669 if len(c.Args) == 0 {670 return false671 }672 s := analyze(ct, p, c)673 updateSizes := true674 for stop, ok := false, false; !stop; stop = ok && r.oneOf(3) {675 ok = true676 ma := &mutationArgs{target: p.Target}677 ForeachArg(c, ma.collectArg)678 log.Logf(1, "len(ma.args) : %v", len(ma.args))679 var idx int680 if len(ma.args) == 0 {681 return false682 } else {683 idx = 0684 }685 arg, ctx := ma.args[idx], ma.ctxes[idx]686 calls, ok1 := p.Target.mutateArgForDependencyResource(r, s, arg, ctx, &updateSizes)687 if !ok1 {688 ok = false689 continue690 }691 p.insertBefore(c, calls)692 if updateSizes {693 p.Target.assignSizesCall(c)694 }695 p.Target.SanitizeCall(c)696 }697 return true698}699func (p *Prog) MutateIoctl3Arg(rs rand.Source, idx int, ct *ChoiceTable) bool {700 r := newRand(p.Target, rs)701 c := p.Calls[idx]702 if len(c.Args) == 0 {703 return false704 }705 s := analyze(ct, p, c)706 updateSizes := true707 for stop, ok := false, false; !stop; stop = ok && r.oneOf(4) {708 ok = true709 ma := &mutationArgs{target: p.Target}710 ForeachArg(c, ma.collectArg)711 log.Logf(1, "len(ma.args) : %v", len(ma.args))712 var idx int713 if len(ma.args) == 0 || len(ma.args) == 1 {714 return false715 } else {716 idx = r.Intn(len(ma.args)-1) + 1717 }718 arg, ctx := ma.args[idx], ma.ctxes[idx]719 calls, ok1 := p.Target.mutateArg(r, s, arg, ctx, &updateSizes)720 if !ok1 {721 ok = false722 continue723 }724 p.insertBefore(c, calls)725 if updateSizes {726 p.Target.assignSizesCall(c)727 }728 p.Target.SanitizeCall(c)729 }730 return true731}732func (p *Prog) GetCall(rs rand.Source, meta *Syscall, idx uint32, ct *ChoiceTable) []*Call {733 r := newRand(p.Target, rs)734 c := p.Calls[idx]735 s := analyze(ct, p, c)736 c0c := r.generateParticularCall(s, meta)737 var cc *Call738 if len(c0c) == 2 {739 cc = c0c[1]740 } else if len(c0c) == 1 {741 cc = c0c[0]742 } else {743 log.Fatalf("GetCall more than 2")744 }745 for i, arg := range cc.Args {746 switch arg.(type) {747 case *ResultArg:748 cc.Args[i] = c.Args[i]749 default:750 }751 }752 var rcc []*Call753 rcc = append(rcc, cc)754 return c0c755}756func (p *Prog) InsertCall(c0c []*Call, idx uint32, ncalls int) {757 c := p.Calls[idx]758 p.insertBefore(c, c0c)759 for i := len(p.Calls) - 1; i >= ncalls; i-- {760 p.RemoveCall(i)761 }762}763func (p *Prog) RepeatCall(idx int) {764 c := p.Calls[idx]765 //newargs := make(map[*ResultArg]*ResultArg)766 //c1 := new(Call)767 //c1.Meta = c.Meta768 //if c.Ret != nil {769 // c1.Ret = clone(c.Ret, newargs).(*ResultArg)770 //}771 //c1.Args = make([]Arg, len(c.Args))772 //for ai, arg := range c.Args {773 // c1.Args[ai] = clone(arg, newargs)774 //}775 var c0c []*Call776 c0c = append(c0c, c)777 p.insertBefore(c, c0c)778}779func (p *Prog) mutateArgForCall(r *randGen, ct *ChoiceTable, c *Call) bool {780 // Change args of a call.781 s := analyze(ct, p, c)782 updateSizes := true783 retryArg := false784 for stop := false; !stop || retryArg; stop = r.oneOf(3) {785 retryArg = false786 ma := &mutationArgs{target: p.Target}787 ForeachArg(c, ma.collectArg)788 if len(ma.args) == 0 {789 return true790 }791 idx := r.Intn(len(ma.args))792 arg, ctx := ma.args[idx], ma.ctxes[idx]793 calls, ok := p.Target.mutateArg(r, s, arg, ctx, &updateSizes)794 if !ok {795 retryArg = true796 continue797 }798 p.insertBefore(c, calls)799 //p.replaceAt(c, calls)800 if updateSizes {801 p.Target.assignSizesCall(c)802 }803 p.Target.SanitizeCall(c)804 }805 return false806}...
rewrite.go
Source:rewrite.go
1// adapted from code by Ivan Daniluk from https://github.com/divan2package instrument3import (4 "bytes"5 "go/ast"6 "go/printer"7 "go/token"8 "io/ioutil"9 "path/filepath"10 "golang.org/x/tools/go/ast/astutil"11 "golang.org/x/tools/go/loader"12 "log"13 "strconv"14 _"reflect"15 "fmt"16 "strings"17 "util"18)19// injects and rewrite source in app.OrigPath20// rewritten file(s) are stored in app.NewPath21// nothing returns22func (app *AppExec) RewriteSource() error {23 var data []byte24 var err error25 // debugging info26 log.Println("RewriteSource: Add code to ",app.OrigPath)27 if util.Debug{28 fmt.Println("RewriteSource: Add code to ",app.OrigPath)29 }30 // add tracing code31 data, err = addCode(app.OrigPath,app.Timeout)32 if err != nil {33 fmt.Println("Error in addCode:", err)34 return err35 }36 // create files to store rewritten data37 filename := filepath.Join(app.NewPath, strings.Split(filepath.Base(app.OrigPath),".")[0]+"_mod.go")38 err = ioutil.WriteFile(filename, data, 0666)39 log.Println("RewriteSource: Writes data to ",filename)40 if util.Debug{41 fmt.Println("RewriteSource: Writes data to ",filename)42 }43 if err != nil {44 panic(err)45 return err46 }47 return nil48}49// injects and rewrite source in app.OrigPath50// rewritten file(s) are stored in app.NewPath51// nothing returns52func (app *AppTest) RewriteSourceSched(ver int) error {53 var data []byte54 var err error55 // debugging info56 log.Println("RewriteSourceSched: Add sched code to ",app.OrigPath)57 if util.Debug{58 fmt.Println("RewriteSourceSched: Add sched code to ",app.OrigPath)59 }60 // add sched calls61 data, err = addCodeSched(app.OrigPath,app.Depth,app.ConcUsage,ver)62 if err != nil {63 panic(err)64 }65 // create files to store rewritten data66 filename := filepath.Join(app.TestPath, app.Name+"_s"+strconv.Itoa(ver)+"_sched.go")67 err = ioutil.WriteFile(filename, data, 0666)68 log.Println("writes data to ",filename)69 if err != nil {70 panic(err)71 }72 return nil73}74// This function:75// - traverses the AST76// - finds the main package, file, function77// - adds needed imports78// - adds tracing mechanism (start/stop)79// - adds constant depth, struct type, global counter and Reschedule function declaration80func addCode(path string, timeout int) ([]byte, error) {81 var conf loader.Config82 if _, err := conf.FromArgs([]string{path}, false); err != nil {83 return nil, err84 }85 prog, err := conf.Load()86 if err != nil {87 return nil, err88 }89 pkg := prog.Created[0]90 // TODO: find file with main func inside91 astFile := pkg.Files[0]92 // add imports93 log.Println("AddCode: Add imports")94 astutil.AddImport(prog.Fset, astFile, "os")95 astutil.AddImport(prog.Fset, astFile, "runtime/trace")96 astutil.AddImport(prog.Fset, astFile, "time")97 if timeout > 0{98 astutil.AddNamedImport(prog.Fset, astFile, "_", "net")99 }100 // add start/stop code101 log.Println("AddCode: Add trace")102 ast.Inspect(astFile, func(n ast.Node) bool {103 switch x := n.(type) {104 case *ast.FuncDecl:105 // find 'main' function106 if x.Name.Name == "main" && x.Recv == nil {107 stmts := createTraceStmts(timeout)108 stmts = append(stmts, x.Body.List...)109 x.Body.List = stmts110 return true111 }112 }113 return true114 })115 var buf bytes.Buffer116 err = printer.Fprint(&buf, prog.Fset, astFile)117 if err != nil {118 return nil, err119 }120 return buf.Bytes(), nil121}122// This function:123// - traverses the AST124// - finds the main package, file, function125// - adds needed imports126// - adds tracing mechanism (start/stop)127// - adds constant depth, struct type, global counter and Reschedule function declaration128func addCodeSched(path string,depth int,concUsage map[string]int, ver int) ([]byte, error) {129 var conf loader.Config130 var s string131 if _, err := conf.FromArgs([]string{path}, false); err != nil {132 return nil, err133 }134 prog, err := conf.Load()135 if err != nil {136 return nil, err137 }138 pkg := prog.Created[0]139 // TODO: find file with main func inside140 astFile := pkg.Files[0]141 // add imports142 log.Println("AddCodeSched: Add imports")143 astutil.AddImport(prog.Fset, astFile, "sync")144 astutil.AddImport(prog.Fset, astFile, "runtime")145 astutil.AddImport(prog.Fset, astFile, "math/rand")146 //fmt.Println(" >>> Added Imports")147 // add constant, struct type, global counter, function declration148 // if this is the first iteration, continue as stated above149 // otherwise, remove Reschedules and add them again based on new concurrency usage150 if ver == 0{151 log.Println("AddCodeSched: Add constants, struct, global counter, func. decl.")152 ast.Inspect(astFile, func(n ast.Node) bool {153 switch x := n.(type) {154 case *ast.File:155 // add constant, struct type, global counter, function declration156 decls := newDecls(depth)157 decls2 := x.Decls158 decls = append(decls2,decls...)159 x.Decls = decls160 return true161 }162 return true163 })164 // add GOMACPROCS165 ast.Inspect(astFile, func(n ast.Node) bool {166 switch x := n.(type) {167 case *ast.FuncDecl:168 // find 'main' function169 if x.Name.Name == "main" && x.Recv == nil {170 stmts := goMaxProcs()171 stmts = append(stmts, x.Body.List...)172 x.Body.List = stmts173 return true174 }175 }176 return true177 })178 log.Println("AddCodeSched: Add Gosched() invocations")179 astutil.Apply(astFile, func(cr *astutil.Cursor) bool{180 //_,ok := cr.Node().(*ast.GoStmt)181 n := cr.Node()182 if n != nil{183 t1 := n.Pos()184 t2 := prog.Fset.Position(t1)185 s = fmt.Sprintf("%v",t2)186 if !matches(n,concUsage,s) {187 return true188 }189 } else{190 return true191 }192 cr.InsertBefore(callFuncSched())193 return true194 },nil)195 }else{196 // remove if (Reschedule) ..197 astutil.Apply(astFile, func(cr *astutil.Cursor) bool{198 n := cr.Node()199 if n != nil && isCallReschedule(n){200 cr.Delete()201 return true202 }203 return true204 },nil)205 // add if (Reschedule) ..206 log.Println("AddCodeSched: Add Gosched() invocations")207 astutil.Apply(astFile, func(cr *astutil.Cursor) bool{208 //_,ok := cr.Node().(*ast.GoStmt)209 n := cr.Node()210 if n != nil{211 t1 := n.Pos()212 t2 := prog.Fset.Position(t1)213 s = fmt.Sprintf("%v",t2)214 if !matches(n,concUsage,s) {215 return true216 }217 } else{218 return true219 }220 cr.InsertBefore(callFuncSched())221 return true222 },nil)223 }224 // write modified ast225 var buf bytes.Buffer226 err = printer.Fprint(&buf, prog.Fset, astFile)227 if err != nil {228 return nil, err229 }230 return buf.Bytes(), nil231}232// wrapper for trace statments233func createTraceStmts(timeout int) []ast.Stmt {234 ret := make([]ast.Stmt, 2)235 // trace.Start(os.Stderr)236 ret[0] = &ast.ExprStmt{237 X: &ast.CallExpr{238 Fun: &ast.SelectorExpr{239 X: &ast.Ident{Name: "trace"},240 Sel: &ast.Ident{Name: "Start"},241 },242 Args: []ast.Expr{243 &ast.SelectorExpr{244 X: &ast.Ident{Name: "os"},245 Sel: &ast.Ident{Name: "Stderr"},246 },247 },248 },249 }250 if timeout > 0{251 // go func(){ <-time.After(5 * time.Second) trace.Stop() os.Exit(1) }()252 ret[1] = &ast.GoStmt{253 Call: &ast.CallExpr{254 Fun: &ast.FuncLit{255 Body: &ast.BlockStmt{256 List: []ast.Stmt{257 &ast.ExprStmt{258 X: &ast.CallExpr{259 Fun: &ast.SelectorExpr{260 X: &ast.Ident{Name: "time"},261 Sel: &ast.Ident{Name: "Sleep"},262 },263 Args: []ast.Expr{264 &ast.BinaryExpr{265 X: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(timeout)},266 Op: token.MUL,267 Y: &ast.SelectorExpr{268 X: &ast.Ident{Name: "time"},269 Sel: &ast.Ident{Name: "Second"},270 },271 },272 },273 },274 },275 &ast.ExprStmt{276 X: &ast.CallExpr{277 Fun: &ast.SelectorExpr{278 X: &ast.Ident{Name: "trace"},279 Sel: &ast.Ident{Name: "Stop"},280 },281 },282 },283 &ast.ExprStmt{284 X: &ast.CallExpr{285 Fun: &ast.SelectorExpr{286 X: &ast.Ident{Name: "os"},287 Sel: &ast.Ident{Name: "Exit"},288 },289 Args: []ast.Expr{290 &ast.BasicLit{Kind: token.INT, Value: "0"},291 },292 },293 },294 },295 },296 Type: &ast.FuncType{Params: &ast.FieldList{}},297 },298 },299 }300 } else{301 // defer func(){ time.Sleep(50*time.Millisecond; trace.Stop() }()302 ret[1] = &ast.DeferStmt{303 Call: &ast.CallExpr{304 Fun: &ast.FuncLit{305 Body: &ast.BlockStmt{306 List: []ast.Stmt{307 &ast.ExprStmt{308 X: &ast.CallExpr{309 Fun: &ast.SelectorExpr{310 X: &ast.Ident{Name: "time"},311 Sel: &ast.Ident{Name: "Sleep"},312 },313 Args: []ast.Expr{314 &ast.BinaryExpr{315 X: &ast.BasicLit{Kind: token.INT, Value: "50"},316 Op: token.MUL,317 Y: &ast.SelectorExpr{318 X: &ast.Ident{Name: "time"},319 Sel: &ast.Ident{Name: "Millisecond"},320 },321 },322 },323 },324 },325 &ast.ExprStmt{326 X: &ast.CallExpr{327 Fun: &ast.SelectorExpr{328 X: &ast.Ident{Name: "trace"},329 Sel: &ast.Ident{Name: "Stop"},330 },331 },332 },333 },334 },335 Type: &ast.FuncType{Params: &ast.FieldList{}},336 },337 },338 }339 }340 return ret341}342// checks if current AST node matches with any concUsage instances343func matches(n ast.Node, conc map[string]int, location string) bool{344 if location != "-"{345 t := strings.Split(filepath.Base(location),":")[0] + ":" + strings.Split(filepath.Base(location),":")[1]346 if val,ok := conc[t]; ok && val != 2 {347 conc[t] = 2348 log.Println("ConcUsage Matches: Return True > ",t)349 return true350 }351 return false352 }353 return false354}355// check if current node is (if Reschedule() ...)356func isCallReschedule(n interface{}) bool{357 switch x := n.(type){358 case *ast.IfStmt:359 _ = x360 iff := n.(*ast.IfStmt)361 cond := iff.Cond362 switch y := cond.(type) {363 case *ast.CallExpr:364 _ = y365 callx := cond.(*ast.CallExpr)366 z := callx.Fun.(*ast.Ident)367 if z.Name == "Reschedule"{368 return true369 }370 return false371 }372 return false373 }374 return false375}...
ast.go
Source:ast.go
1package ast2import (3 "fmt"4 "go/ast"5 "go/parser"6 "go/token"7 "go/types"8 "path/filepath"9 "github.com/go-toolsmith/astcopy"10 "github.com/pkg/errors"11 "golang.org/x/tools/go/ast/astutil"12 "golang.org/x/tools/go/loader"13)14func NewProgram(fileName string) (*loader.Program, error) {15 lo := &loader.Config{16 Fset: token.NewFileSet(),17 ParserMode: parser.ParseComments}18 dirPath := filepath.Dir(fileName)19 packages, err := parser.ParseDir(lo.Fset, dirPath, nil, parser.Mode(0))20 if err != nil {21 return nil, errors.Wrap(err, "failed to parse dir: "+dirPath)22 }23 var files []*ast.File24 for _, pkg := range packages {25 for _, file := range pkg.Files {26 files = append(files, file)27 }28 }29 lo.CreateFromFiles("main", files...)30 return lo.Load()31}32func ConvertErrorFuncToMustFunc(prog *loader.Program, currentPkg *loader.PackageInfo, orgFuncDecl *ast.FuncDecl) (*ast.FuncDecl, bool) {33 funcDecl := astcopy.FuncDecl(orgFuncDecl)34 if !IsErrorFunc(funcDecl) {35 return nil, false36 }37 results := funcDecl.Type.Results.List38 funcDecl.Type.Results.List = results[:len(results)-1]39 replaceReturnStmtByPanicIfErrorExist(prog, currentPkg, funcDecl)40 addPrefixToFunc(funcDecl, "Must")41 return funcDecl, true42}43func GenerateErrorFuncWrapper(currentPkg *loader.PackageInfo, orgFuncDecl *ast.FuncDecl) (*ast.FuncDecl, bool) {44 funcDecl := astcopy.FuncDecl(orgFuncDecl)45 if !IsErrorFunc(funcDecl) {46 return nil, false47 }48 results := getFuncDeclResults(funcDecl)49 funcDecl.Type.Results.List = funcDecl.Type.Results.List[:len(funcDecl.Type.Results.List)-1]50 wrappedCallExpr := generateCallExpr(extractRecvName(funcDecl), funcDecl.Name.Name, funcDecl.Type.Params.List)51 var lhs []string52 for _, result := range results {53 for _, name := range result.Names {54 lhs = append(lhs, name.Name)55 }56 }57 if len(lhs) == 0 {58 for range results {59 tempValueName := getAvailableValueName(currentPkg.Pkg, "v", funcDecl.Body.Pos())60 lhs = append(lhs, tempValueName)61 }62 tempErrValueName := getAvailableValueName(currentPkg.Pkg, "err", funcDecl.Body.Pos())63 lhs[len(lhs)-1] = tempErrValueName64 }65 funcDecl.Body = &ast.BlockStmt{66 List: []ast.Stmt{67 generateAssignStmt(lhs, wrappedCallExpr),68 generatePanicIfErrorExistStmtAst(lhs[len(lhs)-1]),69 &ast.ReturnStmt{Results: identsToExprs(newIdents(lhs[:len(lhs)-1]))},70 },71 }72 addPrefixToFunc(funcDecl, "Must")73 return funcDecl, true74}75func replaceReturnStmtByPanicIfErrorExist(orgProg *loader.Program, currentPkg *loader.PackageInfo, funcDecl *ast.FuncDecl) *ast.FuncDecl {76 newFuncDeclNode := astutil.Apply(funcDecl, func(cr *astutil.Cursor) bool {77 returnStmt, ok := cr.Node().(*ast.ReturnStmt)78 if !ok {79 return true80 }81 returnResults := returnStmt.Results82 if len(returnResults) == 0 {83 return true84 }85 lastReturnResult := returnResults[len(returnResults)-1]86 returnStmt.Results = returnResults[:len(returnResults)-1]87 if lastReturnResultIdent, ok := lastReturnResult.(*ast.Ident); ok {88 if lastReturnResultIdent.Name == "nil" {89 return true90 }91 panicIfErrExistIfStmt := generatePanicIfErrorExistStmtAst(lastReturnResultIdent.Name)92 cr.InsertBefore(panicIfErrExistIfStmt)93 return true94 }95 if lastReturnResultCallExpr, ok := lastReturnResult.(*ast.CallExpr); ok {96 typeNames, err := getCallExprReturnTypes(orgProg, currentPkg, lastReturnResultCallExpr)97 if err != nil {98 panic(err)99 }100 var lhs []string101 for range typeNames {102 tempValueName := getAvailableValueName(currentPkg.Pkg, "v", lastReturnResultCallExpr.Pos())103 lhs = append(lhs, tempValueName)104 }105 tempErrValueName := getAvailableValueName(currentPkg.Pkg, "err", lastReturnResultCallExpr.Pos())106 lhs[len(lhs)-1] = tempErrValueName107 for _, lh := range lhs[:len(lhs)-1] {108 returnStmt.Results = append(returnStmt.Results, ast.NewIdent(lh))109 }110 assignStmt := generateAssignStmt(lhs, lastReturnResultCallExpr)111 panicIfErrExistIfStmt := generatePanicIfErrorExistStmtAst(tempErrValueName)112 cr.InsertBefore(assignStmt)113 cr.InsertBefore(panicIfErrExistIfStmt)114 }115 return true116 }, nil)117 newFuncDecl := newFuncDeclNode.(*ast.FuncDecl)118 return newFuncDecl119}120func identsToExprs(idents []*ast.Ident) (exprs []ast.Expr) {121 for _, ident := range idents {122 exprs = append(exprs, ast.Expr(ident))123 }124 return125}126func newIdents(identNames []string) (idents []*ast.Ident) {127 for _, identName := range identNames {128 idents = append(idents, &ast.Ident{129 Name: identName,130 })131 }132 return133}134func getAvailableValueName(currentPkg *types.Package, valName string, pos token.Pos) string {135 innerMost := currentPkg.Scope().Innermost(pos)136 s, _ := innerMost.LookupParent(valName, pos)137 if s == nil {138 return valName139 }140 cnt := 0141 valNameWithNumber := fmt.Sprintf("%v%v", valName, cnt)142 for {143 s, _ := innerMost.LookupParent(valNameWithNumber, pos)144 if s == nil {145 return valNameWithNumber146 }147 cnt++148 valNameWithNumber = fmt.Sprintf("%v%v", valName, cnt)149 }150}151func extractRecvName(funcDecl *ast.FuncDecl) string {152 if funcDecl.Recv == nil || len(funcDecl.Recv.List) <= 0 {153 return ""154 }155 names := funcDecl.Recv.List[0].Names156 if len(names) <= 0 {157 panic(fmt.Sprintf("unexpected recv names: %v from %v", names, funcDecl.Name.Name))158 }159 return names[0].Name160}...
insertBefore
Using AI Code Generation
1import (2func main() {3 m := ir.NewModule()4 foo := m.NewFunc("foo", types.Void)5 entry := foo.NewBlock("entry")6 c := constant.NewInt(types.I32, 10)7 x := m.NewGlobalDef("x", c)8 c = constant.NewInt(types.I32, 20)9 y := m.NewGlobalDef("y", c)10 c = constant.NewInt(types.I32, 30)11 z := m.NewGlobalDef("z", c)12 c = constant.NewInt(types.I32, 40)13 w := m.NewGlobalDef("w", c)14 c = constant.NewInt(types.I32, 50)15 t := m.NewGlobalDef("t", c)16 str := constant.NewGlobalDef("str",
insertBefore
Using AI Code Generation
1import (2func main() {3 m := ir.NewModule()4 f := m.NewFunc("main", ir.Void)5 entry := f.NewBlock("entry")6 i32 := ir.Int32Type()7 fmtStr := m.NewGlobalDef("fmt", constant.NewCharArrayFromString("fmt"))8 lnStr := m.NewGlobalDef("ln", constant.NewCharArrayFromString("ln"))9 helloStr := m.NewGlobalDef("hello", constant.NewCharArrayFromString("Hello, world!"))10 fmtPrintln := m.NewFunc("fmt.Println", ir.Void, ir.NewParam("p", ir.NewPointerType(i32)))11 fmtPrintlnCall := entry.NewCall(fmtPrintln, helloStr)12 entry.NewRet(nil)13 block := f.NewBlock("block")14 fmtPrintlnCall = block.NewCall(fmtPrintln, helloStr)15 block.NewRet(nil)16 block2 := f.NewBlock("block2")17 fmtPrintlnCall = block2.NewCall(fmtPrintln, helloStr)
insertBefore
Using AI Code Generation
1import java.io.*;2import java.util.*;3import java.text.*;4import java.math.*;5import java.util.regex.*;6public class Solution {7 public static void main(String[] args) {8 Scanner in = new Scanner(System.in);9 int n = in.nextInt();10 int k = in.nextInt();11 int q = in.nextInt();12 int a[] = new int[n];13 for(int a_i=0; a_i < n; a_i++){14 a[a_i] = in.nextInt();15 }16 int m = n-k;17 int b[] = new int[m];18 int c[] = new int[k];19 int d[] = new int[n];20 for(int i=0;i<m;i++)21 {22 b[i]=a[i];23 }24 for(int i=0;i<k;i++)25 {26 c[i]=a[m+i];27 }28 for(int i=0;i<k;i++)29 {30 d[i]=c[i];31 }32 for(int i=0;i<m;i++)33 {34 d[i+k]=b[i];35 }36 for(int a0 = 0; a0 < q; a0++){37 int m = in.nextInt();38 System.out.println(d[m]);39 }40 }41}
insertBefore
Using AI Code Generation
1import "fmt"2func main() {3 var a []int = []int{1, 2, 3, 4, 5}4 var b []int = []int{6, 7, 8, 9, 10}5 var c []int = []int{11, 12, 13, 14, 15}6 var d []int = []int{16, 17, 18, 19, 20}7 var e []int = []int{21, 22, 23, 24, 25}8 var f []int = []int{26, 27, 28, 29, 30}9 var g []int = []int{31, 32, 33, 34, 35}10 var h []int = []int{36, 37, 38, 39, 40}11 var i []int = []int{41, 42, 43, 44, 45}12 var j []int = []int{46, 47, 48, 49, 50}13 var k []int = []int{51, 52, 53, 54, 55}14 var l []int = []int{56, 57, 58, 59, 60}15 var m []int = []int{61, 62, 63, 64, 65}16 var n []int = []int{66, 67, 68, 69, 70}17 var o []int = []int{71, 72, 73, 74, 75}18 var p []int = []int{76, 77, 78, 79, 80}19 var q []int = []int{81, 82, 83, 84, 85}20 var r []int = []int{86, 87, 88, 89, 90}21 var s []int = []int{91, 92, 93, 94, 95}22 var t []int = []int{96, 97, 98, 99, 100}23 var u []int = []int{101, 102, 103, 104, 105}
insertBefore
Using AI Code Generation
1import (2type node struct {3}4type prog struct {5}6func (p *prog) insertAtEnd(data int) {7 newNode := &node{data: data}8 if p.head == nil {9 } else {10 for currentNode.next != nil {11 }12 }13}14func (p *prog) insertAtStart(data int) {15 newNode := &node{data: data}16 if p.head == nil {17 } else {18 }19}20func (p *prog) insertAtPosition(data, position int) {21 newNode := &node{data: data}22 if p.head == nil {23 } else {24 for i := 1; i < position-1; i++ {25 }26 }27}28func (p *prog) insertBefore(data, before int) {29 newNode := &node{data: data}30 if p.head == nil {31 } else {32 for currentNode.next.data != before {33 }34 }35}36func (p *prog) display() {37 if p.head == nil {38 fmt.Println("The list is empty")39 } else {40 for currentNode != nil {41 fmt.Print(currentNode.data, " ")42 }43 fmt.Println()44 }45}46func main() {47 scanner := bufio.NewScanner(os.Stdin)48 fmt.Println("Enter the elements of the list")49 for scanner.Scan() {50 if scanner.Text() == "" {51 }52 data, err := strconv.Atoi(scanner.Text())53 if err != nil {54 fmt.Println("Invalid input")55 } else {56 p.insertAtEnd(data)
insertBefore
Using AI Code Generation
1import "fmt"2type node struct {3}4type prog struct {5}6func (p *prog) insertBefore(n1, n2 *node) {7if p.head == nil {8}9if p.head == n2 {10}11for n := p.head; n != nil; n = n.next {12if n == n2 {13}14}15}16func (p *prog) insertAtHead(n *node) {17}18func (p *prog) insertAtTail(n *node) {19if p.head == nil {20}21for i := p.head; i != nil; i = i.next {22if i.next == nil {23}24}25}26func (p *prog) insertAtPosition(n *node, pos int) {27if pos == 1 {28}29for i, c := p.head, 1; i != nil; i = i.next {30if c == pos {31}32}33}34func (p *prog) delete(n *node) {35if p.head == nil {36}37if p.head == n {38}39for i := p.head; i != nil; i = i.next {40if i == n {41}42}43}44func (p *prog) print() {45for i := p.head; i != nil; i = i.next {46fmt.Println(i.data)47}48}49func main() {
insertBefore
Using AI Code Generation
1import "fmt"2type node struct {3}4type prog struct {5}6func (p *prog) insertBefore(value int) {7 newNode := &node{data: value}8 if p.head == nil {9 }10}11func (p *prog) printList() {12 for current != nil {13 fmt.Printf("%d ", current.data)14 }15 fmt.Println()16}17func main() {18 p := &prog{}19 p.insertBefore(1)20 p.insertBefore(2)21 p.insertBefore(3)22 p.insertBefore(4)23 p.insertBefore(5)24 p.printList()25}
insertBefore
Using AI Code Generation
1import (2func main() {3 prog := NewProgram()4 m := make(map[int]int)5 e := make(map[int]int)6 e2 := make(map[int]int)7 e3 := make(map[int]int)
insertBefore
Using AI Code Generation
1import (2func main() {3 prog := program.NewProg()4 prog.Parse("a = 1 + 2")5 prog.Parse("b = 2 + 3")6 prog.Parse("c = 3 + 4")7 prog.Parse("d = 4 + 5")8 prog.Parse("e = 5 + 6")9 prog.Parse("f = 6 + 7")10 prog.Parse("g = 7 + 8")11 prog.Parse("h = 8 + 9")12 prog.Parse("i = 9 + 10")13 prog.Parse("j = 10 + 11")14 prog.Parse("k = 11 + 12")15 prog.Parse("l = 12 + 13")16 prog.Parse("m = 13 + 14")17 prog.Parse("n = 14 + 15")18 prog.Parse("o = 15 + 16")19 prog.Parse("p = 16 + 17")20 prog.Parse("q = 17 + 18")21 prog.Parse("r = 18 + 19")22 prog.Parse("s = 19 + 20")23 prog.Parse("t = 20 + 21")24 prog.Parse("u = 21 + 22")25 prog.Parse("v = 22 + 23")26 prog.Parse("w = 23 + 24")27 prog.Parse("x = 24 + 25")28 prog.Parse("y = 25 + 26")29 prog.Parse("z = 26 + 27")30 prog.Parse("aa = 27 + 28")31 prog.Parse("bb = 28 + 29")32 prog.Parse("cc = 29 + 30")33 prog.Parse("dd = 30 + 31")34 prog.Parse("ee = 31 + 32")35 prog.Parse("ff = 32 + 33")36 prog.Parse("gg = 33 + 34")37 prog.Parse("hh = 34 + 35")38 prog.Parse("ii = 35 + 36")39 prog.Parse("jj = 36 + 37")40 prog.Parse("kk = 37 + 38")41 prog.Parse("ll = 38 + 39")42 prog.Parse("mm = 39 +
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!