草庐IT

并发模式帮助 - 扇入并返回结果?

coder 2024-07-12 原文

我正在编写一个快速并发集成测试包。我已经编写了 POC,现在我正在尝试为它想出一个新模式。我希望遵守以下规则:

  • 一个测试套件可能有很多测试
  • 一个测试套件必须有 n 个 worker 来运行 TestPreppers
  • 一个测试套件必须有 n 个 worker 来运行 TestValidators
  • 测试必须通过准备才能运行验证
  • 一个测试可能有很多 child
  • 在运行子测试之前,测试必须通过验证

结构如下:

package conctest

func New() *TestSuite {
    return &TestSuite{nil, 1, 1, make(chan TestPrepper), make(chan TestValidator)}
}

type TestSuite struct {
    Tests                []*Test
    ConcurrentPreppers   int
    ConcurrentValidators int

    prepperChan   chan TestPrepper
    validatorChan chan TestValidator
}

type TestPrepper func() error
type TestValidator func() ValidatorResult

type ValidatorResult struct {
    Pass  bool
    Error error
}

type Test struct {
    Convey    string
    Details   string
    Prepper   TestPrepper
    Validator TestValidator
    MaxRuns   int
    Children  []*Test

    runs   int
    errors []error
}

我无法提出满足要求的并发设计。我需要从 TestSuite 公开一个可供测试使用的方法,该方法将允许它将其工作发送给 TestSuites 工作人员并将结果返回给测试。

最佳答案

这是我想出的解决方案。我欢迎任何批评或更好的方法,并将接受该答案。我创建了一个私有(private)传输结构,其中包含我的函数和一个返回结果的 channel :

package conctest

import (
    "sync"
    "time"
)

func New() *ConcTest {
    return &ConcTest{nil, 1, 1, make(chan *prepperTransport), make(chan *validatorTransport), nil}
}

type ConcTest struct {
    Tests                []*Test
    ConcurrentPreppers   int
    ConcurrentValidators int

    prepperChan   chan *prepperTransport
    validatorChan chan *validatorTransport

    testSync *sync.WaitGroup
}

func (ct *ConcTest) Run() {
    // start up prepper workers
    for i := 0; i < ct.ConcurrentPreppers; i++ {
        go func() {
            for p := range ct.prepperChan {
                time.Sleep(time.Second)
                p.Result <- p.Prepper()
            }
        }()
    }

    // start up validator workers
    for i := 0; i < ct.ConcurrentValidators; i++ {
        go func() {
            for v := range ct.validatorChan {
                time.Sleep(time.Second)
                v.Result <- v.Validator()
            }
        }()
    }

    // start parent tests, child tests will be called recursively
    ct.testSync = &sync.WaitGroup{}
    for _, t := range ct.Tests {
        ct.testSync.Add(1)
        go ct.runTest(t)
    }

    // wait for all tests to complete
    ct.testSync.Wait()
}

func (ct *ConcTest) runTest(t *Test) {
    // test is a pass until failure encountered
    t.Pass = true

    // run and wait for prep to finish
    pt := &prepperTransport{t.Prepper, make(chan PrepperResult)}
    ct.prepperChan <- pt
    pr := <-pt.Result

    // return on prep failure
    if pr != nil {
        t.Pass = false
        t.Errors = append(t.Errors, pr)
        ct.testSync.Done()
        return
    }

    // run the validator until pass or max runs reached
    for {
        // sleep for given frequency
        time.Sleep(t.Frequency)

        // send the validator to the queue
        t.Runs++
        vt := &validatorTransport{t.Validator, make(chan ValidatorResult)}
        ct.validatorChan <- vt

        // wait for validator response
        vr := <-vt.Result

        // append error to the test
        if vr.Error != nil {
            t.Errors = append(t.Errors, vr.Error)
        }

        // break on pass
        if vr.Pass {
            break
        }

        // break on max attempts
        if t.MaxRuns == t.Runs {
            t.Pass = false
            break
        }
    }

    // break on validator failure
    if !t.Pass {
        ct.testSync.Done()
        return
    }

    // run all children tests
    for _, c := range t.Children {
        ct.testSync.Add(1)
        go ct.runTest(c)
    }

    ct.testSync.Done()
    return
}

type Prepper func() PrepperResult
type PrepperResult error
type prepperTransport struct {
    Prepper Prepper
    Result  chan PrepperResult
}

type Validator func() ValidatorResult
type ValidatorResult struct {
    Pass  bool
    Error error
}
type validatorTransport struct {
    Validator Validator
    Result    chan ValidatorResult
}

type Test struct {
    Convey    string
    Details   string
    Frequency time.Duration
    MaxRuns   int

    Prepper   Prepper
    Validator Validator
    Children  []*Test

    Runs   int
    Errors []error
    Pass   bool
}

关于并发模式帮助 - 扇入并返回结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29101909/

有关并发模式帮助 - 扇入并返回结果?的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  4. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  5. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  6. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  7. ruby - Ruby 中的隐式返回值是怎么回事? - 2

    所以我开始关注ruby​​,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出

  8. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  9. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  10. ruby-on-rails - ruby 日期方程不返回预期的真值 - 2

    为什么以下不同?Time.now.end_of_day==Time.now.end_of_day-0.days#falseTime.now.end_of_day.to_s==Time.now.end_of_day-0.days.to_s#true 最佳答案 因为纳秒数不同:ruby-1.9.2-p180:014>(Time.now.end_of_day-0.days).nsec=>999999000ruby-1.9.2-p180:015>Time.now.end_of_day.nsec=>999999998

随机推荐