Вызов метода Ruby сделан для неправильного класса (модуль верхнего уровня)

У меня очень странная проблема с отправкой метода в неправильный класс с помощью Minitest.

Я помогаю поддерживать библиотеку dnsruby (https://github.com/alexdalitz/dnsruby) и у нас есть конкретный тест, который периодически не работает (https://github.com/alexdalitz/dnsruby/blob/master/test/tc_resolv.rb#L56).

Установив точку останова в pry, я обнаружил, что в случае сбоя он использует метод getname для внутреннего класса Ruby Resolv, а не класс Dnsruby::Resolv, указанный в коде. И ::Resolv, и Dnsruby::Resolv, похоже, указывают на внутренний класс Ruby Resolv:

[1] pry(#<TestResolv>)> ::Resolv.object_id => 70320518250220 [2] pry(#<TestResolv>)> Dnsruby::Resolv.object_id => 70320518250220

Ошибка не возникает, когда тест запускается сам по себе (ruby test/tc_resolv.rb). В сочетании с другими тестами (с использованием ruby test/ts_online.rb и изменением списка запускаемых тестовых файлов) он часто дает сбой, но иногда нет. Единственный обнаруженный мной фактор, который, по-видимому, коррелирует с ошибкой, — это объем выполняемого тестового кода; чем больше тестов выполняется, тем больше вероятность того, что они потерпят неудачу.

Когда тест проходит успешно, ::Resolv даже не определяется:

[1] pry(#<TestResolv>)> ::Resolv.object_id NameError: uninitialized constant Resolv

Я искал в нашей кодовой базе require 'resolv', но не нашел. Возможно, это требуется для другой библиотеки, которую мы используем. Однако даже в этом случае не следует ли Dnsruby:: в Dnsruby::Resolv указывать наш настроенный класс Resolv?

Как мы можем это исправить?


person Keith Bennett    schedule 04.03.2017    source источник
comment
Я предполагаю, что как только вы узнаете, кто требует resolv, вам будет легче определить, что там происходит. Вы пытались вызвать ошибку в resolv.rb, чтобы увидеть, где это требуется?   -  person phoet    schedule 04.03.2017
comment
Интересная идея. Я сделал это и нашел тестовый файл, который требовал этого. Однако, когда я тестировал только этот файл и рассматриваемый тестовый файл, ошибки не возникало; ::Resolv и Dnsruby::Resolv были доступны, но указывали на правильные (то есть разные) классы, и тесты были пройдены. Таким образом, проблема не в том, что требуется 'resolv', а в том, что каким-то образом Dnsruby::Resolv перезаписывается, чтобы указывать на ::Resolv в некоторых случаях.   -  person Keith Bennett    schedule 04.03.2017
comment
вы можете использовать TracePoint или set_trace_func, чтобы проверить, не переопределен ли он каким-либо классом.   -  person phoet    schedule 05.03.2017
comment
я только что кратко изучил проблему github. может быть дело в целлулоиде? два одновременных экземпляра или неправильное завершение работы или что-то в этом роде? в наборе тестов есть два вызова Celluloid.boot, но нет Celluloid.shutdown.   -  person phoet    schedule 05.03.2017
comment
@phoet Спасибо за ваши предложения. Я открыл новый выпуск Github об обнаруженной вами проблеме Celluloid по адресу github.com/alexdalitz/. dnsruby/issues/122.   -  person Keith Bennett    schedule 06.03.2017


Ответы (1)


Проблема была решена путем удаления include Dnsruby на верхнем уровне, которого там не должно было быть. (Спасибо участнику сообщества Ruby https://github.com/matt-glover за то, что заметил это!)

Более подробная информация содержится в выпуске Github по адресу https://github.com/alexdalitz/dnsruby/issues/112, а исправление находится в запросе на вытягивание по адресу https://github.com/alexdalitz/dnsruby/pull/121/files.

Одна вещь, которая меня смущает... Я ожидал, что это включение приведет к перемещению класса Dnsruby::Resolv на верхний уровень, но, похоже, произошло обратное.

person Keith Bennett    schedule 06.03.2017