Как сгенерировать UUID, которые могут работать с test.check в Clojure

Генеративное тестирование кажется интересным, но мне нужно было сгенерировать случайные UUID как часть тестирования. java.util.UUID/newRandom плохо работает с сокращением test.check.

Java-код выглядит так:

public static UUID randomUUID()
{
  long lsb = r.nextLong();
  long msb = r.nextLong();

  lsb &= 0x3FFFFFFFFFFFFFFFL;
  lsb |= 0x8000000000000000L; // set top two bits to variant 2

  msb &= 0xFFFFFFFFFFFF0FFFL;
  msb |= 0x4000; // Version 4;

  return new UUID( msb, lsb );
}

Что сложнее перевести на Clojure, чем может показаться.

Как мне написать случайную функцию UUID в Clojure, которую можно успешно уменьшить?


person jwhitlark    schedule 30.01.2015    source источник


Ответы (2)


fn, которая принимает два значения long и генерирует правильный UUID типа 4:

(defn make-uuid [[msb lsb]]
  (java.util.UUID. (-> msb
                   (bit-clear 15)
                   (bit-set   14)
                   (bit-clear 13)
                   (bit-clear 12))
               (-> lsb
                   (bit-set   63)
                   (bit-clear 62))))

Вы можете использовать регулярное выражение для проверки результата (сначала нужно преобразовать его в строку).

(def uuid-v4-regex
     #"(?i)[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[98ab][a-f0-9]{3}-[a-f0-9]{12}")

Затем вы можете протестировать его следующим образом:

(def uuids (gen/fmap make-uuid (gen/tuple (gen/choose 0 Long/MAX_VALUE)
                                          (gen/choose 0 Long/MAX_VALUE))))

(defspec check-random-uuid 100000
  (for-all [uuid uuids]
       (re-find uuid-v4-regex (str uuid))))

И тесты выглядят так:

(check-random-uuid)
=> {:result true, :num-tests 100000, :seed 1422050154338}

Ради интереса я удалил один из допустимых символов (9) для второго поля, и вот как выглядит неудачный тест, чтобы вы могли увидеть, как может помочь сокращение от :fail до :smalest.

(pp/pprint (check-random-uuid))
{:result nil,
 :seed 1422050276824,
 :failing-size 2,
 :num-tests 3,
 :fail [#uuid "2c6d1442-eec3-4800-972e-02905c1b3c00"],
 :shrunk
 {:total-nodes-visited 932,
  :depth 29,
  :result nil,
  :smallest [#uuid "00000000-0000-4000-9000-000000000000"]}}

Это показывает, насколько сокращение шума может удалить ваш тестовый пример.

person jwhitlark    schedule 30.01.2015

Начиная с версии test.check 0.9.0 имеется встроенный gen/uuid, который генерирует случайные uuid, которые не уменьшаются.

Если вы действительно хотите сократить UUID, метод generate-two-long также проще, так как новый gen/large-integer будет генерировать полный диапазон длинных идентификаторов, хотя и не с равномерным распределением.

До сих пор нет встроенного генератора, который мог бы генерировать равномерное распределение по всем длинным, но я думаю, что это то, что следует добавить в будущем.

person gfredericks    schedule 29.12.2015