Nano Hash - криптовалюты, майнинг, программирование

Счетчик вызовов во время компиляции в Nim

Следующий код не компилируется, но иллюстрирует то, что я хотел бы сделать: totalTests должен хранить количество раз, когда вызывается assertEquals() (assertEquals(), вероятно, должен быть макросом, чтобы это было возможно, но я не знаком с этот аспект Ним еще).

Любая идея, как этот код следует изменить, чтобы следующий код мог печатать [1/2] и [2/2] в начале каждой строки отчета о тестировании?

from strutils import format

var countTested = 0
var countFailed = 0
var countPassed = 0
let totalTests = 0 # <-- need let or other compile-time type here (?)

# using proc here... macro may be needed to be able to count calls (?)
proc assertEquals*[T](testName: string, expected: T, p: (proc(): T)) =
   countTested += 1
   totalTests += 1 # <-- compilation error (how can we increase each time it is called?)
   write(stdout, format("[$num/$total] $name: ", "num", countTested, "total", totalTests, "name", testName))
   var val = p()
   if val == expected:
     write(stdout, "passed\n")
     countPassed += 1
   else:
     write(stdout, "failed\n")
     countFailed += 1

when isMainModule:
  assertEquals("test #A", 12, proc(): int = 14-2)
  assertEquals("test #B", 12, proc(): int = 12-2)

Изменить: добавлены вопросы в код

12.05.2015


Ответы:


1

Вот один из способов сделать это. Вы можете выполнить код во время компиляции, используя макрос или оператор static. Обратите внимание, что по-прежнему нет способа надежно подсчитать их для нескольких модулей.

import macros, strutils

proc beginTests()

var countTested = 0
var countFailed = 0
var countPassed = 0
var totalTests = 0
var totalTestsCT {.compiletime.} = 0

macro endTests(): stmt =
  quote do:
    proc beginTests() =
      totalTests = `totalTestsCT`

proc assertEqualsImpl*[T](testName: string, expected: T, p: (proc(): T)) =
   countTested += 1
   write(stdout, format("[$num/$total] $name: ",
         "num", countTested, "total", totalTests, "name", testName))
   var val = p()
   if val == expected:
     write(stdout, "passed\n")
     countPassed += 1
   else:
     write(stdout, "failed\n")
     countFailed += 1

macro assertEquals*[T](testName: string, expected: T, p: (proc(): T)): stmt =
  totalTestsCT += 1
  quote do:
    assertEqualsImpl(`testName`, `expected`, `p`)

when isMainModule:
  beginTests()
  assertEquals("test #A", 12, proc(): int = 14-2)
  assertEquals("test #B", 12, proc(): int = 12-2)
  endTests()

Альтернативной реализацией может быть встраивание тестов в оператор пользовательского блока, например.

testSuite:
  assertEquals("test #A", 12, proc(): int = 14-2)
  assertEquals("test #B", 12, proc(): int = 12-2)

Затем макрос testSuite будет подсчитывать утверждения во встроенном коде и соответствующим образом инициализировать переменную.

Еще одним решением было бы не выполнять тесты напрямую, а хранить их в списке и выполнять только в конце.

12.05.2015
  • Спасибо Реймер. Ваше последнее предложение сработало лучше всего для меня. Я отредактировал ваш ответ, чтобы добавить код для этого. Ваше решение, основанное на макросах, приятно и точно, но, похоже, макросы, вероятно, не лучший способ сделать это. Особенно это не сработало при вызове из другого модуля (как вы упоминаете в своем ответе). В этом случае компилятор жалуется на то, что beginTests() не реализован, даже если в этом модуле вызывается endTests(). В любом случае, спасибо за ваш ответ! 13.05.2015
  • Ну, на самом деле мое редактирование было отклонено, поэтому реализация добавляется как отдельный ответ... 13.05.2015
  • Что бы это ни стоило, я даже не видел правку до того, как она была отклонена, и мне кажется, что это нормально. 13.05.2015

  • 2

    Вот реализация третьего предложения Реймера, которое лучше всего сработало для меня.

    import macros, strutils
    
    type 
      TestSuiteObj = object
        countTested: int
        countFailed: int
        countPassed: int
        totalTests: int
        tests: seq[(proc (self: TestSuite))]
      TestSuite* = ref TestSuiteObj
    
    
    proc newTestSuite*(): TestSuite =
      new(result)
      result.countTested = 0
      result.countFailed = 0
      result.countPassed = 0
      result.totalTests = 0
      result.tests = @[]
    
    proc assertEquals*[T](self: TestSuite, testName: string, expected: T, p: (proc(): T)) =
      self.totalTests += 1
    
      var testProc = proc(self: TestSuite) =
        self.countTested += 1
        write(stdout, format("[$num/$total] $name: ", "num", self.countTested, "total", self.totalTests, "name", testName))
        var val = p()
        if val == expected:
          write(stdout, "passed\n")
          self.countPassed += 1
        else:
          write(stdout, "failed\n")
          self.countFailed += 1
    
      self.tests.add(testProc)
    
    
    proc run*(self: TestSuite) =
      self.totalTests = self.tests.len
      for p in self.tests:
        p(self)
    
      var verdict = case (self.countTested == self.countPassed)
      of true: "PASSED"
      of false: "FAILED"
      echo format("$verdict. Passed [$passed/$total] tests.", "verdict", verdict, "passed", self.countPassed, "total", self.countTested)
    
      # Sanity
      assert(self.countTested == (self.countFailed+self.countPassed))
      assert(self.countTested == self.totalTests)
    
    
    when isMainModule:
      var suite = newTestSuite() 
      suite.assertEquals("test #A", 12, proc(): int = 14-2)
      suite.assertEquals("test #B", 12, proc(): int = 12-2)
      suite.run()
    
    13.05.2015
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..