Initial commit

pull/1/head
Andrey Ivanov 2020-05-26 21:16:56 +03:00
commit 4370a1133e
114 changed files with 3528 additions and 0 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* @OtusGolang/teachers-and-mentors

View File

@ -0,0 +1,11 @@
## Домашнее задание №1 «Hello now»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Понятность и чистота кода - до 2 баллов
В случае ошибки получения времени из сети:
- [ ] ошибка выводится на экран - 2 балла
- [ ] программа завершается с ненулевым кодом ответа - 2 балла
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №2 «Распаковка строки»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены новые юнит-тесты - до 4 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №3 «Частотный анализ»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены новые юнит-тесты - до 4 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,10 @@
## Домашнее задание №4 «LRU-кэш»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены новые юнит-тесты для списка - 1 балл
- [ ] Добавлены новые юнит-тесты для кэша (включая тест на логику
выталкивания из кэша редко используемых элементов) - до 3 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №5 «Параллельное исполнение»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены новые юнит-тесты - до 4 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №6 «Пайплайн»
### Критерии оценки
- [ ] CI-пайплайн зелёный - 5 баллов
- [ ] Добавлены новые юнит-тесты - до 2 баллов
- [ ] Понятность и чистота кода - до 3 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №7 «Утилита для копирования файлов»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены юнит-тесты - до 4 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №8 «Утилита envdir»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены юнит-тесты - до 4 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,10 @@
## Домашнее задание №9 «Генератор валидаторов»
### Критерии оценки
- Закоммичен сгенерированный код - 1 балл
- Пайплайн зелёный - 3 балла
- Добавлены новые юнит-тесты (доработаны имеющиеся) в `models` - до 2 баллов
- Добавлены юнит-тесты для тулзы в `govalidate` - до 2 баллов
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №10 «Оптимизация программы»
### Критерии оценки
- [ ] Пайплайн зелёный и нет попытки «обмануть» систему - 4 балла
- [ ] Добавлены юнит-тесты - до 3 баллов
- [ ] Понятность и чистота кода - до 3 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,8 @@
## Домашнее задание №11 «Клиент TELNET»
### Критерии оценки
- [ ] Пайплайн зелёный - 4 балла
- [ ] Добавлены юнит-тесты - до 4 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,16 @@
## Домашнее задание №12 «Заготовка сервиса Календарь»
### Критерии оценки
- [ ] Makefile заполнен и пайплайн зеленый - 1 балл
- [ ] Понятность и чистота кода (включая факт, что проект разбит
на пакеты по определенной логике) - до 2 баллов
- [ ] Реализовано конфигурирование сервиса - 1 балл
- [ ] Используется логгер и он настраивается из конфига - 1 балл
- [ ] Запускается простой HTTP-сервер - 1 балл
- [ ] Присутствуют юнит-тесты - 1 балл
Реализовано хранилище:
- [ ] in-memory - 1 балл
- [ ] sql + миграции - 2 балла
#### Зачёт от 7 баллов

View File

@ -0,0 +1,10 @@
## Домашнее задание №13 «API к Календарю»
### Критерии оценки
- [ ] Makefile заполнен и пайплайн зеленый - 1 балл
- [ ] Реализовано GRPC API и `make generate` - 3 балла
- [ ] Реализовано HTTP API - 2 балла
- [ ] Написаны юнит-тесты на API - до 2 баллов
- [ ] Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,14 @@
## Домашнее задание №14 «Кроликизация Календаря»
### Критерии оценки
- [ ] Makefile заполнен и пайплайн зеленый - 1 балл
- [ ] Работа с RMQ выделена в отдельный пакет, код не дублируется - 1 балл
- [ ] Реализован Рассыльщик - 2 балла
- [ ] Можно собрать сервисы одной командой (`make build`) - 1 балл
- [ ] Понятность и чистота кода - до 2 баллов
Реализован Планировщик:
- [ ] отсылает уведомления о выбранных событиях - 2 балла
- [ ] удаляет старые события - 1 балл
#### Зачёт от 7 баллов

View File

@ -0,0 +1,12 @@
## Домашнее задание №15 «Докеризация и интеграционное тестирование Календаря»
### Критерии оценки
- [ ] Проект полностью запускается и останавливается с помощью `make up` / `make down` - 3 балла
- [ ] Интеграционные тесты запускаются с помощью `make bdd`. Команда возвращает верный код ответа - 1 балл
Интеграционные тесты покрывают бизнес сценарии:
- [ ] добавление события и обработка бизнес ошибок - 2 балла
- [ ] получение листинга событий на день/неделю/месяц - 2 балла
- [ ] отправка уведомлений - 2 балла
#### Зачёт от 7 баллов

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
### Go template
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
go-cp
go-envdir
go-telnet
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
### User defined
.idea/
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

15
.golangci.yml Normal file
View File

@ -0,0 +1,15 @@
run:
tests: false
linters:
disable-all: false
enable-all: true
disable:
- goerr113
- gochecknoglobals
- gochecknoinits
- godox
- gomnd
- lll
- nakedret
- wsl

79
.travis.yml Normal file
View File

@ -0,0 +1,79 @@
language: go
go:
- "1.14"
os:
- linux
git:
depth: 1
quiet: true
submodules: false
notifications:
email: false
env:
global:
- GO111MODULE=on
- GOPROXY=https://proxy.golang.org
- BRANCH="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
before_install:
- echo "current branch is ${BRANCH}"
- if [ "${BRANCH}" == "master" ]; then travis_terminate 0; fi
- ls ${BRANCH} && cd ${BRANCH}
stages:
- name: Tests
jobs:
include:
- stage: "Tests"
name: "go get"
install: echo "skip"
script: cd /tmp && go get "github.com/${TRAVIS_REPO_SLUG}/${BRANCH}@${BRANCH}"
- stage: "Tests"
name: "Linters"
install: go install github.com/golangci/golangci-lint/cmd/golangci-lint
script: golangci-lint run ./...
if: |
(type = push AND branch != hw12_13_14_15_calendar) OR \
(type = pull_request AND head_branch != hw12_13_14_15_calendar)
- stage: "Tests"
name: "Unit tests"
install: go mod download
script: go test -v -count=1 -race -gcflags=-l -timeout=30s ./...
if: |
(type = push AND branch != hw12_13_14_15_calendar) OR \
(type = pull_request AND head_branch != hw12_13_14_15_calendar)
- stage: "Tests"
name: "Optimization tests"
install: go mod download
script: go test -v -count=1 -timeout=30s -tags bench ./...
if: |
(type = push AND branch == hw10_program_optimization) OR \
(type = pull_request AND head_branch == hw10_program_optimization)
- stage: "Tests"
name: "Bash tests"
install: echo "skip"
script: ./test.sh
if: |
(type = push AND branch IN (hw07_file_copying, hw08_envdir_tool, hw09_generator_of_validators, hw11_telnet_client)) OR \
(type = pull_request AND head_branch IN (hw07_file_copying, hw08_envdir_tool, hw09_generator_of_validators, hw11_telnet_client))
- stage: "Tests"
name: "Makefile"
install: go mod download
script:
- make lint
- make test
- make build
if: |
(type = push AND branch == hw12_13_14_15_calendar) OR \
(type = pull_request AND head_branch == hw12_13_14_15_calendar)

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) 2020, Дмитрий Смаль, Антон Телышев
Разрешается повторное распространение и использование как в виде исходного кода, так и в двоичной форме,
с изменениями или без, при соблюдении следующих условий:
- При повторном распространении исходного кода должно оставаться указанное выше уведомление об авторском праве,
этот список условий и последующий отказ от гарантий.
- При повторном распространении двоичного кода должна сохраняться указанная выше информация об авторском праве,
этот список условий и последующий отказ от гарантий в документации и/или в других материалах, поставляемых при распространении.
- Ни название ООО «Отус онлайн-образование», ни имена её сотрудников и преподавателей не могут быть использованы в
качестве поддержки или продвижения продуктов, основанных на этом ПО без предварительного письменного разрешения.
ЭТА ПРОГРАММА ПРЕДОСТАВЛЕНА ВЛАДЕЛЬЦАМИ АВТОРСКИХ ПРАВ И/ИЛИ ДРУГИМИ СТОРОНАМИ «КАК ОНА ЕСТЬ» БЕЗ КАКОГО-ЛИБО ВИДА ГАРАНТИЙ,
ВЫРАЖЕННЫХ ЯВНО ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ, ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИ КОММЕРЧЕСКОЙ ЦЕННОСТИ И ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ.
НИ В КОЕМ СЛУЧАЕ НИ ОДИН ВЛАДЕЛЕЦ АВТОРСКИХ ПРАВ И НИ ОДНО ДРУГОЕ ЛИЦО, КОТОРОЕ МОЖЕТ ИЗМЕНЯТЬ И/ИЛИ ПОВТОРНО РАСПРОСТРАНЯТЬ ПРОГРАММУ,
КАК БЫЛО СКАЗАНО ВЫШЕ, НЕ НЕСЁТ ОТВЕТСТВЕННОСТИ, ВКЛЮЧАЯ ЛЮБЫЕ ОБЩИЕ, СЛУЧАЙНЫЕ, СПЕЦИАЛЬНЫЕ ИЛИ ПОСЛЕДОВАВШИЕ УБЫТКИ, ВСЛЕДСТВИЕ ИСПОЛЬЗОВАНИЯ
ИЛИ НЕВОЗМОЖНОСТИ ИСПОЛЬЗОВАНИЯ ПРОГРАММЫ (ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ПОТЕРЕЙ ДАННЫХ, ИЛИ ДАННЫМИ, СТАВШИМИ НЕПРАВИЛЬНЫМИ, ИЛИ ПОТЕРЯМИ,
ПРИНЕСЕННЫМИ ИЗ-ЗА ВАС ИЛИ ТРЕТЬИХ ЛИЦ, ИЛИ ОТКАЗОМ ПРОГРАММЫ РАБОТАТЬ СОВМЕСТНО С ДРУГИМИ ПРОГРАММАМИ), ДАЖЕ ЕСЛИ ТАКОЙ ВЛАДЕЛЕЦ ИЛИ ДРУГОЕ
ЛИЦО БЫЛИ ИЗВЕЩЕНЫ О ВОЗМОЖНОСТИ ТАКИХ УБЫТКОВ.

25
README.md Normal file
View File

