2013-05-31

Тестирование файловой системы

Симуляция реального мира в песочнице
Май выдался богатым на новую и полезную информацию по тестированию: цикл статей от Сергея Теплякова, интересное обсуждение в Радио-Т.

В это же время появилась интересная задача связанная с доступом к файловой системе. Алгоритм:
1. получаем список файлов
2. каким-то образом фильтруем
3. обрабатываем отфильтрованные файлы.

Работа с ФС (по крайней мере в 6-й java) сосредоточена в классе File: list, delete, работа с путями. По сути эти методы относятся к сервису. Хочется иметь интерфейс чтобы убрать зависимость и получить возможность для подмены реализации, ну или хотя бы подмены mock-ами для тестирования.

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

Постановка задачи

По заданному пути
удалить пять файлов
с учетом сортировки по возрастанию
имя которых начинается с цифр
содержащие первой строкой ШИББОЛЕТ.

Решение

Реализация находится здесь, тесты здесь.

Сервис

FileService представляет собой фактически обертку с некоторыми удобными методами

public class FileService {
    /**
     * Get files in path
     */
    public Collection dir(File path) {
        return Arrays.asList(path.listFiles());
    }

    /**
     * Delete path
     */
    public void delete(File file) {
        if (!file.delete()) {
            log.debug("Cannot delete " + file.getPath());
        }
    }

    /**
     * Open file
     */
    public InputStream openStream(File path) throws FileNotFoundException {
        return new FileInputStream(path);
    }

    public Reader openReader(File path) throws FileNotFoundException {
        return new FileReader(path);
    }

    public boolean isDir(File path) {
        return path.isDirectory();
    }
}

Фильтрация

Фильтрация осуществляется при помощи удобнейшего FluentIterable:

public static Iterable filter(FileService fileService, File dir, int count, String mask, String prefix) {
        return FluentIterable
                .from(Ordering.natural().immutableSortedCopy(fileService.dir(dir)))
                .filter(new NamePredicate(mask))
                .filter(new ContentPredicate(fileService, prefix))
                .limit(count)
                .toImmutableList();
    } 

И пары предикатов для фильтрации по имени и содержимому. В предикат фильтрации по содержимому передается сервис файловой системы, плюс в Java 7 можно использовать очень удобный try для работы с Closeable ресурсами:
public class ContentPredicate implements Predicate {
    private FileService fileService;
    private String prefix;

    public ContentPredicate(FileService fileService, String prefix) {
        this.fileService = fileService;
        this.prefix = prefix;
    }

    @Override
    public boolean apply(@Nullable File file) {
        try (BufferedReader br = new BufferedReader(fileService.openReader(file))) {
            return prefix.equals(br.readLine());
        } catch (IOException ioe) {
            ContentPredicate.log.debug("Cannot read file " + file);
        }
        return false;
    }
}

Тестирование

С помощью mockito подменяем возвращаемые значения при тестировании предикатов и фильтра. Сами тесты получаются очень простыми и быстрыми:

@Test
    public void FilterTest() throws FileNotFoundException {
        FileService fileService = Mockito.mock(FileService.class);
        File dir = new File("/test/dir");
        List files = Arrays.asList(
                new File(dir, "100file.name"), // 0
                new File(dir, "bl"), // 1
                new File(dir, "099file.name"), // 2
                new File(dir, "bla"), // 3
                new File(dir, "098file.name"), // 4
                new File(dir, "blah"), // 5
                new File(dir, "097file.name")); // 6
        Mockito.when(fileService.dir(dir)).thenReturn(files);
        Mockito.when(fileService.openReader(files.get(6))).thenReturn(new StringReader("TEST"));
        Mockito.when(fileService.openReader(files.get(4))).thenReturn(new StringReader("TEST"));
        Mockito.when(fileService.openReader(files.get(2))).thenReturn(new StringReader("!!!!"));
        Mockito.when(fileService.openReader(files.get(0))).thenReturn(new StringReader("TEST"));

        Iterable result = Application.filter(fileService, dir, 3, "^\\d+.*$", "TEST");
        Iterator i = result.iterator();
        Assert.assertEquals(files.get(6), i.next());
        Assert.assertEquals(files.get(4), i.next());
        Assert.assertEquals(files.get(0), i.next());
        Assert.assertFalse(i.hasNext());
    }

Итог

Из минусов: имеется обертка, которая нужна лишь для тестирования "DIP головного мозга". Плюсы: классы становятся простыми и легко модифицируемым, самая важную логику становится легко тестировать.

2013-05-26

Экипировка к походу

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

2013-05-25

Управление по Макиавелли

Не помню когда скачал, и даже не помню откуда (но раз скачал, значит источник был авторитетным). Закинул на телефон, чтобы слушать в дороге. У Тарасова очень своеобразная манера рассказывать, но если не заострять на этом внимание то можно слушать не напрягаясь. Государь гораздо более современный и близкий к нашему времени, чем Сун Цзы, чем и актуален.

2013-05-23

Клуб весёлых радиолюбителей

Устроили с Иваном на работе радиотехническую мастерскую, в качестве верстака применяли блин от штанги на пять кило.

Блок питания

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

Посему было принято решение вскрыть и заменить кондёры. После того как Иван аккуратно вскрыл блок (они оказывается запаянные), нам предстала безрадостная картина выгоревших дорожек и примерно трети оплавившихся деталей. Сильно повезло, что ноут выжил. Мы отпаяли провод от первого, отрезали штекер у второго и начали выяснять как из трёх проводов собирается нужное напряжение. Как и предполагалось: внутри сменных штекеров два провода соединялись с резистором и получалось нужное напряжение. Резистор был извлечен и мы соединили два провода. Теперь зарядка достаточной длины, чтобы ходить по комнате.

Солнечная батарея

Приехала мне спустя 40 дней китайская зарядка YG-020 выдающий по спеке 5.3В и 410мА. Тогда стояла солнечная погода, но подключенный телефон в авиа-режим продолжил разряжаться, может и не так медленно. Напряжение мы измерили, оно соответствовало заявленному, но вот силу тока (которая и была важна для заряки) померить было никак. А ковырять micro-usb кабель очень не хотелось. Дома у меня должен был где-то быть перебитый, который не жалко разрезать.

Кабель я не нашел, но нашел usb удлинитель от безымянной флешки, который мы и разрезали. Китайцы не пожалели пластмассы залив разъем, пока ковыряли пообрывали тонюсенькие провода. На этот раз припаять провода, а не только залудить, было доверено мне, что я и сделал.

6400 китайских люмен не хватает для зарядки телефона
Поскольку погода была пасмурная, Андрей светил на панель фонарём, а мы замеряли напряжение, но добиться больше 2мА не удалось, на окне за стеклом вообще выдавало 120мкА, что было на три порядка меньше необходимого для зарядки телефона. Андрей предложил ультрафиолетовую гипотезу о том, что не кварцевое окно может задерживать большую часть энергии. При открытом окне панель выдала 1.5мА, что больше чем в десять раз - таки да.

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

Про коллективное общение

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