@ -0,0 +1,25 @@
## Домашние задания курса OTUS «Разработчик Golang»
1) [«Hello now»](./hw01_hello_now)
2) [«Распаковка строки»](./hw02_unpack_string)
3) [«Частотный анализ»](./hw03_frequency_analysis)
4) [«LRU-кэш»](./hw04_lru_cache)
5) [«Параллельное исполнение»](./hw05_parallel_execution)
6) [«Пайплайн»](./hw06_pipeline_execution)
7) [«Утилита для копирования файлов»](./hw07_file_copying)
8) [«Утилита envdir»](./hw08_envdir_tool)
9) [«Генератор валидаторов»](./hw09_generator_of_validators)
10) [«Оптимизация программы»](./hw10_program_optimization)
11) [«Клиент TELNET»](./hw11_telnet_client)
12) [«Заготовка сервиса Календарь»](./hw12_13_14_15_calendar/docs/12_README.md)
13) [«API к Календарю»](./hw12_13_14_15_calendar/docs/13_README.md)
14) [«Кроликизация Календаря»](./hw12_13_14_15_calendar/docs/14_README.md)
15) [«Докеризация и интеграционное тестирование Календаря»](./hw12_13_14_15_calendar/docs/15_README.md)
16) [«Проект»](https://github.com/OtusGolang/final_project)
---
[Инструкция по сдаче ДЗ](https://github.com/OtusGolang/home_work/wiki#%D0%A1%D1%82%D1%83%D0%B4%D0%B5%D0%BD%D1%82%D0%B0%D0%BC).
---
Авторы ДЗ:
- [Дмитрий Смаль](https://github.com/mialinx)
- [Антон Телышев](https://github.com/Antonboom)

29
hw01_hello_now/README.md Normal file
View File

@ -0,0 +1,29 @@
## Домашнее задание №1 «Hello now»
Необходимо написать программу, печатающую текущее локальное время и точное время,
полученное с использованием библиотеки NTP (**github.com/beevik/ntp**) в формате:
```text
current time: <время>
exact time: <время>
```
(без учёта [монотонного времени](https://golang.org/pkg/time/#hdr-Monotonic_Clocks)
(без `"m=±<value>"` в выводе))
Программа должна корректно обрабатывать ошибки библиотеки: распечатывать их в STDERR
и возвращать ненулевой код возврата.
Кроме этого необходимо исправить **go.mod** так, чтобы для данного модуля работала
команда `go get`.
### Критерии оценки
- Пайплайн зелёный - 4 балла
- В случае ошибки получения времени из сети:
- ошибка выводится на экран - 2 балла
- программа завершается с ненулевым кодом ответа - 2 балла
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов
### Подсказки
- `log.Fatalf`
- `t.Round`

10
hw01_hello_now/go.mod Normal file
View File

@ -0,0 +1,10 @@
module github.com/fixme_my_friend/hw01_hello_now
go 1.14
require (
bou.ke/monkey v1.0.2
github.com/beevik/ntp v0.2.0
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
)

21
hw01_hello_now/go.sum Normal file
View File

@ -0,0 +1,21 @@
bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI=
bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA=
github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

5
hw01_hello_now/main.go Normal file
View File

@ -0,0 +1,5 @@
package main
func main() {
// Place your code here
}

View File

@ -0,0 +1,70 @@
package main
import (
"io/ioutil"
"os"
"testing"
"time"
"bou.ke/monkey"
"github.com/beevik/ntp"
)
// go test -gcflags=-l
func TestHelloNow(t *testing.T) {
t.Run("test normal behavior", func(t *testing.T) {
layout := "2 Jan 2006 15:04:05"
monkey.Patch(time.Now, func() time.Time {
nowTime, err := time.Parse(layout, "9 May 1945 10:03:00")
if err != nil {
t.Fatal(err)
}
return nowTime
})
monkey.Patch(ntp.Time, func(_ string) (time.Time, error) {
ntpTime, err := time.Parse(layout, "9 May 1945 10:03:02")
if err != nil {
t.Fatal(err)
}
return ntpTime, nil
})
result, err := catchStdout(main)
if err != nil {
t.Fatal(err)
}
expected := `current time: 1945-05-09 10:03:00 +0000 UTC
exact time: 1945-05-09 10:03:02 +0000 UTC
`
if string(result) != expected {
t.Fatalf("invalid output:\n%s, expected:\n%s", result, expected)
}
})
}
func catchStdout(runnable func()) (result []byte, err error) {
realOut := os.Stdout
defer func() { os.Stdout = realOut }()
r, fakeOut, err := os.Pipe()
if err != nil {
return
}
os.Stdout = fakeOut
runnable()
if err = fakeOut.Close(); err != nil {
return
}
result, err = ioutil.ReadAll(r)
if err != nil {
return
}
err = r.Close()
return
}

View File

@ -0,0 +1,39 @@
## Домашнее задание №2 «Распаковка строки»
Необходимо написать Go функцию, осуществляющую примитивную распаковку строки,
содержащую повторяющиеся символы/руны, например:
* "a4bc2d5e" => "aaaabccddddde"
* "abcd" => "abcd"
* "3abc" => "" (некорректная строка)
* "45" => "" (некорректная строка)
* "aaa10b" => "" (некорректная строка)
* "" => ""
* "d\n5abc" => "d\n\n\n\n\nabc"
Как видно из примеров, разрешено использование цифр, но не чисел.
В случае, если была передана некорректная строка, функция должна возвращать ошибку.
При необходимости можно выделять дополнительные функции / ошибки.
**(*) Дополнительное задание: поддержка экранирования через `\`:**
* "qwe\4\5" => "qwe45"
* "qwe\45" => "qwe44444"
* "qwe\\\5a" => "qwe\\\\\\\\\\a"
* "qw\\\ne" => "" (некорректная строка)
Как видно из примера, заэкранировать можно только цифру или слэш.
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены новые юнит-тесты - до 4 баллов
- Понятность и чистота кода - до 2 баллов
- Дополнительное задание на баллы не влияет
#### Зачёт от 7 баллов
### Подсказки
- https://golang.org/ref/spec#String_literals
- `unicode.IsDigit`
- `strings.Builder`
- `strings.Repeat`
- `strconv.Atoi`

View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw02_unpack_string
go 1.14
require github.com/stretchr/testify v1.5.0

10
hw02_unpack_string/go.sum Normal file
View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw=
github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,12 @@
package hw02_unpack_string //nolint:golint,stylecheck
import (
"errors"
)
var ErrInvalidString = errors.New("invalid string")
func Unpack(_ string) (string, error) {
// Place your code here
return "", nil
}

View File

@ -0,0 +1,76 @@
package hw02_unpack_string //nolint:golint,stylecheck
import (
"testing"
"github.com/stretchr/testify/require"
)
type test struct {
input string
expected string
err error
}
func TestUnpack(t *testing.T) {
for _, tst := range [...]test{
{
input: "a4bc2d5e",
expected: "aaaabccddddde",
},
{
input: "abccd",
expected: "abccd",
},
{
input: "3abc",
expected: "",
err: ErrInvalidString,
},
{
input: "45",
expected: "",
err: ErrInvalidString,
},
{
input: "aaa10b",
expected: "",
err: ErrInvalidString,
},
{
input: "",
expected: "",
},
} {
result, err := Unpack(tst.input)
require.Equal(t, tst.err, err)
require.Equal(t, tst.expected, result)
}
}
func TestUnpackWithEscape(t *testing.T) {
t.Skip() // Remove if task with asterisk completed
for _, tst := range [...]test{
{
input: `qwe\4\5`,
expected: `qwe45`,
},
{
input: `qwe\45`,
expected: `qwe44444`,
},
{
input: `qwe\\5`,
expected: `qwe\\\\\`,
},
{
input: `qwe\\\3`,
expected: `qwe\3`,
},
} {
result, err := Unpack(tst.input)
require.Equal(t, tst.err, err)
require.Equal(t, tst.expected, result)
}
}

View File

@ -0,0 +1,37 @@
## Домашнее задание №3 «Частотный анализ»
Необходимо написать Go функцию, принимающую на вход строку с текстом и
возвращающую слайс с 10-ю наиболее часто встречаемыми в тексте словами.
* Если есть более 10 самых частотых слов (например 15 разных слов встречаются ровно 133 раза,
остальные < 100), можно вернуть любые 10 из самых частотных.
* Словоформы не учитываем. "нога", "ногу", "ноги" - это разные слова.
* Слово с большой и маленькой буквы считать за разные слова.
"Нога" и "нога" - это разные слова.
* Знаки препинания считать "буквами" слова или отдельными словами.
"-" (тире) - это отдельное слово. "нога," и "нога" - это разные слова.
Пример: "cat and dog, one dog,two cats and one man".
"and", "one", "dog," - встречаются 2, 2 и 1 раз, это топ-3.
При необходимости можно выделять дополнительные функции / ошибки.
**(*) Дополнительное задание: учитывать большие/маленькие буквы и знаки препинания:**
* "Нога" и "нога" - это одинаковые слова, "нога!", "нога" и " 'нога' " - это одинаковые слова;
* "какой-то" и "какойто" - это разные слова, "—" (тире) - это не слово.
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены новые юнит-тесты - до 4 баллов
- Понятность и чистота кода - до 2 баллов
- Дополнительное задание на баллы не влияет
#### Зачёт от 7 баллов
### Подсказки
- `regexp.MustCompile`
- `strings.Split`
- `sort.Slice`

View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw03_frequency_analysis
go 1.14
require github.com/stretchr/testify v1.5.0

View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw=
github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,6 @@
package hw03_frequency_analysis //nolint:golint,stylecheck
func Top10(_ string) []string {
// Place your code here
return nil
}

View File

@ -0,0 +1,60 @@
package hw03_frequency_analysis //nolint:golint
import (
"testing"
"github.com/stretchr/testify/assert"
)
// Change to true if needed
var taskWithAsteriskIsCompleted = false
var text = `Как видите, он спускается по лестнице вслед за своим
другом Кристофером Робином, головой вниз, пересчитывая
ступеньки собственным затылком: бум-бум-бум. Другого способа
сходить с лестницы он пока не знает. Иногда ему, правда,
кажется, что можно бы найти какой-то другой способ, если бы он
только мог на минутку перестать бумкать и как следует
сосредоточиться. Но увы - сосредоточиться-то ему и некогда.
Как бы то ни было, вот он уже спустился и готов с вами
познакомиться.
- Винни-Пух. Очень приятно!
Вас, вероятно, удивляет, почему его так странно зовут, а
если вы знаете английский, то вы удивитесь еще больше.
Это необыкновенное имя подарил ему Кристофер Робин. Надо
вам сказать, что когда-то Кристофер Робин был знаком с одним
лебедем на пруду, которого он звал Пухом. Для лебедя это было
очень подходящее имя, потому что если ты зовешь лебедя
громко: "Пу-ух! Пу-ух!"- а он не откликается, то ты всегда
можешь сделать вид, что ты просто понарошку стрелял; а если ты
звал его тихо, то все подумают, что ты просто подул себе на
нос. Лебедь потом куда-то делся, а имя осталось, и Кристофер
Робин решил отдать его своему медвежонку, чтобы оно не пропало
зря.
А Винни - так звали самую лучшую, самую добрую медведицу
в зоологическом саду, которую очень-очень любил Кристофер
Робин. А она очень-очень любила его. Ее ли назвали Винни в
честь Пуха, или Пуха назвали в ее честь - теперь уже никто не
знает, даже папа Кристофера Робина. Когда-то он знал, а теперь
забыл.
Словом, теперь мишку зовут Винни-Пух, и вы знаете почему.
Иногда Винни-Пух любит вечерком во что-нибудь поиграть, а
иногда, особенно когда папа дома, он больше любит тихонько
посидеть у огня и послушать какую-нибудь интересную сказку.
В этот вечер...`
func TestTop10(t *testing.T) {
t.Run("no words in empty string", func(t *testing.T) {
assert.Len(t, Top10(""), 0)
})
t.Run("positive test", func(t *testing.T) {
if taskWithAsteriskIsCompleted {
expected := []string{"он", "а", "и", "что", "ты", "не", "если", "то", "его", "кристофер", "робин", "в"}
assert.Subset(t, expected, Top10(text))
} else {
expected := []string{"он", "и", "а", "что", "ты", "не", "если", "-", "то", "Кристофер"}
assert.ElementsMatch(t, expected, Top10(text))
}
})
}

72
hw04_lru_cache/README.md Normal file
View File

@ -0,0 +1,72 @@
## Домашнее задание №4 «LRU-кэш»
Необходимо реализовать LRU-кэш на основе двусвязного списка.
Задание состоит из двух частей, которые необходимо выполнять последовательно.
### 1) Реализация двусвязного списка
Список имеет структуру вида
```text
nil <- (next) front <-> ... <-> elem <-> ... <-> back (prev) -> nil
```
Необходимо реализовать следующий интерфейс List:
- Len() int // длина списка
- Front() *Item // первый Item
- Back() *Item // последний Item
- PushFront(v interface{}) *Item // добавить значение в начало
- PushBack(v interface{}) *Item // добавить значение в конец
- Remove(i *Item) // удалить элемент
- MoveToFront(i *Item) // переместить элемент в начало
**Гарантируется, что методы Remove и MoveToFront вызываются от существующих в списке элементов.**
Элемент списка Item:
- Value interface{} // значение
- Next *Item // следующий элемент
- Prev *Item // предыдущий элемент
Сложность всех операций должна быть O(1),
т.е. не должно быть мест, где осуществляется полный обход списка.
### 2) Реализация кэша на основе ранее написанного списка
Необходимо реализовать следующий интерфейс Cache:
- Set(key string, value interface{}) bool // Добавить значение в кэш по ключу
- Get(key string) (interface{}, bool) // Получить значение из кэша по ключу
- Clear() // Очистить кэш
Структура кэша:
- ёмкость (количество сохраняемых в кэше элементов)
- очередь \[последних используемых элементов\] на основе двусвязного списка
- словарь, отображающий ключ (строка) на элемент очереди
Элемент кэша должен хранить в себе ключ, по которому он лежит в словаре, и само значение.
Для чего это нужно понятно из алгоритма работы кэша (см. ниже).
Алгоритм работы кэша:
- при добавлении элемента:
- если элемент присутствует в словаре, то обновить его значение и переместить элемент в начало очереди;
- если элемента нет в словаре, то добавить в словарь и в начало очереди
(при этом, если размер очереди больше ёмкости кэша,
то необходимо удалить последний элемент из очереди и его значение из словаря);
- возвращаемое значение - флаг, присутствовал ли элемент в кэше.
- при получении элемента:
- если элемент присутствует в словаре, то переместить элемент в начало очереди и вернуть его значение и true;
- если элемента нет в словаре, то вернуть nil и false
(работа с кешом похожа на работу с `map`)
**(*) Дополнительное задание: сделать кэш горутино-безопасным.**
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены новые юнит-тесты для списка - 1 балл
- Добавлены новые юнит-тесты для кэша (включая тест на логику
выталкивания из кэша редко используемых элементов) - до 3 баллов
- Понятность и чистота кода - до 2 баллов
- Дополнительное задание на баллы не влияет
#### Зачёт от 7 баллов
### Подсказки
- https://en.wikipedia.org/wiki/Doubly_linked_list
- https://ru.bmstu.wiki/LRU_(Least_Recently_Used)
- `sync.Mutex`

22
hw04_lru_cache/cache.go Normal file
View File

@ -0,0 +1,22 @@
package hw04_lru_cache //nolint:golint,stylecheck
type Key string
type Cache interface {
// Place your code here
}
type lruCache struct {
// Place your code here:
// - capacity
// - queue
// - items
}
type cacheItem struct {
// Place your code here
}
func NewCache(capacity int) Cache {
return &lruCache{}
}

View File

@ -0,0 +1,79 @@
package hw04_lru_cache //nolint:golint,stylecheck
import (
"math/rand"
"strconv"
"sync"
"testing"
"github.com/stretchr/testify/require"
)
func TestCache(t *testing.T) {
t.Run("empty cache", func(t *testing.T) {
c := NewCache(10)
_, ok := c.Get("aaa")
require.False(t, ok)
_, ok = c.Get("bbb")
require.False(t, ok)
})
t.Run("simple", func(t *testing.T) {
c := NewCache(5)
wasInCache := c.Set("aaa", 100)
require.False(t, wasInCache)
wasInCache = c.Set("bbb", 200)
require.False(t, wasInCache)
val, ok := c.Get("aaa")
require.True(t, ok)
require.Equal(t, 100, val)
val, ok = c.Get("bbb")
require.True(t, ok)
require.Equal(t, 200, val)
wasInCache = c.Set("aaa", 300)
require.True(t, wasInCache)
val, ok = c.Get("aaa")
require.True(t, ok)
require.Equal(t, 300, val)
val, ok = c.Get("ccc")
require.False(t, ok)
require.Nil(t, val)
})
t.Run("purge logic", func(t *testing.T) {
// Write me
})
}
func TestCacheMultithreading(t *testing.T) {
t.Skip() // Remove if task with asterisk completed
c := NewCache(10)
wg := &sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 1_000_000; i++ {
c.Set(Key(strconv.Itoa(i)), i)
}
}()
go func() {
defer wg.Done()
for i := 0; i < 1_000_000; i++ {
c.Get(Key(strconv.Itoa(rand.Intn(1_000_000))))
}
}()
wg.Wait()
}

5
hw04_lru_cache/go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw04_lru_cache
go 1.14
require github.com/stretchr/testify v1.5.0

10
hw04_lru_cache/go.sum Normal file
View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw=
github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

17
hw04_lru_cache/list.go Normal file
View File

@ -0,0 +1,17 @@
package hw04_lru_cache //nolint:golint,stylecheck
type List interface {
// Place your code here
}
type listItem struct {
// Place your code here
}
type list struct {
// Place your code here
}
func NewList() List {
return &list{}
}

View File

@ -0,0 +1,51 @@
package hw04_lru_cache //nolint:golint,stylecheck
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestList(t *testing.T) {
t.Run("empty list", func(t *testing.T) {
l := NewList()
require.Equal(t, l.Len(), 0)
require.Nil(t, l.Front())
require.Nil(t, l.Back())
})
t.Run("complex", func(t *testing.T) {
l := NewList()
l.PushFront(10) // [10]
l.PushBack(20) // [10, 20]
l.PushBack(30) // [10, 20, 30]
require.Equal(t, l.Len(), 3)
middle := l.Back().Next // 20
l.Remove(middle) // [10, 30]
require.Equal(t, l.Len(), 2)
for i, v := range [...]int{40, 50, 60, 70, 80} {
if i%2 == 0 {
l.PushFront(v)
} else {
l.PushBack(v)
}
} // [80, 60, 40, 10, 30, 50, 70]
require.Equal(t, l.Len(), 7)
require.Equal(t, 80, l.Front().Value)
require.Equal(t, 70, l.Back().Value)
l.MoveToFront(l.Front()) // [80, 60, 40, 10, 30, 50, 70]
l.MoveToFront(l.Back()) // [70, 80, 60, 40, 10, 30, 50]
elems := make([]int, 0, l.Len())
for i := l.Back(); i != nil; i = i.Next {
elems = append(elems, i.Value.(int))
}
require.Equal(t, []int{50, 30, 10, 40, 60, 80, 70}, elems)
})
}

View File

@ -0,0 +1,39 @@
## Домашнее задание №5 «Параллельное исполнение»
Необходимо написать функцию для параллельного выполнения заданий в N параллельных горутинах:
* функция должна останавливать свою работу, если произошло M ошибок;
* после завершения работы функции (успешного или из-за превышения M) не должно оставаться работающих горутин;
* если задачи работают без ошибок, то выполнятся `len(tasks)` задач (т.е. все задачи);
* если в первых M задачах происходят ошибки, то всего выполнится не более N+M задач.
Нужно учесть, что задания могут выполняться разное время, а длина списка задач
`len(tasks)` может быть больше или меньше N.
Значение M <= 0 трактуется на усмотрение программиста:
- или это знак игнорировать ошибки в принципе;
- или считать это как "максимум 0 ошибок", значит функция всегда будет возвращать
`ErrErrorsLimitExceeded`;
- на эту логику следует написать юнит-тест.
При необходимости можно выделять дополнительные функции / ошибки.
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены новые юнит-тесты - до 4 баллов
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов
### Подсказки
- https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem
- `sync.WaitGroup`
- `go test -v -race -count=100 .`
### Частые ошибки
1) `racedetector` ругается на строчку с ассертом в тестах:
- простой случай: после выхода из `Run` остаются висячие горутины, отсюда и получаем `data race` -
ассерт в тестах неатомарно обращается к `runTasksCount`, в то время как зомби-горутины атомарно пытаюся её поменять.
- случай посложнее: один тест завершается успешно, но висячие горутины, им порожденные, аффектят ассерты в
последующих тестах.
**Решение**: внимательно посмотреть места выхода из функции и гарантировать, что все порождённые вами горутины
завершились к этому моменту.

View File

@ -0,0 +1,10 @@
module github.com/fixme_my_friend/hw05_parallel_execution
go 1.14
require (
github.com/stretchr/testify v1.5.1
go.uber.org/goleak v1.0.0
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b // indirect
)

View File

@ -0,0 +1,45 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b h1:zSzQJAznWxAh9fZxiPy2FZo+ZZEYoYFYYDYdOrU7AaM=
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,15 @@
package hw05_parallel_execution //nolint:golint,stylecheck
import (
"errors"
)
var ErrErrorsLimitExceeded = errors.New("errors limit exceeded")
type Task func() error
// Run starts tasks in N goroutines and stops its work when receiving M errors from tasks
func Run(tasks []Task, N int, M int) error {
// Place your code here
return nil
}

View File

@ -0,0 +1,69 @@
package hw05_parallel_execution //nolint:golint,stylecheck
import (
"fmt"
"math/rand"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
)
func TestRun(t *testing.T) {
defer goleak.VerifyNone(t)
t.Run("if were errors in first M tasks, than finished not more N+M tasks", func(t *testing.T) {
tasksCount := 50
tasks := make([]Task, 0, tasksCount)
var runTasksCount int32
for i := 0; i < tasksCount; i++ {
err := fmt.Errorf("error from task %d", i)
tasks = append(tasks, func() error {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
atomic.AddInt32(&runTasksCount, 1)
return err
})
}
workersCount := 10
maxErrorsCount := 23
result := Run(tasks, workersCount, maxErrorsCount)
require.Equal(t, ErrErrorsLimitExceeded, result)
require.LessOrEqual(t, runTasksCount, int32(workersCount+maxErrorsCount), "extra tasks were started")
})
t.Run("tasks without errors", func(t *testing.T) {
tasksCount := 50
tasks := make([]Task, 0, tasksCount)
var runTasksCount int32
var sumTime time.Duration
for i := 0; i < tasksCount; i++ {
taskSleep := time.Millisecond * time.Duration(rand.Intn(100))
sumTime += taskSleep
tasks = append(tasks, func() error {
time.Sleep(taskSleep)
atomic.AddInt32(&runTasksCount, 1)
return nil
})
}
workersCount := 5
maxErrorsCount := 1
start := time.Now()
result := Run(tasks, workersCount, maxErrorsCount)
elapsedTime := time.Since(start)
require.Nil(t, result)
require.Equal(t, runTasksCount, int32(tasksCount), "not all tasks were completed")
require.LessOrEqual(t, int64(elapsedTime), int64(sumTime/2), "tasks were run sequentially?")
})
}

View File

@ -0,0 +1,38 @@
## Домашнее задание №6 «Пайплайн»
Необходимо реализовать функцию для запуска конкуррентного пайплайна, состоящего из стейджей.
Стейдж - функция, принимающая канал на чтение и отдающая канал на чтение,
а внутри в горутине выполняющая полезную работу:
```golang
func Stage(in <-chan interface{}) (out <-chan interface{}) {
out = make(<-chan interface{})
go func() { /* Some work */ }()
return out
}
```
Особенность пайплайна в том, что обработка последующего элемента входных данных должна
происходить **без ожидания завершения всего пайплайна** для текущего элемента.
Т.е. пайплан из 4 функций по 100 мс каждая для 5 входных элементов **должен выполняться
гораздо быстрее**, чем за 2 секунды (4 * 100 мс * 5).
Также **должна быть реализована возможность остановить пайплайн** через
дополнительный сигнальный канал (`done`/`terminate`/etc.).
При необходимости можно выделять дополнительные функции.
**Нельзя менять сигнатуры исходных функций.**
Для большего понимания см. тесты.
### Критерии оценки
- CI-пайплайн зелёный - 5 баллов
- Добавлены новые юнит-тесты - до 2 баллов
- Понятность и чистота кода - до 3 баллов
#### Зачёт от 7 баллов
### Подсказки
- https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
- `go test -v -race -count=100 .`

View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw06_pipeline_execution
go 1.14
require github.com/stretchr/testify v1.5.1

View File

@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,15 @@
package hw06_pipeline_execution //nolint:golint,stylecheck
type (
I = interface{}
In = <-chan I
Out = In
Bi = chan I
)
type Stage func(in In) (out Out)
func ExecutePipeline(in In, done In, stages ...Stage) Out {
// Place your code here
return nil
}

View File

@ -0,0 +1,93 @@
package hw06_pipeline_execution //nolint:golint,stylecheck
import (
"strconv"
"testing"
"time"
"github.com/stretchr/testify/require"
)
const (
sleepPerStage = time.Millisecond * 100
fault = sleepPerStage / 2
)
func TestPipeline(t *testing.T) {
// Stage generator
g := func(name string, f func(v I) I) Stage {
return func(in In) Out {
out := make(Bi)
go func() {
defer close(out)
for v := range in {
time.Sleep(sleepPerStage)
out <- f(v)
}
}()
return out
}
}
stages := []Stage{
g("Dummy", func(v I) I { return v }),
g("Multiplier (* 2)", func(v I) I { return v.(int) * 2 }),
g("Adder (+ 100)", func(v I) I { return v.(int) + 100 }),
g("Stringifier", func(v I) I { return strconv.Itoa(v.(int)) }),
}
t.Run("simple case", func(t *testing.T) {
in := make(Bi)
data := []int{1, 2, 3, 4, 5}
go func() {
for _, v := range data {
in <- v
}
close(in)
}()
result := make([]string, 0, 10)
start := time.Now()
for s := range ExecutePipeline(in, nil, stages...) {
result = append(result, s.(string))
}
elapsed := time.Since(start)
require.Equal(t, result, []string{"102", "104", "106", "108", "110"})
require.Less(t,
int64(elapsed),
// ~0.8s for processing 5 values in 4 stages (100ms every) concurrently
int64(sleepPerStage)*int64(len(stages)+len(data)-1)+int64(fault))
})
t.Run("done case", func(t *testing.T) {
in := make(Bi)
done := make(Bi)
data := []int{1, 2, 3, 4, 5}
// Abort after 200ms
abortDur := sleepPerStage * 2
go func() {
<-time.After(abortDur)
close(done)
}()
go func() {
for _, v := range data {
in <- v
}
close(in)
}()
result := make([]string, 0, 10)
start := time.Now()
for s := range ExecutePipeline(in, done, stages...) {
result = append(result, s.(string))
}
elapsed := time.Since(start)
require.Len(t, result, 0)
require.Less(t, int64(elapsed), int64(abortDur)+int64(fault))
})
}

View File

@ -0,0 +1,37 @@
## Домашнее задание №7 «Утилита для копирования файлов»
Необходимо реализовать утилиту копирования файлов (упрощенный аналог `dd`).
Тулза должна принимать следующие аргументы:
* путь к исходному файлу (`-from`);
* путь к копии (`-to`);
* отступ в источнике (`-offset`), по умолчанию - 0;
* количество копируемых байт (`-limit`), по умолчанию - 0 (весь файл из `-from`).
Особенности:
* offset больше, чем размер файла - невалидная ситуация;
* limit больше, чем размер файла - валидная ситуация, копируется исходный файл до его EOF;
* программа может НЕ обрабатывать файлы, у которых неизвестна длина (например, /dev/urandom);
Также необходимо выводить в консоль прогресс копирования в процентах (%),
допускается использовать для этого стороннюю библиотеку.
Юнит-тесты могут использовать файлы из `testdata` (разрешено добавить свои, но запрещено удалять имеющиеся)
и должны чистить за собой создаваемые файлы (или работать в `/tmp`).
При необходимости можно выделять дополнительные функции / ошибки.
**(*) Дополнительное задание: реализовать прогресс-бар самостоятельно.**
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены юнит-тесты - до 4 баллов
- Понятность и чистота кода - до 2 баллов
- Дополнительное задание на баллы не влияет
#### Зачёт от 7 баллов
### Подсказки
- `github.com/cheggaaa/pb`
- `os.OpenFile`, `os.Create`, `os.FileMode`
- `io.CopyN`
- `ioutil.TempFile`

15
hw07_file_copying/copy.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
"errors"
)
var (
ErrUnsupportedFile = errors.New("unsupported file")
ErrOffsetExceedsFileSize = errors.New("offset exceeds file size")
)
func Copy(fromPath string, toPath string, offset, limit int64) error {
// Place your code here
return nil
}

View File

@ -0,0 +1,7 @@
package main
import "testing"
func TestCopy(t *testing.T) {
// Place your code here
}

3
hw07_file_copying/go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/fixme_my_friend/hw07_file_copying
go 1.14

0
hw07_file_copying/go.sum Normal file
View File

22
hw07_file_copying/main.go Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"flag"
)
var (
from, to string
limit, offset int64
)
func init() {
flag.StringVar(&from, "from", "", "file to read from")
flag.StringVar(&to, "to", "", "file to write to")
flag.Int64Var(&limit, "limit", 0, "limit of bytes to copy")
flag.Int64Var(&offset, "offset", 0, "offset in input file")
}
func main() {
flag.Parse()
// Place your code here
}

25
hw07_file_copying/test.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -xeuo pipefail
go build -o go-cp
./go-cp -from testdata/input.txt -to out.txt
cmp out.txt testdata/out_offset0_limit0.txt
./go-cp -from testdata/input.txt -to out.txt -limit 10
cmp out.txt testdata/out_offset0_limit10.txt
./go-cp -from testdata/input.txt -to out.txt -limit 1000
cmp out.txt testdata/out_offset0_limit1000.txt
./go-cp -from testdata/input.txt -to out.txt -limit 10000
cmp out.txt testdata/out_offset0_limit10000.txt
./go-cp -from testdata/input.txt -to out.txt -offset 100 -limit 1000
cmp out.txt testdata/out_offset100_limit1000.txt
./go-cp -from testdata/input.txt -to out.txt -offset 6000 -limit 1000
cmp out.txt testdata/out_offset6000_limit1000.txt
rm -f go-cp out.txt
echo "PASS"

125
hw07_file_copying/testdata/input.txt vendored Normal file
View File

@ -0,0 +1,125 @@
Go
Documents
Packages
The Project
Help
Blog
Play
Search
Getting Started
Install the Go tools
Test your installation
Installing extra Go versions
Uninstalling Go
Getting help
Download the Go distribution
Download Go
Click here to visit the downloads page
Official binary distributions are available for the FreeBSD (release 10-STABLE and above), Linux, macOS (10.10 and above), and Windows operating systems and the 32-bit (386) and 64-bit (amd64) x86 processor architectures.
If a binary distribution is not available for your combination of operating system and architecture, try installing from source or installing gccgo instead of gc.
System requirements
Go binary distributions are available for these supported operating systems and architectures. Please ensure your system meets these requirements before proceeding. If your OS or architecture is not on the list, you may be able to install from source or use gccgo instead.
Operating system Architectures Notes
FreeBSD 10.3 or later amd64, 386 Debian GNU/kFreeBSD not supported
Linux 2.6.23 or later with glibc amd64, 386, arm, arm64,
s390x, ppc64le CentOS/RHEL 5.x not supported.
Install from source for other libc.
macOS 10.10 or later amd64 use the clang or gcc† that comes with Xcode‡ for cgo support
Windows 7, Server 2008R2 or later amd64, 386 use MinGW (386) or MinGW-W64 (amd64) gcc†.
No need for cygwin or msys.
†A C compiler is required only if you plan to use cgo.
‡You only need to install the command line tools for Xcode. If you have already installed Xcode 4.3+, you can install it from the Components tab of the Downloads preferences panel.
Install the Go tools
If you are upgrading from an older version of Go you must first remove the existing version.
Linux, macOS, and FreeBSD tarballs
Download the archive and extract it into /usr/local, creating a Go tree in /usr/local/go. For example:
tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
Choose the archive file appropriate for your installation. For instance, if you are installing Go version 1.2.1 for 64-bit x86 on Linux, the archive you want is called go1.2.1.linux-amd64.tar.gz.
(Typically these commands must be run as root or through sudo.)
Add /usr/local/go/bin to the PATH environment variable. You can do this by adding this line to your /etc/profile (for a system-wide installation) or $HOME/.profile:
export PATH=$PATH:/usr/local/go/bin
Note: changes made to a profile file may not apply until the next time you log into your computer. To apply the changes immediately, just run the shell commands directly or execute them from the profile using a command such as source $HOME/.profile.
macOS package installer
Download the package file, open it, and follow the prompts to install the Go tools. The package installs the Go distribution to /usr/local/go.
The package should put the /usr/local/go/bin directory in your PATH environment variable. You may need to restart any open Terminal sessions for the change to take effect.
Windows
The Go project provides two installation options for Windows users (besides installing from source): a zip archive that requires you to set some environment variables and an MSI installer that configures your installation automatically.
MSI installer
Open the MSI file and follow the prompts to install the Go tools. By default, the installer puts the Go distribution in c:\Go.
The installer should put the c:\Go\bin directory in your PATH environment variable. You may need to restart any open command prompts for the change to take effect.
Zip archive
Download the zip file and extract it into the directory of your choice (we suggest c:\Go).
Add the bin subdirectory of your Go root (for example, c:\Go\bin) to your PATH environment variable.
Setting environment variables under Windows
Under Windows, you may set environment variables through the "Environment Variables" button on the "Advanced" tab of the "System" control panel. Some versions of Windows provide this control panel through the "Advanced System Settings" option inside the "System" control panel.
Test your installation
Check that Go is installed correctly by setting up a workspace and building a simple program, as follows.
Create your workspace directory, $HOME/go. (If you'd like to use a different directory, you will need to set the GOPATH environment variable.)
Next, make the directory src/hello inside your workspace, and in that directory create a file named hello.go that looks like:
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
Then build it with the go tool:
$ cd $HOME/go/src/hello
$ go build
The command above will build an executable named hello in the directory alongside your source code. Execute it to see the greeting:
$ ./hello
hello, world
If you see the "hello, world" message then your Go installation is working.
You can run go install to install the binary into your workspace's bin directory or go clean -i to remove it.
Before rushing off to write Go code please read the How to Write Go Code document, which describes some essential concepts about using the Go tools.
Installing extra Go versions
It may be useful to have multiple Go versions installed on the same machine, for example, to ensure that a package's tests pass on multiple Go versions. Once you have one Go version installed, you can install another (such as 1.10.7) as follows:
$ go get golang.org/dl/go1.10.7
$ go1.10.7 download
The newly downloaded version can be used like go:
$ go1.10.7 version
go version go1.10.7 linux/amd64
All Go versions available via this method are listed on the download page. You can find where each of these extra Go versions is installed by looking at its GOROOT; for example, go1.10.7 env GOROOT. To uninstall a downloaded version, just remove its GOROOT directory and the goX.Y.Z binary.
Uninstalling Go
To remove an existing Go installation from your system delete the go directory. This is usually /usr/local/go under Linux, macOS, and FreeBSD or c:\Go under Windows.
You should also remove the Go bin directory from your PATH environment variable. Under Linux and FreeBSD you should edit /etc/profile or $HOME/.profile. If you installed Go with the macOS package then you should remove the /etc/paths.d/go file. Windows users should read the section about setting environment variables under Windows.
Getting help
For help, see the list of Go mailing lists, forums, and places to chat.
Report bugs either by running “go bug”, or manually at the Go issue tracker.
The Go Gopher
Copyright Terms of Service Privacy Policy Report a website issue
Supported by Google

View File

@ -0,0 +1,125 @@
Go
Documents
Packages
The Project
Help
Blog
Play
Search
Getting Started
Install the Go tools
Test your installation
Installing extra Go versions
Uninstalling Go
Getting help
Download the Go distribution
Download Go
Click here to visit the downloads page
Official binary distributions are available for the FreeBSD (release 10-STABLE and above), Linux, macOS (10.10 and above), and Windows operating systems and the 32-bit (386) and 64-bit (amd64) x86 processor architectures.
If a binary distribution is not available for your combination of operating system and architecture, try installing from source or installing gccgo instead of gc.
System requirements
Go binary distributions are available for these supported operating systems and architectures. Please ensure your system meets these requirements before proceeding. If your OS or architecture is not on the list, you may be able to install from source or use gccgo instead.
Operating system Architectures Notes
FreeBSD 10.3 or later amd64, 386 Debian GNU/kFreeBSD not supported
Linux 2.6.23 or later with glibc amd64, 386, arm, arm64,
s390x, ppc64le CentOS/RHEL 5.x not supported.
Install from source for other libc.
macOS 10.10 or later amd64 use the clang or gcc† that comes with Xcode‡ for cgo support
Windows 7, Server 2008R2 or later amd64, 386 use MinGW (386) or MinGW-W64 (amd64) gcc†.
No need for cygwin or msys.
†A C compiler is required only if you plan to use cgo.
‡You only need to install the command line tools for Xcode. If you have already installed Xcode 4.3+, you can install it from the Components tab of the Downloads preferences panel.
Install the Go tools
If you are upgrading from an older version of Go you must first remove the existing version.
Linux, macOS, and FreeBSD tarballs
Download the archive and extract it into /usr/local, creating a Go tree in /usr/local/go. For example:
tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
Choose the archive file appropriate for your installation. For instance, if you are installing Go version 1.2.1 for 64-bit x86 on Linux, the archive you want is called go1.2.1.linux-amd64.tar.gz.
(Typically these commands must be run as root or through sudo.)
Add /usr/local/go/bin to the PATH environment variable. You can do this by adding this line to your /etc/profile (for a system-wide installation) or $HOME/.profile:
export PATH=$PATH:/usr/local/go/bin
Note: changes made to a profile file may not apply until the next time you log into your computer. To apply the changes immediately, just run the shell commands directly or execute them from the profile using a command such as source $HOME/.profile.
macOS package installer
Download the package file, open it, and follow the prompts to install the Go tools. The package installs the Go distribution to /usr/local/go.
The package should put the /usr/local/go/bin directory in your PATH environment variable. You may need to restart any open Terminal sessions for the change to take effect.
Windows
The Go project provides two installation options for Windows users (besides installing from source): a zip archive that requires you to set some environment variables and an MSI installer that configures your installation automatically.
MSI installer
Open the MSI file and follow the prompts to install the Go tools. By default, the installer puts the Go distribution in c:\Go.
The installer should put the c:\Go\bin directory in your PATH environment variable. You may need to restart any open command prompts for the change to take effect.
Zip archive
Download the zip file and extract it into the directory of your choice (we suggest c:\Go).
Add the bin subdirectory of your Go root (for example, c:\Go\bin) to your PATH environment variable.
Setting environment variables under Windows
Under Windows, you may set environment variables through the "Environment Variables" button on the "Advanced" tab of the "System" control panel. Some versions of Windows provide this control panel through the "Advanced System Settings" option inside the "System" control panel.
Test your installation
Check that Go is installed correctly by setting up a workspace and building a simple program, as follows.
Create your workspace directory, $HOME/go. (If you'd like to use a different directory, you will need to set the GOPATH environment variable.)
Next, make the directory src/hello inside your workspace, and in that directory create a file named hello.go that looks like:
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
Then build it with the go tool:
$ cd $HOME/go/src/hello
$ go build
The command above will build an executable named hello in the directory alongside your source code. Execute it to see the greeting:
$ ./hello
hello, world
If you see the "hello, world" message then your Go installation is working.
You can run go install to install the binary into your workspace's bin directory or go clean -i to remove it.
Before rushing off to write Go code please read the How to Write Go Code document, which describes some essential concepts about using the Go tools.
Installing extra Go versions
It may be useful to have multiple Go versions installed on the same machine, for example, to ensure that a package's tests pass on multiple Go versions. Once you have one Go version installed, you can install another (such as 1.10.7) as follows:
$ go get golang.org/dl/go1.10.7
$ go1.10.7 download
The newly downloaded version can be used like go:
$ go1.10.7 version
go version go1.10.7 linux/amd64
All Go versions available via this method are listed on the download page. You can find where each of these extra Go versions is installed by looking at its GOROOT; for example, go1.10.7 env GOROOT. To uninstall a downloaded version, just remove its GOROOT directory and the goX.Y.Z binary.
Uninstalling Go
To remove an existing Go installation from your system delete the go directory. This is usually /usr/local/go under Linux, macOS, and FreeBSD or c:\Go under Windows.
You should also remove the Go bin directory from your PATH environment variable. Under Linux and FreeBSD you should edit /etc/profile or $HOME/.profile. If you installed Go with the macOS package then you should remove the /etc/paths.d/go file. Windows users should read the section about setting environment variables under Windows.
Getting help
For help, see the list of Go mailing lists, forums, and places to chat.
Report bugs either by running “go bug”, or manually at the Go issue tracker.
The Go Gopher
Copyright Terms of Service Privacy Policy Report a website issue
Supported by Google

View File

@ -0,0 +1,2 @@
Go
Documen

View File

@ -0,0 +1,27 @@
Go
Documents
Packages
The Project
Help
Blog
Play
Search
Getting Started
Install the Go tools
Test your installation
Installing extra Go versions
Uninstalling Go
Getting help
Download the Go distribution
Download Go
Click here to visit the downloads page
Official binary distributions are available for the FreeBSD (release 10-STABLE and above), Linux, macOS (10.10 and above), and Windows operating systems and the 32-bit (386) and 64-bit (amd64) x86 processor architectures.
If a binary distribution is not available for your combination of operating system and architecture, try installing from source or installing gccgo instead of gc.
System requirements
Go binary distributions are available for these supported operating systems and architectures. Please ensure your system meets these requirements before proceeding. If your OS or architecture is not on the list, you may be able to install from source or use gccgo instead.
Operating system Architectures Notes
FreeBSD 10.3 or later amd64

View File

@ -0,0 +1,125 @@
Go
Documents
Packages
The Project
Help
Blog
Play
Search
Getting Started
Install the Go tools
Test your installation
Installing extra Go versions
Uninstalling Go
Getting help
Download the Go distribution
Download Go
Click here to visit the downloads page
Official binary distributions are available for the FreeBSD (release 10-STABLE and above), Linux, macOS (10.10 and above), and Windows operating systems and the 32-bit (386) and 64-bit (amd64) x86 processor architectures.
If a binary distribution is not available for your combination of operating system and architecture, try installing from source or installing gccgo instead of gc.
System requirements
Go binary distributions are available for these supported operating systems and architectures. Please ensure your system meets these requirements before proceeding. If your OS or architecture is not on the list, you may be able to install from source or use gccgo instead.
Operating system Architectures Notes
FreeBSD 10.3 or later amd64, 386 Debian GNU/kFreeBSD not supported
Linux 2.6.23 or later with glibc amd64, 386, arm, arm64,
s390x, ppc64le CentOS/RHEL 5.x not supported.
Install from source for other libc.
macOS 10.10 or later amd64 use the clang or gcc† that comes with Xcode‡ for cgo support
Windows 7, Server 2008R2 or later amd64, 386 use MinGW (386) or MinGW-W64 (amd64) gcc†.
No need for cygwin or msys.
†A C compiler is required only if you plan to use cgo.
‡You only need to install the command line tools for Xcode. If you have already installed Xcode 4.3+, you can install it from the Components tab of the Downloads preferences panel.
Install the Go tools
If you are upgrading from an older version of Go you must first remove the existing version.
Linux, macOS, and FreeBSD tarballs
Download the archive and extract it into /usr/local, creating a Go tree in /usr/local/go. For example:
tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz
Choose the archive file appropriate for your installation. For instance, if you are installing Go version 1.2.1 for 64-bit x86 on Linux, the archive you want is called go1.2.1.linux-amd64.tar.gz.
(Typically these commands must be run as root or through sudo.)
Add /usr/local/go/bin to the PATH environment variable. You can do this by adding this line to your /etc/profile (for a system-wide installation) or $HOME/.profile:
export PATH=$PATH:/usr/local/go/bin
Note: changes made to a profile file may not apply until the next time you log into your computer. To apply the changes immediately, just run the shell commands directly or execute them from the profile using a command such as source $HOME/.profile.
macOS package installer
Download the package file, open it, and follow the prompts to install the Go tools. The package installs the Go distribution to /usr/local/go.
The package should put the /usr/local/go/bin directory in your PATH environment variable. You may need to restart any open Terminal sessions for the change to take effect.
Windows
The Go project provides two installation options for Windows users (besides installing from source): a zip archive that requires you to set some environment variables and an MSI installer that configures your installation automatically.
MSI installer
Open the MSI file and follow the prompts to install the Go tools. By default, the installer puts the Go distribution in c:\Go.
The installer should put the c:\Go\bin directory in your PATH environment variable. You may need to restart any open command prompts for the change to take effect.
Zip archive
Download the zip file and extract it into the directory of your choice (we suggest c:\Go).
Add the bin subdirectory of your Go root (for example, c:\Go\bin) to your PATH environment variable.
Setting environment variables under Windows
Under Windows, you may set environment variables through the "Environment Variables" button on the "Advanced" tab of the "System" control panel. Some versions of Windows provide this control panel through the "Advanced System Settings" option inside the "System" control panel.
Test your installation
Check that Go is installed correctly by setting up a workspace and building a simple program, as follows.
Create your workspace directory, $HOME/go. (If you'd like to use a different directory, you will need to set the GOPATH environment variable.)
Next, make the directory src/hello inside your workspace, and in that directory create a file named hello.go that looks like:
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
Then build it with the go tool:
$ cd $HOME/go/src/hello
$ go build
The command above will build an executable named hello in the directory alongside your source code. Execute it to see the greeting:
$ ./hello
hello, world
If you see the "hello, world" message then your Go installation is working.
You can run go install to install the binary into your workspace's bin directory or go clean -i to remove it.
Before rushing off to write Go code please read the How to Write Go Code document, which describes some essential concepts about using the Go tools.
Installing extra Go versions
It may be useful to have multiple Go versions installed on the same machine, for example, to ensure that a package's tests pass on multiple Go versions. Once you have one Go version installed, you can install another (such as 1.10.7) as follows:
$ go get golang.org/dl/go1.10.7
$ go1.10.7 download
The newly downloaded version can be used like go:
$ go1.10.7 version
go version go1.10.7 linux/amd64
All Go versions available via this method are listed on the download page. You can find where each of these extra Go versions is installed by looking at its GOROOT; for example, go1.10.7 env GOROOT. To uninstall a downloaded version, just remove its GOROOT directory and the goX.Y.Z binary.
Uninstalling Go
To remove an existing Go installation from your system delete the go directory. This is usually /usr/local/go under Linux, macOS, and FreeBSD or c:\Go under Windows.
You should also remove the Go bin directory from your PATH environment variable. Under Linux and FreeBSD you should edit /etc/profile or $HOME/.profile. If you installed Go with the macOS package then you should remove the /etc/paths.d/go file. Windows users should read the section about setting environment variables under Windows.
Getting help
For help, see the list of Go mailing lists, forums, and places to chat.
Report bugs either by running “go bug”, or manually at the Go issue tracker.
The Go Gopher
Copyright Terms of Service Privacy Policy Report a website issue
Supported by Google

View File

@ -0,0 +1,18 @@
our installation
Installing extra Go versions
Uninstalling Go
Getting help
Download the Go distribution
Download Go
Click here to visit the downloads page
Official binary distributions are available for the FreeBSD (release 10-STABLE and above), Linux, macOS (10.10 and above), and Windows operating systems and the 32-bit (386) and 64-bit (amd64) x86 processor architectures.
If a binary distribution is not available for your combination of operating system and architecture, try installing from source or installing gccgo instead of gc.
System requirements
Go binary distributions are available for these supported operating systems and architectures. Please ensure your system meets these requirements before proceeding. If your OS or architecture is not on the list, you may be able to install from source or use gccgo instead.
Operating system Architectures Notes
FreeBSD 10.3 or later amd64, 386 Debian GNU/kFreeBSD not supported
Linux 2.6.23 or later with glibc amd64, 386, arm, arm64,
s39

View File

@ -0,0 +1,12 @@
nder Windows.
You should also remove the Go bin directory from your PATH environment variable. Under Linux and FreeBSD you should edit /etc/profile or $HOME/.profile. If you installed Go with the macOS package then you should remove the /etc/paths.d/go file. Windows users should read the section about setting environment variables under Windows.
Getting help
For help, see the list of Go mailing lists, forums, and places to chat.
Report bugs either by running “go bug”, or manually at the Go issue tracker.
The Go Gopher
Copyright Terms of Service Privacy Policy Report a website issue
Supported by Google

View File

@ -0,0 +1,48 @@
## Домашнее задание №8 «Утилита envdir»
Необходимо реализовать утилиту `envdir` на Go.
Эта утилита позволяет запускать программы, получая переменные окружения из определенной директории:
- если директория содержит файл с именем `S`, первой строкой которого является `T`, то
`envdir` удаляет переменную среды с именем `S`, если таковая существует, а затем добавляет
переменную среды с именем `S` и значением `T`;
- имя `S` не должно содержать `=`; пробелы и табуляция в конце `T` удаляются; терминальные нули (`0x00`) заменяются на перевод строки (`\n`);
- если файл полностью пустой (длина - 0 байт), то `envdir` удаляет переменную окружения с именем `S`.
---
Пример использования:
```bash
$ go-envdir /path/to/env/dir command arg1 arg2
```
Если в директории `/path/to/env/dir` содержатся файлы:
* `FOO` с содержимым `123`;
* `BAR` с содержимым `value`,
то вызов выше эквивалентен вызову
```bash
$ FOO=123 BAR=value command arg1 arg2
```
---
Также необходимо, чтобы:
* стандартные потоки ввода/вывода/ошибок пробрасывались в вызываемую программу;
* код выхода утилиты совпадал с кодом выхода программы.
При необходимости можно выделять дополнительные функции / ошибки.
Юнит-тесты могут использовать файлы из `testdata` или создавать свои директории / файлы,
которые **обязаны** подчищать после своего выполнения.
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены юнит-тесты - до 4 баллов
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов
### Подсказки
- https://www.unix.com/man-page/debian/8/envdir/
- `os.Args`
- `ioutil.ReadDir`
- `bytes.Replace`, `strings.TrimRight`
- `exec.Command`

View File

@ -0,0 +1,10 @@
package main
type Environment map[string]string
// ReadDir reads a specified directory and returns map of env variables.
// Variables represented as files where filename is name of variable, file first line is a value.
func ReadDir(dir string) (Environment, error) {
// Place your code here
return nil, nil
}

View File

@ -0,0 +1,7 @@
package main
import "testing"
func TestReadDir(t *testing.T) {
// Place your code here
}

View File

@ -0,0 +1,7 @@
package main
// RunCmd runs a command + arguments (cmd) with environment variables from env
func RunCmd(cmd []string, env Environment) (returnCode int) {
// Place your code here
return
}

View File

@ -0,0 +1,7 @@
package main
import "testing"
func TestRunCmd(t *testing.T) {
// Place your code here
}

3
hw08_envdir_tool/go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/fixme_my_friend/hw08_envdir_tool
go 1.14

5
hw08_envdir_tool/main.go Normal file
View File

@ -0,0 +1,5 @@
package main
func main() {
// Place your code here
}

21
hw08_envdir_tool/test.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -xeuo pipefail
go build -o go-envdir
export HELLO="SHOULD_REPLACE"
export FOO="SHOULD_REPLACE"
export UNSET="SHOULD_REMOVE"
result=$(./go-envdir "$(pwd)/testdata/env" "/bin/bash" "$(pwd)/testdata/echo.sh" arg1=1 arg2=2)
expected='HELLO is ("hello")
BAR is (bar)
FOO is ( foo
with new line)
UNSET is ()
arguments are arg1=1 arg2=2'
[ "${result}" = "${expected}" ] || (echo -e "invalid output: ${result}" && exit 1)
rm -f go-envdir
echo "PASS"

7
hw08_envdir_tool/testdata/echo.sh vendored Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
echo -e "HELLO is (${HELLO})
BAR is (${BAR})
FOO is (${FOO})
UNSET is (${UNSET})
arguments are $*"

2
hw08_envdir_tool/testdata/env/BAR vendored Normal file
View File

@ -0,0 +1,2 @@
bar
PLEASE IGNORE SECOND LINE

BIN
hw08_envdir_tool/testdata/env/FOO vendored Normal file

Binary file not shown.

1
hw08_envdir_tool/testdata/env/HELLO vendored Normal file
View File

@ -0,0 +1 @@
"hello"

0
hw08_envdir_tool/testdata/env/UNSET vendored Normal file
View File

View File

@ -0,0 +1,106 @@
## Домашнее задание №9 «Генератор валидаторов»
Необходимо реализовать тулзу для кодогенерации методов, валидирующих структуры.
Первым аргументом бинарь принимает путь к файлу, содержащему описание структур.
Пример вызова:
```bash
$ go-validate models/models.go
```
**models.go**:
```golang
package models
type User {
Name string
Age int `validate:"min:18|max:50"`
}
```
Программа видит, что в структуре `User` есть поле `Age`, среди тэгов которого есть
тэг **validate** и генерирует код валидации в файл **models_validation_generated.go**
(где **models** - имя исходного файла без расширения) в том же пакете, что и исходный файл.
**models_validation_generated.go**:
```golang
// Code generated by cool go-validate tool; DO NOT EDIT.
package models
func (u User) Validate() ([]ValidationError, error) {
...
}
```
, где
- `[]ValidationError` - слайс структур, содержащих имя поля и ошибку его валидации;
- `err` - программная ошибка, произошедшая во время валидации.
---
Типы полей, которые обязательно должны поддерживаться:
- `int`, `[]int`;
- `string`, `[]string`.
ри желании можно дополнительно поддержать любые другие типы (на ваше усмотрение)._
Необходимо реализовать следующие валидаторы:
- Для строк:
* `len:32` - длина строки должна быть ровно 32 символа;
* `regexp:\\d+` - согласно регулярному выражению строка должна состоять из цифр
(`\\` - экранирование слэша);
* `in:foo,bar` - строка должна входить в множество строк {"foo", "bar"}.
- Для чисел:
* `min:10` - число не может быть меньше 10;
* `max:20` - число не может быть больше 20;
* `in:256,1024` - число должно входить в множество чисел {256, 1024};
- Для слайсов валидируется каждый элемент слайса.
ри желании можно дополнительно добавить парочку новых правил (на ваше усмотрение)._
Допускается комбинация валидаторов по логическому "И" с помощью `|`, например:
* `min:0|max:10` - число должно находится в пределах [0, 10];
* `regexp:\\d+|len:20` - строка должна состоять из цифр и иметь длину 20.
Для большего понимания см. тесты.
**(\*) Дополнительное задание: поддержка валидации вложенных по композиции структур.**
---
Структура проекта:
- `models` - минимальный тестовый набор структур. `test.sh` компилирует
вашу тулзу и ставит её в систему, затем с помощью `go generate` генерирует код валидаторов,
а затем запускает тесты в пакете `models`.
- `go-validate` - набор файлов (и тестов к ним), реализующих тулзу.
**Сгенерированный код следует закоммитить!** Но имейте в виду, что
`test.sh` генерирует его заного, чтобы протестировать данный процесс.
---
Так как тестирование будет использовать `go generate models/models.go`,
необходимо доработать этот файл так, чтобы данная команда вызывала
нашу тулзу.
---
Не забудьте добавить в сгенерированный файл [дисклеймер](https://github.com/golang/go/blob/master/src/cmd/go/internal/generate/generate.go#L59)!
### Критерии оценки
- Закоммичен сгенерированный код - 1 балл
- Пайплайн зелёный - 3 балла
- Добавлены новые юнит-тесты (доработаны имеющиеся) в `models` - до 2 баллов
- Добавлены юнит-тесты для тулзы в `govalidate` - до 2 баллов
- Понятность и чистота кода - до 2 баллов
- Дополнительное задание на баллы не влияет
#### Зачёт от 7 баллов
### Подсказки
- `$GOFILE`
- `text/template`
- `regexp.MustCompile`
- `parser.ParseFile`
- `*ast.GenDecl`, `*ast.StructType`
- `go/format`

View File

@ -0,0 +1,5 @@
package main
func main() {
// Place your code here
}

View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw09_generator_of_validators
go 1.14
require github.com/stretchr/testify v1.5.1

View File

@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,30 @@
package models
type UserRole string
// NOTE: Several struct specs in one type declaration are allowed
type (
User struct {
ID string `json:"id" validate:"len:36"`
Name string
Age int `validate:"min:18|max:50"`
Email string `validate:"regexp:^\\w+@\\w+\\.\\w+$"`
Role UserRole `validate:"in:admin,stuff"`
Phones []string `validate:"len:11"`
}
App struct {
Version string `validate:"len:5"`
}
)
type Token struct {
Header []byte
Payload []byte
Signature []byte
}
type Response struct {
Code int `validate:"in:200,404,500"`
Body string `json:"omitempty"`
}

View File

@ -0,0 +1,152 @@
// +build generation
package models
import (
"net/http"
"testing"
"github.com/stretchr/testify/require"
)
type Validated interface {
Validate() ([]ValidationError, error)
}
func TestUserValidation(t *testing.T) {
requireValidation(t, User{})
goodUser := User{
ID: "0a44d582-9749-11ea-a056-9ff7f30f0608",
Name: "John",
Age: 24,
Email: "john@abrams.com",
Role: "admin",
}
requireNoValidationErrors(t, goodUser)
t.Run("ID length", func(t *testing.T) {
u := goodUser
u.ID = "123"
errs, err := u.Validate()
require.Nil(t, err)
requireOneFieldErr(t, errs, "ID")
})
t.Run("email regexp", func(t *testing.T) {
u := goodUser
u.Email = "isnotvalid@@email"
errs, err := u.Validate()
require.Nil(t, err)
requireOneFieldErr(t, errs, "Email")
})
t.Run("age borders", func(t *testing.T) {
u := goodUser
for _, a := range []int{18, 34, 50} {
u.Age = a
errs, err := u.Validate()
require.Nil(t, err)
require.Len(t, errs, 0)
}
u.Age = 51
errs, err := u.Validate()
require.Nil(t, err)
requireOneFieldErr(t, errs, "Age")
})
t.Run("phones slice", func(t *testing.T) {
// Write me :)
t.Fail()
})
t.Run("many errors", func(t *testing.T) {
u := goodUser
u.Age = -100
u.Email = "123"
u.Role = "unknown"
errs, err := u.Validate()
require.Nil(t, err)
fields := make([]string, 0, len(errs))
for _, e := range errs {
fields = append(fields, e.Field)
}
require.ElementsMatch(t, fields, []string{"Age", "Email", "Role"})
})
}
func TestAppValidation(t *testing.T) {
requireValidation(t, App{})
goodApp := App{
Version: "1.1.0",
}
requireNoValidationErrors(t, goodApp)
t.Run("version length", func(t *testing.T) {
a := goodApp
a.Version = "0.1"
errs, err := a.Validate()
require.Nil(t, err)
requireOneFieldErr(t, errs, "Version")
})
}
func TestTokenValidation(t *testing.T) {
requireNoValidation(t, Token{}, "no validated fields - no Validation() method")
}
func TestResponseValidation(t *testing.T) {
requireValidation(t, Response{})
goodResponse := Response{
Code: http.StatusOK,
Body: "some body",
}
requireNoValidationErrors(t, goodResponse)
t.Run("code set", func(t *testing.T) {
r := goodResponse
for _, c := range []int{200, 404, 500} {
r.Code = c
errs, err := r.Validate()
require.Nil(t, err)
require.Len(t, errs, 0)
}
r.Code = 133
errs, err := r.Validate()
require.Nil(t, err)
requireOneFieldErr(t, errs, "Code")
})
}
func requireValidation(t *testing.T, v interface{}, msgAndArgs ...interface{}) {
_, ok := v.(Validated)
require.True(t, ok, msgAndArgs)
}
func requireNoValidation(t *testing.T, v interface{}, msgAndArgs ...interface{}) {
_, ok := v.(Validated)
require.False(t, ok, msgAndArgs)
}
func requireNoValidationErrors(t *testing.T, v Validated) {
errs, err := v.Validate()
require.Nil(t, err)
require.Len(t, errs, 0)
}
func requireOneFieldErr(t *testing.T, errors []ValidationError, fieldName string) {
require.Len(t, errors, 1)
require.Equal(t, fieldName, errors[0].Field)
require.NotNil(t, errors[0].Err)
}

View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -xeuo pipefail
rm -f "$(command -v go-validate)"
rm -f ./models/*generated.go
go install ./go-validate
go generate models/models.go
go test -v -tags generation ./models
echo "PASS"

View File

@ -0,0 +1,38 @@
## Домашнее задание №10 «Оптимизация программы»
Вам дан исходный код функции `GetDomainStat(r io.Reader, domain string)`, которая:
* читает построчно из `r` пользовательские данные вида
```text
{"Id":1,"Name":"Howard Mendoza","Username":"0Oliver","Email":"aliquid_qui_ea@Browsedrive.gov","Phone":"6-866-899-36-79","Password":"InAQJvsq","Address":"Blackbird Place 25"}
{"Id":2,"Name":"Brian Olson","Username":"non_quia_id","Email":"FrancesEllis@Quinu.edu","Phone":"237-75-34","Password":"cmEPhX8","Address":"Butterfield Junction 74"}
{"Id":3,"Name":"Justin Oliver Jr. Sr.","Username":"oPerez","Email":"MelissaGutierrez@Twinte.gov","Phone":"106-05-18","Password":"f00GKr9i","Address":"Oak Valley Lane 19"}
```
(осторожно, в отличие от конкретной строки файл целиком не является валидным JSON);
* подсчитывает количество email-доменов пользователей на основе домена первого уровня `domain`.
Например, для данных, представленных выше:
```text
GetDomainStat(r, "com") // {}
GetDomainStat(r, "gov") // {"browsedrive": 1, "twinte": 1}
GetDomainStat(r, "edu") // {"quinu": 1}
```
Для большего понимания см. исходный код и тесты.
**Необходимо оптимизировать программу таким образом, чтобы она проходила все тесты.**
Нельзя:
- изменять сигнатуру функции `GetDomainStat`;
- удалять или изменять существующие юнит-тесты.
Можно:
- писать любой новый необходимый код;
- удалять имеющийся лишний код (кроме функции `GetDomainStat`);
- добавлять юнит-тесты.
### Критерии оценки
- Пайплайн зелёный и нет попытки «обмануть» систему - 4 балла
- Добавлены юнит-тесты - до 3 баллов
- Понятность и чистота кода - до 3 баллов
#### Зачёт от 7 баллов

View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw10_program_optimization
go 1.14
require github.com/stretchr/testify v1.5.1

View File

@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,67 @@
package hw10_program_optimization //nolint:golint,stylecheck
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"regexp"
"strings"
)
type User struct {
ID int
Name string
Username string
Email string
Phone string
Password string
Address string
}
type DomainStat map[string]int
func GetDomainStat(r io.Reader, domain string) (DomainStat, error) {
u, err := getUsers(r)
if err != nil {
return nil, fmt.Errorf("get users error: %s", err)
}
return countDomains(u, domain)
}
type users [100_000]User
func getUsers(r io.Reader) (result users, err error) {
content, err := ioutil.ReadAll(r)
if err != nil {
return
}
lines := strings.Split(string(content), "\n")
for i, line := range lines {
var user User
if err = json.Unmarshal([]byte(line), &user); err != nil {
return
}
result[i] = user
}
return
}
func countDomains(u users, domain string) (DomainStat, error) {
result := make(DomainStat)
for _, user := range u {
matched, err := regexp.Match("\\."+domain, []byte(user.Email))
if err != nil {
return nil, err
}
if matched {
num := result[strings.ToLower(strings.SplitN(user.Email, "@", 2)[1])]
num++
result[strings.ToLower(strings.SplitN(user.Email, "@", 2)[1])] = num
}
}
return result, nil
}

View File

@ -0,0 +1,435 @@
// +build bench
package hw10_program_optimization //nolint:golint,stylecheck
import (
"archive/zip"
"testing"
"time"
"github.com/stretchr/testify/require"
)
const (
mb int64 = 1 << 20
memoryLimit = 30 * mb
timeLimit = 300 * time.Millisecond
)
// go test -v -count=1 -timeout=30s -tags bench .
func TestGetDomainStat_Time_And_Memory(t *testing.T) {
bench := func(b *testing.B) {
b.StopTimer()
r, err := zip.OpenReader("testdata/users.dat.zip")
require.NoError(t, err)
defer r.Close()
require.Equal(t, len(r.File), 1)
data, err := r.File[0].Open()
require.NoError(t, err)
b.StartTimer()
stat, err := GetDomainStat(data, "biz")
b.StopTimer()
require.NoError(t, err)
require.Equal(t, stat, expectedBizStat)
}
result := testing.Benchmark(bench)
mem := int64(result.MemBytes) / mb
t.Logf("time used: %s", result.T)
t.Logf("memory used: %dMb", mem)
require.Less(t, int64(result.T), int64(timeLimit), "the program is too slow")
require.Less(t, mem, memoryLimit, "the program is too greedy")
}
var expectedBizStat = DomainStat{
"abata.biz": 25,
"abatz.biz": 25,
"agimba.biz": 28,
"agivu.biz": 17,
"aibox.biz": 31,
"ailane.biz": 23,
"aimbo.biz": 25,
"aimbu.biz": 36,
"ainyx.biz": 35,
"aivee.biz": 25,
"avamba.biz": 21,
"avamm.biz": 17,
"avavee.biz": 35,
"avaveo.biz": 30,
"babbleblab.biz": 29,
"babbleopia.biz": 36,
"babbleset.biz": 28,
"babblestorm.biz": 29,
"blognation.biz": 32,
"blogpad.biz": 34,
"blogspan.biz": 21,
"blogtag.biz": 23,
"blogtags.biz": 34,
"blogxs.biz": 35,
"bluejam.biz": 36,
"bluezoom.biz": 27,
"brainbox.biz": 30,
"brainlounge.biz": 38,
"brainsphere.biz": 31,
"brainverse.biz": 39,
"brightbean.biz": 23,
"brightdog.biz": 32,
"browseblab.biz": 31,
"browsebug.biz": 25,
"browsecat.biz": 34,
"browsedrive.biz": 24,
"browsetype.biz": 34,
"browsezoom.biz": 29,
"bubblebox.biz": 19,
"bubblemix.biz": 38,
"bubbletube.biz": 34,
"buzzbean.biz": 26,
"buzzdog.biz": 30,
"buzzshare.biz": 26,
"buzzster.biz": 28,
"camido.biz": 27,
"camimbo.biz": 36,
"centidel.biz": 32,
"centimia.biz": 17,
"centizu.biz": 18,
"chatterbridge.biz": 30,
"chatterpoint.biz": 32,
"cogibox.biz": 30,
"cogidoo.biz": 34,
"cogilith.biz": 24,
"dabfeed.biz": 26,
"dabjam.biz": 30,
"dablist.biz": 30,
"dabshots.biz": 33,
"dabtype.biz": 21,
"dabvine.biz": 26,
"dabz.biz": 19,
"dazzlesphere.biz": 24,
"demimbu.biz": 27,
"demivee.biz": 39,
"demizz.biz": 30,
"devbug.biz": 20,
"devcast.biz": 35,
"devify.biz": 27,
"devpoint.biz": 26,
"devpulse.biz": 27,
"devshare.biz": 30,
"digitube.biz": 30,
"divanoodle.biz": 33,
"divape.biz": 32,
"divavu.biz": 28,
"dynabox.biz": 66,
"dynava.biz": 21,
"dynazzy.biz": 29,
"eabox.biz": 28,
"eadel.biz": 25,
"eamia.biz": 18,
"eare.biz": 30,
"eayo.biz": 30,
"eazzy.biz": 27,
"edgeblab.biz": 29,
"edgeclub.biz": 29,
"edgeify.biz": 36,
"edgepulse.biz": 21,
"edgetag.biz": 24,
"edgewire.biz": 29,
"eidel.biz": 33,
"eimbee.biz": 22,
"einti.biz": 19,
"eire.biz": 28,
"fadeo.biz": 35,
"fanoodle.biz": 23,
"fatz.biz": 30,
"feedbug.biz": 29,
"feedfire.biz": 30,
"feedfish.biz": 35,
"feedmix.biz": 31,
"feednation.biz": 24,
"feedspan.biz": 28,
"fivebridge.biz": 20,
"fivechat.biz": 29,
"fiveclub.biz": 23,
"fivespan.biz": 27,
"flashdog.biz": 20,
"flashpoint.biz": 35,
"flashset.biz": 30,
"flashspan.biz": 32,
"flipbug.biz": 27,
"flipopia.biz": 30,
"flipstorm.biz": 21,
"fliptune.biz": 29,
"gabcube.biz": 29,
"gabspot.biz": 24,
"gabtune.biz": 29,
"gabtype.biz": 29,
"gabvine.biz": 24,
"geba.biz": 24,
"gevee.biz": 23,
"gigabox.biz": 28,
"gigaclub.biz": 25,
"gigashots.biz": 26,
"gigazoom.biz": 29,
"innojam.biz": 26,
"innotype.biz": 27,
"innoz.biz": 24,
"izio.biz": 26,
"jabberbean.biz": 28,
"jabbercube.biz": 31,
"jabbersphere.biz": 55,
"jabberstorm.biz": 22,
"jabbertype.biz": 27,
"jaloo.biz": 35,
"jamia.biz": 33,
"janyx.biz": 33,
"jatri.biz": 18,
"jaxbean.biz": 28,
"jaxnation.biz": 21,
"jaxspan.biz": 27,
"jaxworks.biz": 30,
"jayo.biz": 44,
"jazzy.biz": 32,
"jetpulse.biz": 25,
"jetwire.biz": 26,
"jumpxs.biz": 29,
"kamba.biz": 30,
"kanoodle.biz": 19,
"kare.biz": 30,
"katz.biz": 62,
"kaymbo.biz": 34,
"kayveo.biz": 22,
"kazio.biz": 21,
"kazu.biz": 16,
"kimia.biz": 25,
"kwideo.biz": 17,
"kwilith.biz": 25,
"kwimbee.biz": 34,
"kwinu.biz": 15,
"lajo.biz": 20,
"latz.biz": 24,
"layo.biz": 32,
"lazz.biz": 27,
"lazzy.biz": 26,
"leenti.biz": 26,
"leexo.biz": 32,
"linkbridge.biz": 38,
"linkbuzz.biz": 24,
"linklinks.biz": 31,
"linktype.biz": 31,
"livefish.biz": 31,
"livepath.biz": 23,
"livetube.biz": 53,
"livez.biz": 28,
"meedoo.biz": 23,
"meejo.biz": 24,
"meembee.biz": 26,
"meemm.biz": 23,
"meetz.biz": 33,
"meevee.biz": 62,
"meeveo.biz": 27,
"meezzy.biz": 24,
"miboo.biz": 26,
"midel.biz": 28,
"minyx.biz": 25,
"mita.biz": 29,
"mudo.biz": 36,
"muxo.biz": 25,
"mybuzz.biz": 32,
"mycat.biz": 32,
"mydeo.biz": 20,
"mydo.biz": 30,
"mymm.biz": 21,
"mynte.biz": 54,
"myworks.biz": 27,
"nlounge.biz": 25,
"npath.biz": 33,
"ntag.biz": 28,
"ntags.biz": 32,
"oba.biz": 22,
"oloo.biz": 19,
"omba.biz": 26,
"ooba.biz": 27,
"oodoo.biz": 30,
"oozz.biz": 22,
"oyoba.biz": 27,
"oyoloo.biz": 30,
"oyonder.biz": 29,
"oyondu.biz": 23,
"oyope.biz": 24,
"oyoyo.biz": 32,
"ozu.biz": 18,
"photobean.biz": 25,
"photobug.biz": 57,
"photofeed.biz": 25,
"photojam.biz": 35,
"photolist.biz": 19,
"photospace.biz": 33,
"pixoboo.biz": 14,
"pixonyx.biz": 30,
"pixope.biz": 32,
"plajo.biz": 32,
"plambee.biz": 29,
"podcat.biz": 31,
"quamba.biz": 31,
"quatz.biz": 54,
"quaxo.biz": 25,
"quimba.biz": 25,
"quimm.biz": 33,
"quinu.biz": 60,
"quire.biz": 25,
"realblab.biz": 32,
"realbridge.biz": 30,
"realbuzz.biz": 22,
"realcube.biz": 57,
"realfire.biz": 37,
"reallinks.biz": 25,
"realmix.biz": 27,
"realpoint.biz": 22,
"rhybox.biz": 30,
"rhycero.biz": 28,
"rhyloo.biz": 32,
"rhynoodle.biz": 25,
"rhynyx.biz": 17,
"rhyzio.biz": 36,
"riffpath.biz": 21,
"riffpedia.biz": 33,
"riffwire.biz": 31,
"roodel.biz": 29,
"roombo.biz": 29,
"roomm.biz": 32,
"rooxo.biz": 34,
"shufflebeat.biz": 32,
"shuffledrive.biz": 25,
"shufflester.biz": 26,
"shuffletag.biz": 23,
"skaboo.biz": 35,
"skajo.biz": 26,
"skalith.biz": 30,
"skiba.biz": 22,
"skibox.biz": 27,
"skidoo.biz": 24,
"skilith.biz": 29,
"skimia.biz": 45,
"skinder.biz": 25,
"skinix.biz": 23,
"skinte.biz": 39,
"skipfire.biz": 29,
"skippad.biz": 26,
"skipstorm.biz": 30,
"skiptube.biz": 26,
"skivee.biz": 34,
"skyba.biz": 40,
"skyble.biz": 32,
"skyndu.biz": 32,
"skynoodle.biz": 28,
"skyvu.biz": 34,
"snaptags.biz": 33,
"tagcat.biz": 33,
"tagchat.biz": 37,
"tagfeed.biz": 30,
"tagopia.biz": 17,
"tagpad.biz": 28,
"tagtune.biz": 22,
"talane.biz": 22,
"tambee.biz": 24,
"tanoodle.biz": 38,
"tavu.biz": 37,
"tazz.biz": 27,
"tazzy.biz": 28,
"tekfly.biz": 31,
"teklist.biz": 26,
"thoughtbeat.biz": 30,
"thoughtblab.biz": 24,
"thoughtbridge.biz": 30,
"thoughtmix.biz": 33,
"thoughtsphere.biz": 20,
"thoughtstorm.biz": 38,
"thoughtworks.biz": 24,
"topdrive.biz": 35,
"topicblab.biz": 32,
"topiclounge.biz": 21,
"topicshots.biz": 30,
"topicstorm.biz": 22,
"topicware.biz": 35,
"topiczoom.biz": 38,
"trilia.biz": 28,
"trilith.biz": 25,
"trudeo.biz": 29,
"trudoo.biz": 28,
"trunyx.biz": 33,
"trupe.biz": 34,
"twimbo.biz": 19,
"twimm.biz": 30,
"twinder.biz": 28,
"twinte.biz": 33,
"twitterbeat.biz": 33,
"twitterbridge.biz": 20,
"twitterlist.biz": 26,
"twitternation.biz": 22,
"twitterwire.biz": 21,
"twitterworks.biz": 39,
"twiyo.biz": 37,
"vidoo.biz": 28,
"vimbo.biz": 21,
"vinder.biz": 31,
"vinte.biz": 34,
"vipe.biz": 25,
"vitz.biz": 26,
"viva.biz": 30,
"voolia.biz": 34,
"voolith.biz": 26,
"voomm.biz": 61,
"voonder.biz": 32,
"voonix.biz": 32,
"voonte.biz": 26,
"voonyx.biz": 25,
"wikibox.biz": 27,
"wikido.biz": 21,
"wikivu.biz": 23,
"wikizz.biz": 61,
"wordify.biz": 28,
"wordpedia.biz": 25,
"wordtune.biz": 27,
"wordware.biz": 19,
"yabox.biz": 24,
"yacero.biz": 34,
"yadel.biz": 27,
"yakidoo.biz": 21,
"yakijo.biz": 29,
"yakitri.biz": 26,
"yambee.biz": 20,
"yamia.biz": 17,
"yata.biz": 25,
"yodel.biz": 26,
"yodo.biz": 21,
"yodoo.biz": 24,
"yombu.biz": 29,
"yotz.biz": 26,
"youbridge.biz": 40,
"youfeed.biz": 32,
"youopia.biz": 22,
"youspan.biz": 59,
"youtags.biz": 22,
"yoveo.biz": 31,
"yozio.biz": 33,
"zava.biz": 29,
"zazio.biz": 18,
"zoombeat.biz": 28,
"zoombox.biz": 30,
"zoomcast.biz": 38,
"zoomdog.biz": 29,
"zoomlounge.biz": 25,
"zoomzone.biz": 32,
"zoonder.biz": 29,
"zoonoodle.biz": 27,
"zooveo.biz": 22,
"zoovu.biz": 38,
"zooxo.biz": 33,
"zoozzy.biz": 23,
}

View File

@ -0,0 +1,39 @@
// +build !bench
package hw10_program_optimization //nolint:golint,stylecheck
import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
)
func TestGetDomainStat(t *testing.T) {
data := `{"Id":1,"Name":"Howard Mendoza","Username":"0Oliver","Email":"aliquid_qui_ea@Browsedrive.gov","Phone":"6-866-899-36-79","Password":"InAQJvsq","Address":"Blackbird Place 25"}
{"Id":2,"Name":"Jesse Vasquez","Username":"qRichardson","Email":"mLynch@broWsecat.com","Phone":"9-373-949-64-00","Password":"SiZLeNSGn","Address":"Fulton Hill 80"}
{"Id":3,"Name":"Clarence Olson","Username":"RachelAdams","Email":"RoseSmith@Browsecat.com","Phone":"988-48-97","Password":"71kuz3gA5w","Address":"Monterey Park 39"}
{"Id":4,"Name":"Gregory Reid","Username":"tButler","Email":"5Moore@Teklist.net","Phone":"520-04-16","Password":"r639qLNu","Address":"Sunfield Park 20"}
{"Id":5,"Name":"Janice Rose","Username":"KeithHart","Email":"nulla@Linktype.com","Phone":"146-91-01","Password":"acSBF5","Address":"Russell Trail 61"}`
t.Run("find 'com'", func(t *testing.T) {
result, err := GetDomainStat(bytes.NewBufferString(data), "com")
require.NoError(t, err)
require.Equal(t, DomainStat{
"browsecat.com": 2,
"linktype.com": 1,
}, result)
})
t.Run("find 'gov'", func(t *testing.T) {
result, err := GetDomainStat(bytes.NewBufferString(data), "gov")
require.NoError(t, err)
require.Equal(t, DomainStat{"browsedrive.gov": 1}, result)
})
t.Run("find 'unknown'", func(t *testing.T) {
result, err := GetDomainStat(bytes.NewBufferString(data), "unknown")
require.NoError(t, err)
require.Equal(t, DomainStat{}, result)
})
}

Binary file not shown.

View File

@ -0,0 +1,107 @@
## Домашнее задание №11 «Клиент TELNET»
Необходимо реализовать крайне примитивный TELNET клиент
(без поддержки команд, опций и протокола в целом).
Примеры вызовов:
```bash
$ go-telnet --timeout=10s host port
$ go-telnet mysite.ru 8080
$ go-telnet --timeout=3s 1.1.1.1 123
```
* Программа должна подключаться к указанному хосту (IP или доменное имя) и порту по протоколу TCP.
* После подключения STDIN программы должен записываться в сокет,
а данные, полученные из сокета, должны выводиться в STDOUT - всё происходит конкурентно.
* Опционально в программу можно передать таймаут на подключение к серверу
(через аргумент `--timeout`) - по умолчанию `10s`.
* При нажатии `Ctrl+D` программа должна закрывать сокет и завершаться с сообщением.
* При получении `SIGINT` программа должна завершать свою работу.
* Если сокет закрылся со стороны сервера, то при следующей попытке отправить сообщение программа
должна завершаться (допускается завершать программу после "неудачной" отправки нескольких сообщений).
* При подключении к несуществующему серверу, программа должна завершаться с ошибкой соединения/таймаута.
При необходимости можно выделять дополнительные функции / ошибки.
Примеры работы:
1) Сервер обрывает соединение
```bash
$ nc -l localhost 4242
Hello from NC
I'm telnet client
Bye, client!
^C
```
```bash
$ go-telnet --timeout=5s localhost 4242
...Connected to localhost:4242
Hello from NC
I'm telnet client
Bye, client!
Bye-bye
...Connection was closed by peer
```
Здесь сообщения
```
Hello from NC
Bye, client!
```
и операция Ctrl+C (Unix) относятся к `nc`,
а сообщения
```
I'm telnet client
Bye-bye
```
относятся к `go-telnet`.
2) Клиент завершает ввод
```bash
$ go-telnet localhost 4242
...Connected to localhost:4242
I
will be
back!
^D
...EOF
```
```bash
$ nc -l localhost 4242
I
will be
back!
```
Здесь сообщения
```
I
will be
back!
```
и операция Ctrl+D (Unix) относятся к `go-telnet`,
Сообщения
```
...Connected to localhost:4242
...Connection was closed by peer
...EOF
```
являются служебными.
Они **выводятся в STDERR** и не тестируются `test.sh`, их формат на усмотрение автора.
### Критерии оценки
- Пайплайн зелёный - 4 балла
- Добавлены юнит-тесты - до 4 баллов
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов
### Подсказки
- `flag.StringVar`
- `net.JoinHostPort`, `net.DialTimeout`
- `bufio` / `io`
- https://stackoverflow.com/questions/51317968/write-on-a-closed-net-conn-but-returned-nil-error

View File

@ -0,0 +1,5 @@
module github.com/fixme_my_friend/hw11_telnet_client
go 1.14
require github.com/stretchr/testify v1.5.1

11
hw11_telnet_client/go.sum Normal file
View File

@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,6 @@
package main
func main() {
// Place your code here
// P.S. Do not rush to throw context down, think think if it is useful with blocking operation?
}

View File

@ -0,0 +1,18 @@
package main
import (
"io"
"time"
)
type TelnetClient interface {
// Place your code here
}
func NewTelnetClient(address string, timeout time.Duration, in io.ReadCloser, out io.Writer) TelnetClient {
// Place your code here
return nil
}
// Place your code here
// P.S. Author's solution takes no more than 50 lines

View File

@ -0,0 +1,65 @@
package main
import (
"bytes"
"io/ioutil"
"net"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestTelnetClient(t *testing.T) {
t.Run("basic", func(t *testing.T) {
l, err := net.Listen("tcp", "127.0.0.1:")
require.NoError(t, err)
defer func() { require.NoError(t, l.Close()) }()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
in := &bytes.Buffer{}
out := &bytes.Buffer{}
timeout, err := time.ParseDuration("10s")
require.NoError(t, err)
client := NewTelnetClient(l.Addr().String(), timeout, ioutil.NopCloser(in), out)
require.NoError(t, client.Connect())
defer func() { require.NoError(t, client.Close()) }()
in.WriteString("hello\n")
err = client.Send()
require.NoError(t, err)
err = client.Receive()
require.NoError(t, err)
require.Equal(t, "world\n", out.String())
}()
go func() {
defer wg.Done()
conn, err := l.Accept()
require.NoError(t, err)
require.NotNil(t, conn)
defer func() { require.NoError(t, conn.Close()) }()
request := make([]byte, 1024)
n, err := conn.Read(request)
require.NoError(t, err)
require.Equal(t, "hello\n", string(request)[:n])
n, err = conn.Write([]byte("world\n"))
require.NoError(t, err)
require.NotEqual(t, 0, n)
}()
wg.Wait()
})
}

Some files were not shown because too many files have changed in this diff Show More