Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/1307tags/v1.22.1.1
| @@ -24,11 +24,27 @@ | |||||
| .am-pt-20{ padding-top: 2.0rem !important;} | .am-pt-20{ padding-top: 2.0rem !important;} | ||||
| .am-pt-30{ padding-top: 3.0rem !important;} | .am-pt-30{ padding-top: 3.0rem !important;} | ||||
| .am-pl-30{ padding-left: 3.0rem !important;} | .am-pl-30{ padding-left: 3.0rem !important;} | ||||
| .am-pl-50{ padding-left: 5.0em !important;} | |||||
| .am-ml-10{ margin-left: 1.0rem !important;} | |||||
| .am-ml-30{ margin-left: 3.0rem !important;} | .am-ml-30{ margin-left: 3.0rem !important;} | ||||
| .am-pr-30{ padding-right: 3.0rem !important;} | .am-pr-30{ padding-right: 3.0rem !important;} | ||||
| .am-mr-30{ margin-right: 3.0rem !important;} | .am-mr-30{ margin-right: 3.0rem !important;} | ||||
| .am-lh-18{ line-height: 1.8em;} | .am-lh-18{ line-height: 1.8em;} | ||||
| .nowrap{ | |||||
| white-space: nowrap; | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| } | |||||
| .nowrap-2 { | |||||
| max-height: 2.837em; | |||||
| line-height: 1.4285em; | |||||
| overflow: hidden; | |||||
| display: -webkit-box; | |||||
| -webkit-line-clamp: 2; | |||||
| -webkit-box-orient: vertical; | |||||
| } | |||||
| .opacity5{ opacity:0.5;} | .opacity5{ opacity:0.5;} | ||||
| .radius15{ border-radius:1.5rem !important; } | .radius15{ border-radius:1.5rem !important; } | ||||
| .radius10{ border-radius:1.0rem !important; } | .radius10{ border-radius:1.0rem !important; } | ||||
| @@ -42,12 +58,15 @@ | |||||
| box-shadow: 0 2px 4px 0 rgba(34,36,38,.3); | box-shadow: 0 2px 4px 0 rgba(34,36,38,.3); | ||||
| } | } | ||||
| .ui.blue { | |||||
| color: #0366d6; | |||||
| } | |||||
| .ui.secondary.hometop.segment{ | .ui.secondary.hometop.segment{ | ||||
| background: #DFE9F0; | background: #DFE9F0; | ||||
| padding-top: 0; | padding-top: 0; | ||||
| border: none; | border: none; | ||||
| margin-bottom: 90px; | |||||
| margin-bottom: 11em; | |||||
| } | } | ||||
| .ui.secondary.hometop.segment #navbar{ | .ui.secondary.hometop.segment #navbar{ | ||||
| z-index: 10; | z-index: 10; | ||||
| @@ -68,6 +87,7 @@ | |||||
| .homebanner{ | .homebanner{ | ||||
| position: relative; | position: relative; | ||||
| padding: 100px 32px 80px; | padding: 100px 32px 80px; | ||||
| padding-bottom: 0; | |||||
| z-index: 9; | z-index: 9; | ||||
| } | } | ||||
| .homebanner .ui.header .sub.header{ | .homebanner .ui.header .sub.header{ | ||||
| @@ -75,10 +95,15 @@ | |||||
| } | } | ||||
| .bannerpic{ | .bannerpic{ | ||||
| position: absolute; | position: absolute; | ||||
| right: 50px; | |||||
| right: 0px; | |||||
| bottom: -64px; | bottom: -64px; | ||||
| width: 560px; | width: 560px; | ||||
| z-index: 6; | |||||
| } | |||||
| .homebanner .ui.button{ | |||||
| font-weight: normal; | |||||
| } | } | ||||
| .ui[class*="very padded"].segment.i-code{ | .ui[class*="very padded"].segment.i-code{ | ||||
| padding-left: 6.0rem; | padding-left: 6.0rem; | ||||
| } | } | ||||
| @@ -115,25 +140,141 @@ | |||||
| .i-env .ui.cards>.card>.content{ | .i-env .ui.cards>.card>.content{ | ||||
| border-top: none; | border-top: none; | ||||
| } | } | ||||
| .leftline01{ | |||||
| #homenews{ | |||||
| position: relative; | |||||
| z-index: 9; | |||||
| bottom: -6em; | |||||
| } | |||||
| #homenews > p{ | |||||
| color: #BBBBBB; | |||||
| margin-left: 2.3em; | |||||
| } | |||||
| .homenews{ | |||||
| border-radius: 2em; | |||||
| background-color: rgba(16, 16, 16, .9); | |||||
| position: relative; | |||||
| padding-left: 2.3em !important; | |||||
| } | |||||
| .homeorg, .homepro, .homemodel, .i-env{ | |||||
| position: relative; | |||||
| padding-bottom: 5em; | |||||
| } | |||||
| .homenews::before{ | |||||
| content: ''; | |||||
| position: absolute; | position: absolute; | ||||
| left: 3.0rem; | |||||
| left: 3em; | |||||
| top: 0; | top: 0; | ||||
| bottom: 0; | bottom: 0; | ||||
| border-left: 2px solid #505559; | |||||
| border-bottom: 2px solid #505559; | |||||
| border-radius: 0 0 0 2.0rem; | |||||
| width: 2.0rem; | |||||
| background-color: rgba(105, 192, 255, .4); | |||||
| width: 2px; | |||||
| } | |||||
| .homenews .time-since{ | |||||
| padding-left: 1em; | |||||
| color: #888888; | |||||
| } | |||||
| .homenews a{ | |||||
| color: #69C0FF; | |||||
| } | } | ||||
| .leftline02{ | |||||
| .homenews .ui.list>.item>.content{ | |||||
| color: #E8E8E8; | |||||
| line-height: 1.8em; | |||||
| width: calc(100% - 3.25em) !important; | |||||
| } | |||||
| .homenews .ui.list>.item{ | |||||
| padding: 0; | |||||
| } | |||||
| .newslist{ | |||||
| height: 325px; | |||||
| overflow: hidden; | |||||
| } | |||||
| .leftline01, .leftline03, .leftline04{ | |||||
| position: absolute; | position: absolute; | ||||
| left: 5rem; | |||||
| top: calc(-5.0rem - 2px); | |||||
| border-top: 2px solid #505559; | |||||
| border-right: 2px solid #505559; | |||||
| border-radius: 0 2.0rem 0 0; | |||||
| width: 17.5rem; | |||||
| height: 6.0rem; | |||||
| width: 5em; | |||||
| border-radius: 0 0 0 4.0em; | |||||
| border-left: 2px solid #3291F8; | |||||
| border-bottom: 2px solid #3291F8; | |||||
| top: -5em; | |||||
| bottom: 0; | |||||
| left: 2em; | |||||
| z-index: 6; | |||||
| } | |||||
| .leftline02, .leftline02-2 { | |||||
| position: absolute; | |||||
| left: 7.0em; | |||||
| top: -2px; | |||||
| bottom: 2px; | |||||
| height: auto; | |||||
| border-top: 2px solid #3291F8; | |||||
| border-right: 2px solid #3291F8; | |||||
| border-bottom: 2px solid #3291F8; | |||||
| border-radius: 0 4.0em 4.0em 0; | |||||
| width: 10em; | |||||
| z-index: 6; | |||||
| } | |||||
| .leftline03{ | |||||
| border-radius: 4.0em 0 0 0; | |||||
| border-top: 2px solid #3291F8; | |||||
| border-bottom:none; | |||||
| top: -2.0em; | |||||
| } | |||||
| .leftline02-2{ | |||||
| border-color: rgba(105, 192, 255, .4); | |||||
| width: 7em; | |||||
| z-index: 5; | |||||
| } | |||||
| .leftline04{ | |||||
| border-radius: 0; | |||||
| border-top: none; | |||||
| border-bottom: none; | |||||
| } | |||||
| .homeorg-tit{ | |||||
| padding-left: 5em !important; | |||||
| } | |||||
| .homeorg-tit::after{ | |||||
| content: ''; | |||||
| position: absolute; | |||||
| width: 1.6em; | |||||
| height: 1.6em; | |||||
| background-color: #FFF; | |||||
| border: 2px solid #3291F8; | |||||
| left: 2.3em; | |||||
| top: 1.3em; | |||||
| border-radius: 1em; | |||||
| z-index: 9; | |||||
| } | |||||
| .homeorg-list .card{ | |||||
| background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important; | |||||
| box-shadow: none !important; | |||||
| } | |||||
| .homeorg-list .card .ui.small.header .content{ | |||||
| width: calc(100% - 3.25em); | |||||
| } | |||||
| .homepro-list{ | |||||
| position: relative; | |||||
| z-index: 9; | |||||
| padding: 1.0em 1.0em 3.0em; | |||||
| overflow: hidden; | |||||
| } | |||||
| .homepro-list .ui.card{ | |||||
| border-radius: 15px; | |||||
| background-color: #FFF; | |||||
| box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, 30); | |||||
| border: 1px solid rgba(105, 192, 255, 40); | |||||
| min-height: 10.8em; | |||||
| } | |||||
| .homepro-list .ui.card>.content>.header{ | |||||
| line-height: 40px !important; | |||||
| } | |||||
| .homepro-list .swiper-pagination-bullet-active{ | |||||
| width: 40px; | |||||
| border-radius: 4px; | |||||
| } | |||||
| .i-env > div{ | |||||
| position: relative; | |||||
| } | } | ||||
| @media only screen and (max-width: 767px) { | @media only screen and (max-width: 767px) { | ||||
| @@ -32,6 +32,7 @@ require ( | |||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible | github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||
| github.com/dustin/go-humanize v1.0.0 | github.com/dustin/go-humanize v1.0.0 | ||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | ||||
| github.com/elliotchance/orderedmap v1.4.0 | |||||
| github.com/emirpasic/gods v1.12.0 | github.com/emirpasic/gods v1.12.0 | ||||
| github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a | github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a | ||||
| github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect | github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect | ||||
| @@ -87,6 +88,7 @@ require ( | |||||
| github.com/niklasfasching/go-org v0.1.9 | github.com/niklasfasching/go-org v0.1.9 | ||||
| github.com/oliamb/cutter v0.2.2 | github.com/oliamb/cutter v0.2.2 | ||||
| github.com/olivere/elastic/v7 v7.0.9 | github.com/olivere/elastic/v7 v7.0.9 | ||||
| github.com/patrickmn/go-cache v2.1.0+incompatible | |||||
| github.com/pkg/errors v0.9.1 | github.com/pkg/errors v0.9.1 | ||||
| github.com/pquerna/otp v1.2.0 | github.com/pquerna/otp v1.2.0 | ||||
| github.com/prometheus/client_golang v1.1.0 | github.com/prometheus/client_golang v1.1.0 | ||||
| @@ -99,7 +101,7 @@ require ( | |||||
| github.com/sergi/go-diff v1.1.0 | github.com/sergi/go-diff v1.1.0 | ||||
| github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect | github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect | ||||
| github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd | github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd | ||||
| github.com/stretchr/testify v1.4.0 | |||||
| github.com/stretchr/testify v1.7.0 | |||||
| github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect | github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect | ||||
| github.com/tinylib/msgp v1.1.2 // indirect | github.com/tinylib/msgp v1.1.2 // indirect | ||||
| github.com/tstranex/u2f v1.0.0 | github.com/tstranex/u2f v1.0.0 | ||||
| @@ -39,11 +39,9 @@ gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Y | |||||
| gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok= | gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok= | ||||
| gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= | gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= | ||||
| gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw= | gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw= | ||||
| gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= | |||||
| gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= | gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= | ||||
| gitea.com/macaron/macaron v1.4.0 h1:FY1QDGqyuUzs21K6ChkbYbRUfwL7v2aUrhNEJ0IgsAw= | gitea.com/macaron/macaron v1.4.0 h1:FY1QDGqyuUzs21K6ChkbYbRUfwL7v2aUrhNEJ0IgsAw= | ||||
| gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= | gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= | ||||
| gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 h1:mvkQGAlON1Z6Y8pqa/+FpYIskk54mazuECUfZK5oTg0= | |||||
| gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA= | gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA= | ||||
| gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d h1:XLww3CvnFZkXVwauN67fniDaIpIqsE+9KVcxlZKlvLU= | gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d h1:XLww3CvnFZkXVwauN67fniDaIpIqsE+9KVcxlZKlvLU= | ||||
| gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY= | gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY= | ||||
| @@ -70,7 +68,6 @@ github.com/RichardKnop/machinery v1.6.9 h1:dQu1c7ENgPFrN9qWweEe7xDDvNYGSqEyprK0G | |||||
| github.com/RichardKnop/machinery v1.6.9/go.mod h1:BO7MG/5tvdpgMVkOT8V94SEf8x8H8aceRzTt8Tx1IMc= | github.com/RichardKnop/machinery v1.6.9/go.mod h1:BO7MG/5tvdpgMVkOT8V94SEf8x8H8aceRzTt8Tx1IMc= | ||||
| github.com/RichardKnop/redsync v1.2.0 h1:gK35hR3zZkQigHKm8wOGb9MpJ9BsrW6MzxezwjTcHP0= | github.com/RichardKnop/redsync v1.2.0 h1:gK35hR3zZkQigHKm8wOGb9MpJ9BsrW6MzxezwjTcHP0= | ||||
| github.com/RichardKnop/redsync v1.2.0/go.mod h1:9b8nBGAX3bE2uCfJGSnsDvF23mKyHTZzmvmj5FH3Tp0= | github.com/RichardKnop/redsync v1.2.0/go.mod h1:9b8nBGAX3bE2uCfJGSnsDvF23mKyHTZzmvmj5FH3Tp0= | ||||
| github.com/RoaringBitmap/roaring v0.4.21 h1:WJ/zIlNX4wQZ9x8Ey33O1UaD9TCTakYsdLFSBcTwH+8= | |||||
| github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= | github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= | ||||
| github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= | github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= | ||||
| github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= | github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= | ||||
| @@ -140,11 +137,9 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7 | |||||
| github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||||
| github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= | ||||
| github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= | github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k= | ||||
| github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d h1:XMf4E1U+b9E3ElF0mjvfXZdflBRZz4gLp16nQ/QSHQM= | |||||
| github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | ||||
| github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 h1:vZryARwW4PSFXd9arwegEywvMTvPuXL3/oa+4L5NTe8= | github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 h1:vZryARwW4PSFXd9arwegEywvMTvPuXL3/oa+4L5NTe8= | ||||
| github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= | ||||
| github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b h1:bZ9rKU2/V8sY+NulSfxDOnXTWcs1rySqdF1sVepihvo= | |||||
| github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | ||||
| github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85 h1:0WMIDtuXCKEm4wtAJgAAXa/qtM5O9MariLwgHaRlYmk= | github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85 h1:0WMIDtuXCKEm4wtAJgAAXa/qtM5O9MariLwgHaRlYmk= | ||||
| github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= | ||||
| @@ -169,7 +164,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | |||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= | github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= | ||||
| github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 h1:bpWCJ5MddHsv4Xtl3azkK89mZzd/vvut32mvAnKbyUA= | |||||
| github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | ||||
| github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg= | github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg= | ||||
| github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | ||||
| @@ -185,8 +179,9 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 | |||||
| github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= | ||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU= | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU= | ||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k= | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k= | ||||
| github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= | |||||
| github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | ||||
| github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A= | |||||
| github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= | |||||
| github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | ||||
| github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | ||||
| github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE= | github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE= | ||||
| @@ -306,7 +301,6 @@ github.com/go-swagger/go-swagger v0.21.0 h1:AX9mdfzp6eJtUe92nFrWmbK7ocRgkCDPJs0F | |||||
| github.com/go-swagger/go-swagger v0.21.0/go.mod h1:tDb8PdDVFcaE8EPXkMOsuxpL3UEPiwu1UDZar9Z/1RY= | github.com/go-swagger/go-swagger v0.21.0/go.mod h1:tDb8PdDVFcaE8EPXkMOsuxpL3UEPiwu1UDZar9Z/1RY= | ||||
| github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0= | github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0= | ||||
| github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= | github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= | ||||
| github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= | |||||
| github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= | ||||
| github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= | github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= | ||||
| github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= | github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= | ||||
| @@ -330,13 +324,11 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb | |||||
| github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= | |||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= | |||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= | github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| @@ -349,7 +341,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z | |||||
| github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | |||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| @@ -375,7 +366,6 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+ | |||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= | |||||
| github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= | github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= | ||||
| github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= | ||||
| @@ -406,7 +396,6 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo | |||||
| github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | ||||
| github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= | github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= | ||||
| github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | ||||
| github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= | |||||
| github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | ||||
| github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= | github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= | ||||
| github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | ||||
| @@ -458,7 +447,6 @@ github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+ | |||||
| github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= | github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= | ||||
| github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY= | |||||
| github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= | github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= | ||||
| github.com/klauspost/compress v1.10.2 h1:Znfn6hXZAHaLPNnlqUYRrBSReFHYybslgv4PTiyz6P0= | github.com/klauspost/compress v1.10.2 h1:Znfn6hXZAHaLPNnlqUYRrBSReFHYybslgv4PTiyz6P0= | ||||
| github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= | github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= | ||||
| @@ -472,7 +460,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN | |||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= | ||||
| github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | |||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| @@ -545,7 +532,6 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9 | |||||
| github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= | ||||
| github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA= | github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA= | ||||
| github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= | github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= | ||||
| github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= | |||||
| github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= | github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= | ||||
| github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= | github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= | ||||
| github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= | github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= | ||||
| @@ -580,6 +566,8 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt | |||||
| github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= | ||||
| github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= | github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= | ||||
| github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= | ||||
| github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= | |||||
| github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= | |||||
| github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= | ||||
| github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | ||||
| github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= | github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= | ||||
| @@ -588,7 +576,6 @@ github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= | |||||
| github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= | github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= | ||||
| github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= | ||||
| github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | |||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| @@ -715,12 +702,13 @@ github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 h1:wR6aLKdbJ5E8m+NZ | |||||
| github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY= | github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= | |||||
| github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= | ||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | |||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||||
| github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | ||||
| github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | ||||
| github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= | ||||
| @@ -731,7 +719,6 @@ github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481/go.mod h1:ahpPrc7 | |||||
| github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= | github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= | ||||
| github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= | ||||
| github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= | ||||
| github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= | |||||
| github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= | github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= | ||||
| github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= | github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= | ||||
| github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= | github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= | ||||
| @@ -746,7 +733,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr | |||||
| github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | ||||
| github.com/unknwon/cae v1.0.0 h1:i39lOFaBXZxhGjQOy/RNbi8uzettCs6OQxpR0xXohGU= | github.com/unknwon/cae v1.0.0 h1:i39lOFaBXZxhGjQOy/RNbi8uzettCs6OQxpR0xXohGU= | ||||
| github.com/unknwon/cae v1.0.0/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU= | github.com/unknwon/cae v1.0.0/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU= | ||||
| github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= | |||||
| github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= | github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= | ||||
| github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= | github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= | ||||
| github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= | github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= | ||||
| @@ -772,7 +758,6 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: | |||||
| github.com/yohcop/openid-go v1.0.0 h1:EciJ7ZLETHR3wOtxBvKXx9RV6eyHZpCaSZ1inbBaUXE= | github.com/yohcop/openid-go v1.0.0 h1:EciJ7ZLETHR3wOtxBvKXx9RV6eyHZpCaSZ1inbBaUXE= | ||||
| github.com/yohcop/openid-go v1.0.0/go.mod h1:/408xiwkeItSPJZSTPF7+VtZxPkPrRRpRNK2vjGh6yI= | github.com/yohcop/openid-go v1.0.0/go.mod h1:/408xiwkeItSPJZSTPF7+VtZxPkPrRRpRNK2vjGh6yI= | ||||
| github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.25 h1:isv+Q6HQAmmL2Ofcmg8QauBmDPlUUnSoNhEcC940Rds= | |||||
| github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo= | github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo= | ||||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| @@ -813,16 +798,15 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U | |||||
| golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ= | |||||
| golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= | |||||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= | golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= | ||||
| golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | ||||
| golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0= | |||||
| golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| @@ -832,9 +816,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk | |||||
| golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | ||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= | |||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= | |||||
| golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= | ||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| @@ -865,7 +847,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL | |||||
| golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= | |||||
| golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= | golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= | ||||
| golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| @@ -876,7 +857,6 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG | |||||
| golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | |||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| @@ -915,13 +895,10 @@ golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7w | |||||
| golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= | |||||
| golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= | |||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= | |||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI= | golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI= | ||||
| golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @@ -932,7 +909,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | |||||
| golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= | |||||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= | ||||
| golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| @@ -955,14 +931,11 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw | |||||
| golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20200225230052-807dcd883420 h1:4RJNOV+2rLxMEfr6QIpC7GEv9MjD6ApGXTCLrNF9+eA= | |||||
| golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI= | |||||
| golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= | golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= | ||||
| golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53 h1:vmsb6v0zUdmUlXfwKaYrHPPRCV0lHq/IwNIf0ASGjyQ= | golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53 h1:vmsb6v0zUdmUlXfwKaYrHPPRCV0lHq/IwNIf0ASGjyQ= | ||||
| golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53/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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= | |||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| @@ -984,7 +957,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 | |||||
| google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||
| google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||
| google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo= | |||||
| google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||
| google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= | google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= | ||||
| google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| @@ -1016,7 +988,6 @@ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLY | |||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= | |||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= | google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= | ||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| @@ -1027,7 +998,6 @@ gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8 | |||||
| gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= | gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | |||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| @@ -1058,12 +1028,12 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= | |||||
| gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= | ||||
| gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= | ||||
| gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | |||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= | |||||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | ||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | |||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||||
| grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= | ||||
| honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| @@ -1078,13 +1048,11 @@ sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2 | |||||
| sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= | sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= | ||||
| strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= | strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= | ||||
| strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= | strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= | ||||
| xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8= | |||||
| xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= | xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= | ||||
| xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI= | xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI= | ||||
| xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= | xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= | ||||
| xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw= | xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw= | ||||
| xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= | xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= | ||||
| xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM= | |||||
| xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY= | xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY= | ||||
| xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw= | xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw= | ||||
| xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY= | xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY= | ||||
| @@ -346,6 +346,25 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) { | |||||
| return actions, nil | return actions, nil | ||||
| } | } | ||||
| func GetLast20PublicFeeds() ([]*Action, error) { | |||||
| cond := builder.NewCond() | |||||
| cond = cond.And(builder.Eq{"is_private": false}) | |||||
| cond = cond.And(builder.Eq{"is_deleted": false}) | |||||
| actions := make([]*Action, 0, 20) | |||||
| if err := x.Limit(30).Desc("id").Where(cond).Find(&actions); err != nil { | |||||
| return nil, fmt.Errorf("Find: %v", err) | |||||
| } | |||||
| if err := ActionList(actions).LoadAttributes(); err != nil { | |||||
| return nil, fmt.Errorf("LoadAttributes: %v", err) | |||||
| } | |||||
| return actions, nil | |||||
| } | |||||
| func GetUnTransformedActions() ([]*Action, error) { | func GetUnTransformedActions() ([]*Action, error) { | ||||
| actions := make([]*Action, 0, 10) | actions := make([]*Action, 0, 10) | ||||
| err := x.Where("op_type = ?", ActionCommitRepo). | err := x.Where("op_type = ?", ActionCommitRepo). | ||||
| @@ -19,8 +19,8 @@ type JobType string | |||||
| type ModelArtsJobStatus string | type ModelArtsJobStatus string | ||||
| const ( | const ( | ||||
| NPUResource = "NPU" | |||||
| GPUResource = "CPU/GPU" | |||||
| NPUResource = "NPU" | |||||
| GPUResource = "CPU/GPU" | |||||
| JobWaiting CloudbrainStatus = "WAITING" | JobWaiting CloudbrainStatus = "WAITING" | ||||
| JobStopped CloudbrainStatus = "STOPPED" | JobStopped CloudbrainStatus = "STOPPED" | ||||
| @@ -210,7 +210,7 @@ type CloudbrainsOptions struct { | |||||
| JobType string | JobType string | ||||
| VersionName string | VersionName string | ||||
| IsLatestVersion string | IsLatestVersion string | ||||
| JobTypeNot bool | |||||
| JobTypeNot bool | |||||
| } | } | ||||
| type TaskPod struct { | type TaskPod struct { | ||||
| @@ -975,7 +975,9 @@ func QueryModelTrainJobList(repoId int64) ([]*CloudbrainInfo, int, error) { | |||||
| cond = cond.And( | cond = cond.And( | ||||
| builder.Eq{"Status": "COMPLETED"}, | builder.Eq{"Status": "COMPLETED"}, | ||||
| ) | ) | ||||
| cond = cond.And( | |||||
| builder.Eq{"job_type": "TRAIN"}, | |||||
| ) | |||||
| cloudbrains := make([]*CloudbrainInfo, 0) | cloudbrains := make([]*CloudbrainInfo, 0) | ||||
| if err := sess.Select("job_id,job_name").Table(&Cloudbrain{}).Where(cond).OrderBy("created_unix DESC"). | if err := sess.Select("job_id,job_name").Table(&Cloudbrain{}).Where(cond).OrderBy("created_unix DESC"). | ||||
| Find(&cloudbrains); err != nil { | Find(&cloudbrains); err != nil { | ||||
| @@ -1208,3 +1210,28 @@ func GetCloudbrainTrainJobCountByUserID(userID int64) (int, error) { | |||||
| And("job_type = ? and user_id = ? and type = ?", JobTypeTrain, userID, TypeCloudBrainTwo).Count(new(Cloudbrain)) | And("job_type = ? and user_id = ? and type = ?", JobTypeTrain, userID, TypeCloudBrainTwo).Count(new(Cloudbrain)) | ||||
| return int(count), err | return int(count), err | ||||
| } | } | ||||
| func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| if err = sess.Begin(); err != nil { | |||||
| return err | |||||
| } | |||||
| if _, err = sess.Delete(old); err != nil { | |||||
| sess.Rollback() | |||||
| return err | |||||
| } | |||||
| if _, err = sess.Insert(new); err != nil { | |||||
| sess.Rollback() | |||||
| return err | |||||
| } | |||||
| if err = sess.Commit(); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -143,6 +143,12 @@ func init() { | |||||
| new(SummaryStatistic), | new(SummaryStatistic), | ||||
| new(UserBusinessAnalysis), | new(UserBusinessAnalysis), | ||||
| new(UserBusinessAnalysisAll), | new(UserBusinessAnalysisAll), | ||||
| new(UserBusinessAnalysisCurrentYear), | |||||
| new(UserBusinessAnalysisLast30Day), | |||||
| new(UserBusinessAnalysisLastMonth), | |||||
| new(UserBusinessAnalysisCurrentMonth), | |||||
| new(UserBusinessAnalysisCurrentWeek), | |||||
| new(UserBusinessAnalysisYesterday), | |||||
| new(UserLoginLog), | new(UserLoginLog), | ||||
| ) | ) | ||||
| @@ -6,6 +6,7 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/git" | |||||
| "context" | "context" | ||||
| "crypto/md5" | "crypto/md5" | ||||
| "errors" | "errors" | ||||
| @@ -2519,3 +2520,53 @@ func UpdateRepositoryCommitNum(repo *Repository) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| type RepoFile struct { | |||||
| CommitId string | |||||
| Content []byte | |||||
| } | |||||
| // ReadLatestFileInRepo read latest version of file in repository | |||||
| // return a RepoFile | |||||
| func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFile, error) { | |||||
| var err error | |||||
| repoPath := RepoPath(userName, repoName) | |||||
| gitRepo, err := git.OpenRepository(repoPath) | |||||
| if err != nil { | |||||
| log.Error("ReadLatestFileInRepo error when OpenRepository,error=%v", err) | |||||
| return nil, err | |||||
| } | |||||
| commitID, err := gitRepo.GetBranchCommitID(refName) | |||||
| if err != nil { | |||||
| log.Error("ReadLatestFileInRepo error when GetBranchCommitID,error=%v", err) | |||||
| return nil, err | |||||
| } | |||||
| commit, err := gitRepo.GetBranchCommit(refName) | |||||
| if err != nil { | |||||
| log.Error("ReadLatestFileInRepo error when GetBranchCommit,error=%v", err) | |||||
| return nil, err | |||||
| } | |||||
| blob, err := commit.GetBlobByPath(treePath) | |||||
| if err != nil { | |||||
| log.Error("ReadLatestFileInRepo error when GetBlobByPath,error=%v", err) | |||||
| return nil, err | |||||
| } | |||||
| reader, err := blob.DataAsync() | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| defer func() { | |||||
| if err = reader.Close(); err != nil { | |||||
| log.Error("ReadLatestFileInRepo: Close: %v", err) | |||||
| } | |||||
| }() | |||||
| buf := make([]byte, 1024) | |||||
| n, _ := reader.Read(buf) | |||||
| if n >= 0 { | |||||
| buf = buf[:n] | |||||
| } | |||||
| return &RepoFile{CommitId: commitID, Content: buf}, nil | |||||
| } | |||||
| @@ -280,13 +280,16 @@ func notifyWatchers(e Engine, actions ...*Action) error { | |||||
| // NotifyWatchers creates batch of actions for every watcher. | // NotifyWatchers creates batch of actions for every watcher. | ||||
| func NotifyWatchers(actions ...*Action) error { | func NotifyWatchers(actions ...*Action) error { | ||||
| error := notifyWatchers(x, actions...) | |||||
| producer(actions...) | producer(actions...) | ||||
| return notifyWatchers(x, actions...) | |||||
| return error | |||||
| } | } | ||||
| func producer(actions ...*Action) { | func producer(actions ...*Action) { | ||||
| for _, action := range actions { | for _, action := range actions { | ||||
| ActionChan <- action | |||||
| if !action.IsPrivate{ | |||||
| ActionChan <- action | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -10,10 +10,12 @@ import ( | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/timeutil" | "code.gitea.io/gitea/modules/timeutil" | ||||
| "xorm.io/builder" | "xorm.io/builder" | ||||
| "xorm.io/xorm" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| Page_SIZE = 2000 | |||||
| PAGE_SIZE = 2000 | |||||
| BATCH_INSERT_SIZE = 50 | |||||
| ) | ) | ||||
| type UserBusinessAnalysisAll struct { | type UserBusinessAnalysisAll struct { | ||||
| @@ -163,14 +165,6 @@ func (ulist UserBusinessAnalysisList) Less(i, j int) bool { | |||||
| return ulist[i].ID > ulist[j].ID | return ulist[i].ID > ulist[j].ID | ||||
| } | } | ||||
| type UserBusinessAnalysisAllList []*UserBusinessAnalysisAll | |||||
| func (ulist UserBusinessAnalysisAllList) Swap(i, j int) { ulist[i], ulist[j] = ulist[j], ulist[i] } | |||||
| func (ulist UserBusinessAnalysisAllList) Len() int { return len(ulist) } | |||||
| func (ulist UserBusinessAnalysisAllList) Less(i, j int) bool { | |||||
| return ulist[i].ID > ulist[j].ID | |||||
| } | |||||
| func getLastCountDate() int64 { | func getLastCountDate() int64 { | ||||
| statictisSess := xStatistic.NewSession() | statictisSess := xStatistic.NewSession() | ||||
| defer statictisSess.Close() | defer statictisSess.Close() | ||||
| @@ -189,6 +183,29 @@ func getLastCountDate() int64 { | |||||
| return pageStartTime.Unix() | return pageStartTime.Unix() | ||||
| } | } | ||||
| func QueryUserStaticDataByTableName(start int, pageSize int, tableName string, queryObj interface{}, userName string) ([]*UserBusinessAnalysisAll, int64) { | |||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| var cond = builder.NewCond() | |||||
| if len(userName) > 0 { | |||||
| cond = cond.And( | |||||
| builder.Like{"name", userName}, | |||||
| ) | |||||
| } | |||||
| allCount, err := statictisSess.Where(cond).Count(queryObj) | |||||
| if err != nil { | |||||
| log.Info("query error." + err.Error()) | |||||
| return nil, 0 | |||||
| } | |||||
| log.Info("query return total:" + fmt.Sprint(allCount)) | |||||
| userBusinessAnalysisAllList := make([]*UserBusinessAnalysisAll, 0) | |||||
| if err := statictisSess.Table(tableName).Where(cond).OrderBy("commit_count desc,id desc").Limit(pageSize, start). | |||||
| Find(&userBusinessAnalysisAllList); err != nil { | |||||
| return nil, 0 | |||||
| } | |||||
| return userBusinessAnalysisAllList, allCount | |||||
| } | |||||
| func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysisAll, int64) { | func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysisAll, int64) { | ||||
| log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime) + " isAll=" + fmt.Sprint(opts.IsAll)) | log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime) + " isAll=" + fmt.Sprint(opts.IsAll)) | ||||
| @@ -202,9 +219,9 @@ func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusi | |||||
| } | } | ||||
| log.Info("query return total:" + fmt.Sprint(allCount)) | log.Info("query return total:" + fmt.Sprint(allCount)) | ||||
| pageSize := 1000 | |||||
| pageSize := PAGE_SIZE | |||||
| totalPage := int(allCount) / pageSize | totalPage := int(allCount) / pageSize | ||||
| userBusinessAnalysisReturnList := UserBusinessAnalysisAllList{} | |||||
| userBusinessAnalysisReturnList := make([]*UserBusinessAnalysisAll, 0) | |||||
| for i := 0; i <= int(totalPage); i++ { | for i := 0; i <= int(totalPage); i++ { | ||||
| userBusinessAnalysisAllList := make([]*UserBusinessAnalysisAll, 0) | userBusinessAnalysisAllList := make([]*UserBusinessAnalysisAll, 0) | ||||
| if err := statictisSess.Table("user_business_analysis_all").OrderBy("id desc").Limit(pageSize, i*pageSize). | if err := statictisSess.Table("user_business_analysis_all").OrderBy("id desc").Limit(pageSize, i*pageSize). | ||||
| @@ -217,7 +234,6 @@ func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusi | |||||
| } | } | ||||
| } | } | ||||
| sort.Sort(userBusinessAnalysisReturnList) | |||||
| log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | ||||
| return userBusinessAnalysisReturnList, allCount | return userBusinessAnalysisReturnList, allCount | ||||
| } | } | ||||
| @@ -337,28 +353,24 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus | |||||
| return userBusinessAnalysisReturnList, count | return userBusinessAnalysisReturnList, count | ||||
| } | } | ||||
| func RefreshUserStaticAllTabel(wikiCountMap map[string]int, CommitCodeSizeMap map[string]*git.UserKPIStats) { | |||||
| func refreshUserStaticTable(wikiCountMap map[string]int, CommitCodeSizeMap map[string]*git.UserKPIStats, tableName string, pageStartTime time.Time, pageEndTime time.Time) { | |||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| statictisSess := xStatistic.NewSession() | statictisSess := xStatistic.NewSession() | ||||
| defer statictisSess.Close() | defer statictisSess.Close() | ||||
| log.Info("truncate all data from table: user_business_analysis_all") | |||||
| statictisSess.Exec("TRUNCATE TABLE user_business_analysis_all") | |||||
| log.Info("truncate all data from table: " + tableName) | |||||
| statictisSess.Exec("TRUNCATE TABLE " + tableName) | |||||
| currentTimeNow := time.Now() | |||||
| startTime := currentTimeNow.AddDate(0, 0, -1) | |||||
| pageStartTime := time.Date(2021, 11, 5, 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| log.Info("pageStartTime:" + pageStartTime.Format("2006-01-02 15:04:05")) | log.Info("pageStartTime:" + pageStartTime.Format("2006-01-02 15:04:05")) | ||||
| pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||||
| log.Info("pageEndTime time:" + pageEndTime.Format("2006-01-02 15:04:05")) | log.Info("pageEndTime time:" + pageEndTime.Format("2006-01-02 15:04:05")) | ||||
| start_unix := pageStartTime.Unix() | start_unix := pageStartTime.Unix() | ||||
| end_unix := pageEndTime.Unix() | end_unix := pageEndTime.Unix() | ||||
| currentTimeNow := time.Now() | |||||
| startTime := currentTimeNow.AddDate(0, 0, -1) | |||||
| CodeMergeCountMap := queryPullRequest(start_unix, end_unix) | CodeMergeCountMap := queryPullRequest(start_unix, end_unix) | ||||
| CommitCountMap := queryCommitAction(start_unix, end_unix, 5) | CommitCountMap := queryCommitAction(start_unix, end_unix, 5) | ||||
| IssueCountMap := queryCreateIssue(start_unix, end_unix) | IssueCountMap := queryCreateIssue(start_unix, end_unix) | ||||
| @@ -385,12 +397,14 @@ func RefreshUserStaticAllTabel(wikiCountMap map[string]int, CommitCodeSizeMap ma | |||||
| } | } | ||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| insertCount := 0 | |||||
| dateRecordBatch := make([]UserBusinessAnalysisAll, 0) | |||||
| for { | for { | ||||
| sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| userList := make([]*User, 0) | userList := make([]*User, 0) | ||||
| sess.Find(&userList) | sess.Find(&userList) | ||||
| for i, userRecord := range userList { | |||||
| log.Info("insert all static, i=" + fmt.Sprint(i) + " userName=" + userRecord.Name) | |||||
| for _, userRecord := range userList { | |||||
| var dateRecordAll UserBusinessAnalysisAll | var dateRecordAll UserBusinessAnalysisAll | ||||
| dateRecordAll.ID = userRecord.ID | dateRecordAll.ID = userRecord.ID | ||||
| dateRecordAll.Email = userRecord.Email | dateRecordAll.Email = userRecord.Email | ||||
| @@ -484,18 +498,85 @@ func RefreshUserStaticAllTabel(wikiCountMap map[string]int, CommitCodeSizeMap ma | |||||
| } | } | ||||
| dateRecordAll.CommitModelCount = 0 | dateRecordAll.CommitModelCount = 0 | ||||
| _, err = statictisSess.Insert(&dateRecordAll) | |||||
| if err != nil { | |||||
| log.Info("insert all data failed." + err.Error()) | |||||
| dateRecordBatch = append(dateRecordBatch, dateRecordAll) | |||||
| if len(dateRecordBatch) >= BATCH_INSERT_SIZE { | |||||
| insertTable(dateRecordBatch, tableName, statictisSess) | |||||
| insertCount += BATCH_INSERT_SIZE | |||||
| if err != nil { | |||||
| log.Info("insert all data failed." + err.Error()) | |||||
| } | |||||
| dateRecordBatch = make([]UserBusinessAnalysisAll, 0) | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| } | } | ||||
| if len(dateRecordBatch) > 0 { | |||||
| insertTable(dateRecordBatch, tableName, statictisSess) | |||||
| insertCount += len(dateRecordBatch) | |||||
| if err != nil { | |||||
| log.Info("insert all data failed." + err.Error()) | |||||
| } | |||||
| } | |||||
| log.Info("refresh data finished.tableName=" + tableName + " total record:" + fmt.Sprint(insertCount)) | |||||
| } | |||||
| func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, statictisSess *xorm.Session) { | |||||
| insertBatchSql := "INSERT INTO public." + tableName + | |||||
| "(id, count_date, code_merge_count, commit_count, issue_count, comment_count, focus_repo_count, star_repo_count, watched_count, gitea_age_month, commit_code_size, commit_dataset_size, " + | |||||
| "commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date) " + | |||||
| "VALUES" | |||||
| for i, record := range dateRecords { | |||||
| insertBatchSql += "(" + fmt.Sprint(record.ID) + ", " + fmt.Sprint(record.CountDate) + ", " + fmt.Sprint(record.CodeMergeCount) + ", " + fmt.Sprint(record.CommitCount) + | |||||
| ", " + fmt.Sprint(record.IssueCount) + ", " + fmt.Sprint(record.CommentCount) + ", " + fmt.Sprint(record.FocusRepoCount) + ", " + fmt.Sprint(record.StarRepoCount) + | |||||
| ", " + fmt.Sprint(record.WatchedCount) + ", " + fmt.Sprint(record.GiteaAgeMonth) + ", " + fmt.Sprint(record.CommitCodeSize) + ", " + fmt.Sprint(record.CommitDatasetSize) + | |||||
| ", " + fmt.Sprint(record.CommitModelCount) + ", " + fmt.Sprint(record.SolveIssueCount) + ", " + fmt.Sprint(record.EncyclopediasCount) + ", " + fmt.Sprint(record.RegistDate) + | |||||
| ", " + fmt.Sprint(record.CreateRepoCount) + ", " + fmt.Sprint(record.LoginCount) + ", " + fmt.Sprint(record.OpenIIndex) + ", '" + record.Email + "', '" + record.Name + "', '" + record.DataDate + "')" | |||||
| if i < (len(dateRecords) - 1) { | |||||
| insertBatchSql += "," | |||||
| } | |||||
| } | |||||
| statictisSess.Exec(insertBatchSql) | |||||
| } | |||||
| func RefreshUserStaticAllTabel(wikiCountMap map[string]int, CommitCodeSizeMap map[string]*git.UserKPIStats) { | |||||
| currentTimeNow := time.Now() | |||||
| pageStartTime := time.Date(2021, 11, 5, 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_all", pageStartTime, pageEndTime) | |||||
| log.Info("refresh all data finished.") | log.Info("refresh all data finished.") | ||||
| pageStartTime = time.Date(currentTimeNow.Year(), 1, 1, 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_current_year", pageStartTime, pageEndTime) | |||||
| thisMonth := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_current_month", thisMonth, pageEndTime) | |||||
| offset := int(time.Monday - currentTimeNow.Weekday()) | |||||
| if offset > 0 { | |||||
| offset = -6 | |||||
| } | |||||
| pageStartTime = time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_current_week", pageStartTime, pageEndTime) | |||||
| pageStartTime = time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -30) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_last30_day", pageStartTime, pageEndTime) | |||||
| pageStartTime = time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -1) | |||||
| pageEndTime = time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()).AddDate(0, 0, -1) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_yesterday", pageStartTime, pageEndTime) | |||||
| pageStartTime = thisMonth.AddDate(0, -1, 0) | |||||
| pageEndTime = time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 23, 59, 59, 0, currentTimeNow.Location()).AddDate(0, 0, -1) | |||||
| refreshUserStaticTable(wikiCountMap, CommitCodeSizeMap, "user_business_analysis_last_month", pageStartTime, pageEndTime) | |||||
| } | } | ||||
| func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, endTime time.Time, isReCount bool) error { | func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, endTime time.Time, isReCount bool) error { | ||||
| @@ -550,7 +631,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| userList := make([]*User, 0) | userList := make([]*User, 0) | ||||
| sess.Find(&userList) | sess.Find(&userList) | ||||
| @@ -660,7 +741,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -700,7 +781,7 @@ func querySolveIssue(start_unix int64, end_unix int64) map[int64]int { | |||||
| issueAssigneesList := make([]*IssueAssignees, 0) | issueAssigneesList := make([]*IssueAssignees, 0) | ||||
| sess.Select("issue_assignees.*").Table("issue_assignees"). | sess.Select("issue_assignees.*").Table("issue_assignees"). | ||||
| Join("inner", "issue", "issue.id=issue_assignees.issue_id"). | Join("inner", "issue", "issue.id=issue_assignees.issue_id"). | ||||
| Where(cond).OrderBy("issue_assignees.id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| Where(cond).OrderBy("issue_assignees.id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| sess.Find(&issueAssigneesList) | sess.Find(&issueAssigneesList) | ||||
| @@ -712,7 +793,7 @@ func querySolveIssue(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap[issueAssigneesRecord.AssigneeID] += 1 | resultMap[issueAssigneesRecord.AssigneeID] += 1 | ||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -735,7 +816,7 @@ func queryPullRequest(start_unix int64, end_unix int64) map[int64]int { | |||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| issueList := make([]*Issue, 0) | issueList := make([]*Issue, 0) | ||||
| sess.Select("issue.*").Table("issue").Join("inner", "pull_request", "issue.id=pull_request.issue_id").Where(cond).OrderBy("issue.id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("issue.*").Table("issue").Join("inner", "pull_request", "issue.id=pull_request.issue_id").Where(cond).OrderBy("issue.id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| sess.Find(&issueList) | sess.Find(&issueList) | ||||
| log.Info("query issue(PR) size=" + fmt.Sprint(len(issueList))) | log.Info("query issue(PR) size=" + fmt.Sprint(len(issueList))) | ||||
| for _, issueRecord := range issueList { | for _, issueRecord := range issueList { | ||||
| @@ -745,7 +826,7 @@ func queryPullRequest(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap[issueRecord.PosterID] += 1 | resultMap[issueRecord.PosterID] += 1 | ||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -768,7 +849,7 @@ func queryCommitAction(start_unix int64, end_unix int64, actionType int64) map[i | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,user_id,op_type,act_user_id").Table("action").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,user_id,op_type,act_user_id").Table("action").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| actionList := make([]*Action, 0) | actionList := make([]*Action, 0) | ||||
| sess.Find(&actionList) | sess.Find(&actionList) | ||||
| @@ -781,7 +862,7 @@ func queryCommitAction(start_unix int64, end_unix int64, actionType int64) map[i | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -805,7 +886,7 @@ func queryCreateIssue(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,poster_id").Table("issue").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,poster_id").Table("issue").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| issueList := make([]*Issue, 0) | issueList := make([]*Issue, 0) | ||||
| sess.Find(&issueList) | sess.Find(&issueList) | ||||
| log.Info("query issue size=" + fmt.Sprint(len(issueList))) | log.Info("query issue size=" + fmt.Sprint(len(issueList))) | ||||
| @@ -816,7 +897,7 @@ func queryCreateIssue(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap[issueRecord.PosterID] += 1 | resultMap[issueRecord.PosterID] += 1 | ||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -839,7 +920,7 @@ func queryComment(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,type,poster_id").Table("comment").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,type,poster_id").Table("comment").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| commentList := make([]*Comment, 0) | commentList := make([]*Comment, 0) | ||||
| sess.Find(&commentList) | sess.Find(&commentList) | ||||
| log.Info("query Comment size=" + fmt.Sprint(len(commentList))) | log.Info("query Comment size=" + fmt.Sprint(len(commentList))) | ||||
| @@ -850,7 +931,7 @@ func queryComment(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap[commentRecord.PosterID] += 1 | resultMap[commentRecord.PosterID] += 1 | ||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -875,7 +956,7 @@ func queryWatch(start_unix int64, end_unix int64) map[int64]int { | |||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| watchList := make([]*Watch, 0) | watchList := make([]*Watch, 0) | ||||
| sess.Select("id,user_id,repo_id").Table("watch").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,user_id,repo_id").Table("watch").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| sess.Find(&watchList) | sess.Find(&watchList) | ||||
| log.Info("query Watch size=" + fmt.Sprint(len(watchList))) | log.Info("query Watch size=" + fmt.Sprint(len(watchList))) | ||||
| @@ -887,7 +968,7 @@ func queryWatch(start_unix int64, end_unix int64) map[int64]int { | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -913,7 +994,7 @@ func queryStar(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,uid,repo_id").Table("star").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,uid,repo_id").Table("star").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| starList := make([]*Star, 0) | starList := make([]*Star, 0) | ||||
| sess.Find(&starList) | sess.Find(&starList) | ||||
| @@ -926,7 +1007,7 @@ func queryStar(start_unix int64, end_unix int64) map[int64]int { | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -949,7 +1030,7 @@ func queryFollow(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,user_id,follow_id").Table("follow").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,user_id,follow_id").Table("follow").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| followList := make([]*Follow, 0) | followList := make([]*Follow, 0) | ||||
| sess.Find(&followList) | sess.Find(&followList) | ||||
| @@ -962,7 +1043,7 @@ func queryFollow(start_unix int64, end_unix int64) map[int64]int { | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -985,7 +1066,7 @@ func queryDatasetSize(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,uploader_id,size").Table("attachment").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,uploader_id,size").Table("attachment").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| attachmentList := make([]*Attachment, 0) | attachmentList := make([]*Attachment, 0) | ||||
| sess.Find(&attachmentList) | sess.Find(&attachmentList) | ||||
| @@ -998,7 +1079,7 @@ func queryDatasetSize(start_unix int64, end_unix int64) map[int64]int { | |||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -1021,7 +1102,7 @@ func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| sess.Select("id,owner_id,name").Table("repository").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| sess.Select("id,owner_id,name").Table("repository").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| repoList := make([]*Repository, 0) | repoList := make([]*Repository, 0) | ||||
| sess.Find(&repoList) | sess.Find(&repoList) | ||||
| log.Info("query Repository size=" + fmt.Sprint(len(repoList))) | log.Info("query Repository size=" + fmt.Sprint(len(repoList))) | ||||
| @@ -1032,7 +1113,7 @@ func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap[repoRecord.OwnerID] += 1 | resultMap[repoRecord.OwnerID] += 1 | ||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -1111,7 +1192,7 @@ func queryLoginCount(start_unix int64, end_unix int64) map[int64]int { | |||||
| var indexTotal int64 | var indexTotal int64 | ||||
| indexTotal = 0 | indexTotal = 0 | ||||
| for { | for { | ||||
| statictisSess.Select("id,u_id").Table("user_login_log").Where(cond).OrderBy("id asc").Limit(Page_SIZE, int(indexTotal)) | |||||
| statictisSess.Select("id,u_id").Table("user_login_log").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal)) | |||||
| userLoginLogList := make([]*UserLoginLog, 0) | userLoginLogList := make([]*UserLoginLog, 0) | ||||
| statictisSess.Find(&userLoginLogList) | statictisSess.Find(&userLoginLogList) | ||||
| log.Info("query user login size=" + fmt.Sprint(len(userLoginLogList))) | log.Info("query user login size=" + fmt.Sprint(len(userLoginLogList))) | ||||
| @@ -1122,7 +1203,7 @@ func queryLoginCount(start_unix int64, end_unix int64) map[int64]int { | |||||
| resultMap[loginRecord.UId] += 1 | resultMap[loginRecord.UId] += 1 | ||||
| } | } | ||||
| } | } | ||||
| indexTotal += Page_SIZE | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | if indexTotal >= count { | ||||
| break | break | ||||
| } | } | ||||
| @@ -0,0 +1,267 @@ | |||||
| package models | |||||
| import "code.gitea.io/gitea/modules/timeutil" | |||||
| type UserBusinessAnalysisCurrentYear struct { | |||||
| ID int64 `xorm:"pk"` | |||||
| CountDate int64 `xorm:"pk"` | |||||
| //action :ActionMergePullRequest // 11 | |||||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //action :ActionCommitRepo | |||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue // 10 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //comment table current date | |||||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //watch table current date | |||||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //star table current date | |||||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //follow table | |||||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // user table | |||||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // | |||||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //attachement table | |||||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //0 | |||||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue, issueassignees | |||||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //baike | |||||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||||
| //repo | |||||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //login count, from elk | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //openi index | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| Email string `xorm:"NOT NULL"` | |||||
| //user | |||||
| Name string `xorm:"NOT NULL"` | |||||
| DataDate string `xorm:"NULL"` | |||||
| } | |||||
| type UserBusinessAnalysisLast30Day struct { | |||||
| ID int64 `xorm:"pk"` | |||||
| CountDate int64 `xorm:"pk"` | |||||
| //action :ActionMergePullRequest // 11 | |||||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //action :ActionCommitRepo | |||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue // 10 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //comment table current date | |||||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //watch table current date | |||||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //star table current date | |||||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //follow table | |||||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // user table | |||||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // | |||||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //attachement table | |||||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //0 | |||||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue, issueassignees | |||||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //baike | |||||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||||
| //repo | |||||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //login count, from elk | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //openi index | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| Email string `xorm:"NOT NULL"` | |||||
| //user | |||||
| Name string `xorm:"NOT NULL"` | |||||
| DataDate string `xorm:"NULL"` | |||||
| } | |||||
| type UserBusinessAnalysisLastMonth struct { | |||||
| ID int64 `xorm:"pk"` | |||||
| CountDate int64 `xorm:"pk"` | |||||
| //action :ActionMergePullRequest // 11 | |||||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //action :ActionCommitRepo | |||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue // 10 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //comment table current date | |||||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //watch table current date | |||||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //star table current date | |||||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //follow table | |||||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // user table | |||||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // | |||||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //attachement table | |||||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //0 | |||||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue, issueassignees | |||||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //baike | |||||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||||
| //repo | |||||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //login count, from elk | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //openi index | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| Email string `xorm:"NOT NULL"` | |||||
| //user | |||||
| Name string `xorm:"NOT NULL"` | |||||
| DataDate string `xorm:"NULL"` | |||||
| } | |||||
| type UserBusinessAnalysisCurrentMonth struct { | |||||
| ID int64 `xorm:"pk"` | |||||
| CountDate int64 `xorm:"pk"` | |||||
| //action :ActionMergePullRequest // 11 | |||||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //action :ActionCommitRepo | |||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue // 10 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //comment table current date | |||||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //watch table current date | |||||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //star table current date | |||||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //follow table | |||||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // user table | |||||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // | |||||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //attachement table | |||||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //0 | |||||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue, issueassignees | |||||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //baike | |||||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||||
| //repo | |||||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //login count, from elk | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //openi index | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| Email string `xorm:"NOT NULL"` | |||||
| //user | |||||
| Name string `xorm:"NOT NULL"` | |||||
| DataDate string `xorm:"NULL"` | |||||
| } | |||||
| type UserBusinessAnalysisCurrentWeek struct { | |||||
| ID int64 `xorm:"pk"` | |||||
| CountDate int64 `xorm:"pk"` | |||||
| //action :ActionMergePullRequest // 11 | |||||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //action :ActionCommitRepo | |||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue // 10 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //comment table current date | |||||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //watch table current date | |||||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //star table current date | |||||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //follow table | |||||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // user table | |||||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // | |||||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //attachement table | |||||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //0 | |||||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue, issueassignees | |||||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //baike | |||||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||||
| //repo | |||||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //login count, from elk | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //openi index | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| Email string `xorm:"NOT NULL"` | |||||
| //user | |||||
| Name string `xorm:"NOT NULL"` | |||||
| DataDate string `xorm:"NULL"` | |||||
| } | |||||
| type UserBusinessAnalysisYesterday struct { | |||||
| ID int64 `xorm:"pk"` | |||||
| CountDate int64 `xorm:"pk"` | |||||
| //action :ActionMergePullRequest // 11 | |||||
| CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //action :ActionCommitRepo | |||||
| CommitCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue // 10 | |||||
| IssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //comment table current date | |||||
| CommentCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //watch table current date | |||||
| FocusRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //star table current date | |||||
| StarRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //follow table | |||||
| WatchedCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // user table | |||||
| GiteaAgeMonth int `xorm:"NOT NULL DEFAULT 0"` | |||||
| // | |||||
| CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //attachement table | |||||
| CommitDatasetSize int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //0 | |||||
| CommitModelCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //issue, issueassignees | |||||
| SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //baike | |||||
| EncyclopediasCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` | |||||
| //repo | |||||
| CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //login count, from elk | |||||
| LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| //openi index | |||||
| OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //user | |||||
| Email string `xorm:"NOT NULL"` | |||||
| //user | |||||
| Name string `xorm:"NOT NULL"` | |||||
| DataDate string `xorm:"NULL"` | |||||
| } | |||||
| @@ -82,7 +82,7 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| job, err := models.GetCloudbrainByJobID(jobID) | job, err := models.GetCloudbrainByJobID(jobID) | ||||
| ctx.Cloudbrain = job | |||||
| if !isAdminOrOwnerOrJobCreater(ctx, job, err) { | if !isAdminOrOwnerOrJobCreater(ctx, job, err) { | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| @@ -94,6 +94,7 @@ func AdminOrJobCreaterRight(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| job, err := models.GetCloudbrainByJobID(jobID) | job, err := models.GetCloudbrainByJobID(jobID) | ||||
| ctx.Cloudbrain = job | |||||
| if !isAdminOrJobCreater(ctx, job, err) { | if !isAdminOrJobCreater(ctx, job, err) { | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| @@ -222,7 +223,7 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||||
| return nil | return nil | ||||
| } | } | ||||
| func RestartTask(ctx *context.Context, task *models.Cloudbrain) error { | |||||
| func RestartTask(ctx *context.Context, task *models.Cloudbrain, newJobID *string) error { | |||||
| dataActualPath := setting.Attachment.Minio.RealPath + | dataActualPath := setting.Attachment.Minio.RealPath + | ||||
| setting.Attachment.Minio.Bucket + "/" + | setting.Attachment.Minio.Bucket + "/" + | ||||
| setting.Attachment.Minio.BasePath + | setting.Attachment.Minio.BasePath + | ||||
| @@ -312,7 +313,7 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain) error { | |||||
| }, | }, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("CreateJob failed:", err.Error(), ctx.Data["MsgID"]) | |||||
| log.Error("CreateJob failed:%v", err.Error(), ctx.Data["MsgID"]) | |||||
| return err | return err | ||||
| } | } | ||||
| if jobResult.Code != Success { | if jobResult.Code != Success { | ||||
| @@ -321,14 +322,29 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain) error { | |||||
| } | } | ||||
| var jobID = jobResult.Payload["jobId"].(string) | var jobID = jobResult.Payload["jobId"].(string) | ||||
| task.JobID = jobID | |||||
| task.Status = string(models.JobWaiting) | |||||
| err = models.UpdateJob(task) | |||||
| newTask := &models.Cloudbrain{ | |||||
| Status: string(models.JobWaiting), | |||||
| UserID: task.UserID, | |||||
| RepoID: task.RepoID, | |||||
| JobID: jobID, | |||||
| JobName: task.JobName, | |||||
| SubTaskName: task.SubTaskName, | |||||
| JobType: task.JobType, | |||||
| Type: task.Type, | |||||
| Uuid: task.Uuid, | |||||
| Image: task.Image, | |||||
| GpuQueue: task.GpuQueue, | |||||
| ResourceSpecId: task.ResourceSpecId, | |||||
| ComputeResource: task.ComputeResource, | |||||
| } | |||||
| err = models.RestartCloudbrain(task, newTask) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("UpdateJob(%s) failed:%v", jobName, err.Error(), ctx.Data["MsgID"]) | |||||
| log.Error("RestartCloudbrain(%s) failed:%v", jobName, err.Error(), ctx.Data["MsgID"]) | |||||
| return err | return err | ||||
| } | } | ||||
| *newJobID = jobID | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -6,6 +6,7 @@ | |||||
| package context | package context | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/routers/notice" | |||||
| "html" | "html" | ||||
| "html/template" | "html/template" | ||||
| "io" | "io" | ||||
| @@ -46,6 +47,7 @@ type Context struct { | |||||
| Repo *Repository | Repo *Repository | ||||
| Org *Organization | Org *Organization | ||||
| Cloudbrain *models.Cloudbrain | |||||
| } | } | ||||
| // IsUserSiteAdmin returns true if current user is a site admin | // IsUserSiteAdmin returns true if current user is a site admin | ||||
| @@ -345,6 +347,10 @@ func Contexter() macaron.Handler { | |||||
| ctx.Data["EnableSwagger"] = setting.API.EnableSwagger | ctx.Data["EnableSwagger"] = setting.API.EnableSwagger | ||||
| ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn | ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn | ||||
| notice, _ := notice.GetNewestNotice() | |||||
| if notice != nil { | |||||
| ctx.Data["notice"] = *notice | |||||
| } | |||||
| c.Map(ctx) | c.Map(ctx) | ||||
| } | } | ||||
| } | } | ||||
| @@ -357,7 +357,7 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job | |||||
| CommitID: req.CommitID, | CommitID: req.CommitID, | ||||
| IsLatestVersion: req.IsLatestVersion, | IsLatestVersion: req.IsLatestVersion, | ||||
| PreVersionName: req.PreVersionName, | PreVersionName: req.PreVersionName, | ||||
| ComputeResource: models.GPUResource, | |||||
| ComputeResource: models.NPUResource, | |||||
| EngineID: req.EngineID, | EngineID: req.EngineID, | ||||
| TrainUrl: req.TrainUrl, | TrainUrl: req.TrainUrl, | ||||
| BranchName: req.BranchName, | BranchName: req.BranchName, | ||||
| @@ -162,21 +162,10 @@ func ProjectViewInit(User string, Project string, Gte string, Lte string) (proje | |||||
| inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[2].FilterMatchPhrase = &projectName | inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[2].FilterMatchPhrase = &projectName | ||||
| //限定页面 | //限定页面 | ||||
| var bool Bool | var bool Bool | ||||
| bool.Should = make([]Should, 14) | |||||
| bool.Should[0].MatchPhrase.TagName = "%{[request][3]}" | |||||
| bool.Should[1].MatchPhrase.TagName = "datasets?type=0" | |||||
| bool.Should[2].MatchPhrase.TagName = "datasets?type=1" | |||||
| bool.Should[3].MatchPhrase.TagName = "issues" | |||||
| bool.Should[4].MatchPhrase.TagName = "labels" | |||||
| bool.Should[5].MatchPhrase.TagName = "pulls" | |||||
| bool.Should[6].MatchPhrase.TagName = "wiki" | |||||
| bool.Should[7].MatchPhrase.TagName = "activity" | |||||
| bool.Should[8].MatchPhrase.TagName = "cloudbrain" | |||||
| bool.Should[9].MatchPhrase.TagName = "modelarts" | |||||
| bool.Should[10].MatchPhrase.TagName = "blockchain" | |||||
| bool.Should[11].MatchPhrase.TagName = "watchers" | |||||
| bool.Should[12].MatchPhrase.TagName = "stars" | |||||
| bool.Should[13].MatchPhrase.TagName = "forks" | |||||
| bool.Should = make([]Should, len(setting.PROJECT_LIMIT_PAGES)) | |||||
| for i, pageName := range setting.PROJECT_LIMIT_PAGES { | |||||
| bool.Should[i].MatchPhrase.TagName = pageName | |||||
| } | |||||
| inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[3].Bool = &bool | inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[3].Bool = &bool | ||||
| return inputStruct | return inputStruct | ||||
| } | } | ||||
| @@ -436,6 +436,14 @@ var ( | |||||
| //home page | //home page | ||||
| RecommentRepoAddr string | RecommentRepoAddr string | ||||
| //notice config | |||||
| UserNameOfNoticeRepo string | |||||
| RepoNameOfNoticeRepo string | |||||
| RefNameOfNoticeRepo string | |||||
| TreePathOfNoticeRepo string | |||||
| CacheTimeOutSecond int | |||||
| CacheOn bool | |||||
| //labelsystem config | //labelsystem config | ||||
| LabelTaskName string | LabelTaskName string | ||||
| LabelDatasetDeleteQueue string | LabelDatasetDeleteQueue string | ||||
| @@ -508,12 +516,13 @@ var ( | |||||
| TrainJobFLAVORINFOS string | TrainJobFLAVORINFOS string | ||||
| //elk config | //elk config | ||||
| ElkUrl string | |||||
| ElkUser string | |||||
| ElkPassword string | |||||
| Index string | |||||
| TimeField string | |||||
| ElkTimeFormat string | |||||
| ElkUrl string | |||||
| ElkUser string | |||||
| ElkPassword string | |||||
| Index string | |||||
| TimeField string | |||||
| ElkTimeFormat string | |||||
| PROJECT_LIMIT_PAGES []string | |||||
| //nginx proxy | //nginx proxy | ||||
| PROXYURL string | PROXYURL string | ||||
| @@ -1235,6 +1244,14 @@ func NewContext() { | |||||
| sec = Cfg.Section("homepage") | sec = Cfg.Section("homepage") | ||||
| RecommentRepoAddr = sec.Key("Address").MustString("https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/") | RecommentRepoAddr = sec.Key("Address").MustString("https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/") | ||||
| sec = Cfg.Section("notice") | |||||
| UserNameOfNoticeRepo = sec.Key("USER_NAME").MustString("OpenIOSSG") | |||||
| RepoNameOfNoticeRepo = sec.Key("REPO_NAME").MustString("promote") | |||||
| RefNameOfNoticeRepo = sec.Key("REF_NAME").MustString("master") | |||||
| TreePathOfNoticeRepo = sec.Key("TREE_PATH").MustString("notice.json") | |||||
| CacheTimeOutSecond = sec.Key("CACHE_TIME_OUT_SECOND").MustInt(60) | |||||
| CacheOn = sec.Key("CACHE_ON").MustBool(true) | |||||
| sec = Cfg.Section("cloudbrain") | sec = Cfg.Section("cloudbrain") | ||||
| CBAuthUser = sec.Key("USER").MustString("") | CBAuthUser = sec.Key("USER").MustString("") | ||||
| CBAuthPassword = sec.Key("PWD").MustString("") | CBAuthPassword = sec.Key("PWD").MustString("") | ||||
| @@ -1308,6 +1325,7 @@ func NewContext() { | |||||
| Index = sec.Key("INDEX").MustString("") | Index = sec.Key("INDEX").MustString("") | ||||
| TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest") | TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest") | ||||
| ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | ||||
| PROJECT_LIMIT_PAGES = strings.Split(sec.Key("project_limit_pages").MustString(""), ",") | |||||
| SetRadarMapConfig() | SetRadarMapConfig() | ||||
| @@ -6,6 +6,7 @@ package util | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "strconv" | |||||
| "strings" | "strings" | ||||
| ) | ) | ||||
| @@ -100,3 +101,12 @@ func NormalizeEOL(input []byte) []byte { | |||||
| } | } | ||||
| return tmp[:pos] | return tmp[:pos] | ||||
| } | } | ||||
| func AddZero(t int64) (m string) { | |||||
| if t < 10 { | |||||
| m = "0" + strconv.FormatInt(t, 10) | |||||
| return m | |||||
| } else { | |||||
| return strconv.FormatInt(t, 10) | |||||
| } | |||||
| } | |||||
| @@ -220,6 +220,37 @@ show_only_public = Showing only public | |||||
| issues.in_your_repos = In your repositories | issues.in_your_repos = In your repositories | ||||
| contributors = Contributors | contributors = Contributors | ||||
| page_title=Explore Better AI | |||||
| page_small_title=OpenI AI development cooperation platform | |||||
| page_description=The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation | |||||
| page_use=Use Now | |||||
| page_only_dynamic=Show only open source project dynamics | |||||
| page_recommend_org=Recommended organization | |||||
| page_recommend_org_desc=These excellent organizations are using Qizhi AI to develop collaboration platforms; Your organization also wants to show here, | |||||
| page_recommend_org_commit=Click here to submit | |||||
| page_recommend_org_more=More organizations | |||||
| page_recommend_repo=Recommended projects | |||||
| page_recommend_repo_desc=Excellent AI project recommendation; Your project also wants to show here, | |||||
| page_recommend_repo_commit=Click here to submit | |||||
| page_recommend_repo_go=. Click here | |||||
| page_recommend_repo_more=Project Square | |||||
| page_dev_env=Collaborative development environment | |||||
| page_dev_env_desc=The biggest difference between Qizhi AI collaborative development platform and traditional git platform is that it provides a collaborative development environment for AI development | |||||
| page_dev_env_desc_title=Unified management of development elements | |||||
| page_dev_env_desc_desc=The platform provides four elements of AI development: unified management of model code, data set, model and execution environment | |||||
| page_dev_env_desc1_title=Data collaboration and sharing | |||||
| page_dev_env_desc1_desc=By uploading data sets in the project, many project members cooperate to complete data preprocessing; You can also establish a better model with community developers by setting the data as a public dataset | |||||
| page_dev_env_desc2_title=Model management and sharing | |||||
| page_dev_env_desc2_desc=Associate the model with the code version, adjust the model in different ways based on the code history version, and save the results; The trained model can be open and shared, so that more people can use the model to test and give feedback | |||||
| page_dev_env_desc3_title=One configuration, multiple use | |||||
| page_dev_env_desc3_desc=Provide execution environment sharing, one-time configuration and multiple use, reduce the threshold of model development, and avoid spending repeated time configuring complex environments | |||||
| page_dev_yunlao=PengCheng Cloudbrain open source collaboration | |||||
| page_dev_yunlao_desc1=The platform has been connected with Pengcheng Cloudbrain and can use the rich computing resources of Pengcheng Cloudbrain to complete AI development tasks | |||||
| page_dev_yunlao_desc2=Pengcheng Cloudbrain's existing AI computing power is 100p FLOPS@FP16 (billions of half precision floating-point calculations per second), the main hardware infrastructure is composed of GPU server equipped with NVIDIA Tesla V100 and Atlas 900 AI cluster equipped with Kunpeng and shengteng processors | |||||
| page_dev_yunlao_desc3=Developers can freely choose the corresponding computing resources according to the use requirements, and can test the adaptability, performance and stability of the model in different hardware environments | |||||
| page_dev_yunlao_desc4=If your model needs more computing resources, you can also apply separately | |||||
| page_dev_yunlao_apply=Separate apply | |||||
| [explore] | [explore] | ||||
| repos = Repositories | repos = Repositories | ||||
| select_repos = Select the project | select_repos = Select the project | ||||
| @@ -424,7 +455,13 @@ static.openiindex=OpenI Index | |||||
| static.registdate=Regist Date | static.registdate=Regist Date | ||||
| static.countdate=Count Date | static.countdate=Count Date | ||||
| static.all=All | static.all=All | ||||
| static.public.user_business_analysis_current_month=Current_Month | |||||
| static.public.user_business_analysis_current_week=Current_Week | |||||
| static.public.user_business_analysis_current_year=Current_Year | |||||
| static.public.user_business_analysis_last30_day=Last_30_day | |||||
| static.public.user_business_analysis_last_month=Last_Month | |||||
| static.public.user_business_analysis_yesterday=Yesterday | |||||
| static.public.user_business_analysis_all=All | |||||
| [settings] | [settings] | ||||
| profile = Profile | profile = Profile | ||||
| account = Account | account = Account | ||||
| @@ -844,6 +881,7 @@ modelarts.current_version=Current version | |||||
| modelarts.parent_version=Parent Version | modelarts.parent_version=Parent Version | ||||
| modelarts.run_version=Run Version | modelarts.run_version=Run Version | ||||
| modelarts.train_job.compute_node=Compute Node | modelarts.train_job.compute_node=Compute Node | ||||
| modelarts.create_model = Create Model | |||||
| modelarts.train_job.basic_info=Basic Info | modelarts.train_job.basic_info=Basic Info | ||||
| @@ -2659,3 +2697,5 @@ foot.member_news = Member news | |||||
| foot.industry_advisory = Industry Advisory | foot.industry_advisory = Industry Advisory | ||||
| foot.help = help | foot.help = help | ||||
| foot.copyright= Copyright: New Generation Artificial Intelligence Open Source Open Platform (OpenI) | foot.copyright= Copyright: New Generation Artificial Intelligence Open Source Open Platform (OpenI) | ||||
| Platform_Tutorial = Tutorial | |||||
| foot.advice_feedback = Feedback | |||||
| @@ -222,6 +222,37 @@ issues.in_your_repos=属于该用户项目的 | |||||
| contributors=贡献者 | contributors=贡献者 | ||||
| page_title=探索更好的AI | |||||
| page_small_title=启智AI开发协作平台 | |||||
| page_description=面向AI领域的一站式协同开发环境,提供集代码开发、数据管理、模型调试、推理和评测为一体的AI开发流水线 | |||||
| page_use=立即使用 | |||||
| page_only_dynamic=仅展示开源项目动态 | |||||
| page_recommend_org=推荐组织 | |||||
| page_recommend_org_desc=这些优秀的组织正在使用启智AI开发协作平台;你的组织也想展示到这里, | |||||
| page_recommend_org_commit=点此提交 | |||||
| page_recommend_org_more=更多组织 | |||||
| page_recommend_repo=推荐项目 | |||||
| page_recommend_repo_desc=优秀的AI项目推荐;你的项目也想展示到这里, | |||||
| page_recommend_repo_commit=点此提交 | |||||
| page_recommend_repo_go=。进入 | |||||
| page_recommend_repo_more=项目广场 | |||||
| page_dev_env=协同开发环境 | |||||
| page_dev_env_desc=启智AI协作开发平台与传统git平台最大的不同就在于提供了面向AI开发的协同开发环境 | |||||
| page_dev_env_desc_title=开发要素统一管理 | |||||
| page_dev_env_desc_desc=平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理 | |||||
| page_dev_env_desc1_title=数据协同与共享 | |||||
| page_dev_env_desc1_desc=通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型 | |||||
| page_dev_env_desc2_title=模型管理与共享 | |||||
| page_dev_env_desc2_desc=将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 | |||||
| page_dev_env_desc3_title=一次配置,多次使用 | |||||
| page_dev_env_desc3_desc=提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 | |||||
| page_dev_yunlao=鹏城云脑开源协同 | |||||
| page_dev_yunlao_desc1=平台已经与鹏城云脑打通,可以利用鹏城云脑的丰富算力资源,完成AI开发任务 | |||||
| page_dev_yunlao_desc2=鹏城云脑现有AI算力100P FLOPS@FP16(每秒十亿亿次半精度浮点计算),主要硬件基础设施由搭载英伟达Tesla V100 的GPU服务器和搭载鲲鹏、昇腾处理器的Atlas 900 AI集群构成 | |||||
| page_dev_yunlao_desc3=开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等 | |||||
| page_dev_yunlao_desc4=如果您的模型需要更多的计算资源,也可以单独申请 | |||||
| page_dev_yunlao_apply=单独申请 | |||||
| [explore] | [explore] | ||||
| repos=项目 | repos=项目 | ||||
| select_repos=精选项目 | select_repos=精选项目 | ||||
| @@ -428,6 +459,13 @@ static.openiindex=OpenI指数 | |||||
| static.registdate=用户注册时间 | static.registdate=用户注册时间 | ||||
| static.countdate=系统统计时间 | static.countdate=系统统计时间 | ||||
| static.all=所有 | static.all=所有 | ||||
| static.public.user_business_analysis_current_month=本月 | |||||
| static.public.user_business_analysis_current_week=本周 | |||||
| static.public.user_business_analysis_current_year=今年 | |||||
| static.public.user_business_analysis_last30_day=近30天 | |||||
| static.public.user_business_analysis_last_month=上月 | |||||
| static.public.user_business_analysis_yesterday=昨天 | |||||
| static.public.user_business_analysis_all=所有 | |||||
| [settings] | [settings] | ||||
| profile=个人信息 | profile=个人信息 | ||||
| account=账号 | account=账号 | ||||
| @@ -849,6 +887,7 @@ modelarts.modify=修改 | |||||
| modelarts.current_version=当前版本 | modelarts.current_version=当前版本 | ||||
| modelarts.parent_version=父版本 | modelarts.parent_version=父版本 | ||||
| modelarts.run_version=运行版本 | modelarts.run_version=运行版本 | ||||
| modelarts.create_model=创建模型 | |||||
| @@ -1430,7 +1469,7 @@ milestones.open_tab=%d 开启中 | |||||
| milestones.close_tab=%d 已关闭 | milestones.close_tab=%d 已关闭 | ||||
| milestones.closed=于 %s关闭 | milestones.closed=于 %s关闭 | ||||
| milestones.no_due_date=暂无截止日期 | milestones.no_due_date=暂无截止日期 | ||||
| milestones.open=开启中 | |||||
| milestones.open=开启 | |||||
| milestones.close=关闭 | milestones.close=关闭 | ||||
| milestones.new_subheader=里程碑组织任务,合并请求和跟踪进度。 | milestones.new_subheader=里程碑组织任务,合并请求和跟踪进度。 | ||||
| milestones.completeness=%d%% 完成 | milestones.completeness=%d%% 完成 | ||||
| @@ -2667,3 +2706,5 @@ foot.member_news=成员动态 | |||||
| foot.industry_advisory=行业资讯 | foot.industry_advisory=行业资讯 | ||||
| foot.help=帮助 | foot.help=帮助 | ||||
| foot.copyright= 版权所有:新一代人工智能开源开放平台(OpenI) | foot.copyright= 版权所有:新一代人工智能开源开放平台(OpenI) | ||||
| Platform_Tutorial=新手指引 | |||||
| foot.advice_feedback = 意见反馈 | |||||
| @@ -0,0 +1,448 @@ | |||||
| var token; | |||||
| if(isEmpty(token)){ | |||||
| var meta = $("meta[name=_uid]"); | |||||
| if(!isEmpty(meta)){ | |||||
| token = meta.attr("content"); | |||||
| console.log("token is uid:" + token); | |||||
| } | |||||
| } | |||||
| var swiperNewMessage = new Swiper(".newslist", { | |||||
| direction: "vertical", | |||||
| slidesPerView: 10, | |||||
| loop: true, | |||||
| autoplay: { | |||||
| delay: 2500, | |||||
| disableOnInteraction: false, | |||||
| }, | |||||
| }); | |||||
| var swiperRepo = new Swiper(".homepro-list", { | |||||
| slidesPerView: 3, | |||||
| slidesPerColumn: 2, | |||||
| slidesPerColumnFill:'row', | |||||
| spaceBetween: 30, | |||||
| pagination: { | |||||
| el: ".swiper-pagination", | |||||
| clickable: true, | |||||
| }, | |||||
| autoplay: { | |||||
| delay: 2500, | |||||
| disableOnInteraction: false, | |||||
| }, | |||||
| }); | |||||
| var output = document.getElementById("newmessage"); | |||||
| console.log("document.location.host="+document.location.host); | |||||
| console.log("document.URL="+document.URL); | |||||
| var url = "ws://" + document.location.host + "/action/notification"; | |||||
| if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){ | |||||
| url = "wss://" + document.location.host + "/action/notification" | |||||
| } | |||||
| var socket = new WebSocket(url); | |||||
| socket.onopen = function () { | |||||
| console.log("message has connected."); | |||||
| }; | |||||
| var messageQueue = []; | |||||
| var maxSize = 20; | |||||
| var html =document.documentElement; | |||||
| var lang = html.attributes["lang"] | |||||
| var isZh = true; | |||||
| if(lang != null && lang.nodeValue =="en-US" ){ | |||||
| console.log("the language is " + lang.nodeValue); | |||||
| isZh=false; | |||||
| }else{ | |||||
| console.log("default lang=zh"); | |||||
| } | |||||
| socket.onmessage = function (e) { | |||||
| var data =JSON.parse(e.data) | |||||
| console.log("recevie data=" + e.data) | |||||
| var html = ""; | |||||
| if (data != null){ | |||||
| console.log("queue length=" + messageQueue.length); | |||||
| if(messageQueue.length > maxSize){ | |||||
| delete messageQueue[0]; | |||||
| }else{ | |||||
| messageQueue.push(data); | |||||
| } | |||||
| var currentTime = new Date().getTime(); | |||||
| for(var i = 0; i < messageQueue.length; i++){ | |||||
| var record = messageQueue[i]; | |||||
| var recordPrefix = getMsg(record); | |||||
| var actionName = getAction(record.OpType,isZh); | |||||
| if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){ | |||||
| html += recordPrefix + actionName; | |||||
| html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>" | |||||
| } | |||||
| else if(record.OpType == "7" || record.OpType == "11" || record.OpType == "14" || record.OpType == "15" || record.OpType == "22" | |||||
| || record.OpType == "23"){ | |||||
| html += recordPrefix + actionName; | |||||
| html += " <a href=\"" + getPRLink(record) + "\" rel=\"nofollow\">" + getPRText(record) + "</a>" | |||||
| } | |||||
| else if(record.OpType == "1"){ | |||||
| html += recordPrefix + actionName; | |||||
| html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
| } | |||||
| else if(record.OpType == "9" || record.OpType == "5"){ | |||||
| branch = "<a href=\"" + getRepoLink(record) + "/src/branch/" + record.RefName + "\" rel=\"nofollow\">" + record.RefName + "</a>" | |||||
| actionName = actionName.replace("{branch}",branch); | |||||
| html += recordPrefix + actionName; | |||||
| html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
| }else if(record.OpType == "17"){ | |||||
| actionName = actionName.replace("{deleteBranchName}",record.RefName); | |||||
| var repoLink = "<a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
| actionName = actionName.replace("{repoName}",repoLink); | |||||
| html += recordPrefix + actionName; | |||||
| } | |||||
| else if(record.OpType == "2"){ | |||||
| actionName = actionName.replace("{oldRepoName}",record.Content); | |||||
| html += recordPrefix + actionName; | |||||
| html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
| } | |||||
| else{ | |||||
| continue; | |||||
| } | |||||
| if(record.Repo != null){ | |||||
| var time = getTime(record.CreatedUnix,currentTime); | |||||
| html += " " + time; | |||||
| } | |||||
| html += "</div>"; | |||||
| html += "</div>"; | |||||
| } | |||||
| /* | |||||
| <div class="swiper-slide item"> | |||||
| <img class="ui avatar image" src="/user/avatar/zhoupzh/-1" alt=""> | |||||
| <div class="middle aligned content"> | |||||
| <a href="/zhoupzh" title="">zhoupzh</a> 合并了合并请求 <a href="/OpenI/aiforge/pulls/1168" rel="nofollow">OpenI/aiforge#1168</a><span class="time-since">22 分钟前</span> | |||||
| </div> | |||||
| </div> | |||||
| */ | |||||
| } | |||||
| console.log("html=" + html) | |||||
| output.innerHTML = html; | |||||
| swiperNewMessage.updateSlides(); | |||||
| swiperNewMessage.updateProgress(); | |||||
| }; | |||||
| function getMsg(record){ | |||||
| var html =""; | |||||
| html += "<div class=\"swiper-slide item\">"; | |||||
| html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + record.ActUser.Name + "/-1\" alt=\"\">" | |||||
| html += " <div class=\"middle aligned content nowrap\">" | |||||
| html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>" | |||||
| return html; | |||||
| } | |||||
| function getRepoLink(record){ | |||||
| return "/" + record.Repo.OwnerName + "/" + record.Repo.Name; | |||||
| } | |||||
| function getRepoLink(record){ | |||||
| return record.Repo.OwnerName + "/" + record.Repo.Name; | |||||
| } | |||||
| function getTime(UpdatedUnix,currentTime){ | |||||
| UpdatedUnix = UpdatedUnix; | |||||
| currentTime = currentTime / 1000; | |||||
| var timeEscSecond = currentTime - UpdatedUnix; | |||||
| if( timeEscSecond < 0){ | |||||
| timeEscSecond = 1; | |||||
| } | |||||
| console.log("currentTime=" + currentTime + " updateUnix=" + UpdatedUnix); | |||||
| var hours= Math.floor(timeEscSecond / 3600); | |||||
| //计算相差分钟数 | |||||
| var leave2 = Math.floor(timeEscSecond % (3600)); //计算小时数后剩余的秒数 | |||||
| var minutes= Math.floor(leave2 / 60);//计算相差分钟数 | |||||
| var leave3=Math.floor(leave2 % 60); //计算分钟数后剩余的秒数 | |||||
| var seconds= leave3; | |||||
| if(hours == 0 && minutes == 0){ | |||||
| return seconds + getRepoOrOrg(6,isZh); | |||||
| }else{ | |||||
| if(hours > 0){ | |||||
| return hours + getRepoOrOrg(4,isZh); | |||||
| }else{ | |||||
| return minutes + getRepoOrOrg(5,isZh); | |||||
| } | |||||
| } | |||||
| } | |||||
| function getPRLink(record){ | |||||
| return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/pulls/" + getIssueId(record); | |||||
| } | |||||
| function getPRText(record){ | |||||
| return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||||
| } | |||||
| function getIssueLink(record){ | |||||
| return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/issues/" + getIssueId(record); | |||||
| } | |||||
| function getIssueId(record){ | |||||
| var Id = "1"; | |||||
| if(!isEmpty(record.Comment) && !isEmpty(record.Comment.Issue)){ | |||||
| Id = record.Comment.Issue.Index; | |||||
| }else{ | |||||
| if(!isEmpty(record.Content)){ | |||||
| var content = record.Content; | |||||
| var index = content.indexOf("|"); | |||||
| if(index != -1){ | |||||
| Id = content.substring(0,index); | |||||
| } | |||||
| } | |||||
| } | |||||
| return Id; | |||||
| } | |||||
| function getIssueText(record){ | |||||
| return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||||
| } | |||||
| /* | |||||
| ActionCreateRepo ActionType = iota + 1 // 1 | |||||
| ActionRenameRepo // 2 | |||||
| ActionStarRepo // 3 | |||||
| ActionWatchRepo // 4 | |||||
| ActionCommitRepo // 5 | |||||
| ActionCreateIssue // 6 | |||||
| ActionCreatePullRequest // 7 | |||||
| ActionTransferRepo // 8 | |||||
| ActionPushTag // 9 | |||||
| ActionCommentIssue // 10 | |||||
| ActionMergePullRequest // 11 | |||||
| ActionCloseIssue // 12 | |||||
| ActionReopenIssue // 13 | |||||
| ActionClosePullRequest // 14 | |||||
| ActionReopenPullRequest // 15 | |||||
| ActionDeleteTag // 16 | |||||
| ActionDeleteBranch // 17 | |||||
| ActionMirrorSyncPush // 18 | |||||
| ActionMirrorSyncCreate // 19 | |||||
| ActionMirrorSyncDelete // 20 | |||||
| ActionApprovePullRequest // 21 | |||||
| ActionRejectPullRequest // 22 | |||||
| ActionCommentPull // 23 | |||||
| */ | |||||
| var actionNameZH={ | |||||
| "1":"创建了项目", | |||||
| "2":"重命名项目 {oldRepoName} 为", | |||||
| "5":"推送了 {branch} 分支的代码到", | |||||
| "6":"创建了任务", | |||||
| "7":"创建了合并请求", | |||||
| "9":"推送了 {branch} 分支的代码到", | |||||
| "10":"评论了任务", | |||||
| "11":"合并了合并请求", | |||||
| "12":"关闭了任务", | |||||
| "13":"重新开启了任务", | |||||
| "14":"关闭了合并请求", | |||||
| "15":"重新开启了合并请求", | |||||
| "17":"从 {repoName} 删除分支 {deleteBranchName}", | |||||
| "22":"拒绝了合并请求", | |||||
| "23":"评论了合并请求" | |||||
| }; | |||||
| var actionNameEN={ | |||||
| "1":" created repository", | |||||
| "2":" renamed repository from {oldRepoName} to ", | |||||
| "5":" pushed to {branch} at", | |||||
| "6":" opened issue", | |||||
| "7":" created pull request", | |||||
| "9":" pushed to {branch} at", | |||||
| "10":" commented on issue", | |||||
| "11":" merged pull request", | |||||
| "12":" closed issue", | |||||
| "13":" reopened issue", | |||||
| "14":" closed pull request", | |||||
| "15":" reopened pull request", | |||||
| "17":" deleted branch {deleteBranchName} from {repoName}", | |||||
| "22":" rejected pull request", | |||||
| "23":" commented on pull request" | |||||
| }; | |||||
| var repoAndOrgZH={ | |||||
| "1":"项目", | |||||
| "2":"成员", | |||||
| "3":"团队", | |||||
| "4":"小时前", | |||||
| "5":"分钟前", | |||||
| "6":"秒前" | |||||
| }; | |||||
| var repoAndOrgEN={ | |||||
| "1":"repository", | |||||
| "2":"Members ", | |||||
| "3":"Teams", | |||||
| "4":" hours ago", | |||||
| "5":" minutes ago", | |||||
| "6":" seconds ago" | |||||
| }; | |||||
| function getAction(opType,isZh){ | |||||
| if(isZh){ | |||||
| return actionNameZH[opType] | |||||
| }else{ | |||||
| return actionNameEN[opType] | |||||
| } | |||||
| } | |||||
| queryRecommendData(); | |||||
| function queryRecommendData(){ | |||||
| $.ajax({ | |||||
| type:"GET", | |||||
| url:"/recommend/org", | |||||
| headers: { | |||||
| authorization:token, | |||||
| }, | |||||
| dataType:"json", | |||||
| async:false, | |||||
| success:function(json){ | |||||
| console.log(json); | |||||
| displayOrg(json); | |||||
| }, | |||||
| error:function(response) { | |||||
| console.log(response); | |||||
| } | |||||
| }); | |||||
| $.ajax({ | |||||
| type:"GET", | |||||
| url:"/recommend/repo", | |||||
| headers: { | |||||
| authorization:token, | |||||
| }, | |||||
| dataType:"json", | |||||
| async:false, | |||||
| success:function(json){ | |||||
| console.log(json); | |||||
| displayRepo(json); | |||||
| }, | |||||
| error:function(response) { | |||||
| console.log(response); | |||||
| } | |||||
| }); | |||||
| } | |||||
| /* | |||||
| <div class="swiper-slide"> | |||||
| <div class="ui fluid card"> | |||||
| <div class="content"> | |||||
| <span class="right floated meta"> | |||||
| <i class="star icon"></i>276 <i class="star icon"></i>32 | |||||
| </span> | |||||
| <img class="left floated mini ui image" src="/repo-avatars/278-a9f45e21b92b86dbf969c9f70dff1efc"> | |||||
| <a class="header nowrap" href="/OpenI/aiforge">aiforge </a> | |||||
| <div class="description nowrap-2"> | |||||
| 本项目是群体化方法与技术的开源实现案例,在基于Gitea的基础上,进一步支持社交化的协同开发、协同学习、协同研究等群体创新实践服务,特别是针对新一代人工智能技术特点,重点支持项目管理、git代码管理、大数据集存储管理与智能计算平台接入。 | |||||
| </div> | |||||
| <div class="ui tags nowrap am-mt-10"> | |||||
| <a class="ui small label topic" href="/explore/repos?q=ai%e5%bc%80%e5%8f%91%e5%b7%a5%e5%85%b7&topic=">ai开发工具</a> | |||||
| <a class="ui small label topic" href="/explore/repos?q=openi&topic=">openi</a> | |||||
| <a class="ui small label topic" href="/explore/repos?q=golang&topic=">golang</a> | |||||
| <a class="ui small label topic" href="/explore/repos?q=git&topic=">git</a> | |||||
| <a class="ui small label topic" href="/explore/repos?q=pcl&topic=">pcl</a> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| */ | |||||
| function displayRepo(json){ | |||||
| var orgRepo = document.getElementById("recommendrepo"); | |||||
| var html = ""; | |||||
| if (json != null && json.length > 0){ | |||||
| for(var i = 0; i < json.length;i++){ | |||||
| var record = json[i] | |||||
| html += "<div class=\"swiper-slide\">"; | |||||
| html += " <div class=\"ui fluid card\">"; | |||||
| html += " <div class=\"content\">"; | |||||
| html += " <span class=\"right floated meta\">"; | |||||
| html += " <i class=\"ri-star-line\"></i>" + record["NumStars"] + "<i class=\"ri-git-branch-line am-ml-10\"></i>" + record["NumForks"]; | |||||
| html += " </span>"; | |||||
| html += " <img class=\"left floated mini ui image\" src=\"" + record["Avatar"] + "\">"; | |||||
| html += " <a class=\"header nowrap\" href=\"/" + record["OwnerName"] + "/" + record["Name"] + "\">" + record["Name"] +"</a>"; | |||||
| html += " <div class=\"description nowrap-2\">" + record["Description"] + " </div>"; | |||||
| html += " <div class=\"ui tags nowrap am-mt-10\">" | |||||
| if(record["Topics"] != null){ | |||||
| for(var j = 0; j < record["Topics"].length; j++){ | |||||
| topic = record["Topics"][j]; | |||||
| url = "/explore/repos?q=" + (topic) + "&topic=" | |||||
| html += "<a class=\"ui small label topic\" href=\"" + url + "\">" + topic + "</a>"; | |||||
| } | |||||
| } | |||||
| html += " </div>"; | |||||
| html += " </div>"; | |||||
| html += " </div>"; | |||||
| html += "</div>"; | |||||
| } | |||||
| } | |||||
| orgRepo.innerHTML = html; | |||||
| swiperRepo.updateSlides(); | |||||
| swiperRepo.updateProgress(); | |||||
| } | |||||
| /** | |||||
| * | |||||
| * <div class="column"> | |||||
| <div class="ui fluid card"> | |||||
| <div class="content"> | |||||
| <div class="ui small header"> | |||||
| <img class="ui image" src="/user/avatar/OpenI/-1"> | |||||
| <div class="content nowrap"> | |||||
| <a href="/OpenI">OpenI</a> 启智社区 | |||||
| <div class="sub header">39 项目 ・ 60 成员 ・ 23 团队</div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| */ | |||||
| //var repoAndOrgZH = new Map([['1', "项目"], ['2', "成员"], ['3', "团队"]]); | |||||
| //var repoAndOrgEN = new Map([['1', "Repository"], ['2', "Members"], ['3', "Teams"]]); | |||||
| function getRepoOrOrg(key,isZhLang){ | |||||
| if(isZhLang){ | |||||
| return repoAndOrgZH[key]; | |||||
| }else{ | |||||
| return repoAndOrgEN[key]; | |||||
| } | |||||
| } | |||||
| function displayOrg(json){ | |||||
| var orgDiv = document.getElementById("recommendorg"); | |||||
| var html = ""; | |||||
| if (json != null && json.length > 0){ | |||||
| for(var i = 0; i < json.length;i++){ | |||||
| var record = json[i] | |||||
| html += "<div class=\"column\">"; | |||||
| html += " <a href=\"/" + record["Name"] + "\" class=\"ui fluid card\">"; | |||||
| html += " <div class=\"content\">"; | |||||
| html += " <div class=\"ui small header\">"; | |||||
| html += " <img class=\"ui image\" src=\"" + record["Avatar"] + "\">"; | |||||
| html += " <div class=\"content nowrap\">"; | |||||
| html += " <span class=\"ui blue\">" + record["Name"] + "</span> " + record["FullName"]; | |||||
| html += " <div class=\"sub header\">" + record["NumRepos"] +" " + getRepoOrOrg(1,isZh) + " ・ " + record["NumMembers"] +" " + getRepoOrOrg(2,isZh) + " ・ " + record["NumTeams"] + " " + getRepoOrOrg(3,isZh) + "</div>"; | |||||
| html += " </div>"; | |||||
| html += " </div>"; | |||||
| html += " </div>"; | |||||
| html += " </a>"; | |||||
| html += "</div>"; | |||||
| } | |||||
| } | |||||
| orgDiv.innerHTML = html; | |||||
| } | |||||
| @@ -1,6 +1,8 @@ | |||||
| package routers | package routers | ||||
| import ( | import ( | ||||
| "net/http" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| @@ -11,6 +13,9 @@ import ( | |||||
| var upgrader = websocket.Upgrader{ | var upgrader = websocket.Upgrader{ | ||||
| ReadBufferSize: 1024, | ReadBufferSize: 1024, | ||||
| WriteBufferSize: 1024, | WriteBufferSize: 1024, | ||||
| CheckOrigin: func(r *http.Request) bool { | |||||
| return true | |||||
| }, | |||||
| } | } | ||||
| var SocketManager = socketwrap.NewClientsManager() | var SocketManager = socketwrap.NewClientsManager() | ||||
| @@ -24,7 +29,7 @@ func ActionNotification(ctx *context.Context) { | |||||
| } | } | ||||
| client := &socketwrap.Client{Manager: SocketManager, Conn: conn, Send: make(chan *models.Action, 256)} | client := &socketwrap.Client{Manager: SocketManager, Conn: conn, Send: make(chan *models.Action, 256)} | ||||
| WriteLastTenActionsIfHave(conn) | |||||
| WriteLastActionsIfHave(conn) | |||||
| client.Manager.Register <- client | client.Manager.Register <- client | ||||
| @@ -32,22 +37,20 @@ func ActionNotification(ctx *context.Context) { | |||||
| } | } | ||||
| func WriteLastTenActionsIfHave(conn *websocket.Conn) { | |||||
| socketwrap.LastTenActionsQueue.Mutex.RLock() | |||||
| func WriteLastActionsIfHave(conn *websocket.Conn) { | |||||
| socketwrap.LastActionsQueue.Mutex.RLock() | |||||
| { | { | ||||
| size := socketwrap.LastTenActionsQueue.Queue.Len() | |||||
| size := socketwrap.LastActionsQueue.Queue.Len() | |||||
| if size > 0 { | if size > 0 { | ||||
| tempE := socketwrap.LastTenActionsQueue.Queue.Front() | |||||
| conn.WriteJSON(tempE) | |||||
| tempE := socketwrap.LastActionsQueue.Queue.Front() | |||||
| conn.WriteJSON(tempE.Value) | |||||
| for i := 1; i < size; i++ { | for i := 1; i < size; i++ { | ||||
| tempE = tempE.Next() | tempE = tempE.Next() | ||||
| conn.WriteJSON(tempE) | |||||
| conn.WriteJSON(tempE.Value) | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| socketwrap.LastTenActionsQueue.Mutex.RUnlock() | |||||
| socketwrap.LastActionsQueue.Mutex.RUnlock() | |||||
| } | } | ||||
| @@ -524,7 +524,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| Get(notify.GetThread). | Get(notify.GetThread). | ||||
| Patch(notify.ReadThread) | Patch(notify.ReadThread) | ||||
| }, reqToken()) | }, reqToken()) | ||||
| operationReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, OperationRequired: true}) | operationReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, OperationRequired: true}) | ||||
| //Project board | //Project board | ||||
| m.Group("/projectboard", func() { | m.Group("/projectboard", func() { | ||||
| @@ -544,7 +544,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| }, operationReq) | }, operationReq) | ||||
| m.Get("/query_user_static_page", operationReq, repo_ext.QueryUserStaticDataPage) | m.Get("/query_user_static_page", operationReq, repo_ext.QueryUserStaticDataPage) | ||||
| m.Get("/query_user_current_month", operationReq, repo_ext.QueryUserStaticCurrentMonth) | |||||
| m.Get("/query_user_current_week", operationReq, repo_ext.QueryUserStaticCurrentWeek) | |||||
| m.Get("/query_user_current_year", operationReq, repo_ext.QueryUserStaticCurrentYear) | |||||
| m.Get("/query_user_last30_day", operationReq, repo_ext.QueryUserStaticLast30Day) | |||||
| m.Get("/query_user_last_month", operationReq, repo_ext.QueryUserStaticLastMonth) | |||||
| m.Get("/query_user_yesterday", operationReq, repo_ext.QueryUserStaticYesterday) | |||||
| m.Get("/query_user_all", operationReq, repo_ext.QueryUserStaticAll) | |||||
| // Users | // Users | ||||
| m.Group("/users", func() { | m.Group("/users", func() { | ||||
| m.Get("/search", user.Search) | m.Get("/search", user.Search) | ||||
| @@ -6,6 +6,7 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/util" | |||||
| "net/http" | "net/http" | ||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| @@ -106,7 +107,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { | |||||
| job.TrainJobDuration = result.TrainJobDuration | job.TrainJobDuration = result.TrainJobDuration | ||||
| if result.Duration != 0 { | if result.Duration != 0 { | ||||
| job.TrainJobDuration = addZero(result.Duration/3600000) + ":" + addZero(result.Duration%3600000/60000) + ":" + addZero(result.Duration%60000/1000) | |||||
| job.TrainJobDuration = util.AddZero(result.Duration/3600000) + ":" + util.AddZero(result.Duration%3600000/60000) + ":" + util.AddZero(result.Duration%60000/1000) | |||||
| } else { | } else { | ||||
| job.TrainJobDuration = "00:00:00" | job.TrainJobDuration = "00:00:00" | ||||
| @@ -125,15 +126,6 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { | |||||
| } | } | ||||
| func addZero(t int64) (m string) { | |||||
| if t < 10 { | |||||
| m = "0" + strconv.FormatInt(t, 10) | |||||
| return m | |||||
| } else { | |||||
| return strconv.FormatInt(t, 10) | |||||
| } | |||||
| } | |||||
| func TrainJobGetLog(ctx *context.APIContext) { | func TrainJobGetLog(ctx *context.APIContext) { | ||||
| var ( | var ( | ||||
| err error | err error | ||||
| @@ -44,9 +44,52 @@ const ( | |||||
| func Home(ctx *context.Context) { | func Home(ctx *context.Context) { | ||||
| ctx.Data["PageIsHome"] = true | ctx.Data["PageIsHome"] = true | ||||
| ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||
| setRecommendURL(ctx) | |||||
| ctx.HTML(200, tplHome) | ctx.HTML(200, tplHome) | ||||
| } | } | ||||
| func setRecommendURL(ctx *context.Context) { | |||||
| addr := setting.RecommentRepoAddr[10:] | |||||
| start := strings.Index(addr, "/") | |||||
| end := strings.Index(addr, "raw") | |||||
| if start != -1 && end != -1 { | |||||
| ctx.Data["RecommendURL"] = addr[start:end] | |||||
| } else { | |||||
| ctx.Data["RecommendURL"] = setting.RecommentRepoAddr | |||||
| } | |||||
| ctx.Data["page_title"] = ctx.Tr("home.page_title") | |||||
| ctx.Data["page_small_title"] = ctx.Tr("home.page_small_title") | |||||
| ctx.Data["page_description"] = ctx.Tr("home.page_description") | |||||
| ctx.Data["page_use"] = ctx.Tr("home.page_use") | |||||
| ctx.Data["page_only_dynamic"] = ctx.Tr("home.page_only_dynamic") | |||||
| ctx.Data["page_recommend_org"] = ctx.Tr("home.page_recommend_org") | |||||
| ctx.Data["page_recommend_org_desc"] = ctx.Tr("home.page_recommend_org_desc") | |||||
| ctx.Data["page_recommend_org_commit"] = ctx.Tr("home.page_recommend_org_commit") | |||||
| ctx.Data["page_recommend_org_more"] = ctx.Tr("home.page_recommend_org_more") | |||||
| ctx.Data["page_recommend_repo"] = ctx.Tr("home.page_recommend_repo") | |||||
| ctx.Data["page_recommend_repo_desc"] = ctx.Tr("home.page_recommend_repo_desc") | |||||
| ctx.Data["page_recommend_repo_commit"] = ctx.Tr("home.page_recommend_repo_commit") | |||||
| ctx.Data["page_recommend_repo_go"] = ctx.Tr("home.page_recommend_repo_go") | |||||
| ctx.Data["page_recommend_repo_more"] = ctx.Tr("home.page_recommend_repo_more") | |||||
| ctx.Data["page_dev_env"] = ctx.Tr("home.page_dev_env") | |||||
| ctx.Data["page_dev_env_desc"] = ctx.Tr("home.page_dev_env_desc") | |||||
| ctx.Data["page_dev_env_desc_title"] = ctx.Tr("home.page_dev_env_desc_title") | |||||
| ctx.Data["page_dev_env_desc_desc"] = ctx.Tr("home.page_dev_env_desc_desc") | |||||
| ctx.Data["page_dev_env_desc1_title"] = ctx.Tr("home.page_dev_env_desc1_title") | |||||
| ctx.Data["page_dev_env_desc1_desc"] = ctx.Tr("home.page_dev_env_desc1_desc") | |||||
| ctx.Data["page_dev_env_desc2_title"] = ctx.Tr("home.page_dev_env_desc2_title") | |||||
| ctx.Data["page_dev_env_desc2_desc"] = ctx.Tr("home.page_dev_env_desc2_desc") | |||||
| ctx.Data["page_dev_env_desc3_title"] = ctx.Tr("home.page_dev_env_desc3_title") | |||||
| ctx.Data["page_dev_env_desc3_desc"] = ctx.Tr("home.page_dev_env_desc3_desc") | |||||
| ctx.Data["page_dev_yunlao"] = ctx.Tr("home.page_dev_yunlao") | |||||
| ctx.Data["page_dev_yunlao_desc1"] = ctx.Tr("home.page_dev_yunlao_desc1") | |||||
| ctx.Data["page_dev_yunlao_desc2"] = ctx.Tr("home.page_dev_yunlao_desc2") | |||||
| ctx.Data["page_dev_yunlao_desc3"] = ctx.Tr("home.page_dev_yunlao_desc3") | |||||
| ctx.Data["page_dev_yunlao_desc4"] = ctx.Tr("home.page_dev_yunlao_desc4") | |||||
| ctx.Data["page_dev_yunlao_apply"] = ctx.Tr("home.page_dev_yunlao_apply") | |||||
| } | |||||
| func Dashboard(ctx *context.Context) { | func Dashboard(ctx *context.Context) { | ||||
| if ctx.IsSigned { | if ctx.IsSigned { | ||||
| if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | ||||
| @@ -77,7 +120,7 @@ func Dashboard(ctx *context.Context) { | |||||
| ctx.Redirect(setting.AppSubURL + "/user/login") | ctx.Redirect(setting.AppSubURL + "/user/login") | ||||
| return | return | ||||
| } | } | ||||
| setRecommendURL(ctx) | |||||
| ctx.Data["PageIsHome"] = true | ctx.Data["PageIsHome"] = true | ||||
| ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||
| ctx.HTML(200, tplHome) | ctx.HTML(200, tplHome) | ||||
| @@ -513,43 +556,92 @@ func NotFound(ctx *context.Context) { | |||||
| ctx.Data["Title"] = "Page Not Found" | ctx.Data["Title"] = "Page Not Found" | ||||
| ctx.NotFound("home.NotFound", nil) | ctx.NotFound("home.NotFound", nil) | ||||
| } | } | ||||
| func RecommendOrgFromPromote(ctx *context.Context) { | func RecommendOrgFromPromote(ctx *context.Context) { | ||||
| url := setting.RecommentRepoAddr + "organizations" | url := setting.RecommentRepoAddr + "organizations" | ||||
| recommendFromPromote(ctx, url) | |||||
| result, err := recommendFromPromote(url) | |||||
| if err != nil { | |||||
| ctx.ServerError("500", err) | |||||
| return | |||||
| } | |||||
| resultOrg := make([]map[string]interface{}, 0) | |||||
| for _, userName := range result { | |||||
| user, err := models.GetUserByName(userName) | |||||
| if err == nil { | |||||
| userMap := make(map[string]interface{}) | |||||
| userMap["Name"] = user.Name | |||||
| userMap["Description"] = user.Description | |||||
| userMap["FullName"] = user.FullName | |||||
| userMap["ID"] = user.ID | |||||
| userMap["Avatar"] = user.RelAvatarLink() | |||||
| userMap["NumRepos"] = user.NumRepos | |||||
| userMap["NumTeams"] = user.NumTeams | |||||
| userMap["NumMembers"] = user.NumMembers | |||||
| resultOrg = append(resultOrg, userMap) | |||||
| } else { | |||||
| log.Info("query user error," + err.Error()) | |||||
| } | |||||
| } | |||||
| ctx.JSON(200, resultOrg) | |||||
| } | } | ||||
| func recommendFromPromote(ctx *context.Context, url string) { | |||||
| func recommendFromPromote(url string) ([]string, error) { | |||||
| resp, err := http.Get(url) | resp, err := http.Get(url) | ||||
| if err != nil { | |||||
| if err != nil || resp.StatusCode != 200 { | |||||
| log.Info("Get organizations url error=" + err.Error()) | log.Info("Get organizations url error=" + err.Error()) | ||||
| ctx.ServerError("QueryTrainJobList:", err) | |||||
| return | |||||
| return nil, err | |||||
| } | } | ||||
| bytes, err := ioutil.ReadAll(resp.Body) | bytes, err := ioutil.ReadAll(resp.Body) | ||||
| resp.Body.Close() | resp.Body.Close() | ||||
| if err != nil { | if err != nil { | ||||
| log.Info("Get organizations url error=" + err.Error()) | log.Info("Get organizations url error=" + err.Error()) | ||||
| ctx.ServerError("QueryTrainJobList:", err) | |||||
| return | |||||
| return nil, err | |||||
| } | } | ||||
| allLineStr := string(bytes) | allLineStr := string(bytes) | ||||
| lines := strings.Split(allLineStr, "\n") | lines := strings.Split(allLineStr, "\n") | ||||
| result := make([]string, len(lines)) | result := make([]string, len(lines)) | ||||
| for i, line := range lines { | for i, line := range lines { | ||||
| tmpIndex := strings.Index(line, ".") | |||||
| log.Info("i=" + fmt.Sprint(i) + " line=" + line + " tmpIndex=" + fmt.Sprint(tmpIndex)) | |||||
| if tmpIndex == -1 { | |||||
| result[i] = strings.Trim(line, " ") | |||||
| } else { | |||||
| result[i] = strings.Trim(line[tmpIndex+1:], " ") | |||||
| } | |||||
| log.Info("i=" + fmt.Sprint(i) + " line=" + line) | |||||
| result[i] = strings.Trim(line, " ") | |||||
| } | } | ||||
| ctx.JSON(http.StatusOK, result) | |||||
| return result, nil | |||||
| } | } | ||||
| func RecommendRepoFromPromote(ctx *context.Context) { | func RecommendRepoFromPromote(ctx *context.Context) { | ||||
| url := setting.RecommentRepoAddr + "projects" | url := setting.RecommentRepoAddr + "projects" | ||||
| recommendFromPromote(ctx, url) | |||||
| result, err := recommendFromPromote(url) | |||||
| if err != nil { | |||||
| ctx.ServerError("500", err) | |||||
| return | |||||
| } | |||||
| resultRepo := make([]map[string]interface{}, 0) | |||||
| //resultRepo := make([]*models.Repository, 0) | |||||
| for _, repoName := range result { | |||||
| tmpIndex := strings.Index(repoName, "/") | |||||
| if tmpIndex == -1 { | |||||
| log.Info("error repo name format.") | |||||
| } else { | |||||
| ownerName := strings.Trim(repoName[0:tmpIndex], " ") | |||||
| repoName := strings.Trim(repoName[tmpIndex+1:], " ") | |||||
| repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) | |||||
| if err == nil { | |||||
| repoMap := make(map[string]interface{}) | |||||
| repoMap["ID"] = fmt.Sprint(repo.ID) | |||||
| repoMap["Name"] = repo.Name | |||||
| repoMap["OwnerName"] = repo.OwnerName | |||||
| repoMap["NumStars"] = repo.NumStars | |||||
| repoMap["NumForks"] = repo.NumForks | |||||
| repoMap["Description"] = repo.Description | |||||
| repoMap["NumWatchs"] = repo.NumWatches | |||||
| repoMap["Topics"] = repo.Topics | |||||
| repoMap["Avatar"] = repo.RelAvatarLink() | |||||
| resultRepo = append(resultRepo, repoMap) | |||||
| } else { | |||||
| log.Info("query repo error," + err.Error()) | |||||
| } | |||||
| } | |||||
| } | |||||
| ctx.JSON(200, resultRepo) | |||||
| } | } | ||||
| @@ -0,0 +1,87 @@ | |||||
| package notice | |||||
| import ( | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "encoding/json" | |||||
| "github.com/patrickmn/go-cache" | |||||
| "time" | |||||
| ) | |||||
| var noticeCache = cache.New(2*time.Minute, 1*time.Minute) | |||||
| const ( | |||||
| NOTICE_CACHE_KEY = "notice" | |||||
| ) | |||||
| type Notice struct { | |||||
| Title string | |||||
| Link string | |||||
| Visible int //0 invisible, 1 visible | |||||
| CommitId string | |||||
| } | |||||
| var lock int32 = 0 | |||||
| func GetNewestNotice() (*Notice, error) { | |||||
| defer func() { | |||||
| if err := recover(); err != nil { | |||||
| log.Error("recover error", err) | |||||
| } | |||||
| }() | |||||
| var notice *Notice | |||||
| var err error | |||||
| if setting.CacheOn { | |||||
| notice, err = getNewestNoticeFromCacheAndDisk() | |||||
| } else { | |||||
| notice, err = getNewestNoticeFromDisk() | |||||
| } | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return notice, nil | |||||
| } | |||||
| func getNoticeTimeout() time.Duration { | |||||
| return time.Duration(setting.CacheTimeOutSecond) * time.Second | |||||
| } | |||||
| func getNewestNoticeFromDisk() (*Notice, error) { | |||||
| log.Debug("Get notice from disk") | |||||
| repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfNoticeRepo, setting.RepoNameOfNoticeRepo, setting.RefNameOfNoticeRepo, setting.TreePathOfNoticeRepo) | |||||
| if err != nil { | |||||
| log.Error("GetNewestNotice failed, error=%v", err) | |||||
| return nil, err | |||||
| } | |||||
| notice := &Notice{} | |||||
| json.Unmarshal(repoFile.Content, notice) | |||||
| if notice.Title == "" { | |||||
| return nil, err | |||||
| } | |||||
| notice.CommitId = repoFile.CommitId | |||||
| return notice, nil | |||||
| } | |||||
| func getNewestNoticeFromCacheAndDisk() (*Notice, error) { | |||||
| v, success := noticeCache.Get(NOTICE_CACHE_KEY) | |||||
| if success { | |||||
| log.Debug("Get notice from cache,value = %v", v) | |||||
| if v == nil { | |||||
| return nil, nil | |||||
| } | |||||
| n := v.(*Notice) | |||||
| return n, nil | |||||
| } | |||||
| notice, err := getNewestNoticeFromDisk() | |||||
| if err != nil { | |||||
| log.Error("GetNewestNotice failed, error=%v", err) | |||||
| noticeCache.Set(NOTICE_CACHE_KEY, nil, 30*time.Second) | |||||
| return nil, err | |||||
| } | |||||
| noticeCache.Set(NOTICE_CACHE_KEY, notice, getNoticeTimeout()) | |||||
| return notice, nil | |||||
| } | |||||
| @@ -99,6 +99,18 @@ func saveModelByParameters(jobId string, versionName string, name string, versio | |||||
| //udpate status and version count | //udpate status and version count | ||||
| models.ModifyModelNewProperty(lastNewModelId, MODEL_NOT_LATEST, 0) | models.ModifyModelNewProperty(lastNewModelId, MODEL_NOT_LATEST, 0) | ||||
| } | } | ||||
| var units []models.RepoUnit | |||||
| var deleteUnitTypes []models.UnitType | |||||
| units = append(units, models.RepoUnit{ | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| Type: models.UnitTypeModelManage, | |||||
| Config: &models.ModelManageConfig{ | |||||
| EnableModelManage: true, | |||||
| }, | |||||
| }) | |||||
| deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeModelManage) | |||||
| models.UpdateRepositoryUnits(ctx.Repo.Repository, units, deleteUnitTypes) | |||||
| log.Info("save model end.") | log.Info("save model end.") | ||||
| @@ -130,10 +142,13 @@ func SaveModel(ctx *context.Context) { | |||||
| version := ctx.Query("Version") | version := ctx.Query("Version") | ||||
| label := ctx.Query("Label") | label := ctx.Query("Label") | ||||
| description := ctx.Query("Description") | description := ctx.Query("Description") | ||||
| trainTaskCreate := ctx.QueryBool("trainTaskCreate") | |||||
| if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { | |||||
| ctx.ServerError("No right.", errors.New(ctx.Tr("repo.model_noright"))) | |||||
| return | |||||
| if !trainTaskCreate { | |||||
| if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { | |||||
| ctx.ServerError("No right.", errors.New(ctx.Tr("repo.model_noright"))) | |||||
| return | |||||
| } | |||||
| } | } | ||||
| if JobId == "" || VersionName == "" { | if JobId == "" || VersionName == "" { | ||||
| @@ -474,6 +489,23 @@ func ShowOneVersionOtherModel(ctx *context.Context) { | |||||
| func ShowModelTemplate(ctx *context.Context) { | func ShowModelTemplate(ctx *context.Context) { | ||||
| ctx.Data["isModelManage"] = true | ctx.Data["isModelManage"] = true | ||||
| repoId := ctx.Repo.Repository.ID | |||||
| Type := -1 | |||||
| _, count, _ := models.QueryModel(&models.AiModelQueryOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: 1, | |||||
| PageSize: 2, | |||||
| }, | |||||
| RepoID: repoId, | |||||
| Type: Type, | |||||
| New: MODEL_LATEST, | |||||
| }) | |||||
| ctx.Data["MODEL_COUNT"] = count | |||||
| _, trainCount, _ := models.QueryModelTrainJobList(repoId) | |||||
| log.Info("query train count=" + fmt.Sprint(trainCount)) | |||||
| ctx.Data["TRAIN_COUNT"] = trainCount | |||||
| ctx.HTML(200, tplModelManageIndex) | ctx.HTML(200, tplModelManageIndex) | ||||
| } | } | ||||
| @@ -586,3 +618,67 @@ func ModifyModelInfo(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| func QueryModelListForPredict(ctx *context.Context) { | |||||
| repoId := ctx.Repo.Repository.ID | |||||
| modelResult, count, err := models.QueryModel(&models.AiModelQueryOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: -1, | |||||
| PageSize: -1, | |||||
| }, | |||||
| RepoID: repoId, | |||||
| Type: -1, | |||||
| New: -1, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("Cloudbrain", err) | |||||
| return | |||||
| } | |||||
| log.Info("query return count=" + fmt.Sprint(count)) | |||||
| nameList := make([]string, 0) | |||||
| nameMap := make(map[string][]*models.AiModelManage) | |||||
| for _, model := range modelResult { | |||||
| if _, value := nameMap[model.Name]; !value { | |||||
| models := make([]*models.AiModelManage, 0) | |||||
| models = append(models, model) | |||||
| nameMap[model.Name] = models | |||||
| nameList = append(nameList, model.Name) | |||||
| } else { | |||||
| nameMap[model.Name] = append(nameMap[model.Name], model) | |||||
| } | |||||
| } | |||||
| mapInterface := make(map[string]interface{}) | |||||
| mapInterface["nameList"] = nameList | |||||
| mapInterface["nameMap"] = nameMap | |||||
| ctx.JSON(http.StatusOK, mapInterface) | |||||
| } | |||||
| func QueryModelFileForPredict(ctx *context.Context) { | |||||
| id := ctx.Query("ID") | |||||
| model, err := models.QueryModelById(id) | |||||
| if err != nil { | |||||
| log.Error("no such model!", err.Error()) | |||||
| ctx.ServerError("no such model:", err) | |||||
| return | |||||
| } | |||||
| prefix := model.Path[len(setting.Bucket)+1:] | |||||
| fileinfos, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, prefix) | |||||
| ctx.JSON(http.StatusOK, fileinfos) | |||||
| } | |||||
| func QueryOneLevelModelFile(ctx *context.Context) { | |||||
| id := ctx.Query("ID") | |||||
| parentDir := ctx.Query("parentDir") | |||||
| model, err := models.QueryModelById(id) | |||||
| if err != nil { | |||||
| log.Error("no such model!", err.Error()) | |||||
| ctx.ServerError("no such model:", err) | |||||
| return | |||||
| } | |||||
| prefix := model.Path[len(setting.Bucket)+1:] | |||||
| fileinfos, err := storage.GetOneLevelAllObjectUnderDir(setting.Bucket, prefix, parentDir) | |||||
| ctx.JSON(http.StatusOK, fileinfos) | |||||
| } | |||||
| @@ -6,13 +6,6 @@ package repo | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "container/list" | |||||
| "fmt" | |||||
| "html" | |||||
| gotemplate "html/template" | |||||
| "net/url" | |||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| @@ -22,6 +15,12 @@ import ( | |||||
| "code.gitea.io/gitea/modules/markup" | "code.gitea.io/gitea/modules/markup" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/timeutil" | "code.gitea.io/gitea/modules/timeutil" | ||||
| "container/list" | |||||
| "fmt" | |||||
| "html" | |||||
| gotemplate "html/template" | |||||
| "net/url" | |||||
| "strings" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| @@ -35,7 +34,52 @@ func RefBlame(ctx *context.Context) { | |||||
| ctx.NotFound("Blame FileName", nil) | ctx.NotFound("Blame FileName", nil) | ||||
| return | return | ||||
| } | } | ||||
| //get repo contributors info | |||||
| contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath(), ctx.Repo.BranchName) | |||||
| if err == nil && contributors != nil { | |||||
| var contributorInfos []*ContributorInfo | |||||
| contributorInfoHash := make(map[string]*ContributorInfo) | |||||
| count := 0 | |||||
| for _, c := range contributors { | |||||
| if count >= 25 { | |||||
| continue | |||||
| } | |||||
| if strings.Compare(c.Email, "") == 0 { | |||||
| continue | |||||
| } | |||||
| // get user info from committer email | |||||
| user, err := models.GetUserByActivateEmail(c.Email) | |||||
| if err == nil { | |||||
| // committer is system user, get info through user's primary email | |||||
| if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok { | |||||
| // existed: same primary email, different committer name | |||||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||||
| } else { | |||||
| // new committer info | |||||
| var newContributor = &ContributorInfo{ | |||||
| user, user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt, | |||||
| } | |||||
| count++ | |||||
| contributorInfos = append(contributorInfos, newContributor) | |||||
| contributorInfoHash[user.Email] = newContributor | |||||
| } | |||||
| } else { | |||||
| // committer is not system user | |||||
| if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok { | |||||
| // existed: same primary email, different committer name | |||||
| existedContributorInfo.CommitCnt += c.CommitCnt | |||||
| } else { | |||||
| var newContributor = &ContributorInfo{ | |||||
| user, "", "", c.Email, c.CommitCnt, | |||||
| } | |||||
| count++ | |||||
| contributorInfos = append(contributorInfos, newContributor) | |||||
| contributorInfoHash[c.Email] = newContributor | |||||
| } | |||||
| } | |||||
| } | |||||
| ctx.Data["ContributorInfo"] = contributorInfos | |||||
| } | |||||
| userName := ctx.Repo.Owner.Name | userName := ctx.Repo.Owner.Name | ||||
| repoName := ctx.Repo.Repository.Name | repoName := ctx.Repo.Repository.Name | ||||
| commitID := ctx.Repo.CommitID | commitID := ctx.Repo.CommitID | ||||
| @@ -14,18 +14,17 @@ import ( | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| "code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
| "code.gitea.io/gitea/modules/cloudbrain" | "code.gitea.io/gitea/modules/cloudbrain" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "code.gitea.io/gitea/modules/util" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| @@ -252,17 +251,10 @@ func CloudBrainRestart(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| var resultCode = "0" | var resultCode = "0" | ||||
| var errorMsg = "" | var errorMsg = "" | ||||
| var status = "" | |||||
| var status = string(models.JobWaiting) | |||||
| task := ctx.Cloudbrain | |||||
| for { | for { | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error(), ctx.Data["MsgID"]) | |||||
| resultCode = "-1" | |||||
| errorMsg = "system error" | |||||
| break | |||||
| } | |||||
| if task.Status != string(models.JobStopped) && task.Status != string(models.JobSucceeded) && task.Status != string(models.JobFailed) { | if task.Status != string(models.JobStopped) && task.Status != string(models.JobSucceeded) && task.Status != string(models.JobFailed) { | ||||
| log.Error("the job(%s) is not stopped", task.JobName, ctx.Data["MsgID"]) | log.Error("the job(%s) is not stopped", task.JobName, ctx.Data["MsgID"]) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| @@ -277,7 +269,7 @@ func CloudBrainRestart(ctx *context.Context) { | |||||
| break | break | ||||
| } | } | ||||
| if !ctx.IsSigned || (ctx.User.ID != task.UserID && !ctx.IsUserSiteAdmin()){ | |||||
| if !ctx.IsSigned || (ctx.User.ID != task.UserID && !ctx.IsUserSiteAdmin()) { | |||||
| log.Error("the user has no right ro restart the job", task.JobName, ctx.Data["MsgID"]) | log.Error("the user has no right ro restart the job", task.JobName, ctx.Data["MsgID"]) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| errorMsg = "you have no right to restart the job" | errorMsg = "you have no right to restart the job" | ||||
| @@ -299,7 +291,7 @@ func CloudBrainRestart(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| err = cloudbrain.RestartTask(ctx, task) | |||||
| err = cloudbrain.RestartTask(ctx, task, &jobID) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("RestartTask failed:%v", err.Error(), ctx.Data["MsgID"]) | log.Error("RestartTask failed:%v", err.Error(), ctx.Data["MsgID"]) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| @@ -307,9 +299,6 @@ func CloudBrainRestart(ctx *context.Context) { | |||||
| break | break | ||||
| } | } | ||||
| status = task.Status | |||||
| jobID = task.JobID | |||||
| break | break | ||||
| } | } | ||||
| @@ -370,46 +359,19 @@ func CloudBrainShow(ctx *context.Context) { | |||||
| } | } | ||||
| func CloudBrainDebug(ctx *context.Context) { | func CloudBrainDebug(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | |||||
| if !ctx.IsSigned { | |||||
| log.Error("the user has not signed in") | |||||
| ctx.Error(http.StatusForbidden, "", "the user has not signed in") | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | |||||
| debugUrl := setting.DebugServerHost + "jpylab_" + ctx.Cloudbrain.JobID + "_" + ctx.Cloudbrain.SubTaskName | |||||
| ctx.Redirect(debugUrl) | ctx.Redirect(debugUrl) | ||||
| } | } | ||||
| func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | ||||
| var jobID = ctx.Params(":jobid") | |||||
| if !ctx.IsSigned { | |||||
| log.Error("the user has not signed in") | |||||
| ctx.Error(http.StatusForbidden, "", "the user has not signed in") | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.JSON(200, map[string]string{ | |||||
| "result_code": "-1", | |||||
| "error_msg": "GetCloudbrainByJobID failed", | |||||
| }) | |||||
| return | |||||
| } | |||||
| err = cloudbrain.CommitImage(jobID, models.CommitImageParams{ | |||||
| Ip: task.ContainerIp, | |||||
| TaskContainerId: task.ContainerID, | |||||
| err := cloudbrain.CommitImage(ctx.Cloudbrain.JobID, models.CommitImageParams{ | |||||
| Ip: ctx.Cloudbrain.ContainerIp, | |||||
| TaskContainerId: ctx.Cloudbrain.ContainerID, | |||||
| ImageDescription: form.Description, | ImageDescription: form.Description, | ||||
| ImageTag: form.Tag, | ImageTag: form.Tag, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("CommitImage(%s) failed:%v", task.JobName, err.Error(), ctx.Data["msgID"]) | |||||
| log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"]) | |||||
| ctx.JSON(200, map[string]string{ | ctx.JSON(200, map[string]string{ | ||||
| "result_code": "-1", | "result_code": "-1", | ||||
| "error_msg": "CommitImage failed", | "error_msg": "CommitImage failed", | ||||
| @@ -429,15 +391,8 @@ func CloudBrainStop(ctx *context.Context) { | |||||
| var errorMsg = "" | var errorMsg = "" | ||||
| var status = "" | var status = "" | ||||
| task := ctx.Cloudbrain | |||||
| for { | for { | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) | |||||
| resultCode = "-1" | |||||
| errorMsg = "system error" | |||||
| break | |||||
| } | |||||
| if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) { | if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) { | ||||
| log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) | log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| @@ -445,7 +400,7 @@ func CloudBrainStop(ctx *context.Context) { | |||||
| break | break | ||||
| } | } | ||||
| err = cloudbrain.StopJob(jobID) | |||||
| err := cloudbrain.StopJob(jobID) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("StopJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) | log.Error("StopJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) | ||||
| resultCode = "-1" | resultCode = "-1" | ||||
| @@ -555,12 +510,7 @@ func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) { | |||||
| } | } | ||||
| func CloudBrainDel(ctx *context.Context) { | func CloudBrainDel(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| task := ctx.Cloudbrain | |||||
| if task.Status != string(models.JobStopped) && task.Status != string(models.JobFailed) { | if task.Status != string(models.JobStopped) && task.Status != string(models.JobFailed) { | ||||
| log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"]) | log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"]) | ||||
| @@ -568,7 +518,7 @@ func CloudBrainDel(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| err = models.DeleteJob(task) | |||||
| err := models.DeleteJob(task) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("DeleteJob failed", err) | ctx.ServerError("DeleteJob failed", err) | ||||
| return | return | ||||
| @@ -949,6 +899,13 @@ func SyncCloudbrainStatus() { | |||||
| task.Duration = result.Duration | task.Duration = result.Duration | ||||
| task.TrainJobDuration = result.TrainJobDuration | task.TrainJobDuration = result.TrainJobDuration | ||||
| if result.Duration != 0 { | |||||
| task.TrainJobDuration = util.AddZero(result.Duration/3600000) + ":" + util.AddZero(result.Duration%3600000/60000) + ":" + util.AddZero(result.Duration%60000/1000) | |||||
| } else { | |||||
| task.TrainJobDuration = "00:00:00" | |||||
| } | |||||
| err = models.UpdateJob(task) | err = models.UpdateJob(task) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("UpdateJob(%s) failed:%v", task.JobName, err) | log.Error("UpdateJob(%s) failed:%v", task.JobName, err) | ||||
| @@ -140,6 +140,20 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) | |||||
| return | return | ||||
| } | } | ||||
| } | } | ||||
| _, err = models.GetCloudbrainByName(jobName) | |||||
| if err == nil { | |||||
| log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("the job name did already exist", tplModelArtsNotebookNew, &form) | |||||
| return | |||||
| } else { | |||||
| if !models.IsErrJobNotExist(err) { | |||||
| log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("system error", tplModelArtsNotebookNew, &form) | |||||
| return | |||||
| } | |||||
| } | |||||
| err = modelarts.GenerateTask(ctx, jobName, uuid, description, flavor) | err = modelarts.GenerateTask(ctx, jobName, uuid, description, flavor) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -192,11 +206,6 @@ func NotebookShow(ctx *context.Context) { | |||||
| func NotebookDebug(ctx *context.Context) { | func NotebookDebug(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| _, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetJob(jobID) | result, err := modelarts.GetJob(jobID) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -325,11 +334,7 @@ func NotebookManage(ctx *context.Context) { | |||||
| func NotebookDel(ctx *context.Context) { | func NotebookDel(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| task := ctx.Cloudbrain | |||||
| if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { | if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { | ||||
| log.Error("the job(%s) has not been stopped", task.JobName) | log.Error("the job(%s) has not been stopped", task.JobName) | ||||
| @@ -337,7 +342,7 @@ func NotebookDel(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| _, err = modelarts.DelNotebook(jobID) | |||||
| _, err := modelarts.DelNotebook(jobID) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | ||||
| ctx.ServerError("DelJob failed", err) | ctx.ServerError("DelJob failed", err) | ||||
| @@ -1251,6 +1256,10 @@ func paramCheckCreateTrainJob(form auth.CreateModelArtsTrainJobForm) error { | |||||
| log.Error("the WorkServerNumber(%d) must be in (1,25)", form.WorkServerNumber) | log.Error("the WorkServerNumber(%d) must be in (1,25)", form.WorkServerNumber) | ||||
| return errors.New("计算节点数必须在1-25之间") | return errors.New("计算节点数必须在1-25之间") | ||||
| } | } | ||||
| if form.BranchName == "" { | |||||
| log.Error("the branch must not be null!", form.BranchName) | |||||
| return errors.New("代码分支不能为空!") | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -1421,14 +1430,9 @@ func TrainJobDel(ctx *context.Context) { | |||||
| func TrainJobStop(ctx *context.Context) { | func TrainJobStop(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | |||||
| return | |||||
| } | |||||
| task := ctx.Cloudbrain | |||||
| _, err = modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10)) | |||||
| _, err := modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10)) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("StopTrainJob(%s) failed:%v", task.JobName, err.Error()) | log.Error("StopTrainJob(%s) failed:%v", task.JobName, err.Error()) | ||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | ||||
| @@ -15,6 +15,132 @@ import ( | |||||
| "github.com/360EntSecGroup-Skylar/excelize/v2" | "github.com/360EntSecGroup-Skylar/excelize/v2" | ||||
| ) | ) | ||||
| const ( | |||||
| PAGE_SIZE = 2000 | |||||
| ) | |||||
| func queryUserDataPage(ctx *context.Context, tableName string, queryObj interface{}) { | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| pageSize := ctx.QueryInt("pageSize") | |||||
| if pageSize <= 0 { | |||||
| pageSize = setting.UI.IssuePagingNum | |||||
| } | |||||
| userName := ctx.Query("userName") | |||||
| IsReturnFile := ctx.QueryBool("IsReturnFile") | |||||
| if IsReturnFile { | |||||
| //writer exec file. | |||||
| xlsx := excelize.NewFile() | |||||
| sheetName := ctx.Tr("user.static.sheetname") | |||||
| index := xlsx.NewSheet(sheetName) | |||||
| xlsx.DeleteSheet("Sheet1") | |||||
| dataHeader := map[string]string{ | |||||
| "A1": ctx.Tr("user.static.id"), | |||||
| "B1": ctx.Tr("user.static.name"), | |||||
| "C1": ctx.Tr("user.static.codemergecount"), | |||||
| "D1": ctx.Tr("user.static.commitcount"), | |||||
| "E1": ctx.Tr("user.static.issuecount"), | |||||
| "F1": ctx.Tr("user.static.commentcount"), | |||||
| "G1": ctx.Tr("user.static.focusrepocount"), | |||||
| "H1": ctx.Tr("user.static.starrepocount"), | |||||
| "I1": ctx.Tr("user.static.logincount"), | |||||
| "J1": ctx.Tr("user.static.watchedcount"), | |||||
| "K1": ctx.Tr("user.static.commitcodesize"), | |||||
| "L1": ctx.Tr("user.static.solveissuecount"), | |||||
| "M1": ctx.Tr("user.static.encyclopediascount"), | |||||
| "N1": ctx.Tr("user.static.createrepocount"), | |||||
| "O1": ctx.Tr("user.static.openiindex"), | |||||
| "P1": ctx.Tr("user.static.registdate"), | |||||
| "Q1": ctx.Tr("user.static.countdate"), | |||||
| } | |||||
| for k, v := range dataHeader { | |||||
| //设置单元格的值 | |||||
| xlsx.SetCellValue(sheetName, k, v) | |||||
| } | |||||
| _, count := models.QueryUserStaticDataByTableName(1, 1, tableName, queryObj, userName) | |||||
| var indexTotal int64 | |||||
| indexTotal = 0 | |||||
| for { | |||||
| re, _ := models.QueryUserStaticDataByTableName(int(indexTotal), PAGE_SIZE, tableName, queryObj, "") | |||||
| log.Info("return count=" + fmt.Sprint(count)) | |||||
| for i, userRecord := range re { | |||||
| rows := fmt.Sprint(i + 2) | |||||
| xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID) | |||||
| xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name) | |||||
| xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount) | |||||
| xlsx.SetCellValue(sheetName, "D"+rows, userRecord.CommitCount) | |||||
| xlsx.SetCellValue(sheetName, "E"+rows, userRecord.IssueCount) | |||||
| xlsx.SetCellValue(sheetName, "F"+rows, userRecord.CommentCount) | |||||
| xlsx.SetCellValue(sheetName, "G"+rows, userRecord.FocusRepoCount) | |||||
| xlsx.SetCellValue(sheetName, "H"+rows, userRecord.StarRepoCount) | |||||
| xlsx.SetCellValue(sheetName, "I"+rows, userRecord.LoginCount) | |||||
| xlsx.SetCellValue(sheetName, "J"+rows, userRecord.WatchedCount) | |||||
| xlsx.SetCellValue(sheetName, "K"+rows, userRecord.CommitCodeSize) | |||||
| xlsx.SetCellValue(sheetName, "L"+rows, userRecord.SolveIssueCount) | |||||
| xlsx.SetCellValue(sheetName, "M"+rows, userRecord.EncyclopediasCount) | |||||
| xlsx.SetCellValue(sheetName, "N"+rows, userRecord.CreateRepoCount) | |||||
| xlsx.SetCellValue(sheetName, "O"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex)) | |||||
| formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05") | |||||
| xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3]) | |||||
| formatTime = userRecord.DataDate | |||||
| xlsx.SetCellValue(sheetName, "Q"+rows, formatTime+" 00:01") | |||||
| } | |||||
| //设置默认打开的表单 | |||||
| xlsx.SetActiveSheet(index) | |||||
| filename := sheetName + "_" + ctx.Tr("user.static."+tableName) + ".xlsx" | |||||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename)) | |||||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||||
| if _, err := xlsx.WriteTo(ctx.Resp); err != nil { | |||||
| log.Info("writer exel error." + err.Error()) | |||||
| } | |||||
| indexTotal += PAGE_SIZE | |||||
| if indexTotal >= count { | |||||
| break | |||||
| } | |||||
| } | |||||
| } else { | |||||
| re, count := models.QueryUserStaticDataByTableName((page-1)*pageSize, pageSize, tableName, queryObj, userName) | |||||
| mapInterface := make(map[string]interface{}) | |||||
| mapInterface["data"] = re | |||||
| mapInterface["count"] = count | |||||
| ctx.JSON(http.StatusOK, mapInterface) | |||||
| } | |||||
| } | |||||
| func QueryUserStaticCurrentMonth(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_current_month", new(models.UserBusinessAnalysisCurrentMonth)) | |||||
| } | |||||
| func QueryUserStaticCurrentWeek(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_current_week", new(models.UserBusinessAnalysisCurrentWeek)) | |||||
| } | |||||
| func QueryUserStaticCurrentYear(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_current_year", new(models.UserBusinessAnalysisCurrentYear)) | |||||
| } | |||||
| func QueryUserStaticLast30Day(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_last30_day", new(models.UserBusinessAnalysisLast30Day)) | |||||
| } | |||||
| func QueryUserStaticLastMonth(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_last_month", new(models.UserBusinessAnalysisLastMonth)) | |||||
| } | |||||
| func QueryUserStaticYesterday(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_yesterday", new(models.UserBusinessAnalysisYesterday)) | |||||
| } | |||||
| func QueryUserStaticAll(ctx *context.Context) { | |||||
| queryUserDataPage(ctx, "public.user_business_analysis_all", new(models.UserBusinessAnalysisAll)) | |||||
| } | |||||
| func QueryUserStaticDataPage(ctx *context.Context) { | func QueryUserStaticDataPage(ctx *context.Context) { | ||||
| startDate := ctx.Query("startDate") | startDate := ctx.Query("startDate") | ||||
| endDate := ctx.Query("endDate") | endDate := ctx.Query("endDate") | ||||
| @@ -969,20 +969,20 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Group("/:jobid", func() { | m.Group("/:jobid", func() { | ||||
| m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) | m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) | ||||
| m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDebug) | m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDebug) | ||||
| m.Post("/commit_image", cloudbrain.AdminOrOwnerOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage) | |||||
| m.Post("/commit_image", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage) | |||||
| m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) | m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) | ||||
| m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel) | m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel) | ||||
| m.Post("/restart", reqRepoCloudBrainWriter, repo.CloudBrainRestart) | |||||
| m.Post("/restart", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainRestart) | |||||
| m.Get("/rate", reqRepoCloudBrainReader, repo.GetRate) | m.Get("/rate", reqRepoCloudBrainReader, repo.GetRate) | ||||
| m.Get("/models", reqRepoCloudBrainReader, repo.CloudBrainShowModels) | m.Get("/models", reqRepoCloudBrainReader, repo.CloudBrainShowModels) | ||||
| m.Get("/download_model", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDownloadModel) | |||||
| m.Get("/download_model", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDownloadModel) | |||||
| }) | }) | ||||
| m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | ||||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | ||||
| }, context.RepoRef()) | }, context.RepoRef()) | ||||
| m.Group("/modelmanage", func() { | m.Group("/modelmanage", func() { | ||||
| m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel) | m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel) | ||||
| m.Post("/create_new_model", reqRepoModelManageWriter, repo.SaveNewNameModel) | |||||
| m.Post("/create_new_model", repo.SaveNewNameModel) | |||||
| m.Delete("/delete_model", repo.DeleteModel) | m.Delete("/delete_model", repo.DeleteModel) | ||||
| m.Put("/modify_model", repo.ModifyModelInfo) | m.Put("/modify_model", repo.ModifyModelInfo) | ||||
| m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate) | m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate) | ||||
| @@ -992,6 +992,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/show_model_child_api", repo.ShowOneVersionOtherModel) | m.Get("/show_model_child_api", repo.ShowOneVersionOtherModel) | ||||
| m.Get("/query_train_job", reqRepoCloudBrainReader, repo.QueryTrainJobList) | m.Get("/query_train_job", reqRepoCloudBrainReader, repo.QueryTrainJobList) | ||||
| m.Get("/query_train_job_version", reqRepoCloudBrainReader, repo.QueryTrainJobVersionList) | m.Get("/query_train_job_version", reqRepoCloudBrainReader, repo.QueryTrainJobVersionList) | ||||
| m.Get("/query_model_for_predict", reqRepoCloudBrainReader, repo.QueryModelListForPredict) | |||||
| m.Get("/query_modelfile_for_predict", reqRepoCloudBrainReader, repo.QueryModelFileForPredict) | |||||
| m.Get("/query_onelevel_modelfile", reqRepoCloudBrainReader, repo.QueryOneLevelModelFile) | |||||
| m.Group("/:ID", func() { | m.Group("/:ID", func() { | ||||
| m.Get("", repo.ShowSingleModel) | m.Get("", repo.ShowSingleModel) | ||||
| m.Get("/downloadsingle", repo.DownloadSingleModelFile) | m.Get("/downloadsingle", repo.DownloadSingleModelFile) | ||||
| @@ -1021,7 +1024,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow) | m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow) | ||||
| m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobStop) | m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobStop) | ||||
| m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobDel) | m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobDel) | ||||
| m.Get("/model_download", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.ModelDownload) | |||||
| m.Get("/model_download", cloudbrain.AdminOrJobCreaterRight, repo.ModelDownload) | |||||
| m.Get("/create_version", cloudbrain.AdminOrJobCreaterRight, repo.TrainJobNewVersion) | m.Get("/create_version", cloudbrain.AdminOrJobCreaterRight, repo.TrainJobNewVersion) | ||||
| m.Post("/create_version", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion) | m.Post("/create_version", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion) | ||||
| }) | }) | ||||
| @@ -5,13 +5,12 @@ | |||||
| package repository | package repository | ||||
| import ( | import ( | ||||
| "fmt" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/notification" | "code.gitea.io/gitea/modules/notification" | ||||
| repo_module "code.gitea.io/gitea/modules/repository" | repo_module "code.gitea.io/gitea/modules/repository" | ||||
| pull_service "code.gitea.io/gitea/services/pull" | pull_service "code.gitea.io/gitea/services/pull" | ||||
| "fmt" | |||||
| ) | ) | ||||
| // CreateRepository creates a repository for the user/organization. | // CreateRepository creates a repository for the user/organization. | ||||
| @@ -14,6 +14,11 @@ type Client struct { | |||||
| Send chan *models.Action | Send chan *models.Action | ||||
| } | } | ||||
| func (c *Client) Close() { | |||||
| close(c.Send) | |||||
| c.Conn.Close() | |||||
| } | |||||
| func (c *Client) WritePump() { | func (c *Client) WritePump() { | ||||
| defer func() { | defer func() { | ||||
| @@ -1,11 +1,17 @@ | |||||
| package socketwrap | package socketwrap | ||||
| import ( | import ( | ||||
| "os" | |||||
| "os/signal" | |||||
| "syscall" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "github.com/elliotchance/orderedmap" | |||||
| ) | ) | ||||
| type ClientsManager struct { | type ClientsManager struct { | ||||
| Clients map[*Client]bool | |||||
| Clients *orderedmap.OrderedMap | |||||
| Register chan *Client | Register chan *Client | ||||
| Unregister chan *Client | Unregister chan *Client | ||||
| } | } | ||||
| @@ -14,32 +20,69 @@ func NewClientsManager() *ClientsManager { | |||||
| return &ClientsManager{ | return &ClientsManager{ | ||||
| Register: make(chan *Client), | Register: make(chan *Client), | ||||
| Unregister: make(chan *Client), | Unregister: make(chan *Client), | ||||
| Clients: make(map[*Client]bool), | |||||
| Clients: orderedmap.NewOrderedMap(), | |||||
| } | } | ||||
| } | } | ||||
| var LastTenActionsQueue = NewSyncQueue(10) | |||||
| const MaxClients = 100 | |||||
| var LastActionsQueue = NewSyncQueue(15) | |||||
| func (h *ClientsManager) Run() { | func (h *ClientsManager) Run() { | ||||
| initActionQueue() | |||||
| sig := make(chan os.Signal, 1) | |||||
| signal.Notify(sig, os.Interrupt, syscall.SIGTERM) | |||||
| var signalsReceived uint | |||||
| for { | for { | ||||
| select { | select { | ||||
| case client := <-h.Register: | case client := <-h.Register: | ||||
| h.Clients[client] = true | |||||
| h.Clients.Set(client, true) | |||||
| if h.Clients.Len() > MaxClients { | |||||
| h.Clients.Delete(h.Clients.Front().Key) | |||||
| } | |||||
| case client := <-h.Unregister: | case client := <-h.Unregister: | ||||
| if _, ok := h.Clients[client]; ok { | |||||
| delete(h.Clients, client) | |||||
| if _, ok := h.Clients.Get(client); ok { | |||||
| h.Clients.Delete(client) | |||||
| close(client.Send) | close(client.Send) | ||||
| } | } | ||||
| case message := <-models.ActionChan: | case message := <-models.ActionChan: | ||||
| LastTenActionsQueue.Push(message) | |||||
| for client := range h.Clients { | |||||
| LastActionsQueue.Push(message) | |||||
| for _, client := range h.Clients.Keys() { | |||||
| select { | select { | ||||
| case client.Send <- message: | |||||
| case client.(*Client).Send <- message: | |||||
| default: | default: | ||||
| close(client.Send) | |||||
| delete(h.Clients, client) | |||||
| close(client.(*Client).Send) | |||||
| h.Clients.Delete(client) | |||||
| } | } | ||||
| } | } | ||||
| case s := <-sig: | |||||
| log.Info("received signal", s) | |||||
| signalsReceived++ | |||||
| if signalsReceived < 2 { | |||||
| for _, client := range h.Clients.Keys() { | |||||
| h.Clients.Delete(client) | |||||
| client.(*Client).Close() | |||||
| } | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| func initActionQueue() { | |||||
| actions, err := models.GetLast20PublicFeeds() | |||||
| if err == nil { | |||||
| for i := len(actions) - 1; i >= 0; i-- { | |||||
| user, err := models.GetUserByID(actions[i].UserID) | |||||
| if err == nil { | |||||
| if !user.IsOrganization() { | |||||
| LastActionsQueue.Push(actions[i]) | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -26,7 +26,15 @@ | |||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{if .EnableSwagger}}<a href="/api/swagger" class="ui item">API</a>{{end}} | |||||
| <a href="https://git.openi.org.cn/zeizei/OpenI_Learning" class=" item a_margin" target="_blank"><i class="ri-creative-commons-by-line footer_icon" ></i><p class="footer_icon">{{.i18n.Tr "custom.Platform_Tutorial"}}</p> </a> | |||||
| {{if .EnableSwagger}}<a href="/api/swagger" class=" item a_margin"><i class="ri-exchange-line footer_icon" > </i><p class="footer_icon">API</p> </a>{{end}} | |||||
| {{if .IsSigned}} | |||||
| <a href="https://git.openi.org.cn/zeizei/OpenI_Learning/issues/new" class=" item a_margin" target="_blank"><i class="ri-mail-send-line footer_icon"></i><p class="footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
| {{else}} | |||||
| <a href="{{AppSubUrl}}/user/login" class=" item a_margin" ><i class="ri-mail-send-line footer_icon" ></i><p class="footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
| {{end}} | |||||
| {{template "custom/extra_links_footer" .}} | {{template "custom/extra_links_footer" .}} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -26,7 +26,13 @@ | |||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{if .EnableSwagger}}<a href="/api/swagger" class="ui item">API</a>{{end}} | |||||
| <a href="https://git.openi.org.cn/zeizei/OpenI_Learning" class=" item a_margin" target="_blank"><i class="ri-creative-commons-by-line footer_icon" ></i><p class="footer_icon">{{.i18n.Tr "custom.Platform_Tutorial"}}</p> </a> | |||||
| {{if .EnableSwagger}}<a href="/api/swagger" class=" item a_margin"><i class="ri-exchange-line footer_icon" > </i><p class="footer_icon">API</p> </a>{{end}} | |||||
| {{if .IsSigned}} | |||||
| <a href="https://git.openi.org.cn/zeizei/OpenI_Learning/issues/new" class=" item a_margin" target="_blank"><i class="ri-mail-send-line footer_icon"></i><p class="footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
| {{else}} | |||||
| <a href="{{AppSubUrl}}/user/login" class=" item a_margin" ><i class="ri-mail-send-line footer_icon" ></i><p class="footer_icon footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
| {{end}} | |||||
| {{template "custom/extra_links_footer" .}} | {{template "custom/extra_links_footer" .}} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -174,6 +174,7 @@ | |||||
| {{else if ne DefaultTheme "gitea"}} | {{else if ne DefaultTheme "gitea"}} | ||||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | ||||
| {{end}} | {{end}} | ||||
| <link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
| {{template "custom/header" .}} | {{template "custom/header" .}} | ||||
| <script> | <script> | ||||
| @@ -186,7 +187,6 @@ var _hmt = _hmt || []; | |||||
| })(); | })(); | ||||
| </script> | </script> | ||||
| <script src="/self/func.js" type="text/javascript"></script> | <script src="/self/func.js" type="text/javascript"></script> | ||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| {{template "custom/body_outer_pre" .}} | {{template "custom/body_outer_pre" .}} | ||||
| @@ -195,14 +195,57 @@ var _hmt = _hmt || []; | |||||
| <noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | <noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | ||||
| {{template "custom/body_inner_pre" .}} | {{template "custom/body_inner_pre" .}} | ||||
| {{if not .PageIsInstall}} | {{if not .PageIsInstall}} | ||||
| <div class="ui top secondary stackable main menu following bar dark"> | <div class="ui top secondary stackable main menu following bar dark"> | ||||
| {{template "base/head_navbar" .}} | {{template "base/head_navbar" .}} | ||||
| </div><!-- end bar --> | </div><!-- end bar --> | ||||
| <div class="notic_content" id ="notic_content" > | |||||
| <a href={{.notice.Link}} class="a_width"> | |||||
| <marquee behavior="scroll" direction="left"> | |||||
| {{.notice.Title}} | |||||
| </marquee> | |||||
| </a> | |||||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||||
| </div> | |||||
| {{end}} | {{end}} | ||||
| {{/* | {{/* | ||||
| </div> | </div> | ||||
| </body> | </body> | ||||
| </html> | </html> | ||||
| */}} | */}} | ||||
| <script> | |||||
| function closeNoice(){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| localStorage.setItem("isCloseNotice",true) | |||||
| } | |||||
| function isShowNotice(){ | |||||
| var current_notice = localStorage.getItem("notice") | |||||
| if (current_notice != "{{.notice.CommitId}}"){ | |||||
| localStorage.setItem('notice',"{{.notice.CommitId}}"); | |||||
| isNewNotice=true; | |||||
| localStorage.setItem("isCloseNotice",false) | |||||
| }else{ | |||||
| isNewNotice=false; | |||||
| } | |||||
| if (JSON.parse("{{.notice.Visible}}")){ | |||||
| if(isNewNotice){ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| }else{ | |||||
| isCloseNotice = localStorage.getItem("isCloseNotice") | |||||
| if (JSON.parse(isCloseNotice)){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| } | |||||
| } | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| } | |||||
| } | |||||
| isShowNotice(); | |||||
| </script> | |||||
| @@ -174,6 +174,7 @@ | |||||
| {{else if ne DefaultTheme "gitea"}} | {{else if ne DefaultTheme "gitea"}} | ||||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | ||||
| {{end}} | {{end}} | ||||
| <link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
| {{template "custom/header" .}} | {{template "custom/header" .}} | ||||
| <script> | <script> | ||||
| @@ -200,9 +201,52 @@ var _hmt = _hmt || []; | |||||
| <div class="ui top secondary stackable main menu following bar dark"> | <div class="ui top secondary stackable main menu following bar dark"> | ||||
| {{template "base/head_navbar_fluid" .}} | {{template "base/head_navbar_fluid" .}} | ||||
| </div><!-- end bar --> | </div><!-- end bar --> | ||||
| <div class="notic_content" id ="notic_content" > | |||||
| <a href={{.notice.Link}} class="a_width"> | |||||
| <marquee behavior="scroll" direction="left"> | |||||
| {{.notice.Title}} | |||||
| </marquee> | |||||
| </a> | |||||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||||
| </div> | |||||
| {{end}} | {{end}} | ||||
| {{/* | {{/* | ||||
| </div> | </div> | ||||
| </body> | </body> | ||||
| </html> | </html> | ||||
| */}} | */}} | ||||
| <script> | |||||
| function closeNoice(){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| localStorage.setItem("isCloseNotice",true) | |||||
| } | |||||
| function isShowNotice(){ | |||||
| var current_notice = localStorage.getItem("notice") | |||||
| if (current_notice != "{{.notice.CommitId}}"){ | |||||
| localStorage.setItem('notice',"{{.notice.CommitId}}"); | |||||
| isNewNotice=true; | |||||
| localStorage.setItem("isCloseNotice",false) | |||||
| }else{ | |||||
| isNewNotice=false; | |||||
| } | |||||
| if (JSON.parse("{{.notice.Visible}}")){ | |||||
| if(isNewNotice){ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| }else{ | |||||
| isCloseNotice = localStorage.getItem("isCloseNotice") | |||||
| if (JSON.parse(isCloseNotice)){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| } | |||||
| } | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| } | |||||
| } | |||||
| isShowNotice(); | |||||
| </script> | |||||
| @@ -186,25 +186,71 @@ var _hmt = _hmt || []; | |||||
| s.parentNode.insertBefore(hm, s); | s.parentNode.insertBefore(hm, s); | ||||
| })(); | })(); | ||||
| </script> | </script> | ||||
| <script src="/self/func.js" type="text/javascript"></script> | |||||
| <!--RemixIcon Fonts v2.5.0--> | |||||
| <link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
| <!-- Swiper --> | |||||
| <link rel="stylesheet" href="/swiper/swiper-bundle.min.css"> | |||||
| <script src="/swiper/swiper-bundle.min.js"></script> | |||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| {{template "custom/body_outer_pre" .}} | {{template "custom/body_outer_pre" .}} | ||||
| <div class="full height"> | <div class="full height"> | ||||
| <noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | <noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | ||||
| <div class="ui vertical masthead secondary hometop segment"> | |||||
| {{template "custom/body_inner_pre" .}} | |||||
| {{if not .PageIsInstall}} | |||||
| <div class="ui container"> | |||||
| <div class="ui top secondary stackable main menu following bar"> | |||||
| {{template "base/head_navbar_home" .}} | |||||
| </div><!-- end bar --> | |||||
| </div> | |||||
| {{end}} | |||||
| {{template "custom/body_inner_pre" .}} | |||||
| {{if not .PageIsInstall}} | |||||
| <div class="ui top secondary stackable main menu following bar dark"> | |||||
| {{template "base/head_navbar" .}} | |||||
| </div><!-- end bar --> | |||||
| <div class="notic_content" id ="notic_content" > | |||||
| <a href={{.notice.Link}} class="a_width"> | |||||
| <marquee behavior="scroll" direction="left"> | |||||
| {{.notice.Title}} | |||||
| </marquee> | |||||
| </a> | |||||
| <i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||||
| </div> | |||||
| {{end}} | |||||
| {{/* | {{/* | ||||
| </div> | |||||
| </div> | </div> | ||||
| </body> | </body> | ||||
| </html> | </html> | ||||
| */}} | */}} | ||||
| <script> | |||||
| function closeNoice(){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| localStorage.setItem("isCloseNotice",true) | |||||
| } | |||||
| function isShowNotice(){ | |||||
| var current_notice = localStorage.getItem("notice") | |||||
| if (current_notice != "{{.notice.CommitId}}"){ | |||||
| localStorage.setItem('notice',"{{.notice.CommitId}}"); | |||||
| isNewNotice=true; | |||||
| localStorage.setItem("isCloseNotice",false) | |||||
| }else{ | |||||
| isNewNotice=false; | |||||
| } | |||||
| if (JSON.parse("{{.notice.Visible}}")){ | |||||
| if(isNewNotice){ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| }else{ | |||||
| isCloseNotice = localStorage.getItem("isCloseNotice") | |||||
| if (JSON.parse(isCloseNotice)){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| } | |||||
| } | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| } | |||||
| } | |||||
| isShowNotice(); | |||||
| </script> | |||||
| @@ -168,6 +168,14 @@ | |||||
| {{svg "octicon-question" 16}} | {{svg "octicon-question" 16}} | ||||
| {{.i18n.Tr "help"}}<!-- Help --> | {{.i18n.Tr "help"}}<!-- Help --> | ||||
| </a--> | </a--> | ||||
| <a class="item" href="https://git.openi.org.cn/zeizei/OpenI_Learning" target="_blank"> | |||||
| <i class="tutorial_icon"> | |||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | |||||
| <path fill="none" d="M0 0h24v24H0z"/><path d="M2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM12 5v14h8V5h-8zm1 2h6v2h-6V7zm0 3h6v2h-6v-2z"/> | |||||
| </svg> | |||||
| </i> | |||||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||||
| </a> | |||||
| {{if .IsAdmin}} | {{if .IsAdmin}} | ||||
| <div class="divider"></div> | <div class="divider"></div> | ||||
| @@ -213,4 +221,6 @@ | |||||
| </div><!-- end anonymous right menu --> | </div><!-- end anonymous right menu --> | ||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| @@ -166,6 +166,14 @@ | |||||
| {{svg "octicon-question" 16}} | {{svg "octicon-question" 16}} | ||||
| {{.i18n.Tr "help"}}<!-- Help --> | {{.i18n.Tr "help"}}<!-- Help --> | ||||
| </a--> | </a--> | ||||
| <a class="item" href="https://git.openi.org.cn/zeizei/OpenI_Learning" target="_blank"> | |||||
| <i class="tutorial_icon"> | |||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | |||||
| <path fill="none" d="M0 0h24v24H0z"/><path d="M2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM12 5v14h8V5h-8zm1 2h6v2h-6V7zm0 3h6v2h-6v-2z"/> | |||||
| </svg> | |||||
| </i> | |||||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||||
| </a> | |||||
| {{if .IsAdmin}} | {{if .IsAdmin}} | ||||
| <div class="divider"></div> | <div class="divider"></div> | ||||
| @@ -148,6 +148,14 @@ | |||||
| {{svg "octicon-question" 16}} | {{svg "octicon-question" 16}} | ||||
| {{.i18n.Tr "help"}}<!-- Help --> | {{.i18n.Tr "help"}}<!-- Help --> | ||||
| </a--> | </a--> | ||||
| <a class="item" href="https://git.openi.org.cn/zeizei/OpenI_Learning" target="_blank"> | |||||
| <i class="tutorial_icon"> | |||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | |||||
| <path fill="none" d="M0 0h24v24H0z"/><path d="M2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM12 5v14h8V5h-8zm1 2h6v2h-6V7zm0 3h6v2h-6v-2z"/> | |||||
| </svg> | |||||
| </i> | |||||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||||
| </a> | |||||
| {{if .IsAdmin}} | {{if .IsAdmin}} | ||||
| <div class="divider"></div> | <div class="divider"></div> | ||||
| @@ -169,6 +169,14 @@ | |||||
| {{svg "octicon-question" 16}} | {{svg "octicon-question" 16}} | ||||
| {{.i18n.Tr "help"}}<!-- Help --> | {{.i18n.Tr "help"}}<!-- Help --> | ||||
| </a--> | </a--> | ||||
| <a class="item" href="https://git.openi.org.cn/zeizei/OpenI_Learning" target="_blank"> | |||||
| <i class="tutorial_icon"> | |||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | |||||
| <path fill="none" d="M0 0h24v24H0z"/><path d="M2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM12 5v14h8V5h-8zm1 2h6v2h-6V7zm0 3h6v2h-6v-2z"/> | |||||
| </svg> | |||||
| </i> | |||||
| {{.i18n.Tr "custom.Platform_Tutorial"}} | |||||
| </a> | |||||
| {{if .IsAdmin}} | {{if .IsAdmin}} | ||||
| <div class="divider"></div> | <div class="divider"></div> | ||||
| @@ -174,6 +174,7 @@ | |||||
| {{else if ne DefaultTheme "gitea"}} | {{else if ne DefaultTheme "gitea"}} | ||||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | ||||
| {{end}} | {{end}} | ||||
| <link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
| {{template "custom/header" .}} | {{template "custom/header" .}} | ||||
| <script> | <script> | ||||
| @@ -200,9 +201,53 @@ var _hmt = _hmt || []; | |||||
| <div class="ui top secondary stackable main menu following bar dark"> | <div class="ui top secondary stackable main menu following bar dark"> | ||||
| {{template "base/head_navbar_pro" .}} | {{template "base/head_navbar_pro" .}} | ||||
| </div><!-- end bar --> | </div><!-- end bar --> | ||||
| <div class="notic_content" id ="notic_content" > | |||||
| <a href={{.notice.Link}} class="a_width"> | |||||
| <marquee behavior="scroll" direction="left"> | |||||
| {{.notice.Title}} | |||||
| </marquee> | |||||
| </a> | |||||
| <i class="icon icon-octicon x_icon" onclick="closeNoice()">{{svg "octicon-x" 16}}</i> | |||||
| </div> | |||||
| {{end}} | {{end}} | ||||
| {{/* | {{/* | ||||
| </div> | </div> | ||||
| </body> | </body> | ||||
| </html> | </html> | ||||
| */}} | */}} | ||||
| <script> | |||||
| function closeNoice(){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| localStorage.setItem("isCloseNotice",true) | |||||
| } | |||||
| function isShowNotice(){ | |||||
| var current_notice = localStorage.getItem("notice") | |||||
| if (current_notice != "{{.notice.CommitId}}"){ | |||||
| localStorage.setItem('notice',"{{.notice.CommitId}}"); | |||||
| isNewNotice=true; | |||||
| localStorage.setItem("isCloseNotice",false) | |||||
| }else{ | |||||
| isNewNotice=false; | |||||
| } | |||||
| if (JSON.parse("{{.notice.Visible}}")){ | |||||
| if(isNewNotice){ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| }else{ | |||||
| isCloseNotice = localStorage.getItem("isCloseNotice") | |||||
| if (JSON.parse(isCloseNotice)){ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='block' | |||||
| } | |||||
| } | |||||
| }else{ | |||||
| document.getElementById("notic_content").style.display='none' | |||||
| } | |||||
| } | |||||
| isShowNotice(); | |||||
| </script> | |||||
| @@ -1,7 +1,7 @@ | |||||
| {{$paginationLink := .Page.GetParams}} | {{$paginationLink := .Page.GetParams}} | ||||
| {{with .Page.Paginater}} | {{with .Page.Paginater}} | ||||
| {{if gt .TotalPages 1}} | {{if gt .TotalPages 1}} | ||||
| <div class="center page buttons"> | |||||
| <div class="center page buttons" style="margin: 0px auto 15px"> | |||||
| <div class="ui borderless pagination menu"> | <div class="ui borderless pagination menu"> | ||||
| <a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$.Link}}{{if $paginationLink}}?{{$paginationLink}}{{end}}"{{end}}><i class="angle double left icon"></i><span class="navigation_label"> {{$.i18n.Tr "admin.first_page"}}</span></a> | <a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$.Link}}{{if $paginationLink}}?{{$paginationLink}}{{end}}"{{end}}><i class="angle double left icon"></i><span class="navigation_label"> {{$.i18n.Tr "admin.first_page"}}</span></a> | ||||
| <a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> | <a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> | ||||
| @@ -40,20 +40,20 @@ | |||||
| <div class="ui secondary pointing tabular top attached borderless menu navbar"> | <div class="ui secondary pointing tabular top attached borderless menu navbar"> | ||||
| {{if .PageIsExplore}} | {{if .PageIsExplore}} | ||||
| <a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=hot"> | |||||
| <a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=hot&tab={{$.TabName}}"> | |||||
| <svg class="svg octicon-repo" width="16" height="16" aria-hidden="true"> | <svg class="svg octicon-repo" width="16" height="16" aria-hidden="true"> | ||||
| <use xlink:href="#octicon-repo" /> | <use xlink:href="#octicon-repo" /> | ||||
| </svg> | </svg> | ||||
| 热门{{.i18n.Tr "explore.repos"}} | 热门{{.i18n.Tr "explore.repos"}} | ||||
| </a> | </a> | ||||
| <a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=active"> | |||||
| <a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=active&tab={{$.TabName}}"> | |||||
| <svg class="svg octicon-inbox" width="16" height="16" aria-hidden="true"> | <svg class="svg octicon-inbox" width="16" height="16" aria-hidden="true"> | ||||
| <use xlink:href="#octicon-inbox" /> | <use xlink:href="#octicon-inbox" /> | ||||
| </svg> | </svg> | ||||
| 活跃{{.i18n.Tr "explore.repos"}} | 活跃{{.i18n.Tr "explore.repos"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| <a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=recentupdate"> | |||||
| <a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=recentupdate&tab={{$.TabName}}"> | |||||
| <svg class="svg octicon-organization" width="16" height="16" aria-hidden="true"> | <svg class="svg octicon-organization" width="16" height="16" aria-hidden="true"> | ||||
| <use xlink:href="#octicon-organization" /> | <use xlink:href="#octicon-organization" /> | ||||
| </svg> {{.i18n.Tr "repo.issues.filter_sort.recentupdate"}} | </svg> {{.i18n.Tr "repo.issues.filter_sort.recentupdate"}} | ||||
| @@ -67,16 +67,16 @@ | |||||
| <i class="dropdown icon"></i> | <i class="dropdown icon"></i> | ||||
| </span> | </span> | ||||
| <div class="menu"> | <div class="menu"> | ||||
| <a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a> | |||||
| <a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a> | |||||
| <a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a> | |||||
| <a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a> | |||||
| <a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a> | |||||
| <a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a> | |||||
| <a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a> | |||||
| <a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a> | |||||
| <a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a> | |||||
| <a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a> | |||||
| <a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a> | |||||
| <a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a> | |||||
| <a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a> | |||||
| <a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a> | |||||
| <a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a> | |||||
| <a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a> | |||||
| <a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a> | |||||
| <a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a> | |||||
| <a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a> | |||||
| <a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&topic={{$.Topic}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -59,7 +59,7 @@ | |||||
| </div> | </div> | ||||
| <div class="swiper-slide"> | <div class="swiper-slide"> | ||||
| <div class="ui card"> | <div class="ui card"> | ||||
| <a class="image" href="https://git.openi.org.cn/TensorLayer"> | |||||
| <a class="image" href="https://git.openi.org.cn/OpenI/TensorLayerX"> | |||||
| <img src="/img/org-tensorlayer@2x-80.jpg" alt="TensorLayer" title="TensorLayer"> | <img src="/img/org-tensorlayer@2x-80.jpg" alt="TensorLayer" title="TensorLayer"> | ||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| @@ -10,6 +10,7 @@ | |||||
| <div class="ui fluid action input"> | <div class="ui fluid action input"> | ||||
| <input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus> | <input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus> | ||||
| <input type="hidden" name="topic" value="{{$.Topic}}"> | <input type="hidden" name="topic" value="{{$.Topic}}"> | ||||
| <input type="hidden" name="tab" value="{{$.TabName}}"> | |||||
| <input type="hidden" name="sort" value="{{$.SortType}}"> | <input type="hidden" name="sort" value="{{$.SortType}}"> | ||||
| <button class="ui green button">{{.i18n.Tr "explore.search"}}</button> | <button class="ui green button">{{.i18n.Tr "explore.search"}}</button> | ||||
| </div> | </div> | ||||
| @@ -1,221 +1,161 @@ | |||||
| {{template "base/head_home" .}} | {{template "base/head_home" .}} | ||||
| <div class="ui container homebanner"> | |||||
| <h1 class="ui header"> | |||||
| 启智AI开发协作平台 | |||||
| <div class="sub header"> | |||||
| 面向AI领域的一站式协同开发环境 | |||||
| </div> | |||||
| </h1> | |||||
| <p class="am-lh-18">免费私有代码仓库,免费计算资源,大容量数据存储,<br>多类型硬件环境(GPU、NPU),AI开发流水线(开发-调试-训练-迭代)</p> | |||||
| {{if .IsSigned}} | |||||
| <a class="circular ui secondary button" href="{{AppSubUrl}}/dashboard">立即使用 <i class="right arrow icon"></i></a> | |||||
| {{else}} | |||||
| <a class="circular ui secondary button" href="{{AppSubUrl}}/user/login">立即使用 <i class="right arrow icon"></i></a> | |||||
| {{end}} | |||||
| <div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div> | |||||
| </div> | |||||
| </div><!-- end segment --> | |||||
| <div class="explore repositories"> | |||||
| <div class="ui sticky container"> | |||||
| <div class="ui secondary pointing fluid five item menu" style="background-color: #FFF;"> | |||||
| <a class="item" href="#first">代码</a> | |||||
| <a class="item" href="#second">数据</a> | |||||
| <a class="item" href="#third">AI流水线</a> | |||||
| <a class="item" href="#fourth">协同开发</a> | |||||
| <a class="item" href="#fifth">云脑协同</a> | |||||
| </div> | |||||
| </div> | |||||
| <div id="railContent"> | |||||
| <a name="first"></a> | |||||
| <div class="ui container am-mt-30 basic tab i-code active" style="position: relative;"> | |||||
| <div class="ui inverted very padded segment radius15 am-pl-30"> | |||||
| <div class="ui mobile reversed stackable grid am-pl-30"> | |||||
| <div class="six wide column"> | |||||
| <h2 class="ui huge blue header">代码管理</h2> | |||||
| <p class="am-lh-18">在这里为你和你的团队创建项目,基于Git工具,提交记录或者回滚代码修改。<br> | |||||
| 不论是公开或者私有仓库,都可免费使用所有功能。<br> | |||||
| 尽情将你喜欢的代码都放在这里,仓库数量、存储容量不受限</p> | |||||
| {{if .IsSigned}} | |||||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a> | |||||
| {{else}} | |||||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="ten wide column computer only i-code-pic am-pt-30"> | |||||
| <img class="ui fluid rounded image am-shadow-2 am-mt-10" src="/img/i-code-pic.jpg" style="position: absolute;"> | |||||
| <div class="ui vertical masthead secondary hometop segment"> | |||||
| <div class="ui container" style="position: relative;"> | |||||
| <div class="ui center homebanner"> | |||||
| <h1 class="ui huge header"> | |||||
| {{.page_title}} | |||||
| <div class="sub header"> | |||||
| {{.page_small_title}} | |||||
| </div> | </div> | ||||
| </div> | |||||
| </h1> | |||||
| <p class="ui am-lh-18">{{.page_description}}</p> | |||||
| {{if .IsSigned}} | |||||
| <a class="circular huge ui secondary button" href="{{AppSubUrl}}/dashboard">{{.page_use}} <i class="right arrow icon"></i></a> | |||||
| {{else}} | |||||
| <a class="circular huge ui secondary button" href="{{AppSubUrl}}/user/login">{{.page_use}} <i class="right arrow icon"></i></a> | |||||
| {{end}} | |||||
| </div> | </div> | ||||
| <div class="am-mt-30 am-pt-30 am-pl-30"> | |||||
| <div class="am-pl-30 am-pb-30"> | |||||
| <h2 class="ui huge header am-bw">协作开发</h2> | |||||
| <p class="am-lh-18">鼓励通过创建合并请求(PR)的方式,更好的进行团队协作<br> | |||||
| 代码评审让每一次的代码修改得以二次确认,提高代码质量<br> | |||||
| 创建并指派任务(Issue),让每一个任务的进展有迹可循,规范管理<br> | |||||
| 被合并的PR,可获得奖励积分;积分总额可以显示出你在项目中的贡献度,也许有一天会有人愿意为此付费<br> | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| <div class="leftline01"></div> | |||||
| </div> | |||||
| <a name="second"></a> | |||||
| <div class="ui basic tab active am-mt-30 bg-gray am-pt-30"> | |||||
| <div class="ui container i-data" style="position: relative;"> | |||||
| <div class="leftline02"></div> | |||||
| <div class="ui stackable grid"> | |||||
| <div class="nine wide column"> | |||||
| <img class="ui centered medium rounded large image" src="/img/gitopeni-index-02.svg"> | |||||
| <div class="i-code-pic" style="margin-top: -4.0rem;"> | |||||
| <img class="ui fluid rounded image am-shadow-2" src="/img/i-data-pic.jpg"> | |||||
| <div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div> | |||||
| <div id="homenews" class="ui container"> | |||||
| <p>* {{.page_only_dynamic}}</p> | |||||
| <div class="ui grid"> | |||||
| <div class="twelve wide tablet ten wide computer column homenews"> | |||||
| <div class="newslist"> | |||||
| <div class="ui mini aligned list swiper-wrapper" id="newmessage"> | |||||
| </div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="seven wide column am-pt-30"> | |||||
| <h2 class="ui huge blue header">数据集管理</h2> | |||||
| <p class="am-lh-18"> | |||||
| 数据是重要的生产要素,AI开发更是离不开数据;<br> | |||||
| - 数据与模型代码的协同,可在项目中上传关联的数据集;<br> | |||||
| - 数据存储免费,不限制文件大小;<br> | |||||
| - 数据可共享,标注清洗过的公开数据集,用户可设置为公有数据,供社区用户下载; | |||||
| </p> | |||||
| <div class="am-mt-30 am-pt-20"></div> | |||||
| <h2 class="ui huge header">使用数据集</h2> | |||||
| <p class="am-lh-18"> | |||||
| 数据集可以直接用于训练或者推理任务中<br> | |||||
| 创建任务时选择对应的数据集,启动任务执行环境(Docker)后,即可在Docker内访问到你的代码和数据,就像在你本地执行一样 | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| </div><!-- end homenews --> | |||||
| </div> | |||||
| </div><!-- end segment --> | |||||
| <!--组织--> | |||||
| <div class="ui container homeorg"> | |||||
| <div class="ui stackable grid"> | |||||
| <div class="sixteen wide tablet four wide computer column homeorg-tit"> | |||||
| <h2>{{.page_recommend_org}}</h2> | |||||
| <p><span class="ui text grey">{{.page_recommend_org_desc}}</span><a href="{{.RecommendURL}}">{{.page_recommend_org_commit}}</a></p> | |||||
| <a href="{{AppSubUrl}}/explore/organizations" class="circular ui primary basic button">{{.page_recommend_org_more}} <i class="arrow circle right icon"></i></a> | |||||
| </div> | |||||
| <div class="sixteen wide tablet twelve wide computer column"> | |||||
| <div class="ui stackable three column grid homeorg-list" id="recommendorg"> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | |||||
| </div> | |||||
| <div class="leftline01"></div> | |||||
| </div> | |||||
| <!--项目--> | |||||
| <div class="ui container homepro"> | |||||
| <div class="leftline02"></div> | |||||
| <div class="leftline02-2"></div> | |||||
| <div class="ui center homepro-tit am-mb-20"> | |||||
| <h2>{{.page_recommend_repo}}</h2> | |||||
| <p><span class="ui text grey">{{.page_recommend_repo_desc}}</span><a href="{{.RecommendURL}}">{{.page_recommend_repo_commit}}</a>{{.page_recommend_repo_go}}<a href="{{AppSubUrl}}/explore/">{{.page_recommend_repo_more}}</a></p> | |||||
| </div> | |||||
| <a name="third"></a> | |||||
| <div class="ui basic tab active container am-mt-30 am-pt-30"> | |||||
| <h2 class="ui center aligned huge blue header am-pt-30"> | |||||
| AI流水线 | |||||
| <div class="sub header am-mt-10">提供集代码开发、数据管理、模型调试、推理和评测为一体的AI开发流水线</div> | |||||
| </h2> | |||||
| <div class="ui divider"></div> | |||||
| <div class="ui centered grid"> | |||||
| <div class="fourteen wide column"> | |||||
| <div class="ui two column grid"> | |||||
| <div class="column"> | |||||
| <div class="ui small header">调试任务:</div> | |||||
| <div class="ui bulleted list"> | |||||
| <div class="item">配置模型运行环境;</div> | |||||
| <div class="item">可在线编辑和调试模型代码,并将改动更新至代码仓库;</div> | |||||
| <div class="item">基于编辑好的脚本,开展模型评测任务;</div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="column"> | |||||
| <div class="ui small header">作业任务:</div> | |||||
| <div class="ui bulleted list"> | |||||
| <div class="item">利用已配置好的模型运行环境;</div> | |||||
| <div class="item">基于编辑好的脚本,一键运行,开展模型训练或模型推理任务;</div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="homepro-list"> | |||||
| <div class="swiper-wrapper" id="recommendrepo"> | |||||
| </div> | </div> | ||||
| <div class="ui divider"></div> | |||||
| <img class="ui centered image" src="/img/develop.svg"> | |||||
| <div class="swiper-pagination"></div> | |||||
| </div> | </div> | ||||
| </div> | |||||
| <a name="fourth"></a> | |||||
| <div class="ui basic tab active container am-mt-30 am-pt-30 i-env"> | |||||
| <h2 class="ui center aligned huge blue header am-pt-30"> | |||||
| 协同开发环境 | |||||
| <div class="sub header am-mt-10">启智AI协作开发平台与传统git平台最大的不同就在于提供了面向AI开发的协同开发环境</div> | |||||
| </h2> | |||||
| <div class="ui four stackable cards am-mt-20"> | |||||
| <div class="card"> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-01.svg"> | |||||
| <a name="fourth"></a> | |||||
| <div class="ui container i-env"> | |||||
| <div class="ui center am-pb-30"> | |||||
| <div class="leftline03"></div> | |||||
| <h2>{{.page_dev_env}}</h2> | |||||
| <p><span class="ui text grey">{{.page_dev_env_desc}}</p> | |||||
| </div> | |||||
| <div class="ui four stackable cards"> | |||||
| <div class="card"> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-01.svg"> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered small header">{{.page_dev_env_desc_title}}</h3> | |||||
| <div class="description"> | |||||
| {{.page_dev_env_desc_desc}} | |||||
| </div> | </div> | ||||
| <div class="content"> | |||||
| <h3 class="ui centered header">开发要素统一管理</h3> | |||||
| <div class="description"> | |||||
| 平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="card"> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-02.svg"> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered small header">{{.page_dev_env_desc1_title}}</h3> | |||||
| <div class="description"> | |||||
| {{.page_dev_env_desc1_desc}} | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | |||||
| <div class="card"> | <div class="card"> | ||||
| <div class="image"> | |||||
| <img src="/img/i-pic-02.svg"> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered header">数据协同与共享</h3> | |||||
| <div class="description"> | |||||
| 通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型 | |||||
| </div> | |||||
| </div> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-03.svg"> | |||||
| </div> | </div> | ||||
| <div class="card"> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-03.svg"> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered header">模型管理与共享</h3> | |||||
| <div class="description"> | |||||
| 将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered small header">{{.page_dev_env_desc2_title}}</h3> | |||||
| <div class="description"> | |||||
| {{.page_dev_env_desc2_desc}} | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="card"> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-04.svg"> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered header">一次配置,多次使用</h3> | |||||
| <div class="description"> | |||||
| 提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 | |||||
| </div> | |||||
| </div> | |||||
| <div class="card"> | |||||
| <div class="image"> | |||||
| <img src="/img/i-pic-04.svg"> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <h3 class="ui centered small header">{{.page_dev_env_desc3_title}}</h3> | |||||
| <div class="description"> | |||||
| {{.page_dev_env_desc3_desc}} | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | |||||
| <a name="fifth"></a> | |||||
| <div class="ui basic tab active container am-mt-30 am-pt-30"> | |||||
| <div class="ui very padded black inverted segment radius15"> | |||||
| <div class="ui stackable grid"> | |||||
| <div class="six wide column"> | |||||
| <img class="ui centered large image" src="/img/i-yunnao.svg"> | |||||
| </div> | |||||
| <div class="ten wide column am-pt-30"> | |||||
| <h2 class="ui huge blue header">鹏城云脑开源协同</h2> | |||||
| <p class="am-lh-18"> | |||||
| 平台已经与鹏城云脑打通,可以利用鹏城云脑的丰富算力资源,完成AI开发任务<br> | |||||
| 鹏城云脑现有AI算力100P FLOPS@FP16(每秒十亿亿次半精度浮点计算),主要硬件基础设施由搭载英伟达Tesla V100 的GPU服务器和搭载鲲鹏、昇腾处理器的Atlas 900 AI集群构成<br> | |||||
| 开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等<br> | |||||
| 如果您的模型需要更多的计算资源,也可以单独申请<br> | |||||
| </p> | |||||
| {{if .IsSigned}} | |||||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||||
| {{else}} | |||||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||||
| {{end}} | |||||
| </div> | |||||
| <a name="fifth"></a> | |||||
| <div class="ui container"> | |||||
| <div class="ui very padded inverted segment radius15"> | |||||
| <div class="ui stackable grid"> | |||||
| <div class="six wide column"> | |||||
| <img class="ui centered large image" src="/img/i-yunnao.svg"> | |||||
| </div> | |||||
| <div class="ten wide column am-pt-30"> | |||||
| <h2 class="ui grey inverted header">{{.page_dev_yunlao}}</h2> | |||||
| <p class="am-lh-18 ui text grey"> | |||||
| {{.page_dev_yunlao_desc1}}<br> | |||||
| {{.page_dev_yunlao_desc2}}<br> | |||||
| {{.page_dev_yunlao_desc3}}<br> | |||||
| {{.page_dev_yunlao_desc4}}<br> | |||||
| </p> | |||||
| {{if .IsSigned}} | |||||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">{{.page_use}}</a> | |||||
| {{else}} | |||||
| <a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">{{.page_use}}</a> | |||||
| {{end}} | |||||
| <a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">{{.page_dev_yunlao_apply}}</a> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="am-mt-30"></div> | |||||
| </div><!-- end railContent --> | |||||
| </div> | |||||
| <div class="am-mt-30"></div> | |||||
| <script src="/self/js/jquery.min.js" type="text/javascript"></script> | |||||
| <script src="/home/home.js?v={{MD5 AppVer}}" type="text/javascript"></script> | |||||
| </div><!-- end explore --> | |||||
| <script> | |||||
| $('.menu .item') | |||||
| .tab(); | |||||
| $('.ui.sticky') | |||||
| .sticky({ | |||||
| context: '#railContent', | |||||
| observeChanges: true, | |||||
| }) | |||||
| .sticky('refresh'); | |||||
| </script> | |||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -321,8 +321,8 @@ | |||||
| <form id="debugAgainForm-{{.JobID}}"> | <form id="debugAgainForm-{{.JobID}}"> | ||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| {{if .CanDebug}} | {{if .CanDebug}} | ||||
| {{if eq .Status "RUNNING"}} | |||||
| <a style="margin: 0 1rem;" id="model-debug-{{.JobID}}" class='ui basic blue button' onclick='debugAgain("{{.JobID}}","{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/")'> | |||||
| {{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | |||||
| <a style="margin: 0 1rem;" id="model-debug-{{.JobID}}" class='ui basic {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}}disabled {{else}}blue {{end}}button' onclick='debugAgain("{{.JobID}}","{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/")'> | |||||
| {{$.i18n.Tr "repo.debug"}} | {{$.i18n.Tr "repo.debug"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{else}} | ||||
| @@ -331,7 +331,7 @@ | |||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{else}} | {{else}} | ||||
| {{if eq .Status "RUNNING"}} | |||||
| {{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | |||||
| <a class="ui basic disabled button"> | <a class="ui basic disabled button"> | ||||
| {{$.i18n.Tr "repo.debug"}} | {{$.i18n.Tr "repo.debug"}} | ||||
| </a> | </a> | ||||
| @@ -348,11 +348,11 @@ | |||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| {{if .CanDel}} | {{if .CanDel}} | ||||
| {{if eq .ComputeResource "CPU/GPU" }} | {{if eq .ComputeResource "CPU/GPU" }} | ||||
| <a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/cloudbrain/{{.JobID}}/stop")'> | |||||
| <a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/cloudbrain/{{.JobID}}/stop")'> | |||||
| {{$.i18n.Tr "repo.stop"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{else}} | ||||
| <a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/modelarts/notebook/{{.JobID}}/stop")'> | |||||
| <a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/modelarts/notebook/{{.JobID}}/stop")'> | |||||
| {{$.i18n.Tr "repo.stop"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| @@ -388,7 +388,7 @@ | |||||
| <div class="item" style="padding: 0 !important;"> | <div class="item" style="padding: 0 !important;"> | ||||
| <!-- 接收结果 --> | <!-- 接收结果 --> | ||||
| <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | ||||
| {{if .CanDel}} | |||||
| {{if .CanDebug}} | |||||
| <a id="model-image-{{.JobID}}" class='imageBtn ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button'>{{$.i18n.Tr "repo.submit_image"}}</a> | <a id="model-image-{{.JobID}}" class='imageBtn ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button'>{{$.i18n.Tr "repo.submit_image"}}</a> | ||||
| {{else}} | {{else}} | ||||
| <a class="imageBtn ui basic disabled button">{{$.i18n.Tr "repo.submit_image"}}</a> | <a class="imageBtn ui basic disabled button">{{$.i18n.Tr "repo.submit_image"}}</a> | ||||
| @@ -396,7 +396,7 @@ | |||||
| </div> | </div> | ||||
| <div class="item" style="padding: 0 !important;"> | <div class="item" style="padding: 0 !important;"> | ||||
| <!-- 模型下载 --> | <!-- 模型下载 --> | ||||
| {{if .CanDel}} | |||||
| {{if .CanDebug}} | |||||
| <a class="ui basic blue button" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/models" target="_blank">{{$.i18n.Tr "repo.download"}}</a> | <a class="ui basic blue button" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/models" target="_blank">{{$.i18n.Tr "repo.download"}}</a> | ||||
| {{else}} | {{else}} | ||||
| <a class="ui basic disabled button">{{$.i18n.Tr "repo.download"}}</a> | <a class="ui basic disabled button">{{$.i18n.Tr "repo.download"}}</a> | ||||
| @@ -553,6 +553,7 @@ | |||||
| $('#' + JobID+ '-text').text(res.status) | $('#' + JobID+ '-text').text(res.status) | ||||
| $('#model-debug-'+JobID).removeClass('blue').addClass('disabled') | $('#model-debug-'+JobID).removeClass('blue').addClass('disabled') | ||||
| $('#model-delete-'+JobID).removeClass('blue').addClass('disabled') | $('#model-delete-'+JobID).removeClass('blue').addClass('disabled') | ||||
| $('#model-debug-'+JobID).text("调试").css("margin","0 1rem") | |||||
| } | } | ||||
| }else{ | }else{ | ||||
| $('.alert').html(res.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(2000).fadeOut(); | $('.alert').html(res.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(2000).fadeOut(); | ||||
| @@ -636,7 +637,7 @@ | |||||
| if(["RUNNING","WAITING"].includes(status)){ | if(["RUNNING","WAITING"].includes(status)){ | ||||
| $('#stop-model-debug-'+jobID).removeClass('disabled').addClass('blue') | $('#stop-model-debug-'+jobID).removeClass('disabled').addClass('blue') | ||||
| } | } | ||||
| if(["CREATING","STOPPING","STARTING","STOPPED","FAILED","START_FAILED"].includes(status)){ | |||||
| if(["CREATING","STOPPING","STARTING","STOPPED","FAILED","START_FAILED","SUCCEEDED"].includes(status)){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('blue').addClass('disabled') | $('#stop-model-debug-'+jobID).removeClass('blue').addClass('disabled') | ||||
| } | } | ||||
| if(status==="STOPPED" || status==="FAILED"|| status==="START_FAILED"){ | if(status==="STOPPED" || status==="FAILED"|| status==="START_FAILED"){ | ||||
| @@ -161,6 +161,15 @@ td, th { | |||||
| padding-top: 0.5rem ; | padding-top: 0.5rem ; | ||||
| } | } | ||||
| </style> | </style> | ||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="repository"> | <div class="repository"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <div class="ui container"> | <div class="ui container"> | ||||
| @@ -186,6 +195,12 @@ td, th { | |||||
| <span class="accordion-panel-title-content"> | <span class="accordion-panel-title-content"> | ||||
| <span> | <span> | ||||
| <div style="float: right;"> | <div style="float: right;"> | ||||
| {{$.CsrfTokenHtml}} | |||||
| {{if and (.CanModify) (eq .Status "COMPLETED")}} | |||||
| <a class="ti-action-menu-item" onclick="showcreate({{.}})">{{$.i18n.Tr "repo.modelarts.create_model"}}</a> | |||||
| {{else}} | |||||
| <a class="ti-action-menu-item disabled">{{$.i18n.Tr "repo.modelarts.create_model"}}</a> | |||||
| {{end}} | |||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| {{if .CanModify}} | {{if .CanModify}} | ||||
| <a class="ti-action-menu-item" href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">{{$.i18n.Tr "repo.modelarts.modify"}}</a> | <a class="ti-action-menu-item" href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">{{$.i18n.Tr "repo.modelarts.modify"}}</a> | ||||
| @@ -446,6 +461,62 @@ td, th { | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <!-- 创建模型 --> | |||||
| <div id="newmodel"> | |||||
| <div class="ui modal second"> | |||||
| <div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);"> | |||||
| <h4 id="model_header">导入新模型</h4> | |||||
| </div> | |||||
| <div class="content content-padding"> | |||||
| <form id="formId" method="POST" class="ui form"> | |||||
| <div class="ui error message"> | |||||
| </div> | |||||
| {{$.CsrfTokenHtml}} | |||||
| <input type="hidden" name="trainTaskCreate" value="true"> | |||||
| <div class="two inline fields "> | |||||
| <div class="required ten wide field"> | |||||
| <label style="margin-left: -23px;">选择训练任务</label> | |||||
| <input type="hidden" class="width83" id="JobId" name="JobId" readonly required> | |||||
| <input class="width83" id="JobName" readonly required> | |||||
| </div> | |||||
| <div class="required six widde field"> | |||||
| <label>版本</label> | |||||
| <input class="width70" id="VersionName" name="VersionName" readonly required> | |||||
| </div> | |||||
| </div> | |||||
| <div class="required inline field" id="modelname"> | |||||
| <label>模型名称</label> | |||||
| <input style="width: 45%;" id="name" name="Name" required maxlength="25" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | |||||
| </div> | |||||
| <div class="required inline field" id="verionname"> | |||||
| <label>模型版本</label> | |||||
| <input style="width: 45%;" id="version" name="Version" value="" readonly required maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>模型标签</label> | |||||
| <input style="width: 83%;margin-left: 7px;" id="label" name="Label" maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label for="description">模型描述</label> | |||||
| <textarea style="width: 83%;margin-left: 7px;" id="Description" name="Description" rows="3" maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.new_place"}}' onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)"></textarea> | |||||
| </div> | |||||
| <div class="inline field" style="margin-left: 75px;"> | |||||
| <button onclick="createModel()" id="submitId" type="button" class="ui create_train_job green button" style="position: absolute;"> | |||||
| {{.i18n.Tr "repo.model.manage.sava_model"}} | |||||
| </button> | |||||
| </div> | |||||
| </form> | |||||
| <div class="actions" style="display: inline-block;margin-left: 180px;"> | |||||
| <button class="ui button cancel" >{{.i18n.Tr "repo.cloudbrain.cancel"}}</button> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -479,7 +550,61 @@ td, th { | |||||
| } | } | ||||
| let timeid = window.setInterval(loadJobStatus, 30000); | let timeid = window.setInterval(loadJobStatus, 30000); | ||||
| $(document).ready(loadJobStatus); | $(document).ready(loadJobStatus); | ||||
| function showcreate(obj){ | |||||
| $('.ui.modal.second') | |||||
| .modal({ | |||||
| centered: false, | |||||
| onShow:function(){ | |||||
| $('input[name="Version"]').addClass('model_disabled') | |||||
| // $('input[name="JobId"]').text(obj.JobName) | |||||
| $('#JobName').val(obj.JobName).addClass('model_disabled') | |||||
| $('input[name="JobId"]').val(obj.JobID) | |||||
| $('input[name="VersionName"]').val(obj.VersionName).addClass('model_disabled') | |||||
| $('.ui.dimmer').css({"background-color":"rgb(136, 136, 136,0.7)"}) | |||||
| createModelName() | |||||
| }, | |||||
| onHide:function(){ | |||||
| document.getElementById("formId").reset(); | |||||
| $('.ui.dimmer').css({"background-color":""}) | |||||
| $('.ui.error.message').text() | |||||
| $('.ui.error.message').css('display','none') | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| } | |||||
| function createModel(){ | |||||
| let url_href = `/${userName}/${repoPath}/modelmanage/create_new_model` | |||||
| let data = $("#formId").serialize() | |||||
| $("#mask").css({"display":"block","z-index":"9999"}) | |||||
| $.ajax({ | |||||
| url:url_href, | |||||
| type:'POST', | |||||
| data:data, | |||||
| success:function(res){ | |||||
| location.href=`/${userName}/${repoPath}/modelmanage/show_model` | |||||
| $('.ui.modal.second').modal('hide') | |||||
| }, | |||||
| error: function(xhr){ | |||||
| // 隐藏 loading | |||||
| // 只有请求不正常(状态码不为200)才会执行 | |||||
| $('.ui.error.message').text(xhr.responseText) | |||||
| $('.ui.error.message').css('display','block') | |||||
| }, | |||||
| complete:function(xhr){ | |||||
| $("#mask").css({"display":"none","z-index":"1"}) | |||||
| } | |||||
| }) | |||||
| } | |||||
| function createModelName(){ | |||||
| let repoName = location.pathname.split('/')[2] | |||||
| let modelName = repoName + '_model_' + Math.random().toString(36).substr(2, 4) | |||||
| $('#name').val(modelName) | |||||
| $('#version').val("0.0.1") | |||||
| } | |||||
| function renderSize(value){ | function renderSize(value){ | ||||
| if(null==value||value==''){ | if(null==value||value==''){ | ||||
| return "0 Bytes"; | return "0 Bytes"; | ||||
| @@ -17,16 +17,30 @@ | |||||
| <div class="repository release dataset-list view"> | <div class="repository release dataset-list view"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <!-- 列表容器 --> | <!-- 列表容器 --> | ||||
| <div class="ui container active loader" id="loadContainer"> | |||||
| <div class="ui container {{if ne $.MODEL_COUNT 0}}active loader {{end}}" id="loadContainer"> | |||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| <div class="ui two column stackable grid" style="display: none;"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"></div> | <div class="column"></div> | ||||
| <div class="column right aligned"> | <div class="column right aligned"> | ||||
| <!-- --> | <!-- --> | ||||
| <a class="ui button {{if .Permission.CanWrite $.UnitTypeCloudBrain}} green {{else}} disabled {{end}}" onclick="showcreate(this)">{{$.i18n.Tr "repo.model.manage.import_new_model"}}</a> | <a class="ui button {{if .Permission.CanWrite $.UnitTypeCloudBrain}} green {{else}} disabled {{end}}" onclick="showcreate(this)">{{$.i18n.Tr "repo.model.manage.import_new_model"}}</a> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{if eq $.MODEL_COUNT 0}} | |||||
| <div class="ui placeholder segment bgtask-none"> | |||||
| <div class="ui icon header bgtask-header-pic"></div> | |||||
| <div class="bgtask-content-header">未创建过模型</div> | |||||
| <div class="bgtask-content"> | |||||
| {{if $.RepoIsEmpty}} | |||||
| <div class="bgtask-content-txt">代码版本:您还没有初始化代码仓库,请先<a href="{{.RepoLink}}">创建代码版本;</a></div> | |||||
| {{end}} | |||||
| {{if eq $.TRAIN_COUNT 0}} | |||||
| <div class="bgtask-content-txt">训练任务:您还没创建过训练任务,请先创建<a href="{{.RepoLink}}/modelarts/train-job">训练任务</a>。</div> | |||||
| {{end}} | |||||
| <div class="bgtask-content-txt">使用说明:可以参考启智AI协作平台<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程。</a></div> | |||||
| </div> | |||||
| </div> | |||||
| {{else}} | |||||
| <!-- 中下列表展示区 --> | <!-- 中下列表展示区 --> | ||||
| <div class="ui grid" style="display: none;"> | <div class="ui grid" style="display: none;"> | ||||
| <div class="row" style="padding-top: 0;"> | <div class="row" style="padding-top: 0;"> | ||||
| @@ -38,6 +52,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} | |||||
| </div> | </div> | ||||
| @@ -89,7 +104,6 @@ | |||||
| <div class="menu" id="job-name"> | <div class="menu" id="job-name"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="required six widde field"> | <div class="required six widde field"> | ||||
| <label>版本</label> | <label>版本</label> | ||||
| @@ -103,7 +117,6 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="required inline field" id="modelname"> | <div class="required inline field" id="modelname"> | ||||
| <label>模型名称</label> | <label>模型名称</label> | ||||
| <input style="width: 45%;" id="name" name="Name" required maxlength="25" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | <input style="width: 45%;" id="name" name="Name" required maxlength="25" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | ||||
| @@ -78,100 +78,121 @@ | |||||
| </select> | </select> | ||||
| </h4> | </h4> | ||||
| <div id="showInfo" style="border:1px solid #e2e2e2;padding: 20px 60px;margin-top:24px"> | <div id="showInfo" style="border:1px solid #e2e2e2;padding: 20px 60px;margin-top:24px"> | ||||
| <div class="half-table"> | |||||
| <span class="model_header_text">基本信息</span> | |||||
| <table class="tableStyle" style="margin-top:20px;"> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.model_name"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="ModelName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.version"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Version" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.migrate_items_labels"}}</td> | |||||
| <td class="ti-text-form-content"> | |||||
| <div id="Label" style="overflow: hidden;width: 95%;"> | |||||
| <div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);"> | |||||
| <a class="active item" data-tab="first">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||||
| <a class="item" data-tab="second">{{$.i18n.Tr "repo.model_download"}}</a> | |||||
| </div> | |||||
| <div class="ui tab active" data-tab="first"> | |||||
| <div class="half-table"> | |||||
| <span class="model_header_text">基本信息</span> | |||||
| <table class="tableStyle" style="margin-top:20px;"> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.model_name"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="ModelName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.version"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Version" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.migrate_items_labels"}}</td> | |||||
| <td class="ti-text-form-content"> | |||||
| <div id="Label" style="overflow: hidden;width: 95%;"> | |||||
| </div> | |||||
| </div> | |||||
| </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.model_size"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Size" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.createtime"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="CreateTime" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.description"}}</td> | |||||
| <td class="ti-text-form-content" > | |||||
| <div id="edit-td" style="display:flex"> | |||||
| <span id="Description" title="" class="iword-elipsis"></span> | |||||
| <i id="edit-pencil" data-id="" data-desc="" class="pencil alternate icon" style="cursor:pointer;vertical-align: top;" id="editor" onclick="editorFn(this)"></i> | |||||
| </div> | |||||
| </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.code_version"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="CodeBranch" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.start_file"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="BootFile" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="DatasetName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Parameters" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="EngineName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.standard"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="FlavorName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.compute_node"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="WorkServerNumber" title=""></span></td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.model_size"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Size" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.createtime"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="CreateTime" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.description"}}</td> | |||||
| <td class="ti-text-form-content" > | |||||
| <div id="edit-td" style="display:flex"> | |||||
| <span id="Description" title="" class="iword-elipsis"></span> | |||||
| <i id="edit-pencil" data-id="" data-desc="" class="pencil alternate icon" style="cursor:pointer;vertical-align: top;" id="editor" onclick="editorFn(this)"></i> | |||||
| </div> | |||||
| </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.code_version"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="CodeBranch" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.start_file"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="BootFile" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="DatasetName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Parameters" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="EngineName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.standard"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="FlavorName" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.modelarts.train_job.compute_node"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="WorkServerNumber" title=""></span></td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <div class="half-table"> | |||||
| <span class="model_header_text">{{$.i18n.Tr "repo.model.manage.model_accuracy"}}</span> | |||||
| <table class="tableStyle" style="margin-top:20px;"> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.Accuracy"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Accuracy" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">F1</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="F1" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.Precision"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Precision" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.Recall"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Recall" title=""></span></td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <div style="clear: both;"></div> | |||||
| </div> | </div> | ||||
| <div class="half-table"> | |||||
| <span class="model_header_text">{{$.i18n.Tr "repo.model.manage.model_accuracy"}}</span> | |||||
| <table class="tableStyle" style="margin-top:20px;"> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.Accuracy"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Accuracy" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">F1</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="F1" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.Precision"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Precision" title=""></span></td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.Recall"}}</td> | |||||
| <td class="ti-text-form-content word-elipsis"><span id="Recall" title=""></span></td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| <div class="ui tab" data-tab="second"> | |||||
| <input type="hidden" name="model" value="-1"> | |||||
| <input type="hidden" name="modelback" value="-1"> | |||||
| <div class='ui breadcrumb model_file_bread' id='file_breadcrumb'> | |||||
| <div class="active section"></div> | |||||
| <div class="divider"> / </div> | |||||
| </div> | |||||
| <div id="dir_list"> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <div style="clear: both;"></div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -179,6 +200,9 @@ | |||||
| <script> | <script> | ||||
| let url = location.href.split('show_model')[0] | let url = location.href.split('show_model')[0] | ||||
| let ID = location.search.split('?name=').pop() | let ID = location.search.split('?name=').pop() | ||||
| $(document).ready(function(){ | |||||
| $('.secondary.menu .item').tab(); | |||||
| }); | |||||
| $(document).ready(loadInfo); | $(document).ready(loadInfo); | ||||
| function changeInfo(version){ | function changeInfo(version){ | ||||
| $.get(`${url}show_model_info_api?name=${ID}`,(data)=>{ | $.get(`${url}show_model_info_api?name=${ID}`,(data)=>{ | ||||
| @@ -190,6 +214,7 @@ function changeInfo(version){ | |||||
| let [initObj,initModelAcc,id] = returnArray | let [initObj,initModelAcc,id] = returnArray | ||||
| editorCancel('','') | editorCancel('','') | ||||
| renderInfo(initObj,initModelAcc,id) | renderInfo(initObj,initModelAcc,id) | ||||
| loadModelFile(versionData[0].ID,versionData[0].Version,'','','init') | |||||
| }) | }) | ||||
| } | } | ||||
| function loadInfo(){ | function loadInfo(){ | ||||
| @@ -206,6 +231,7 @@ function loadInfo(){ | |||||
| returnArray = transObj(data) | returnArray = transObj(data) | ||||
| let [initObj,initModelAcc,id] = returnArray | let [initObj,initModelAcc,id] = returnArray | ||||
| renderInfo(initObj,initModelAcc,id) | renderInfo(initObj,initModelAcc,id) | ||||
| loadModelFile(data[0].ID,data[0].Version,'','','init') | |||||
| }) | }) | ||||
| } | } | ||||
| function transObj(data){ | function transObj(data){ | ||||
| @@ -297,7 +323,6 @@ function renderInfo(obj,accObj,id){ | |||||
| let descriptionText=obj[key].replace(/\r\n|\n/g,'enter;') | let descriptionText=obj[key].replace(/\r\n|\n/g,'enter;') | ||||
| $(`#${key}`).text(obj[key]) | $(`#${key}`).text(obj[key]) | ||||
| $(`#${key}`).attr("title",obj[key]) | $(`#${key}`).attr("title",obj[key]) | ||||
| $('#edit-pencil').attr("data-id",id) | $('#edit-pencil').attr("data-id",id) | ||||
| $('#edit-pencil').attr("data-desc",descriptionText) | $('#edit-pencil').attr("data-desc",descriptionText) | ||||
| } | } | ||||
| @@ -333,8 +358,6 @@ function renderInfo(obj,accObj,id){ | |||||
| $(`#${key}`).text(parameter) | $(`#${key}`).text(parameter) | ||||
| $(`#${key}`).attr("title",parameter) | $(`#${key}`).attr("title",parameter) | ||||
| } | } | ||||
| } | } | ||||
| else{ | else{ | ||||
| $(`#${key}`).text(obj[key]) | $(`#${key}`).text(obj[key]) | ||||
| @@ -348,4 +371,108 @@ function renderInfo(obj,accObj,id){ | |||||
| } | } | ||||
| } | } | ||||
| function loadModelFile(ID,version_name,parents,filename,init){ | |||||
| $.get(`${url}query_onelevel_modelfile?ID=${ID}&parentDir=${parents}`, (data) => { | |||||
| $('#dir_list').empty() | |||||
| renderDir(data,ID,version_name) | |||||
| if(init==="init"){ | |||||
| $('input[name=model]').val("") | |||||
| $('input[name=modelback]').val(version_name) | |||||
| $('#file_breadcrumb').empty() | |||||
| let htmlBread = "" | |||||
| htmlBread += `<div class='active section'>${version_name}</div>` | |||||
| htmlBread += "<div class='divider'> / </div>" | |||||
| $('#file_breadcrumb').append(htmlBread) | |||||
| }else{ | |||||
| renderBrend(ID,version_name,parents,filename,init) | |||||
| } | |||||
| }) | |||||
| } | |||||
| function renderSize(value){ | |||||
| if(null==value||value==''){ | |||||
| return "0 Bytes"; | |||||
| } | |||||
| var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"); | |||||
| var index=0; | |||||
| var srcsize = parseFloat(value); | |||||
| index=Math.floor(Math.log(srcsize)/Math.log(1024)); | |||||
| var size =srcsize/Math.pow(1024,index); | |||||
| size=size.toFixed(2);//保留的小数位数 | |||||
| return size+unitArr[index]; | |||||
| } | |||||
| function renderBrend(ID,version_name,parents,filename,init){ | |||||
| if(init=="folder"){ | |||||
| let htmlBrend = "" | |||||
| let sectionName=$('#file_breadcrumb .active.section').text() | |||||
| let parents1 = $('input[name=model]').val() | |||||
| let filename1 = $('input[name=modelback]').val() | |||||
| if(parents1===""){ | |||||
| $('#file_breadcrumb .active.section').replaceWith(`<a class='section' onclick="loadModelFile('${ID}','${version_name}','${parents1}','','init')">${sectionName}</a>`) | |||||
| }else{ | |||||
| $('#file_breadcrumb .active.section').replaceWith(`<a class='section' onclick="loadModelFile('${ID}','${version_name}','${parents1}','${filename1}')">${sectionName}</a>`) | |||||
| } | |||||
| htmlBrend += `<div class='active section'>${filename}</div>` | |||||
| htmlBrend += "<div class='divider'> / </div>" | |||||
| $('#file_breadcrumb').append(htmlBrend) | |||||
| $('input[name=model]').val(parents) | |||||
| $('input[name=modelback]').val(filename) | |||||
| }else{ | |||||
| $('input[name=model]').val(parents) | |||||
| $('input[name=modelbac]').val(filename) | |||||
| $('#file_breadcrumb a.section:contains(${filename})').nextAll().remove() | |||||
| $('#file_breadcrumb a.section:contains(${filename})').replaceWith(`<div class='active section'>${filename}</div>`) | |||||
| $('#file_breadcrumb div.section:contains(${filename})').append("<div class='divider'> / </div>") | |||||
| } | |||||
| } | |||||
| function renderDir(data,ID,version_name){ | |||||
| let html="" | |||||
| html += "<div class='ui grid' style='margin:0;'>" | |||||
| html += "<div class='row' style='padding: 0;'>" | |||||
| html += "<div class='ui sixteen wide column' style='padding:1rem;'>" | |||||
| html += "<div class='dir list'>" | |||||
| html += "<table id='repo-files-table' class='ui single line table pad20'>" | |||||
| html += '<tbody>' | |||||
| for(let i=0;i<data.length;i++){ | |||||
| let dirs_size = renderSize(data[i].Size) | |||||
| html += "<tr>" | |||||
| html += "<td class='name six wid'>" | |||||
| html += "<span class='truncate'>" | |||||
| html += "<span class='octicon octicon-file-directory'>" | |||||
| html += "</span>" | |||||
| if(data[i].IsDir){ | |||||
| html += `<a onclick="loadModelFile('${ID}','${version_name}','${data[i].ParenDir}','${data[i].FileName}','folder')">` | |||||
| html += "<span class='fitted'><i class='folder icon' width='16' height='16' aria-hidden='true'></i>" + data[i].FileName + "</span>" | |||||
| }else{ | |||||
| html += `<a href="${url}${ID}/downloadsingle?parentDir=${data[i].ParenDir}&fileName=${data[i].FileName}">` | |||||
| html += "<span class='fitted'><i class='file icon' width='16' height='16' aria-hidden='true'></i>" + data[i].FileName + "</span>" | |||||
| } | |||||
| html += '</a>' | |||||
| html += "</span>" | |||||
| html += "</td>" | |||||
| html += "<td class='message seven wide'>" | |||||
| if(data[i].IsDir){ | |||||
| html += "<span class='truncate has-emoji'></span>" | |||||
| }else{ | |||||
| html += "<span class='truncate has-emoji'>"+ `${dirs_size}` + "</span>" | |||||
| } | |||||
| html += "</td>" | |||||
| html += "<td class='text right age three wide'>" | |||||
| html += "<span class='truncate has-emoji'>" + data[i].ModTime + "</span>" | |||||
| html += "</td>" | |||||
| html += "</tr>" | |||||
| } | |||||
| html += "</tbody>" | |||||
| html += "</table>" | |||||
| html += "</div>" | |||||
| html += "</div>" | |||||
| html += "</div>" | |||||
| html += "</div>" | |||||
| $('#dir_list').append(html) | |||||
| } | |||||
| </script> | </script> | ||||
| @@ -0,0 +1,12 @@ | |||||
| root = true | |||||
| [*] | |||||
| charset = utf-8 | |||||
| end_of_line = lf | |||||
| insert_final_newline = true | |||||
| trim_trailing_whitespace = true | |||||
| [*.go] | |||||
| indent_style = tab | |||||
| indent_size = 4 | |||||
| max_line_length = 80 | |||||
| @@ -0,0 +1 @@ | |||||
| /.idea | |||||
| @@ -0,0 +1,9 @@ | |||||
| language: go | |||||
| go: | |||||
| - 1.11.x | |||||
| - 1.12.x | |||||
| - master | |||||
| script: | |||||
| - env GO111MODULE=on go test | |||||
| @@ -0,0 +1,21 @@ | |||||
| MIT License | |||||
| Copyright (c) 2020 Elliot Chance | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| SOFTWARE. | |||||
| @@ -0,0 +1,102 @@ | |||||
| # 🔃 github.com/elliotchance/orderedmap [](https://godoc.org/github.com/elliotchance/orderedmap) [](https://travis-ci.org/elliotchance/orderedmap) | |||||
| ## Installation | |||||
| ```bash | |||||
| go get -u github.com/elliotchance/orderedmap | |||||
| ``` | |||||
| ## Basic Usage | |||||
| An `*OrderedMap` is a high performance ordered map that maintains amortized O(1) | |||||
| for `Set`, `Get`, `Delete` and `Len`: | |||||
| ```go | |||||
| m := orderedmap.NewOrderedMap() | |||||
| m.Set("foo", "bar") | |||||
| m.Set("qux", 1.23) | |||||
| m.Set(123, true) | |||||
| m.Delete("qux") | |||||
| ``` | |||||
| Internally an `*OrderedMap` uses a combination of a map and linked list. | |||||
| ## Iterating | |||||
| Be careful using `Keys()` as it will create a copy of all of the keys so it's | |||||
| only suitable for a small number of items: | |||||
| ```go | |||||
| for _, key := range m.Keys() { | |||||
| value, _:= m.Get(key) | |||||
| fmt.Println(key, value) | |||||
| } | |||||
| ``` | |||||
| For larger maps you should use `Front()` or `Back()` to iterate per element: | |||||
| ```go | |||||
| // Iterate through all elements from oldest to newest: | |||||
| for el := m.Front(); el != nil; el = el.Next() { | |||||
| fmt.Println(el.Key, el.Value) | |||||
| } | |||||
| // You can also use Back and Prev to iterate in reverse: | |||||
| for el := m.Back(); el != nil; el = el.Prev() { | |||||
| fmt.Println(el.Key, el.Value) | |||||
| } | |||||
| ``` | |||||
| The iterator is safe to use bidirectionally, and will return `nil` once it goes | |||||
| beyond the first or last item. | |||||
| If the map is changing while the iteration is in-flight it may produce | |||||
| unexpected behavior. | |||||
| ## Performance | |||||
| CPU: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz | |||||
| RAM: 8GB | |||||
| System: Windows 10 | |||||
| ```shell | |||||
| $go test -benchmem -run=^$ github.com/elliotchance/orderedmap -bench BenchmarkAll | |||||
| ``` | |||||
| map[int]bool | |||||
| | | map | orderedmap | | |||||
| | ------- | ------------------- | ------------------- | | |||||
| | set | 198 ns/op, 44 B/op | 722 ns/op, 211 B/op | | |||||
| | get | 18 ns/op, 0 B/op | 37.3 ns/op, 0 B/op | | |||||
| | delete | 888 ns/op, 211 B/op | 280 ns/op, 44 B/op | | |||||
| | Iterate | 206 ns/op, 44 B/op | 693 ns/op, 259 B/op | | |||||
| map[string]bool(PS : Use strconv.Itoa()) | |||||
| | | map | orderedmap | | |||||
| | ----------- | ------------------- | ----------------------- | | |||||
| | set | 421 ns/op, 86 B/op | 1048 ns/op, 243 B/op | | |||||
| | get | 81.1 ns/op, 2 B/op | 97.8 ns/op, 2 B/op | | |||||
| | delete | 737 ns/op, 122 B/op | 1188 ns/op, 251 B/op | | |||||
| | Iterate all | 14706 ns/op, 1 B/op | 52671 ns/op, 16391 B/op | | |||||
| Big map[int]bool (10000000 keys) | |||||
| | | map | orderedmap | | |||||
| | ----------- | -------------------------------- | ------------------------------- | | |||||
| | set all | 1.834559 s/op, 423.9470291 MB/op | 7.5564667 s/op, 1784.1483 MB/op | | |||||
| | get all | 2.6367878 s/op, 423.9698 MB/op | 9.0232475 s/op, 1784.1086 MB/op | | |||||
| | Iterate all | 1.9526784 s/op, 423.9042 MB/op | 8.2495265 s/op, 1936.7619 MB/op | | |||||
| Big map[string]bool (10000000 keys) | |||||
| | | map | orderedmap | | |||||
| | ----------- | --------------------------------- | ----------------------------------- | | |||||
| | set all | 4.8893923 s/op, 921.33435 MB/op | 10.4405527 s/op, 2089.0144 MB/op | | |||||
| | get all | 7.122791 s/op, 997.3802643 MB/op | 13.2613692 s/op, 2165.09521 MB/op | | |||||
| | Iterate all | 5.1688922 s/op, 921.4619293 MB/op | 12.6623711 s/op, 2241.5272064 MB/op | | |||||
| @@ -0,0 +1,33 @@ | |||||
| package orderedmap | |||||
| import "container/list" | |||||
| type Element struct { | |||||
| Key, Value interface{} | |||||
| element *list.Element | |||||
| } | |||||
| func newElement(e *list.Element) *Element { | |||||
| if e == nil { | |||||
| return nil | |||||
| } | |||||
| element := e.Value.(*orderedMapElement) | |||||
| return &Element{ | |||||
| element: e, | |||||
| Key: element.key, | |||||
| Value: element.value, | |||||
| } | |||||
| } | |||||
| // Next returns the next element, or nil if it finished. | |||||
| func (e *Element) Next() *Element { | |||||
| return newElement(e.element.Next()) | |||||
| } | |||||
| // Prev returns the previous element, or nil if it finished. | |||||
| func (e *Element) Prev() *Element { | |||||
| return newElement(e.element.Prev()) | |||||
| } | |||||
| @@ -0,0 +1,5 @@ | |||||
| module github.com/elliotchance/orderedmap | |||||
| go 1.12 | |||||
| require github.com/stretchr/testify v1.7.0 | |||||
| @@ -0,0 +1,15 @@ | |||||
| 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= | |||||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | |||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||||
| 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= | |||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | |||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||||
| @@ -0,0 +1,150 @@ | |||||
| package orderedmap | |||||
| import "container/list" | |||||
| type orderedMapElement struct { | |||||
| key, value interface{} | |||||
| } | |||||
| type OrderedMap struct { | |||||
| kv map[interface{}]*list.Element | |||||
| ll *list.List | |||||
| } | |||||
| func NewOrderedMap() *OrderedMap { | |||||
| return &OrderedMap{ | |||||
| kv: make(map[interface{}]*list.Element), | |||||
| ll: list.New(), | |||||
| } | |||||
| } | |||||
| // Get returns the value for a key. If the key does not exist, the second return | |||||
| // parameter will be false and the value will be nil. | |||||
| func (m *OrderedMap) Get(key interface{}) (interface{}, bool) { | |||||
| value, ok := m.kv[key] | |||||
| if ok { | |||||
| return value.Value.(*orderedMapElement).value, true | |||||
| } | |||||
| return nil, false | |||||
| } | |||||
| // Set will set (or replace) a value for a key. If the key was new, then true | |||||
| // will be returned. The returned value will be false if the value was replaced | |||||
| // (even if the value was the same). | |||||
| func (m *OrderedMap) Set(key, value interface{}) bool { | |||||
| _, didExist := m.kv[key] | |||||
| if !didExist { | |||||
| element := m.ll.PushBack(&orderedMapElement{key, value}) | |||||
| m.kv[key] = element | |||||
| } else { | |||||
| m.kv[key].Value.(*orderedMapElement).value = value | |||||
| } | |||||
| return !didExist | |||||
| } | |||||
| // GetOrDefault returns the value for a key. If the key does not exist, returns | |||||
| // the default value instead. | |||||
| func (m *OrderedMap) GetOrDefault(key, defaultValue interface{}) interface{} { | |||||
| if value, ok := m.kv[key]; ok { | |||||
| return value.Value.(*orderedMapElement).value | |||||
| } | |||||
| return defaultValue | |||||
| } | |||||
| // GetElement returns the element for a key. If the key does not exist, the | |||||
| // pointer will be nil. | |||||
| func (m *OrderedMap) GetElement(key interface{}) *Element { | |||||
| value, ok := m.kv[key] | |||||
| if ok { | |||||
| element := value.Value.(*orderedMapElement) | |||||
| return &Element{ | |||||
| element: value, | |||||
| Key: element.key, | |||||
| Value: element.value, | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // Len returns the number of elements in the map. | |||||
| func (m *OrderedMap) Len() int { | |||||
| return len(m.kv) | |||||
| } | |||||
| // Keys returns all of the keys in the order they were inserted. If a key was | |||||
| // replaced it will retain the same position. To ensure most recently set keys | |||||
| // are always at the end you must always Delete before Set. | |||||
| func (m *OrderedMap) Keys() (keys []interface{}) { | |||||
| keys = make([]interface{}, m.Len()) | |||||
| element := m.ll.Front() | |||||
| for i := 0; element != nil; i++ { | |||||
| keys[i] = element.Value.(*orderedMapElement).key | |||||
| element = element.Next() | |||||
| } | |||||
| return keys | |||||
| } | |||||
| // Delete will remove a key from the map. It will return true if the key was | |||||
| // removed (the key did exist). | |||||
| func (m *OrderedMap) Delete(key interface{}) (didDelete bool) { | |||||
| element, ok := m.kv[key] | |||||
| if ok { | |||||
| m.ll.Remove(element) | |||||
| delete(m.kv, key) | |||||
| } | |||||
| return ok | |||||
| } | |||||
| // Front will return the element that is the first (oldest Set element). If | |||||
| // there are no elements this will return nil. | |||||
| func (m *OrderedMap) Front() *Element { | |||||
| front := m.ll.Front() | |||||
| if front == nil { | |||||
| return nil | |||||
| } | |||||
| element := front.Value.(*orderedMapElement) | |||||
| return &Element{ | |||||
| element: front, | |||||
| Key: element.key, | |||||
| Value: element.value, | |||||
| } | |||||
| } | |||||
| // Back will return the element that is the last (most recent Set element). If | |||||
| // there are no elements this will return nil. | |||||
| func (m *OrderedMap) Back() *Element { | |||||
| back := m.ll.Back() | |||||
| if back == nil { | |||||
| return nil | |||||
| } | |||||
| element := back.Value.(*orderedMapElement) | |||||
| return &Element{ | |||||
| element: back, | |||||
| Key: element.key, | |||||
| Value: element.value, | |||||
| } | |||||
| } | |||||
| // Copy returns a new OrderedMap with the same elements. | |||||
| // Using Copy while there are concurrent writes may mangle the result. | |||||
| func (m *OrderedMap) Copy() *OrderedMap { | |||||
| m2 := NewOrderedMap() | |||||
| for el := m.Front(); el != nil; el = el.Next() { | |||||
| m2.Set(el.Key, el.Value) | |||||
| } | |||||
| return m2 | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| This is a list of people who have contributed code to go-cache. They, or their | |||||
| employers, are the copyright holders of the contributed code. Contributed code | |||||
| is subject to the license restrictions listed in LICENSE (as they were when the | |||||
| code was contributed.) | |||||
| Dustin Sallings <dustin@spy.net> | |||||
| Jason Mooberry <jasonmoo@me.com> | |||||
| Sergey Shepelev <temotor@gmail.com> | |||||
| Alex Edwards <ajmedwards@gmail.com> | |||||
| @@ -0,0 +1,19 @@ | |||||
| Copyright (c) 2012-2017 Patrick Mylund Nielsen and the go-cache contributors | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in | |||||
| all copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
| THE SOFTWARE. | |||||
| @@ -0,0 +1,83 @@ | |||||
| # go-cache | |||||
| go-cache is an in-memory key:value store/cache similar to memcached that is | |||||
| suitable for applications running on a single machine. Its major advantage is | |||||
| that, being essentially a thread-safe `map[string]interface{}` with expiration | |||||
| times, it doesn't need to serialize or transmit its contents over the network. | |||||
| Any object can be stored, for a given duration or forever, and the cache can be | |||||
| safely used by multiple goroutines. | |||||
| Although go-cache isn't meant to be used as a persistent datastore, the entire | |||||
| cache can be saved to and loaded from a file (using `c.Items()` to retrieve the | |||||
| items map to serialize, and `NewFrom()` to create a cache from a deserialized | |||||
| one) to recover from downtime quickly. (See the docs for `NewFrom()` for caveats.) | |||||
| ### Installation | |||||
| `go get github.com/patrickmn/go-cache` | |||||
| ### Usage | |||||
| ```go | |||||
| import ( | |||||
| "fmt" | |||||
| "github.com/patrickmn/go-cache" | |||||
| "time" | |||||
| ) | |||||
| func main() { | |||||
| // Create a cache with a default expiration time of 5 minutes, and which | |||||
| // purges expired items every 10 minutes | |||||
| c := cache.New(5*time.Minute, 10*time.Minute) | |||||
| // Set the value of the key "foo" to "bar", with the default expiration time | |||||
| c.Set("foo", "bar", cache.DefaultExpiration) | |||||
| // Set the value of the key "baz" to 42, with no expiration time | |||||
| // (the item won't be removed until it is re-set, or removed using | |||||
| // c.Delete("baz") | |||||
| c.Set("baz", 42, cache.NoExpiration) | |||||
| // Get the string associated with the key "foo" from the cache | |||||
| foo, found := c.Get("foo") | |||||
| if found { | |||||
| fmt.Println(foo) | |||||
| } | |||||
| // Since Go is statically typed, and cache values can be anything, type | |||||
| // assertion is needed when values are being passed to functions that don't | |||||
| // take arbitrary types, (i.e. interface{}). The simplest way to do this for | |||||
| // values which will only be used once--e.g. for passing to another | |||||
| // function--is: | |||||
| foo, found := c.Get("foo") | |||||
| if found { | |||||
| MyFunction(foo.(string)) | |||||
| } | |||||
| // This gets tedious if the value is used several times in the same function. | |||||
| // You might do either of the following instead: | |||||
| if x, found := c.Get("foo"); found { | |||||
| foo := x.(string) | |||||
| // ... | |||||
| } | |||||
| // or | |||||
| var foo string | |||||
| if x, found := c.Get("foo"); found { | |||||
| foo = x.(string) | |||||
| } | |||||
| // ... | |||||
| // foo can then be passed around freely as a string | |||||
| // Want performance? Store pointers! | |||||
| c.Set("foo", &MyStruct, cache.DefaultExpiration) | |||||
| if x, found := c.Get("foo"); found { | |||||
| foo := x.(*MyStruct) | |||||
| // ... | |||||
| } | |||||
| } | |||||
| ``` | |||||
| ### Reference | |||||
| `godoc` or [http://godoc.org/github.com/patrickmn/go-cache](http://godoc.org/github.com/patrickmn/go-cache) | |||||
| @@ -0,0 +1,192 @@ | |||||
| package cache | |||||
| import ( | |||||
| "crypto/rand" | |||||
| "math" | |||||
| "math/big" | |||||
| insecurerand "math/rand" | |||||
| "os" | |||||
| "runtime" | |||||
| "time" | |||||
| ) | |||||
| // This is an experimental and unexported (for now) attempt at making a cache | |||||
| // with better algorithmic complexity than the standard one, namely by | |||||
| // preventing write locks of the entire cache when an item is added. As of the | |||||
| // time of writing, the overhead of selecting buckets results in cache | |||||
| // operations being about twice as slow as for the standard cache with small | |||||
| // total cache sizes, and faster for larger ones. | |||||
| // | |||||
| // See cache_test.go for a few benchmarks. | |||||
| type unexportedShardedCache struct { | |||||
| *shardedCache | |||||
| } | |||||
| type shardedCache struct { | |||||
| seed uint32 | |||||
| m uint32 | |||||
| cs []*cache | |||||
| janitor *shardedJanitor | |||||
| } | |||||
| // djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead. | |||||
| func djb33(seed uint32, k string) uint32 { | |||||
| var ( | |||||
| l = uint32(len(k)) | |||||
| d = 5381 + seed + l | |||||
| i = uint32(0) | |||||
| ) | |||||
| // Why is all this 5x faster than a for loop? | |||||
| if l >= 4 { | |||||
| for i < l-4 { | |||||
| d = (d * 33) ^ uint32(k[i]) | |||||
| d = (d * 33) ^ uint32(k[i+1]) | |||||
| d = (d * 33) ^ uint32(k[i+2]) | |||||
| d = (d * 33) ^ uint32(k[i+3]) | |||||
| i += 4 | |||||
| } | |||||
| } | |||||
| switch l - i { | |||||
| case 1: | |||||
| case 2: | |||||
| d = (d * 33) ^ uint32(k[i]) | |||||
| case 3: | |||||
| d = (d * 33) ^ uint32(k[i]) | |||||
| d = (d * 33) ^ uint32(k[i+1]) | |||||
| case 4: | |||||
| d = (d * 33) ^ uint32(k[i]) | |||||
| d = (d * 33) ^ uint32(k[i+1]) | |||||
| d = (d * 33) ^ uint32(k[i+2]) | |||||
| } | |||||
| return d ^ (d >> 16) | |||||
| } | |||||
| func (sc *shardedCache) bucket(k string) *cache { | |||||
| return sc.cs[djb33(sc.seed, k)%sc.m] | |||||
| } | |||||
| func (sc *shardedCache) Set(k string, x interface{}, d time.Duration) { | |||||
| sc.bucket(k).Set(k, x, d) | |||||
| } | |||||
| func (sc *shardedCache) Add(k string, x interface{}, d time.Duration) error { | |||||
| return sc.bucket(k).Add(k, x, d) | |||||
| } | |||||
| func (sc *shardedCache) Replace(k string, x interface{}, d time.Duration) error { | |||||
| return sc.bucket(k).Replace(k, x, d) | |||||
| } | |||||
| func (sc *shardedCache) Get(k string) (interface{}, bool) { | |||||
| return sc.bucket(k).Get(k) | |||||
| } | |||||
| func (sc *shardedCache) Increment(k string, n int64) error { | |||||
| return sc.bucket(k).Increment(k, n) | |||||
| } | |||||
| func (sc *shardedCache) IncrementFloat(k string, n float64) error { | |||||
| return sc.bucket(k).IncrementFloat(k, n) | |||||
| } | |||||
| func (sc *shardedCache) Decrement(k string, n int64) error { | |||||
| return sc.bucket(k).Decrement(k, n) | |||||
| } | |||||
| func (sc *shardedCache) Delete(k string) { | |||||
| sc.bucket(k).Delete(k) | |||||
| } | |||||
| func (sc *shardedCache) DeleteExpired() { | |||||
| for _, v := range sc.cs { | |||||
| v.DeleteExpired() | |||||
| } | |||||
| } | |||||
| // Returns the items in the cache. This may include items that have expired, | |||||
| // but have not yet been cleaned up. If this is significant, the Expiration | |||||
| // fields of the items should be checked. Note that explicit synchronization | |||||
| // is needed to use a cache and its corresponding Items() return values at | |||||
| // the same time, as the maps are shared. | |||||
| func (sc *shardedCache) Items() []map[string]Item { | |||||
| res := make([]map[string]Item, len(sc.cs)) | |||||
| for i, v := range sc.cs { | |||||
| res[i] = v.Items() | |||||
| } | |||||
| return res | |||||
| } | |||||
| func (sc *shardedCache) Flush() { | |||||
| for _, v := range sc.cs { | |||||
| v.Flush() | |||||
| } | |||||
| } | |||||
| type shardedJanitor struct { | |||||
| Interval time.Duration | |||||
| stop chan bool | |||||
| } | |||||
| func (j *shardedJanitor) Run(sc *shardedCache) { | |||||
| j.stop = make(chan bool) | |||||
| tick := time.Tick(j.Interval) | |||||
| for { | |||||
| select { | |||||
| case <-tick: | |||||
| sc.DeleteExpired() | |||||
| case <-j.stop: | |||||
| return | |||||
| } | |||||
| } | |||||
| } | |||||
| func stopShardedJanitor(sc *unexportedShardedCache) { | |||||
| sc.janitor.stop <- true | |||||
| } | |||||
| func runShardedJanitor(sc *shardedCache, ci time.Duration) { | |||||
| j := &shardedJanitor{ | |||||
| Interval: ci, | |||||
| } | |||||
| sc.janitor = j | |||||
| go j.Run(sc) | |||||
| } | |||||
| func newShardedCache(n int, de time.Duration) *shardedCache { | |||||
| max := big.NewInt(0).SetUint64(uint64(math.MaxUint32)) | |||||
| rnd, err := rand.Int(rand.Reader, max) | |||||
| var seed uint32 | |||||
| if err != nil { | |||||
| os.Stderr.Write([]byte("WARNING: go-cache's newShardedCache failed to read from the system CSPRNG (/dev/urandom or equivalent.) Your system's security may be compromised. Continuing with an insecure seed.\n")) | |||||
| seed = insecurerand.Uint32() | |||||
| } else { | |||||
| seed = uint32(rnd.Uint64()) | |||||
| } | |||||
| sc := &shardedCache{ | |||||
| seed: seed, | |||||
| m: uint32(n), | |||||
| cs: make([]*cache, n), | |||||
| } | |||||
| for i := 0; i < n; i++ { | |||||
| c := &cache{ | |||||
| defaultExpiration: de, | |||||
| items: map[string]Item{}, | |||||
| } | |||||
| sc.cs[i] = c | |||||
| } | |||||
| return sc | |||||
| } | |||||
| func unexportedNewSharded(defaultExpiration, cleanupInterval time.Duration, shards int) *unexportedShardedCache { | |||||
| if defaultExpiration == 0 { | |||||
| defaultExpiration = -1 | |||||
| } | |||||
| sc := newShardedCache(shards, defaultExpiration) | |||||
| SC := &unexportedShardedCache{sc} | |||||
| if cleanupInterval > 0 { | |||||
| runShardedJanitor(sc, cleanupInterval) | |||||
| runtime.SetFinalizer(SC, stopShardedJanitor) | |||||
| } | |||||
| return SC | |||||
| } | |||||
| @@ -1,6 +1,6 @@ | |||||
| MIT License | MIT License | ||||
| Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell | |||||
| Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||
| @@ -0,0 +1,394 @@ | |||||
| package assert | |||||
| import ( | |||||
| "fmt" | |||||
| "reflect" | |||||
| ) | |||||
| type CompareType int | |||||
| const ( | |||||
| compareLess CompareType = iota - 1 | |||||
| compareEqual | |||||
| compareGreater | |||||
| ) | |||||
| var ( | |||||
| intType = reflect.TypeOf(int(1)) | |||||
| int8Type = reflect.TypeOf(int8(1)) | |||||
| int16Type = reflect.TypeOf(int16(1)) | |||||
| int32Type = reflect.TypeOf(int32(1)) | |||||
| int64Type = reflect.TypeOf(int64(1)) | |||||
| uintType = reflect.TypeOf(uint(1)) | |||||
| uint8Type = reflect.TypeOf(uint8(1)) | |||||
| uint16Type = reflect.TypeOf(uint16(1)) | |||||
| uint32Type = reflect.TypeOf(uint32(1)) | |||||
| uint64Type = reflect.TypeOf(uint64(1)) | |||||
| float32Type = reflect.TypeOf(float32(1)) | |||||
| float64Type = reflect.TypeOf(float64(1)) | |||||
| stringType = reflect.TypeOf("") | |||||
| ) | |||||
| func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { | |||||
| obj1Value := reflect.ValueOf(obj1) | |||||
| obj2Value := reflect.ValueOf(obj2) | |||||
| // throughout this switch we try and avoid calling .Convert() if possible, | |||||
| // as this has a pretty big performance impact | |||||
| switch kind { | |||||
| case reflect.Int: | |||||
| { | |||||
| intobj1, ok := obj1.(int) | |||||
| if !ok { | |||||
| intobj1 = obj1Value.Convert(intType).Interface().(int) | |||||
| } | |||||
| intobj2, ok := obj2.(int) | |||||
| if !ok { | |||||
| intobj2 = obj2Value.Convert(intType).Interface().(int) | |||||
| } | |||||
| if intobj1 > intobj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if intobj1 == intobj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if intobj1 < intobj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Int8: | |||||
| { | |||||
| int8obj1, ok := obj1.(int8) | |||||
| if !ok { | |||||
| int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) | |||||
| } | |||||
| int8obj2, ok := obj2.(int8) | |||||
| if !ok { | |||||
| int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) | |||||
| } | |||||
| if int8obj1 > int8obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if int8obj1 == int8obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if int8obj1 < int8obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Int16: | |||||
| { | |||||
| int16obj1, ok := obj1.(int16) | |||||
| if !ok { | |||||
| int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) | |||||
| } | |||||
| int16obj2, ok := obj2.(int16) | |||||
| if !ok { | |||||
| int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) | |||||
| } | |||||
| if int16obj1 > int16obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if int16obj1 == int16obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if int16obj1 < int16obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Int32: | |||||
| { | |||||
| int32obj1, ok := obj1.(int32) | |||||
| if !ok { | |||||
| int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) | |||||
| } | |||||
| int32obj2, ok := obj2.(int32) | |||||
| if !ok { | |||||
| int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) | |||||
| } | |||||
| if int32obj1 > int32obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if int32obj1 == int32obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if int32obj1 < int32obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Int64: | |||||
| { | |||||
| int64obj1, ok := obj1.(int64) | |||||
| if !ok { | |||||
| int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) | |||||
| } | |||||
| int64obj2, ok := obj2.(int64) | |||||
| if !ok { | |||||
| int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) | |||||
| } | |||||
| if int64obj1 > int64obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if int64obj1 == int64obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if int64obj1 < int64obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint: | |||||
| { | |||||
| uintobj1, ok := obj1.(uint) | |||||
| if !ok { | |||||
| uintobj1 = obj1Value.Convert(uintType).Interface().(uint) | |||||
| } | |||||
| uintobj2, ok := obj2.(uint) | |||||
| if !ok { | |||||
| uintobj2 = obj2Value.Convert(uintType).Interface().(uint) | |||||
| } | |||||
| if uintobj1 > uintobj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if uintobj1 == uintobj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if uintobj1 < uintobj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint8: | |||||
| { | |||||
| uint8obj1, ok := obj1.(uint8) | |||||
| if !ok { | |||||
| uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) | |||||
| } | |||||
| uint8obj2, ok := obj2.(uint8) | |||||
| if !ok { | |||||
| uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) | |||||
| } | |||||
| if uint8obj1 > uint8obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if uint8obj1 == uint8obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if uint8obj1 < uint8obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint16: | |||||
| { | |||||
| uint16obj1, ok := obj1.(uint16) | |||||
| if !ok { | |||||
| uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) | |||||
| } | |||||
| uint16obj2, ok := obj2.(uint16) | |||||
| if !ok { | |||||
| uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) | |||||
| } | |||||
| if uint16obj1 > uint16obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if uint16obj1 == uint16obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if uint16obj1 < uint16obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint32: | |||||
| { | |||||
| uint32obj1, ok := obj1.(uint32) | |||||
| if !ok { | |||||
| uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) | |||||
| } | |||||
| uint32obj2, ok := obj2.(uint32) | |||||
| if !ok { | |||||
| uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) | |||||
| } | |||||
| if uint32obj1 > uint32obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if uint32obj1 == uint32obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if uint32obj1 < uint32obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint64: | |||||
| { | |||||
| uint64obj1, ok := obj1.(uint64) | |||||
| if !ok { | |||||
| uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) | |||||
| } | |||||
| uint64obj2, ok := obj2.(uint64) | |||||
| if !ok { | |||||
| uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) | |||||
| } | |||||
| if uint64obj1 > uint64obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if uint64obj1 == uint64obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if uint64obj1 < uint64obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Float32: | |||||
| { | |||||
| float32obj1, ok := obj1.(float32) | |||||
| if !ok { | |||||
| float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) | |||||
| } | |||||
| float32obj2, ok := obj2.(float32) | |||||
| if !ok { | |||||
| float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) | |||||
| } | |||||
| if float32obj1 > float32obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if float32obj1 == float32obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if float32obj1 < float32obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.Float64: | |||||
| { | |||||
| float64obj1, ok := obj1.(float64) | |||||
| if !ok { | |||||
| float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) | |||||
| } | |||||
| float64obj2, ok := obj2.(float64) | |||||
| if !ok { | |||||
| float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) | |||||
| } | |||||
| if float64obj1 > float64obj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if float64obj1 == float64obj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if float64obj1 < float64obj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| case reflect.String: | |||||
| { | |||||
| stringobj1, ok := obj1.(string) | |||||
| if !ok { | |||||
| stringobj1 = obj1Value.Convert(stringType).Interface().(string) | |||||
| } | |||||
| stringobj2, ok := obj2.(string) | |||||
| if !ok { | |||||
| stringobj2 = obj2Value.Convert(stringType).Interface().(string) | |||||
| } | |||||
| if stringobj1 > stringobj2 { | |||||
| return compareGreater, true | |||||
| } | |||||
| if stringobj1 == stringobj2 { | |||||
| return compareEqual, true | |||||
| } | |||||
| if stringobj1 < stringobj2 { | |||||
| return compareLess, true | |||||
| } | |||||
| } | |||||
| } | |||||
| return compareEqual, false | |||||
| } | |||||
| // Greater asserts that the first element is greater than the second | |||||
| // | |||||
| // assert.Greater(t, 2, 1) | |||||
| // assert.Greater(t, float64(2), float64(1)) | |||||
| // assert.Greater(t, "b", "a") | |||||
| func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) | |||||
| } | |||||
| // GreaterOrEqual asserts that the first element is greater than or equal to the second | |||||
| // | |||||
| // assert.GreaterOrEqual(t, 2, 1) | |||||
| // assert.GreaterOrEqual(t, 2, 2) | |||||
| // assert.GreaterOrEqual(t, "b", "a") | |||||
| // assert.GreaterOrEqual(t, "b", "b") | |||||
| func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) | |||||
| } | |||||
| // Less asserts that the first element is less than the second | |||||
| // | |||||
| // assert.Less(t, 1, 2) | |||||
| // assert.Less(t, float64(1), float64(2)) | |||||
| // assert.Less(t, "a", "b") | |||||
| func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) | |||||
| } | |||||
| // LessOrEqual asserts that the first element is less than or equal to the second | |||||
| // | |||||
| // assert.LessOrEqual(t, 1, 2) | |||||
| // assert.LessOrEqual(t, 2, 2) | |||||
| // assert.LessOrEqual(t, "a", "b") | |||||
| // assert.LessOrEqual(t, "b", "b") | |||||
| func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) | |||||
| } | |||||
| // Positive asserts that the specified element is positive | |||||
| // | |||||
| // assert.Positive(t, 1) | |||||
| // assert.Positive(t, 1.23) | |||||
| func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { | |||||
| zero := reflect.Zero(reflect.TypeOf(e)) | |||||
| return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) | |||||
| } | |||||
| // Negative asserts that the specified element is negative | |||||
| // | |||||
| // assert.Negative(t, -1) | |||||
| // assert.Negative(t, -1.23) | |||||
| func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { | |||||
| zero := reflect.Zero(reflect.TypeOf(e)) | |||||
| return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) | |||||
| } | |||||
| func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| e1Kind := reflect.ValueOf(e1).Kind() | |||||
| e2Kind := reflect.ValueOf(e2).Kind() | |||||
| if e1Kind != e2Kind { | |||||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
| } | |||||
| compareResult, isComparable := compare(e1, e2, e1Kind) | |||||
| if !isComparable { | |||||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
| } | |||||
| if !containsValue(allowedComparesResults, compareResult) { | |||||
| return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) | |||||
| } | |||||
| return true | |||||
| } | |||||
| func containsValue(values []CompareType, value CompareType) bool { | |||||
| for _, v := range values { | |||||
| if v == value { | |||||
| return true | |||||
| } | |||||
| } | |||||
| return false | |||||
| } | |||||
| @@ -32,7 +32,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args | |||||
| return Contains(t, s, contains, append([]interface{}{msg}, args...)...) | return Contains(t, s, contains, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -92,7 +93,7 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args | |||||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
| // and equal. | // and equal. | ||||
| // | // | ||||
| // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | |||||
| // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") | |||||
| func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -113,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { | |||||
| return Error(t, err, append([]interface{}{msg}, args...)...) | return Error(t, err, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // Eventuallyf asserts that given condition will be met in waitFor time, | // Eventuallyf asserts that given condition will be met in waitFor time, | ||||
| // periodically checking target function each tick. | // periodically checking target function each tick. | ||||
| // | // | ||||
| @@ -126,7 +145,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick | |||||
| // Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
| // | // | ||||
| // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | |||||
| // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") | |||||
| func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -160,7 +179,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { | |||||
| return False(t, value, append([]interface{}{msg}, args...)...) | return False(t, value, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -171,7 +191,7 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool | |||||
| // Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
| // | // | ||||
| // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | ||||
| // assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) | |||||
| // assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") | |||||
| // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | ||||
| func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -223,7 +243,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u | |||||
| // | // | ||||
| // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -235,7 +255,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, | |||||
| // | // | ||||
| // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -243,6 +263,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri | |||||
| return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) | return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // HTTPSuccessf asserts that a specified handler returns a success status code. | // HTTPSuccessf asserts that a specified handler returns a success status code. | ||||
| // | // | ||||
| // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | ||||
| @@ -257,7 +289,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin | |||||
| // Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
| // | // | ||||
| // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
| // assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
| func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -267,7 +299,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms | |||||
| // InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
| // assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
| func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -307,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil | |||||
| return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) | return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // IsDecreasingf asserts that the collection is decreasing | |||||
| // | |||||
| // assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") | |||||
| // assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
| // assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
| func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // IsIncreasingf asserts that the collection is increasing | |||||
| // | |||||
| // assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") | |||||
| // assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
| // assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
| func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||||
| // | |||||
| // assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") | |||||
| // assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
| // assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
| func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // IsNonIncreasingf asserts that the collection is not increasing | |||||
| // | |||||
| // assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") | |||||
| // assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
| // assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
| func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // IsTypef asserts that the specified objects are of the same type. | // IsTypef asserts that the specified objects are of the same type. | ||||
| func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { | func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -325,14 +405,6 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int | |||||
| return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) | return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // Lenf asserts that the specified object has specific length. | // Lenf asserts that the specified object has specific length. | ||||
| // Lenf also fails if the object has a type that len() not accept. | // Lenf also fails if the object has a type that len() not accept. | ||||
| // | // | ||||
| @@ -347,7 +419,7 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf | |||||
| // Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
| // | // | ||||
| // assert.Lessf(t, 1, 2, "error message %s", "formatted") | // assert.Lessf(t, 1, 2, "error message %s", "formatted") | ||||
| // assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) | |||||
| // assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") | |||||
| // assert.Lessf(t, "a", "b", "error message %s", "formatted") | // assert.Lessf(t, "a", "b", "error message %s", "formatted") | ||||
| func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -369,6 +441,28 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . | |||||
| return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) | return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // Negativef asserts that the specified element is negative | |||||
| // | |||||
| // assert.Negativef(t, -1, "error message %s", "formatted") | |||||
| // assert.Negativef(t, -1.23, "error message %s", "formatted") | |||||
| func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Negative(t, e, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
| func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // Nilf asserts that the specified object is nil. | // Nilf asserts that the specified object is nil. | ||||
| // | // | ||||
| // assert.Nilf(t, err, "error message %s", "formatted") | // assert.Nilf(t, err, "error message %s", "formatted") | ||||
| @@ -379,6 +473,15 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool | |||||
| return Nil(t, object, append([]interface{}{msg}, args...)...) | return Nil(t, object, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NoDirExists(t, path, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // NoErrorf asserts that a function returned no error (i.e. `nil`). | // NoErrorf asserts that a function returned no error (i.e. `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -392,6 +495,15 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { | |||||
| return NoError(t, err, append([]interface{}{msg}, args...)...) | return NoError(t, err, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NoFileExists(t, path, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
| // specified substring or element. | // specified substring or element. | ||||
| // | // | ||||
| @@ -431,6 +543,25 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, | |||||
| return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) | return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") | |||||
| func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // NotNilf asserts that the specified object is not nil. | // NotNilf asserts that the specified object is not nil. | ||||
| // | // | ||||
| // assert.NotNilf(t, err, "error message %s", "formatted") | // assert.NotNilf(t, err, "error message %s", "formatted") | ||||
| @@ -453,7 +584,7 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo | |||||
| // NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
| // | // | ||||
| // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
| // assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
| // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | ||||
| func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -462,6 +593,19 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. | |||||
| return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) | return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // NotSamef asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // NotSubsetf asserts that the specified list(array, slice...) contains not all | // NotSubsetf asserts that the specified list(array, slice...) contains not all | ||||
| // elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
| // | // | ||||
| @@ -491,6 +635,18 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool | |||||
| return Panics(t, f, append([]interface{}{msg}, args...)...) | return Panics(t, f, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
| func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | ||||
| // the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
| // | // | ||||
| @@ -502,9 +658,20 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str | |||||
| return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) | return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // Positivef asserts that the specified element is positive | |||||
| // | |||||
| // assert.Positivef(t, 1, "error message %s", "formatted") | |||||
| // assert.Positivef(t, 1.23, "error message %s", "formatted") | |||||
| func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Positive(t, e, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
| // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
| // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | ||||
| func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -557,6 +724,14 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim | |||||
| return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | ||||
| } | } | ||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
| } | |||||
| // Zerof asserts that i is the zero value for its type. | // Zerof asserts that i is the zero value for its type. | ||||
| func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -53,7 +53,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, | |||||
| return Containsf(a.t, s, contains, msg, args...) | return Containsf(a.t, s, contains, msg, args...) | ||||
| } | } | ||||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExists checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -61,7 +62,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | |||||
| return DirExists(a.t, path, msgAndArgs...) | return DirExists(a.t, path, msgAndArgs...) | ||||
| } | } | ||||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { | func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -167,7 +169,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn | |||||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
| // and equal. | // and equal. | ||||
| // | // | ||||
| // a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | |||||
| // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") | |||||
| func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -202,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { | |||||
| return Error(a.t, err, msgAndArgs...) | return Error(a.t, err, msgAndArgs...) | ||||
| } | } | ||||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return ErrorAs(a.t, err, target, msgAndArgs...) | |||||
| } | |||||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return ErrorAsf(a.t, err, target, msg, args...) | |||||
| } | |||||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return ErrorIs(a.t, err, target, msgAndArgs...) | |||||
| } | |||||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return ErrorIsf(a.t, err, target, msg, args...) | |||||
| } | |||||
| // Errorf asserts that a function returned an error (i.e. not `nil`). | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -249,7 +287,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg | |||||
| // Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
| // | // | ||||
| // a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | |||||
| // a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") | |||||
| func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -309,7 +347,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { | |||||
| return Falsef(a.t, value, msg, args...) | return Falsef(a.t, value, msg, args...) | ||||
| } | } | ||||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExists checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -317,7 +356,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | |||||
| return FileExists(a.t, path, msgAndArgs...) | return FileExists(a.t, path, msgAndArgs...) | ||||
| } | } | ||||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { | func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -366,7 +406,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, | |||||
| // Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
| // | // | ||||
| // a.Greaterf(2, 1, "error message %s", "formatted") | // a.Greaterf(2, 1, "error message %s", "formatted") | ||||
| // a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) | |||||
| // a.Greaterf(float64(2), float64(1), "error message %s", "formatted") | |||||
| // a.Greaterf("b", "a", "error message %s", "formatted") | // a.Greaterf("b", "a", "error message %s", "formatted") | ||||
| func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -443,7 +483,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri | |||||
| // | // | ||||
| // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -467,7 +507,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s | |||||
| // | // | ||||
| // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -475,6 +515,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url | |||||
| return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | ||||
| } | } | ||||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) | |||||
| } | |||||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) | |||||
| } | |||||
| // HTTPSuccess asserts that a specified handler returns a success status code. | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||
| // | // | ||||
| // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | ||||
| @@ -511,7 +575,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, | |||||
| // Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
| // | // | ||||
| // a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
| // a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
| func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -521,7 +585,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} | |||||
| // InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // a.InDelta(math.Pi, (22 / 7.0), 0.01) | |||||
| // a.InDelta(math.Pi, 22/7.0, 0.01) | |||||
| func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -563,7 +627,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del | |||||
| // InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
| // a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
| func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -603,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo | |||||
| return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | ||||
| } | } | ||||
| // IsDecreasing asserts that the collection is decreasing | |||||
| // | |||||
| // a.IsDecreasing([]int{2, 1, 0}) | |||||
| // a.IsDecreasing([]float{2, 1}) | |||||
| // a.IsDecreasing([]string{"b", "a"}) | |||||
| func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsDecreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsDecreasingf asserts that the collection is decreasing | |||||
| // | |||||
| // a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") | |||||
| // a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
| // a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsDecreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsIncreasing asserts that the collection is increasing | |||||
| // | |||||
| // a.IsIncreasing([]int{1, 2, 3}) | |||||
| // a.IsIncreasing([]float{1, 2}) | |||||
| // a.IsIncreasing([]string{"a", "b"}) | |||||
| func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsIncreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsIncreasingf asserts that the collection is increasing | |||||
| // | |||||
| // a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") | |||||
| // a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
| // a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsIncreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsNonDecreasing asserts that the collection is not decreasing | |||||
| // | |||||
| // a.IsNonDecreasing([]int{1, 1, 2}) | |||||
| // a.IsNonDecreasing([]float{1, 2}) | |||||
| // a.IsNonDecreasing([]string{"a", "b"}) | |||||
| func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsNonDecreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||||
| // | |||||
| // a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") | |||||
| // a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
| // a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsNonDecreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsNonIncreasing asserts that the collection is not increasing | |||||
| // | |||||
| // a.IsNonIncreasing([]int{2, 1, 1}) | |||||
| // a.IsNonIncreasing([]float{2, 1}) | |||||
| // a.IsNonIncreasing([]string{"b", "a"}) | |||||
| func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsNonIncreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsNonIncreasingf asserts that the collection is not increasing | |||||
| // | |||||
| // a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") | |||||
| // a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
| // a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return IsNonIncreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsType asserts that the specified objects are of the same type. | // IsType asserts that the specified objects are of the same type. | ||||
| func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { | func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -639,22 +799,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. | |||||
| return JSONEqf(a.t, expected, actual, msg, args...) | return JSONEqf(a.t, expected, actual, msg, args...) | ||||
| } | } | ||||
| // YAMLEq asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return YAMLEqf(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // Len asserts that the specified object has specific length. | // Len asserts that the specified object has specific length. | ||||
| // Len also fails if the object has a type that len() not accept. | // Len also fails if the object has a type that len() not accept. | ||||
| // | // | ||||
| @@ -718,7 +862,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar | |||||
| // Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
| // | // | ||||
| // a.Lessf(1, 2, "error message %s", "formatted") | // a.Lessf(1, 2, "error message %s", "formatted") | ||||
| // a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) | |||||
| // a.Lessf(float64(1), float64(2), "error message %s", "formatted") | |||||
| // a.Lessf("a", "b", "error message %s", "formatted") | // a.Lessf("a", "b", "error message %s", "formatted") | ||||
| func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -727,6 +871,50 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i | |||||
| return Lessf(a.t, e1, e2, msg, args...) | return Lessf(a.t, e1, e2, msg, args...) | ||||
| } | } | ||||
| // Negative asserts that the specified element is negative | |||||
| // | |||||
| // a.Negative(-1) | |||||
| // a.Negative(-1.23) | |||||
| func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Negative(a.t, e, msgAndArgs...) | |||||
| } | |||||
| // Negativef asserts that the specified element is negative | |||||
| // | |||||
| // a.Negativef(-1, "error message %s", "formatted") | |||||
| // a.Negativef(-1.23, "error message %s", "formatted") | |||||
| func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Negativef(a.t, e, msg, args...) | |||||
| } | |||||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
| func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Never(a.t, condition, waitFor, tick, msgAndArgs...) | |||||
| } | |||||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
| func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Neverf(a.t, condition, waitFor, tick, msg, args...) | |||||
| } | |||||
| // Nil asserts that the specified object is nil. | // Nil asserts that the specified object is nil. | ||||
| // | // | ||||
| // a.Nil(err) | // a.Nil(err) | ||||
| @@ -747,6 +935,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b | |||||
| return Nilf(a.t, object, msg, args...) | return Nilf(a.t, object, msg, args...) | ||||
| } | } | ||||
| // NoDirExists checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NoDirExists(a.t, path, msgAndArgs...) | |||||
| } | |||||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NoDirExistsf(a.t, path, msg, args...) | |||||
| } | |||||
| // NoError asserts that a function returned no error (i.e. `nil`). | // NoError asserts that a function returned no error (i.e. `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -773,6 +979,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { | |||||
| return NoErrorf(a.t, err, msg, args...) | return NoErrorf(a.t, err, msg, args...) | ||||
| } | } | ||||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NoFileExists(a.t, path, msgAndArgs...) | |||||
| } | |||||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NoFileExistsf(a.t, path, msg, args...) | |||||
| } | |||||
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
| // specified substring or element. | // specified substring or element. | ||||
| // | // | ||||
| @@ -838,6 +1062,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr | |||||
| return NotEqual(a.t, expected, actual, msgAndArgs...) | return NotEqual(a.t, expected, actual, msgAndArgs...) | ||||
| } | } | ||||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // a.NotEqualValues(obj1, obj2) | |||||
| func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotEqualValues(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") | |||||
| func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotEqualValuesf(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // NotEqualf asserts that the specified values are NOT equal. | // NotEqualf asserts that the specified values are NOT equal. | ||||
| // | // | ||||
| // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | ||||
| @@ -851,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str | |||||
| return NotEqualf(a.t, expected, actual, msg, args...) | return NotEqualf(a.t, expected, actual, msg, args...) | ||||
| } | } | ||||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotErrorIs(a.t, err, target, msgAndArgs...) | |||||
| } | |||||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotErrorIsf(a.t, err, target, msg, args...) | |||||
| } | |||||
| // NotNil asserts that the specified object is not nil. | // NotNil asserts that the specified object is not nil. | ||||
| // | // | ||||
| // a.NotNil(err) | // a.NotNil(err) | ||||
| @@ -904,7 +1166,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in | |||||
| // NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
| // | // | ||||
| // a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
| // a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
| // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | ||||
| func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -913,6 +1175,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg | |||||
| return NotRegexpf(a.t, rx, str, msg, args...) | return NotRegexpf(a.t, rx, str, msg, args...) | ||||
| } | } | ||||
| // NotSame asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // a.NotSame(ptr1, ptr2) | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotSame(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // NotSamef asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // a.NotSamef(ptr1, ptr2, "error message %s", "formatted") | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return NotSamef(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // NotSubset asserts that the specified list(array, slice...) contains not all | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||
| // elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
| // | // | ||||
| @@ -961,6 +1249,30 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
| return Panics(a.t, f, msgAndArgs...) | return Panics(a.t, f, msgAndArgs...) | ||||
| } | } | ||||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // a.PanicsWithError("crazy error", func(){ GoCrazy() }) | |||||
| func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return PanicsWithError(a.t, errString, f, msgAndArgs...) | |||||
| } | |||||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
| func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return PanicsWithErrorf(a.t, errString, f, msg, args...) | |||||
| } | |||||
| // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||
| // the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
| // | // | ||||
| @@ -993,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b | |||||
| return Panicsf(a.t, f, msg, args...) | return Panicsf(a.t, f, msg, args...) | ||||
| } | } | ||||
| // Positive asserts that the specified element is positive | |||||
| // | |||||
| // a.Positive(1) | |||||
| // a.Positive(1.23) | |||||
| func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Positive(a.t, e, msgAndArgs...) | |||||
| } | |||||
| // Positivef asserts that the specified element is positive | |||||
| // | |||||
| // a.Positivef(1, "error message %s", "formatted") | |||||
| // a.Positivef(1.23, "error message %s", "formatted") | |||||
| func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Positivef(a.t, e, msg, args...) | |||||
| } | |||||
| // Regexp asserts that a specified regexp matches a string. | // Regexp asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // a.Regexp(regexp.MustCompile("start"), "it's starting") | // a.Regexp(regexp.MustCompile("start"), "it's starting") | ||||
| @@ -1006,7 +1340,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter | |||||
| // Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
| // a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
| // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | ||||
| func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -1103,6 +1437,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta | |||||
| return WithinDurationf(a.t, expected, actual, delta, msg, args...) | return WithinDurationf(a.t, expected, actual, delta, msg, args...) | ||||
| } | } | ||||
| // YAMLEq asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return YAMLEqf(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // Zero asserts that i is the zero value for its type. | // Zero asserts that i is the zero value for its type. | ||||
| func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { | func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -5,305 +5,77 @@ import ( | |||||
| "reflect" | "reflect" | ||||
| ) | ) | ||||
| func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { | |||||
| switch kind { | |||||
| case reflect.Int: | |||||
| { | |||||
| intobj1 := obj1.(int) | |||||
| intobj2 := obj2.(int) | |||||
| if intobj1 > intobj2 { | |||||
| return -1, true | |||||
| } | |||||
| if intobj1 == intobj2 { | |||||
| return 0, true | |||||
| } | |||||
| if intobj1 < intobj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Int8: | |||||
| { | |||||
| int8obj1 := obj1.(int8) | |||||
| int8obj2 := obj2.(int8) | |||||
| if int8obj1 > int8obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if int8obj1 == int8obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if int8obj1 < int8obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Int16: | |||||
| { | |||||
| int16obj1 := obj1.(int16) | |||||
| int16obj2 := obj2.(int16) | |||||
| if int16obj1 > int16obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if int16obj1 == int16obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if int16obj1 < int16obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Int32: | |||||
| { | |||||
| int32obj1 := obj1.(int32) | |||||
| int32obj2 := obj2.(int32) | |||||
| if int32obj1 > int32obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if int32obj1 == int32obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if int32obj1 < int32obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Int64: | |||||
| { | |||||
| int64obj1 := obj1.(int64) | |||||
| int64obj2 := obj2.(int64) | |||||
| if int64obj1 > int64obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if int64obj1 == int64obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if int64obj1 < int64obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint: | |||||
| { | |||||
| uintobj1 := obj1.(uint) | |||||
| uintobj2 := obj2.(uint) | |||||
| if uintobj1 > uintobj2 { | |||||
| return -1, true | |||||
| } | |||||
| if uintobj1 == uintobj2 { | |||||
| return 0, true | |||||
| } | |||||
| if uintobj1 < uintobj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint8: | |||||
| { | |||||
| uint8obj1 := obj1.(uint8) | |||||
| uint8obj2 := obj2.(uint8) | |||||
| if uint8obj1 > uint8obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if uint8obj1 == uint8obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if uint8obj1 < uint8obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint16: | |||||
| { | |||||
| uint16obj1 := obj1.(uint16) | |||||
| uint16obj2 := obj2.(uint16) | |||||
| if uint16obj1 > uint16obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if uint16obj1 == uint16obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if uint16obj1 < uint16obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint32: | |||||
| { | |||||
| uint32obj1 := obj1.(uint32) | |||||
| uint32obj2 := obj2.(uint32) | |||||
| if uint32obj1 > uint32obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if uint32obj1 == uint32obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if uint32obj1 < uint32obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Uint64: | |||||
| { | |||||
| uint64obj1 := obj1.(uint64) | |||||
| uint64obj2 := obj2.(uint64) | |||||
| if uint64obj1 > uint64obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if uint64obj1 == uint64obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if uint64obj1 < uint64obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Float32: | |||||
| { | |||||
| float32obj1 := obj1.(float32) | |||||
| float32obj2 := obj2.(float32) | |||||
| if float32obj1 > float32obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if float32obj1 == float32obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if float32obj1 < float32obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.Float64: | |||||
| { | |||||
| float64obj1 := obj1.(float64) | |||||
| float64obj2 := obj2.(float64) | |||||
| if float64obj1 > float64obj2 { | |||||
| return -1, true | |||||
| } | |||||
| if float64obj1 == float64obj2 { | |||||
| return 0, true | |||||
| } | |||||
| if float64obj1 < float64obj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| case reflect.String: | |||||
| { | |||||
| stringobj1 := obj1.(string) | |||||
| stringobj2 := obj2.(string) | |||||
| if stringobj1 > stringobj2 { | |||||
| return -1, true | |||||
| } | |||||
| if stringobj1 == stringobj2 { | |||||
| return 0, true | |||||
| } | |||||
| if stringobj1 < stringobj2 { | |||||
| return 1, true | |||||
| } | |||||
| } | |||||
| } | |||||
| return 0, false | |||||
| } | |||||
| // Greater asserts that the first element is greater than the second | |||||
| // | |||||
| // assert.Greater(t, 2, 1) | |||||
| // assert.Greater(t, float64(2), float64(1)) | |||||
| // assert.Greater(t, "b", "a") | |||||
| func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| // isOrdered checks that collection contains orderable elements. | |||||
| func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { | |||||
| objKind := reflect.TypeOf(object).Kind() | |||||
| if objKind != reflect.Slice && objKind != reflect.Array { | |||||
| return false | |||||
| } | } | ||||
| e1Kind := reflect.ValueOf(e1).Kind() | |||||
| e2Kind := reflect.ValueOf(e2).Kind() | |||||
| if e1Kind != e2Kind { | |||||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
| } | |||||
| objValue := reflect.ValueOf(object) | |||||
| objLen := objValue.Len() | |||||
| res, isComparable := compare(e1, e2, e1Kind) | |||||
| if !isComparable { | |||||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
| if objLen <= 1 { | |||||
| return true | |||||
| } | } | ||||
| if res != -1 { | |||||
| return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) | |||||
| } | |||||
| value := objValue.Index(0) | |||||
| valueInterface := value.Interface() | |||||
| firstValueKind := value.Kind() | |||||
| return true | |||||
| } | |||||
| for i := 1; i < objLen; i++ { | |||||
| prevValue := value | |||||
| prevValueInterface := valueInterface | |||||
| // GreaterOrEqual asserts that the first element is greater than or equal to the second | |||||
| // | |||||
| // assert.GreaterOrEqual(t, 2, 1) | |||||
| // assert.GreaterOrEqual(t, 2, 2) | |||||
| // assert.GreaterOrEqual(t, "b", "a") | |||||
| // assert.GreaterOrEqual(t, "b", "b") | |||||
| func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| value = objValue.Index(i) | |||||
| valueInterface = value.Interface() | |||||
| e1Kind := reflect.ValueOf(e1).Kind() | |||||
| e2Kind := reflect.ValueOf(e2).Kind() | |||||
| if e1Kind != e2Kind { | |||||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
| } | |||||
| compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) | |||||
| res, isComparable := compare(e1, e2, e1Kind) | |||||
| if !isComparable { | |||||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
| } | |||||
| if !isComparable { | |||||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) | |||||
| } | |||||
| if res != -1 && res != 0 { | |||||
| return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||||
| if !containsValue(allowedComparesResults, compareResult) { | |||||
| return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) | |||||
| } | |||||
| } | } | ||||
| return true | return true | ||||
| } | } | ||||
| // Less asserts that the first element is less than the second | |||||
| // IsIncreasing asserts that the collection is increasing | |||||
| // | // | ||||
| // assert.Less(t, 1, 2) | |||||
| // assert.Less(t, float64(1), float64(2)) | |||||
| // assert.Less(t, "a", "b") | |||||
| func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| e1Kind := reflect.ValueOf(e1).Kind() | |||||
| e2Kind := reflect.ValueOf(e2).Kind() | |||||
| if e1Kind != e2Kind { | |||||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
| } | |||||
| res, isComparable := compare(e1, e2, e1Kind) | |||||
| if !isComparable { | |||||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
| } | |||||
| if res != 1 { | |||||
| return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) | |||||
| } | |||||
| return true | |||||
| // assert.IsIncreasing(t, []int{1, 2, 3}) | |||||
| // assert.IsIncreasing(t, []float{1, 2}) | |||||
| // assert.IsIncreasing(t, []string{"a", "b"}) | |||||
| func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
| return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) | |||||
| } | } | ||||
| // LessOrEqual asserts that the first element is less than or equal to the second | |||||
| // IsNonIncreasing asserts that the collection is not increasing | |||||
| // | // | ||||
| // assert.LessOrEqual(t, 1, 2) | |||||
| // assert.LessOrEqual(t, 2, 2) | |||||
| // assert.LessOrEqual(t, "a", "b") | |||||
| // assert.LessOrEqual(t, "b", "b") | |||||
| func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| e1Kind := reflect.ValueOf(e1).Kind() | |||||
| e2Kind := reflect.ValueOf(e2).Kind() | |||||
| if e1Kind != e2Kind { | |||||
| return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
| } | |||||
| res, isComparable := compare(e1, e2, e1Kind) | |||||
| if !isComparable { | |||||
| return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
| } | |||||
| // assert.IsNonIncreasing(t, []int{2, 1, 1}) | |||||
| // assert.IsNonIncreasing(t, []float{2, 1}) | |||||
| // assert.IsNonIncreasing(t, []string{"b", "a"}) | |||||
| func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
| return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) | |||||
| } | |||||
| if res != 1 && res != 0 { | |||||
| return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||||
| } | |||||
| // IsDecreasing asserts that the collection is decreasing | |||||
| // | |||||
| // assert.IsDecreasing(t, []int{2, 1, 0}) | |||||
| // assert.IsDecreasing(t, []float{2, 1}) | |||||
| // assert.IsDecreasing(t, []string{"b", "a"}) | |||||
| func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
| return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) | |||||
| } | |||||
| return true | |||||
| // IsNonDecreasing asserts that the collection is not decreasing | |||||
| // | |||||
| // assert.IsNonDecreasing(t, []int{1, 1, 2}) | |||||
| // assert.IsNonDecreasing(t, []float{1, 2}) | |||||
| // assert.IsNonDecreasing(t, []string{"a", "b"}) | |||||
| func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
| return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) | |||||
| } | } | ||||
| @@ -11,6 +11,7 @@ import ( | |||||
| "reflect" | "reflect" | ||||
| "regexp" | "regexp" | ||||
| "runtime" | "runtime" | ||||
| "runtime/debug" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "unicode" | "unicode" | ||||
| @@ -18,10 +19,10 @@ import ( | |||||
| "github.com/davecgh/go-spew/spew" | "github.com/davecgh/go-spew/spew" | ||||
| "github.com/pmezard/go-difflib/difflib" | "github.com/pmezard/go-difflib/difflib" | ||||
| yaml "gopkg.in/yaml.v2" | |||||
| yaml "gopkg.in/yaml.v3" | |||||
| ) | ) | ||||
| //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl | |||||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" | |||||
| // TestingT is an interface wrapper around *testing.T | // TestingT is an interface wrapper around *testing.T | ||||
| type TestingT interface { | type TestingT interface { | ||||
| @@ -44,7 +45,7 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool | |||||
| // for table driven tests. | // for table driven tests. | ||||
| type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool | type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool | ||||
| // Comparison a custom function that returns true on success and false on failure | |||||
| // Comparison is a custom function that returns true on success and false on failure | |||||
| type Comparison func() (success bool) | type Comparison func() (success bool) | ||||
| /* | /* | ||||
| @@ -103,11 +104,11 @@ the problem actually occurred in calling code.*/ | |||||
| // failed. | // failed. | ||||
| func CallerInfo() []string { | func CallerInfo() []string { | ||||
| pc := uintptr(0) | |||||
| file := "" | |||||
| line := 0 | |||||
| ok := false | |||||
| name := "" | |||||
| var pc uintptr | |||||
| var ok bool | |||||
| var file string | |||||
| var line int | |||||
| var name string | |||||
| callers := []string{} | callers := []string{} | ||||
| for i := 0; ; i++ { | for i := 0; ; i++ { | ||||
| @@ -171,8 +172,8 @@ func isTest(name, prefix string) bool { | |||||
| if len(name) == len(prefix) { // "Test" is ok | if len(name) == len(prefix) { // "Test" is ok | ||||
| return true | return true | ||||
| } | } | ||||
| rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) | |||||
| return !unicode.IsLower(rune) | |||||
| r, _ := utf8.DecodeRuneInString(name[len(prefix):]) | |||||
| return !unicode.IsLower(r) | |||||
| } | } | ||||
| func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { | func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { | ||||
| @@ -351,6 +352,19 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) | |||||
| } | } | ||||
| // validateEqualArgs checks whether provided arguments can be safely used in the | |||||
| // Equal/NotEqual functions. | |||||
| func validateEqualArgs(expected, actual interface{}) error { | |||||
| if expected == nil && actual == nil { | |||||
| return nil | |||||
| } | |||||
| if isFunction(expected) || isFunction(actual) { | |||||
| return errors.New("cannot take func type as argument") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // Same asserts that two pointers reference the same object. | // Same asserts that two pointers reference the same object. | ||||
| // | // | ||||
| // assert.Same(t, ptr1, ptr2) | // assert.Same(t, ptr1, ptr2) | ||||
| @@ -362,18 +376,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual) | |||||
| if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr { | |||||
| return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...) | |||||
| } | |||||
| expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual) | |||||
| if expectedType != actualType { | |||||
| return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v", | |||||
| expectedType, actualType), msgAndArgs...) | |||||
| } | |||||
| if expected != actual { | |||||
| if !samePointers(expected, actual) { | |||||
| return Fail(t, fmt.Sprintf("Not same: \n"+ | return Fail(t, fmt.Sprintf("Not same: \n"+ | ||||
| "expected: %p %#v\n"+ | "expected: %p %#v\n"+ | ||||
| "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) | "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) | ||||
| @@ -382,6 +385,42 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||||
| return true | return true | ||||
| } | } | ||||
| // NotSame asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // assert.NotSame(t, ptr1, ptr2) | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if samePointers(expected, actual) { | |||||
| return Fail(t, fmt.Sprintf( | |||||
| "Expected and actual point to the same object: %p %#v", | |||||
| expected, expected), msgAndArgs...) | |||||
| } | |||||
| return true | |||||
| } | |||||
| // samePointers compares two generic interface objects and returns whether | |||||
| // they point to the same object | |||||
| func samePointers(first, second interface{}) bool { | |||||
| firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) | |||||
| if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { | |||||
| return false | |||||
| } | |||||
| firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) | |||||
| if firstType != secondType { | |||||
| return false | |||||
| } | |||||
| // compare pointer addresses | |||||
| return first == second | |||||
| } | |||||
| // formatUnequalValues takes two values of arbitrary types and returns string | // formatUnequalValues takes two values of arbitrary types and returns string | ||||
| // representations appropriate to be presented to the user. | // representations appropriate to be presented to the user. | ||||
| // | // | ||||
| @@ -390,12 +429,27 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||||
| // to a type conversion in the Go grammar. | // to a type conversion in the Go grammar. | ||||
| func formatUnequalValues(expected, actual interface{}) (e string, a string) { | func formatUnequalValues(expected, actual interface{}) (e string, a string) { | ||||
| if reflect.TypeOf(expected) != reflect.TypeOf(actual) { | if reflect.TypeOf(expected) != reflect.TypeOf(actual) { | ||||
| return fmt.Sprintf("%T(%#v)", expected, expected), | |||||
| fmt.Sprintf("%T(%#v)", actual, actual) | |||||
| return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), | |||||
| fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) | |||||
| } | |||||
| switch expected.(type) { | |||||
| case time.Duration: | |||||
| return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) | |||||
| } | } | ||||
| return truncatingFormat(expected), truncatingFormat(actual) | |||||
| } | |||||
| return fmt.Sprintf("%#v", expected), | |||||
| fmt.Sprintf("%#v", actual) | |||||
| // truncatingFormat formats the data and truncates it if it's too long. | |||||
| // | |||||
| // This helps keep formatted error messages lines from exceeding the | |||||
| // bufio.MaxScanTokenSize max line length that the go testing framework imposes. | |||||
| func truncatingFormat(data interface{}) string { | |||||
| value := fmt.Sprintf("%#v", data) | |||||
| max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. | |||||
| if len(value) > max { | |||||
| value = value[0:max] + "<... truncated>" | |||||
| } | |||||
| return value | |||||
| } | } | ||||
| // EqualValues asserts that two objects are equal or convertable to the same types | // EqualValues asserts that two objects are equal or convertable to the same types | ||||
| @@ -442,12 +496,12 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} | |||||
| // | // | ||||
| // assert.NotNil(t, err) | // assert.NotNil(t, err) | ||||
| func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if !isNil(object) { | if !isNil(object) { | ||||
| return true | return true | ||||
| } | } | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Fail(t, "Expected value not to be nil.", msgAndArgs...) | return Fail(t, "Expected value not to be nil.", msgAndArgs...) | ||||
| } | } | ||||
| @@ -488,12 +542,12 @@ func isNil(object interface{}) bool { | |||||
| // | // | ||||
| // assert.Nil(t, err) | // assert.Nil(t, err) | ||||
| func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if isNil(object) { | if isNil(object) { | ||||
| return true | return true | ||||
| } | } | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) | return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) | ||||
| } | } | ||||
| @@ -530,12 +584,11 @@ func isEmpty(object interface{}) bool { | |||||
| // | // | ||||
| // assert.Empty(t, obj) | // assert.Empty(t, obj) | ||||
| func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| pass := isEmpty(object) | pass := isEmpty(object) | ||||
| if !pass { | if !pass { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) | Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) | ||||
| } | } | ||||
| @@ -550,12 +603,11 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
| // assert.Equal(t, "two", obj[1]) | // assert.Equal(t, "two", obj[1]) | ||||
| // } | // } | ||||
| func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| pass := !isEmpty(object) | pass := !isEmpty(object) | ||||
| if !pass { | if !pass { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) | Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) | ||||
| } | } | ||||
| @@ -598,16 +650,10 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) | |||||
| // | // | ||||
| // assert.True(t, myBool) | // assert.True(t, myBool) | ||||
| func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if h, ok := t.(interface { | |||||
| Helper() | |||||
| }); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if value != true { | |||||
| if !value { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Fail(t, "Should be true", msgAndArgs...) | return Fail(t, "Should be true", msgAndArgs...) | ||||
| } | } | ||||
| @@ -619,11 +665,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | |||||
| // | // | ||||
| // assert.False(t, myBool) | // assert.False(t, myBool) | ||||
| func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { | func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if value != false { | |||||
| if value { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Fail(t, "Should be false", msgAndArgs...) | return Fail(t, "Should be false", msgAndArgs...) | ||||
| } | } | ||||
| @@ -654,6 +699,21 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ | |||||
| } | } | ||||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // assert.NotEqualValues(t, obj1, obj2) | |||||
| func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if ObjectsAreEqualValues(expected, actual) { | |||||
| return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) | |||||
| } | |||||
| return true | |||||
| } | |||||
| // containsElement try loop over the list check if the list includes the element. | // containsElement try loop over the list check if the list includes the element. | ||||
| // return (false, false) if impossible. | // return (false, false) if impossible. | ||||
| // return (true, false) if element was not found. | // return (true, false) if element was not found. | ||||
| @@ -706,10 +766,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo | |||||
| ok, found := includeElement(s, contains) | ok, found := includeElement(s, contains) | ||||
| if !ok { | if !ok { | ||||
| return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) | |||||
| return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) | |||||
| } | } | ||||
| if !found { | if !found { | ||||
| return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) | |||||
| return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) | |||||
| } | } | ||||
| return true | return true | ||||
| @@ -840,27 +900,39 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface | |||||
| return true | return true | ||||
| } | } | ||||
| aKind := reflect.TypeOf(listA).Kind() | |||||
| bKind := reflect.TypeOf(listB).Kind() | |||||
| if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { | |||||
| return false | |||||
| } | |||||
| extraA, extraB := diffLists(listA, listB) | |||||
| if aKind != reflect.Array && aKind != reflect.Slice { | |||||
| return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...) | |||||
| if len(extraA) == 0 && len(extraB) == 0 { | |||||
| return true | |||||
| } | } | ||||
| if bKind != reflect.Array && bKind != reflect.Slice { | |||||
| return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...) | |||||
| return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) | |||||
| } | |||||
| // isList checks that the provided value is array or slice. | |||||
| func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { | |||||
| kind := reflect.TypeOf(list).Kind() | |||||
| if kind != reflect.Array && kind != reflect.Slice { | |||||
| return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), | |||||
| msgAndArgs...) | |||||
| } | } | ||||
| return true | |||||
| } | |||||
| // diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. | |||||
| // If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and | |||||
| // 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. | |||||
| func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { | |||||
| aValue := reflect.ValueOf(listA) | aValue := reflect.ValueOf(listA) | ||||
| bValue := reflect.ValueOf(listB) | bValue := reflect.ValueOf(listB) | ||||
| aLen := aValue.Len() | aLen := aValue.Len() | ||||
| bLen := bValue.Len() | bLen := bValue.Len() | ||||
| if aLen != bLen { | |||||
| return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...) | |||||
| } | |||||
| // Mark indexes in bValue that we already used | // Mark indexes in bValue that we already used | ||||
| visited := make([]bool, bLen) | visited := make([]bool, bLen) | ||||
| for i := 0; i < aLen; i++ { | for i := 0; i < aLen; i++ { | ||||
| @@ -877,11 +949,38 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface | |||||
| } | } | ||||
| } | } | ||||
| if !found { | if !found { | ||||
| return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...) | |||||
| extraA = append(extraA, element) | |||||
| } | } | ||||
| } | } | ||||
| return true | |||||
| for j := 0; j < bLen; j++ { | |||||
| if visited[j] { | |||||
| continue | |||||
| } | |||||
| extraB = append(extraB, bValue.Index(j).Interface()) | |||||
| } | |||||
| return | |||||
| } | |||||
| func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { | |||||
| var msg bytes.Buffer | |||||
| msg.WriteString("elements differ") | |||||
| if len(extraA) > 0 { | |||||
| msg.WriteString("\n\nextra elements in list A:\n") | |||||
| msg.WriteString(spewConfig.Sdump(extraA)) | |||||
| } | |||||
| if len(extraB) > 0 { | |||||
| msg.WriteString("\n\nextra elements in list B:\n") | |||||
| msg.WriteString(spewConfig.Sdump(extraB)) | |||||
| } | |||||
| msg.WriteString("\n\nlistA:\n") | |||||
| msg.WriteString(spewConfig.Sdump(listA)) | |||||
| msg.WriteString("\n\nlistB:\n") | |||||
| msg.WriteString(spewConfig.Sdump(listB)) | |||||
| return msg.String() | |||||
| } | } | ||||
| // Condition uses a Comparison to assert a complex condition. | // Condition uses a Comparison to assert a complex condition. | ||||
| @@ -901,15 +1000,17 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { | |||||
| type PanicTestFunc func() | type PanicTestFunc func() | ||||
| // didPanic returns true if the function passed to it panics. Otherwise, it returns false. | // didPanic returns true if the function passed to it panics. Otherwise, it returns false. | ||||
| func didPanic(f PanicTestFunc) (bool, interface{}) { | |||||
| func didPanic(f PanicTestFunc) (bool, interface{}, string) { | |||||
| didPanic := false | didPanic := false | ||||
| var message interface{} | var message interface{} | ||||
| var stack string | |||||
| func() { | func() { | ||||
| defer func() { | defer func() { | ||||
| if message = recover(); message != nil { | if message = recover(); message != nil { | ||||
| didPanic = true | didPanic = true | ||||
| stack = string(debug.Stack()) | |||||
| } | } | ||||
| }() | }() | ||||
| @@ -918,7 +1019,7 @@ func didPanic(f PanicTestFunc) (bool, interface{}) { | |||||
| }() | }() | ||||
| return didPanic, message | |||||
| return didPanic, message, stack | |||||
| } | } | ||||
| @@ -930,7 +1031,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { | |||||
| if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { | |||||
| return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | ||||
| } | } | ||||
| @@ -946,12 +1047,34 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr | |||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| funcDidPanic, panicValue := didPanic(f) | |||||
| funcDidPanic, panicValue, panickedStack := didPanic(f) | |||||
| if !funcDidPanic { | if !funcDidPanic { | ||||
| return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | ||||
| } | } | ||||
| if panicValue != expected { | if panicValue != expected { | ||||
| return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...) | |||||
| return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) | |||||
| } | |||||
| return true | |||||
| } | |||||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) | |||||
| func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| funcDidPanic, panicValue, panickedStack := didPanic(f) | |||||
| if !funcDidPanic { | |||||
| return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | |||||
| } | |||||
| panicErr, ok := panicValue.(error) | |||||
| if !ok || panicErr.Error() != errString { | |||||
| return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) | |||||
| } | } | ||||
| return true | return true | ||||
| @@ -965,8 +1088,8 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if funcDidPanic, panicValue := didPanic(f); funcDidPanic { | |||||
| return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...) | |||||
| if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { | |||||
| return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) | |||||
| } | } | ||||
| return true | return true | ||||
| @@ -993,6 +1116,8 @@ func toFloat(x interface{}) (float64, bool) { | |||||
| xok := true | xok := true | ||||
| switch xn := x.(type) { | switch xn := x.(type) { | ||||
| case uint: | |||||
| xf = float64(xn) | |||||
| case uint8: | case uint8: | ||||
| xf = float64(xn) | xf = float64(xn) | ||||
| case uint16: | case uint16: | ||||
| @@ -1014,7 +1139,7 @@ func toFloat(x interface{}) (float64, bool) { | |||||
| case float32: | case float32: | ||||
| xf = float64(xn) | xf = float64(xn) | ||||
| case float64: | case float64: | ||||
| xf = float64(xn) | |||||
| xf = xn | |||||
| case time.Duration: | case time.Duration: | ||||
| xf = float64(xn) | xf = float64(xn) | ||||
| default: | default: | ||||
| @@ -1026,7 +1151,7 @@ func toFloat(x interface{}) (float64, bool) { | |||||
| // InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) | |||||
| // assert.InDelta(t, math.Pi, 22/7.0, 0.01) | |||||
| func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -1128,6 +1253,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { | |||||
| if !aok { | if !aok { | ||||
| return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) | return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) | ||||
| } | } | ||||
| if math.IsNaN(af) { | |||||
| return 0, errors.New("expected value must not be NaN") | |||||
| } | |||||
| if af == 0 { | if af == 0 { | ||||
| return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") | return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") | ||||
| } | } | ||||
| @@ -1135,6 +1263,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { | |||||
| if !bok { | if !bok { | ||||
| return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) | return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) | ||||
| } | } | ||||
| if math.IsNaN(bf) { | |||||
| return 0, errors.New("actual value must not be NaN") | |||||
| } | |||||
| return math.Abs(af-bf) / math.Abs(af), nil | return math.Abs(af-bf) / math.Abs(af), nil | ||||
| } | } | ||||
| @@ -1144,6 +1275,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if math.IsNaN(epsilon) { | |||||
| return Fail(t, "epsilon must not be NaN") | |||||
| } | |||||
| actualEpsilon, err := calcRelativeError(expected, actual) | actualEpsilon, err := calcRelativeError(expected, actual) | ||||
| if err != nil { | if err != nil { | ||||
| return Fail(t, err.Error(), msgAndArgs...) | return Fail(t, err.Error(), msgAndArgs...) | ||||
| @@ -1191,10 +1325,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m | |||||
| // assert.Equal(t, expectedObj, actualObj) | // assert.Equal(t, expectedObj, actualObj) | ||||
| // } | // } | ||||
| func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if err != nil { | if err != nil { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) | return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) | ||||
| } | } | ||||
| @@ -1208,11 +1342,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | |||||
| // assert.Equal(t, expectedError, err) | // assert.Equal(t, expectedError, err) | ||||
| // } | // } | ||||
| func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { | func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if err == nil { | if err == nil { | ||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| return Fail(t, "An error is expected but got nil.", msgAndArgs...) | return Fail(t, "An error is expected but got nil.", msgAndArgs...) | ||||
| } | } | ||||
| @@ -1314,7 +1447,8 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExists checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -1332,7 +1466,24 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| info, err := os.Lstat(path) | |||||
| if err != nil { | |||||
| return true | |||||
| } | |||||
| if info.IsDir() { | |||||
| return true | |||||
| } | |||||
| return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) | |||||
| } | |||||
| // DirExists checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -1350,6 +1501,25 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| // NoDirExists checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| info, err := os.Lstat(path) | |||||
| if err != nil { | |||||
| if os.IsNotExist(err) { | |||||
| return true | |||||
| } | |||||
| return true | |||||
| } | |||||
| if !info.IsDir() { | |||||
| return true | |||||
| } | |||||
| return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) | |||||
| } | |||||
| // JSONEq asserts that two JSON strings are equivalent. | // JSONEq asserts that two JSON strings are equivalent. | ||||
| // | // | ||||
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | ||||
| @@ -1439,15 +1609,6 @@ func diff(expected interface{}, actual interface{}) string { | |||||
| return "\n\nDiff:\n" + diff | return "\n\nDiff:\n" + diff | ||||
| } | } | ||||
| // validateEqualArgs checks whether provided arguments can be safely used in the | |||||
| // Equal/NotEqual functions. | |||||
| func validateEqualArgs(expected, actual interface{}) error { | |||||
| if isFunction(expected) || isFunction(actual) { | |||||
| return errors.New("cannot take func type as argument") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func isFunction(arg interface{}) bool { | func isFunction(arg interface{}) bool { | ||||
| if arg == nil { | if arg == nil { | ||||
| return false | return false | ||||
| @@ -1460,6 +1621,8 @@ var spewConfig = spew.ConfigState{ | |||||
| DisablePointerAddresses: true, | DisablePointerAddresses: true, | ||||
| DisableCapacities: true, | DisableCapacities: true, | ||||
| SortKeys: true, | SortKeys: true, | ||||
| DisableMethods: true, | |||||
| MaxDepth: 10, | |||||
| } | } | ||||
| type tHelper interface { | type tHelper interface { | ||||
| @@ -1475,24 +1638,137 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t | |||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| ch := make(chan bool, 1) | |||||
| timer := time.NewTimer(waitFor) | timer := time.NewTimer(waitFor) | ||||
| ticker := time.NewTicker(tick) | |||||
| checkPassed := make(chan bool) | |||||
| defer timer.Stop() | defer timer.Stop() | ||||
| ticker := time.NewTicker(tick) | |||||
| defer ticker.Stop() | defer ticker.Stop() | ||||
| defer close(checkPassed) | |||||
| for { | |||||
| for tick := ticker.C; ; { | |||||
| select { | select { | ||||
| case <-timer.C: | case <-timer.C: | ||||
| return Fail(t, "Condition never satisfied", msgAndArgs...) | return Fail(t, "Condition never satisfied", msgAndArgs...) | ||||
| case result := <-checkPassed: | |||||
| if result { | |||||
| case <-tick: | |||||
| tick = nil | |||||
| go func() { ch <- condition() }() | |||||
| case v := <-ch: | |||||
| if v { | |||||
| return true | return true | ||||
| } | } | ||||
| case <-ticker.C: | |||||
| go func() { | |||||
| checkPassed <- condition() | |||||
| }() | |||||
| tick = ticker.C | |||||
| } | |||||
| } | |||||
| } | |||||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
| func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| ch := make(chan bool, 1) | |||||
| timer := time.NewTimer(waitFor) | |||||
| defer timer.Stop() | |||||
| ticker := time.NewTicker(tick) | |||||
| defer ticker.Stop() | |||||
| for tick := ticker.C; ; { | |||||
| select { | |||||
| case <-timer.C: | |||||
| return true | |||||
| case <-tick: | |||||
| tick = nil | |||||
| go func() { ch <- condition() }() | |||||
| case v := <-ch: | |||||
| if v { | |||||
| return Fail(t, "Condition satisfied", msgAndArgs...) | |||||
| } | |||||
| tick = ticker.C | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if errors.Is(err, target) { | |||||
| return true | |||||
| } | |||||
| var expectedText string | |||||
| if target != nil { | |||||
| expectedText = target.Error() | |||||
| } | |||||
| chain := buildErrorChainString(err) | |||||
| return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ | |||||
| "expected: %q\n"+ | |||||
| "in chain: %s", expectedText, chain, | |||||
| ), msgAndArgs...) | |||||
| } | |||||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if !errors.Is(err, target) { | |||||
| return true | |||||
| } | |||||
| var expectedText string | |||||
| if target != nil { | |||||
| expectedText = target.Error() | |||||
| } | |||||
| chain := buildErrorChainString(err) | |||||
| return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ | |||||
| "found: %q\n"+ | |||||
| "in chain: %s", expectedText, chain, | |||||
| ), msgAndArgs...) | |||||
| } | |||||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if errors.As(err, target) { | |||||
| return true | |||||
| } | |||||
| chain := buildErrorChainString(err) | |||||
| return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ | |||||
| "expected: %q\n"+ | |||||
| "in chain: %s", target, chain, | |||||
| ), msgAndArgs...) | |||||
| } | |||||
| func buildErrorChainString(err error) string { | |||||
| if err == nil { | |||||
| return "" | |||||
| } | |||||
| e := errors.Unwrap(err) | |||||
| chain := fmt.Sprintf("%q", err.Error()) | |||||
| for e != nil { | |||||
| chain += fmt.Sprintf("\n\t%q", e.Error()) | |||||
| e = errors.Unwrap(e) | |||||
| } | |||||
| return chain | |||||
| } | |||||
| @@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { | |||||
| } | } | ||||
| } | } | ||||
| //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs | |||||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" | |||||
| @@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value | |||||
| code, err := httpCode(handler, method, url, values) | code, err := httpCode(handler, method, url, values) | ||||
| if err != nil { | if err != nil { | ||||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||
| return false | |||||
| } | } | ||||
| isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent | ||||
| @@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu | |||||
| code, err := httpCode(handler, method, url, values) | code, err := httpCode(handler, method, url, values) | ||||
| if err != nil { | if err != nil { | ||||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||
| return false | |||||
| } | } | ||||
| isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect | ||||
| @@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values | |||||
| code, err := httpCode(handler, method, url, values) | code, err := httpCode(handler, method, url, values) | ||||
| if err != nil { | if err != nil { | ||||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||
| return false | |||||
| } | } | ||||
| isErrorCode := code >= http.StatusBadRequest | isErrorCode := code >= http.StatusBadRequest | ||||
| @@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values | |||||
| return isErrorCode | return isErrorCode | ||||
| } | } | ||||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| code, err := httpCode(handler, method, url, values) | |||||
| if err != nil { | |||||
| Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | |||||
| } | |||||
| successful := code == statuscode | |||||
| if !successful { | |||||
| Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code)) | |||||
| } | |||||
| return successful | |||||
| } | |||||
| // HTTPBody is a helper that returns HTTP body of the response. It returns | // HTTPBody is a helper that returns HTTP body of the response. It returns | ||||
| // empty string if building a new request fails. | // empty string if building a new request fails. | ||||
| func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { | ||||
| @@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { | |||||
| } | } | ||||
| } | } | ||||
| //go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs | |||||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" | |||||
| @@ -66,7 +66,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExists checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -77,7 +78,8 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { | func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -210,7 +212,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg | |||||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
| // and equal. | // and equal. | ||||
| // | // | ||||
| // assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | |||||
| // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") | |||||
| func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -254,6 +256,54 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.ErrorAs(t, err, target, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.ErrorAsf(t, err, target, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.ErrorIs(t, err, target, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.ErrorIsf(t, err, target, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Errorf asserts that a function returned an error (i.e. not `nil`). | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -275,12 +325,12 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { | |||||
| // | // | ||||
| // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) | // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) | ||||
| func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | ||||
| if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| @@ -289,12 +339,12 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t | |||||
| // | // | ||||
| // assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | // assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | ||||
| func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | ||||
| if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { | |||||
| return | |||||
| } | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| @@ -313,7 +363,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. | |||||
| // Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
| // | // | ||||
| // assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | |||||
| // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") | |||||
| func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -394,7 +444,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExists checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -405,7 +456,8 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { | func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -466,7 +518,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg | |||||
| // Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
| // | // | ||||
| // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | ||||
| // assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) | |||||
| // assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") | |||||
| // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | ||||
| func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -561,7 +613,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, | |||||
| // | // | ||||
| // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -591,7 +643,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin | |||||
| // | // | ||||
| // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -602,6 +654,36 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // HTTPSuccess asserts that a specified handler returns a success status code. | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||
| // | // | ||||
| // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) | ||||
| @@ -647,7 +729,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg | |||||
| // Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
| // | // | ||||
| // assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
| // assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
| func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -660,7 +742,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms | |||||
| // InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) | |||||
| // assert.InDelta(t, math.Pi, 22/7.0, 0.01) | |||||
| func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -717,7 +799,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f | |||||
| // InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
| // assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
| func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -772,71 +854,169 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // IsType asserts that the specified objects are of the same type. | |||||
| func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||||
| // IsDecreasing asserts that the collection is decreasing | |||||
| // | |||||
| // assert.IsDecreasing(t, []int{2, 1, 0}) | |||||
| // assert.IsDecreasing(t, []float{2, 1}) | |||||
| // assert.IsDecreasing(t, []string{"b", "a"}) | |||||
| func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.IsType(t, expectedType, object, msgAndArgs...) { | |||||
| if assert.IsDecreasing(t, object, msgAndArgs...) { | |||||
| return | return | ||||
| } | } | ||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // IsTypef asserts that the specified objects are of the same type. | |||||
| func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { | |||||
| // IsDecreasingf asserts that the collection is decreasing | |||||
| // | |||||
| // assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") | |||||
| // assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
| // assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
| func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.IsTypef(t, expectedType, object, msg, args...) { | |||||
| if assert.IsDecreasingf(t, object, msg, args...) { | |||||
| return | return | ||||
| } | } | ||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // JSONEq asserts that two JSON strings are equivalent. | |||||
| // IsIncreasing asserts that the collection is increasing | |||||
| // | // | ||||
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||||
| func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
| // assert.IsIncreasing(t, []int{1, 2, 3}) | |||||
| // assert.IsIncreasing(t, []float{1, 2}) | |||||
| // assert.IsIncreasing(t, []string{"a", "b"}) | |||||
| func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.JSONEq(t, expected, actual, msgAndArgs...) { | |||||
| if assert.IsIncreasing(t, object, msgAndArgs...) { | |||||
| return | return | ||||
| } | } | ||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // JSONEqf asserts that two JSON strings are equivalent. | |||||
| // IsIncreasingf asserts that the collection is increasing | |||||
| // | // | ||||
| // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | |||||
| func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
| // assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") | |||||
| // assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
| // assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
| func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.JSONEqf(t, expected, actual, msg, args...) { | |||||
| if assert.IsIncreasingf(t, object, msg, args...) { | |||||
| return | return | ||||
| } | } | ||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // YAMLEq asserts that two YAML strings are equivalent. | |||||
| func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
| // IsNonDecreasing asserts that the collection is not decreasing | |||||
| // | |||||
| // assert.IsNonDecreasing(t, []int{1, 1, 2}) | |||||
| // assert.IsNonDecreasing(t, []float{1, 2}) | |||||
| // assert.IsNonDecreasing(t, []string{"a", "b"}) | |||||
| func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.YAMLEq(t, expected, actual, msgAndArgs...) { | |||||
| if assert.IsNonDecreasing(t, object, msgAndArgs...) { | |||||
| return | return | ||||
| } | } | ||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||||
| // | |||||
| // assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") | |||||
| // assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
| // assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
| func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| } | } | ||||
| if assert.YAMLEqf(t, expected, actual, msg, args...) { | |||||
| if assert.IsNonDecreasingf(t, object, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // IsNonIncreasing asserts that the collection is not increasing | |||||
| // | |||||
| // assert.IsNonIncreasing(t, []int{2, 1, 1}) | |||||
| // assert.IsNonIncreasing(t, []float{2, 1}) | |||||
| // assert.IsNonIncreasing(t, []string{"b", "a"}) | |||||
| func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.IsNonIncreasing(t, object, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // IsNonIncreasingf asserts that the collection is not increasing | |||||
| // | |||||
| // assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") | |||||
| // assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
| // assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
| func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.IsNonIncreasingf(t, object, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // IsType asserts that the specified objects are of the same type. | |||||
| func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.IsType(t, expectedType, object, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // IsTypef asserts that the specified objects are of the same type. | |||||
| func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.IsTypef(t, expectedType, object, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // JSONEq asserts that two JSON strings are equivalent. | |||||
| // | |||||
| // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||||
| func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.JSONEq(t, expected, actual, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // JSONEqf asserts that two JSON strings are equivalent. | |||||
| // | |||||
| // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | |||||
| func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.JSONEqf(t, expected, actual, msg, args...) { | |||||
| return | return | ||||
| } | } | ||||
| t.FailNow() | t.FailNow() | ||||
| @@ -920,7 +1100,7 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . | |||||
| // Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
| // | // | ||||
| // assert.Lessf(t, 1, 2, "error message %s", "formatted") | // assert.Lessf(t, 1, 2, "error message %s", "formatted") | ||||
| // assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) | |||||
| // assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") | |||||
| // assert.Lessf(t, "a", "b", "error message %s", "formatted") | // assert.Lessf(t, "a", "b", "error message %s", "formatted") | ||||
| func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -932,6 +1112,62 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // Negative asserts that the specified element is negative | |||||
| // | |||||
| // assert.Negative(t, -1) | |||||
| // assert.Negative(t, -1.23) | |||||
| func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.Negative(t, e, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Negativef asserts that the specified element is negative | |||||
| // | |||||
| // assert.Negativef(t, -1, "error message %s", "formatted") | |||||
| // assert.Negativef(t, -1.23, "error message %s", "formatted") | |||||
| func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.Negativef(t, e, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
| func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
| func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.Neverf(t, condition, waitFor, tick, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Nil asserts that the specified object is nil. | // Nil asserts that the specified object is nil. | ||||
| // | // | ||||
| // assert.Nil(t, err) | // assert.Nil(t, err) | ||||
| @@ -958,6 +1194,30 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // NoDirExists checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NoDirExists(t, path, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NoDirExistsf(t, path, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NoError asserts that a function returned no error (i.e. `nil`). | // NoError asserts that a function returned no error (i.e. `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -990,6 +1250,30 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NoFileExists(t, path, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NoFileExistsf(t, path, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
| // specified substring or element. | // specified substring or element. | ||||
| // | // | ||||
| @@ -1070,6 +1354,32 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // assert.NotEqualValues(t, obj1, obj2) | |||||
| func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") | |||||
| func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NotEqualValuesf(t, expected, actual, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotEqualf asserts that the specified values are NOT equal. | // NotEqualf asserts that the specified values are NOT equal. | ||||
| // | // | ||||
| // assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") | // assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") | ||||
| @@ -1086,6 +1396,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NotErrorIs(t, err, target, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NotErrorIsf(t, err, target, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotNil asserts that the specified object is not nil. | // NotNil asserts that the specified object is not nil. | ||||
| // | // | ||||
| // assert.NotNil(t, err) | // assert.NotNil(t, err) | ||||
| @@ -1154,7 +1488,7 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf | |||||
| // NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
| // | // | ||||
| // assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
| // assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
| // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | ||||
| func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -1166,6 +1500,38 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // NotSame asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // assert.NotSame(t, ptr1, ptr2) | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NotSame(t, expected, actual, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotSamef asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.NotSamef(t, expected, actual, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // NotSubset asserts that the specified list(array, slice...) contains not all | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||
| // elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
| // | // | ||||
| @@ -1229,6 +1595,36 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) | |||||
| func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.PanicsWithError(t, errString, f, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
| func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.PanicsWithErrorf(t, errString, f, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||
| // the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
| // | // | ||||
| @@ -1270,6 +1666,34 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // Positive asserts that the specified element is positive | |||||
| // | |||||
| // assert.Positive(t, 1) | |||||
| // assert.Positive(t, 1.23) | |||||
| func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.Positive(t, e, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Positivef asserts that the specified element is positive | |||||
| // | |||||
| // assert.Positivef(t, 1, "error message %s", "formatted") | |||||
| // assert.Positivef(t, 1.23, "error message %s", "formatted") | |||||
| func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.Positivef(t, e, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Regexp asserts that a specified regexp matches a string. | // Regexp asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") | // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") | ||||
| @@ -1286,7 +1710,7 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface | |||||
| // Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
| // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
| // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | ||||
| func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -1410,6 +1834,28 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim | |||||
| t.FailNow() | t.FailNow() | ||||
| } | } | ||||
| // YAMLEq asserts that two YAML strings are equivalent. | |||||
| func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.YAMLEq(t, expected, actual, msgAndArgs...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
| if h, ok := t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| if assert.YAMLEqf(t, expected, actual, msg, args...) { | |||||
| return | |||||
| } | |||||
| t.FailNow() | |||||
| } | |||||
| // Zero asserts that i is the zero value for its type. | // Zero asserts that i is the zero value for its type. | ||||
| func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { | func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { | ||||
| if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
| @@ -54,7 +54,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, | |||||
| Containsf(a.t, s, contains, msg, args...) | Containsf(a.t, s, contains, msg, args...) | ||||
| } | } | ||||
| // DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExists checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -62,7 +63,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | |||||
| DirExists(a.t, path, msgAndArgs...) | DirExists(a.t, path, msgAndArgs...) | ||||
| } | } | ||||
| // DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| // DirExistsf checks whether a directory exists in the given path. It also fails | |||||
| // if the path is a file rather a directory or there is an error checking whether it exists. | |||||
| func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { | func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -168,7 +170,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn | |||||
| // EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
| // and equal. | // and equal. | ||||
| // | // | ||||
| // a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | |||||
| // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") | |||||
| func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -203,6 +205,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { | |||||
| Error(a.t, err, msgAndArgs...) | Error(a.t, err, msgAndArgs...) | ||||
| } | } | ||||
| // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| ErrorAs(a.t, err, target, msgAndArgs...) | |||||
| } | |||||
| // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
| // This is a wrapper for errors.As. | |||||
| func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| ErrorAsf(a.t, err, target, msg, args...) | |||||
| } | |||||
| // ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| ErrorIs(a.t, err, target, msgAndArgs...) | |||||
| } | |||||
| // ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| ErrorIsf(a.t, err, target, msg, args...) | |||||
| } | |||||
| // Errorf asserts that a function returned an error (i.e. not `nil`). | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -250,7 +288,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg | |||||
| // Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
| // | // | ||||
| // a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | |||||
| // a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") | |||||
| func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -310,7 +348,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { | |||||
| Falsef(a.t, value, msg, args...) | Falsef(a.t, value, msg, args...) | ||||
| } | } | ||||
| // FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExists checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -318,7 +357,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | |||||
| FileExists(a.t, path, msgAndArgs...) | FileExists(a.t, path, msgAndArgs...) | ||||
| } | } | ||||
| // FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
| // FileExistsf checks whether a file exists in the given path. It also fails if | |||||
| // the path points to a directory or there is an error when trying to check the file. | |||||
| func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { | func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -367,7 +407,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, | |||||
| // Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
| // | // | ||||
| // a.Greaterf(2, 1, "error message %s", "formatted") | // a.Greaterf(2, 1, "error message %s", "formatted") | ||||
| // a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) | |||||
| // a.Greaterf(float64(2), float64(1), "error message %s", "formatted") | |||||
| // a.Greaterf("b", "a", "error message %s", "formatted") | // a.Greaterf("b", "a", "error message %s", "formatted") | ||||
| func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -444,7 +484,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri | |||||
| // | // | ||||
| // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -468,7 +508,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s | |||||
| // | // | ||||
| // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
| // | // | ||||
| // Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -476,6 +516,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url | |||||
| HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | ||||
| } | } | ||||
| // HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) | |||||
| } | |||||
| // HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
| // | |||||
| // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
| // | |||||
| // Returns whether the assertion was successful (true) or not (false). | |||||
| func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) | |||||
| } | |||||
| // HTTPSuccess asserts that a specified handler returns a success status code. | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||
| // | // | ||||
| // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | ||||
| @@ -512,7 +576,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, | |||||
| // Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
| // | // | ||||
| // a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
| // a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
| func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -522,7 +586,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} | |||||
| // InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // a.InDelta(math.Pi, (22 / 7.0), 0.01) | |||||
| // a.InDelta(math.Pi, 22/7.0, 0.01) | |||||
| func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -564,7 +628,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del | |||||
| // InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
| // | // | ||||
| // a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
| // a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
| func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| h.Helper() | h.Helper() | ||||
| @@ -604,6 +668,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo | |||||
| InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | ||||
| } | } | ||||
| // IsDecreasing asserts that the collection is decreasing | |||||
| // | |||||
| // a.IsDecreasing([]int{2, 1, 0}) | |||||
| // a.IsDecreasing([]float{2, 1}) | |||||
| // a.IsDecreasing([]string{"b", "a"}) | |||||
| func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsDecreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsDecreasingf asserts that the collection is decreasing | |||||
| // | |||||
| // a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") | |||||
| // a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
| // a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsDecreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsIncreasing asserts that the collection is increasing | |||||
| // | |||||
| // a.IsIncreasing([]int{1, 2, 3}) | |||||
| // a.IsIncreasing([]float{1, 2}) | |||||
| // a.IsIncreasing([]string{"a", "b"}) | |||||
| func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsIncreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsIncreasingf asserts that the collection is increasing | |||||
| // | |||||
| // a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") | |||||
| // a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
| // a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsIncreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsNonDecreasing asserts that the collection is not decreasing | |||||
| // | |||||
| // a.IsNonDecreasing([]int{1, 1, 2}) | |||||
| // a.IsNonDecreasing([]float{1, 2}) | |||||
| // a.IsNonDecreasing([]string{"a", "b"}) | |||||
| func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsNonDecreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsNonDecreasingf asserts that the collection is not decreasing | |||||
| // | |||||
| // a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") | |||||
| // a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
| // a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsNonDecreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsNonIncreasing asserts that the collection is not increasing | |||||
| // | |||||
| // a.IsNonIncreasing([]int{2, 1, 1}) | |||||
| // a.IsNonIncreasing([]float{2, 1}) | |||||
| // a.IsNonIncreasing([]string{"b", "a"}) | |||||
| func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsNonIncreasing(a.t, object, msgAndArgs...) | |||||
| } | |||||
| // IsNonIncreasingf asserts that the collection is not increasing | |||||
| // | |||||
| // a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") | |||||
| // a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
| // a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
| func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| IsNonIncreasingf(a.t, object, msg, args...) | |||||
| } | |||||
| // IsType asserts that the specified objects are of the same type. | // IsType asserts that the specified objects are of the same type. | ||||
| func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -640,22 +800,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. | |||||
| JSONEqf(a.t, expected, actual, msg, args...) | JSONEqf(a.t, expected, actual, msg, args...) | ||||
| } | } | ||||
| // YAMLEq asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| YAMLEqf(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // Len asserts that the specified object has specific length. | // Len asserts that the specified object has specific length. | ||||
| // Len also fails if the object has a type that len() not accept. | // Len also fails if the object has a type that len() not accept. | ||||
| // | // | ||||
| @@ -719,7 +863,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar | |||||
| // Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
| // | // | ||||
| // a.Lessf(1, 2, "error message %s", "formatted") | // a.Lessf(1, 2, "error message %s", "formatted") | ||||
| // a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) | |||||
| // a.Lessf(float64(1), float64(2), "error message %s", "formatted") | |||||
| // a.Lessf("a", "b", "error message %s", "formatted") | // a.Lessf("a", "b", "error message %s", "formatted") | ||||
| func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -728,6 +872,50 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i | |||||
| Lessf(a.t, e1, e2, msg, args...) | Lessf(a.t, e1, e2, msg, args...) | ||||
| } | } | ||||
| // Negative asserts that the specified element is negative | |||||
| // | |||||
| // a.Negative(-1) | |||||
| // a.Negative(-1.23) | |||||
| func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Negative(a.t, e, msgAndArgs...) | |||||
| } | |||||
| // Negativef asserts that the specified element is negative | |||||
| // | |||||
| // a.Negativef(-1, "error message %s", "formatted") | |||||
| // a.Negativef(-1.23, "error message %s", "formatted") | |||||
| func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Negativef(a.t, e, msg, args...) | |||||
| } | |||||
| // Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
| func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Never(a.t, condition, waitFor, tick, msgAndArgs...) | |||||
| } | |||||
| // Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
| // periodically checking the target function each tick. | |||||
| // | |||||
| // a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
| func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Neverf(a.t, condition, waitFor, tick, msg, args...) | |||||
| } | |||||
| // Nil asserts that the specified object is nil. | // Nil asserts that the specified object is nil. | ||||
| // | // | ||||
| // a.Nil(err) | // a.Nil(err) | ||||
| @@ -748,6 +936,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { | |||||
| Nilf(a.t, object, msg, args...) | Nilf(a.t, object, msg, args...) | ||||
| } | } | ||||
| // NoDirExists checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NoDirExists(a.t, path, msgAndArgs...) | |||||
| } | |||||
| // NoDirExistsf checks whether a directory does not exist in the given path. | |||||
| // It fails if the path points to an existing _directory_ only. | |||||
| func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NoDirExistsf(a.t, path, msg, args...) | |||||
| } | |||||
| // NoError asserts that a function returned no error (i.e. `nil`). | // NoError asserts that a function returned no error (i.e. `nil`). | ||||
| // | // | ||||
| // actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
| @@ -774,6 +980,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { | |||||
| NoErrorf(a.t, err, msg, args...) | NoErrorf(a.t, err, msg, args...) | ||||
| } | } | ||||
| // NoFileExists checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NoFileExists(a.t, path, msgAndArgs...) | |||||
| } | |||||
| // NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
| // if the path points to an existing _file_ only. | |||||
| func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NoFileExistsf(a.t, path, msg, args...) | |||||
| } | |||||
| // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
| // specified substring or element. | // specified substring or element. | ||||
| // | // | ||||
| @@ -839,6 +1063,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr | |||||
| NotEqual(a.t, expected, actual, msgAndArgs...) | NotEqual(a.t, expected, actual, msgAndArgs...) | ||||
| } | } | ||||
| // NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // a.NotEqualValues(obj1, obj2) | |||||
| func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NotEqualValues(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
| // | |||||
| // a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") | |||||
| func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NotEqualValuesf(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // NotEqualf asserts that the specified values are NOT equal. | // NotEqualf asserts that the specified values are NOT equal. | ||||
| // | // | ||||
| // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | ||||
| @@ -852,6 +1096,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str | |||||
| NotEqualf(a.t, expected, actual, msg, args...) | NotEqualf(a.t, expected, actual, msg, args...) | ||||
| } | } | ||||
| // NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NotErrorIs(a.t, err, target, msgAndArgs...) | |||||
| } | |||||
| // NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
| // This is a wrapper for errors.Is. | |||||
| func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NotErrorIsf(a.t, err, target, msg, args...) | |||||
| } | |||||
| // NotNil asserts that the specified object is not nil. | // NotNil asserts that the specified object is not nil. | ||||
| // | // | ||||
| // a.NotNil(err) | // a.NotNil(err) | ||||
| @@ -905,7 +1167,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in | |||||
| // NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
| // | // | ||||
| // a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
| // a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
| // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | ||||
| func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -914,6 +1176,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg | |||||
| NotRegexpf(a.t, rx, str, msg, args...) | NotRegexpf(a.t, rx, str, msg, args...) | ||||
| } | } | ||||
| // NotSame asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // a.NotSame(ptr1, ptr2) | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NotSame(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // NotSamef asserts that two pointers do not reference the same object. | |||||
| // | |||||
| // a.NotSamef(ptr1, ptr2, "error message %s", "formatted") | |||||
| // | |||||
| // Both arguments must be pointer variables. Pointer variable sameness is | |||||
| // determined based on the equality of both type and value. | |||||
| func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| NotSamef(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // NotSubset asserts that the specified list(array, slice...) contains not all | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||
| // elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
| // | // | ||||
| @@ -962,6 +1250,30 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
| Panics(a.t, f, msgAndArgs...) | Panics(a.t, f, msgAndArgs...) | ||||
| } | } | ||||
| // PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // a.PanicsWithError("crazy error", func(){ GoCrazy() }) | |||||
| func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| PanicsWithError(a.t, errString, f, msgAndArgs...) | |||||
| } | |||||
| // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
| // panics, and that the recovered panic value is an error that satisfies the | |||||
| // EqualError comparison. | |||||
| // | |||||
| // a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
| func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| PanicsWithErrorf(a.t, errString, f, msg, args...) | |||||
| } | |||||
| // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||
| // the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
| // | // | ||||
| @@ -994,6 +1306,28 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa | |||||
| Panicsf(a.t, f, msg, args...) | Panicsf(a.t, f, msg, args...) | ||||
| } | } | ||||
| // Positive asserts that the specified element is positive | |||||
| // | |||||
| // a.Positive(1) | |||||
| // a.Positive(1.23) | |||||
| func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Positive(a.t, e, msgAndArgs...) | |||||
| } | |||||
| // Positivef asserts that the specified element is positive | |||||
| // | |||||
| // a.Positivef(1, "error message %s", "formatted") | |||||
| // a.Positivef(1.23, "error message %s", "formatted") | |||||
| func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| Positivef(a.t, e, msg, args...) | |||||
| } | |||||
| // Regexp asserts that a specified regexp matches a string. | // Regexp asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // a.Regexp(regexp.MustCompile("start"), "it's starting") | // a.Regexp(regexp.MustCompile("start"), "it's starting") | ||||
| @@ -1007,7 +1341,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter | |||||
| // Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
| // | // | ||||
| // a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
| // a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
| // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | ||||
| func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -1104,6 +1438,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta | |||||
| WithinDurationf(a.t, expected, actual, delta, msg, args...) | WithinDurationf(a.t, expected, actual, delta, msg, args...) | ||||
| } | } | ||||
| // YAMLEq asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
| } | |||||
| // YAMLEqf asserts that two YAML strings are equivalent. | |||||
| func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { | |||||
| if h, ok := a.t.(tHelper); ok { | |||||
| h.Helper() | |||||
| } | |||||
| YAMLEqf(a.t, expected, actual, msg, args...) | |||||
| } | |||||
| // Zero asserts that i is the zero value for its type. | // Zero asserts that i is the zero value for its type. | ||||
| func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { | func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { | ||||
| if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
| @@ -26,4 +26,4 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) | |||||
| // for table driven tests. | // for table driven tests. | ||||
| type ErrorAssertionFunc func(TestingT, error, ...interface{}) | type ErrorAssertionFunc func(TestingT, error, ...interface{}) | ||||
| //go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs | |||||
| //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" | |||||
| @@ -0,0 +1,16 @@ | |||||
| language: go | |||||
| go: | |||||
| - "1.4.x" | |||||
| - "1.5.x" | |||||
| - "1.6.x" | |||||
| - "1.7.x" | |||||
| - "1.8.x" | |||||
| - "1.9.x" | |||||
| - "1.10.x" | |||||
| - "1.11.x" | |||||
| - "1.12.x" | |||||
| - "1.13.x" | |||||
| - "tip" | |||||
| go_import_path: gopkg.in/yaml.v3 | |||||
| @@ -0,0 +1,50 @@ | |||||
| This project is covered by two different licenses: MIT and Apache. | |||||
| #### MIT License #### | |||||
| The following files were ported to Go from C files of libyaml, and thus | |||||
| are still covered by their original MIT license, with the additional | |||||
| copyright staring in 2011 when the project was ported over: | |||||
| apic.go emitterc.go parserc.go readerc.go scannerc.go | |||||
| writerc.go yamlh.go yamlprivateh.go | |||||
| Copyright (c) 2006-2010 Kirill Simonov | |||||
| Copyright (c) 2006-2011 Kirill Simonov | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | |||||
| this software and associated documentation files (the "Software"), to deal in | |||||
| the Software without restriction, including without limitation the rights to | |||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |||||
| of the Software, and to permit persons to whom the Software is furnished to do | |||||
| so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| SOFTWARE. | |||||
| ### Apache License ### | |||||
| All the remaining project files are covered by the Apache license: | |||||
| Copyright (c) 2011-2019 Canonical Ltd | |||||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| you may not use this file except in compliance with the License. | |||||
| You may obtain a copy of the License at | |||||
| http://www.apache.org/licenses/LICENSE-2.0 | |||||
| Unless required by applicable law or agreed to in writing, software | |||||
| distributed under the License is distributed on an "AS IS" BASIS, | |||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| See the License for the specific language governing permissions and | |||||
| limitations under the License. | |||||
| @@ -0,0 +1,13 @@ | |||||
| Copyright 2011-2016 Canonical Ltd. | |||||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| you may not use this file except in compliance with the License. | |||||
| You may obtain a copy of the License at | |||||
| http://www.apache.org/licenses/LICENSE-2.0 | |||||
| Unless required by applicable law or agreed to in writing, software | |||||
| distributed under the License is distributed on an "AS IS" BASIS, | |||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| See the License for the specific language governing permissions and | |||||
| limitations under the License. | |||||
| @@ -0,0 +1,150 @@ | |||||
| # YAML support for the Go language | |||||
| Introduction | |||||
| ------------ | |||||
| The yaml package enables Go programs to comfortably encode and decode YAML | |||||
| values. It was developed within [Canonical](https://www.canonical.com) as | |||||
| part of the [juju](https://juju.ubuntu.com) project, and is based on a | |||||
| pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) | |||||
| C library to parse and generate YAML data quickly and reliably. | |||||
| Compatibility | |||||
| ------------- | |||||
| The yaml package supports most of YAML 1.2, but preserves some behavior | |||||
| from 1.1 for backwards compatibility. | |||||
| Specifically, as of v3 of the yaml package: | |||||
| - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being | |||||
| decoded into a typed bool value. Otherwise they behave as a string. Booleans | |||||
| in YAML 1.2 are _true/false_ only. | |||||
| - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ | |||||
| as specified in YAML 1.2, because most parsers still use the old format. | |||||
| Octals in the _0o777_ format are supported though, so new files work. | |||||
| - Does not support base-60 floats. These are gone from YAML 1.2, and were | |||||
| actually never supported by this package as it's clearly a poor choice. | |||||
| and offers backwards | |||||
| compatibility with YAML 1.1 in some cases. | |||||
| 1.2, including support for | |||||
| anchors, tags, map merging, etc. Multi-document unmarshalling is not yet | |||||
| implemented, and base-60 floats from YAML 1.1 are purposefully not | |||||
| supported since they're a poor design and are gone in YAML 1.2. | |||||
| Installation and usage | |||||
| ---------------------- | |||||
| The import path for the package is *gopkg.in/yaml.v3*. | |||||
| To install it, run: | |||||
| go get gopkg.in/yaml.v3 | |||||
| API documentation | |||||
| ----------------- | |||||
| If opened in a browser, the import path itself leads to the API documentation: | |||||
| - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) | |||||
| API stability | |||||
| ------------- | |||||
| The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). | |||||
| License | |||||
| ------- | |||||
| The yaml package is licensed under the MIT and Apache License 2.0 licenses. | |||||
| Please see the LICENSE file for details. | |||||
| Example | |||||
| ------- | |||||
| ```Go | |||||
| package main | |||||
| import ( | |||||
| "fmt" | |||||
| "log" | |||||
| "gopkg.in/yaml.v3" | |||||
| ) | |||||
| var data = ` | |||||
| a: Easy! | |||||
| b: | |||||
| c: 2 | |||||
| d: [3, 4] | |||||
| ` | |||||
| // Note: struct fields must be public in order for unmarshal to | |||||
| // correctly populate the data. | |||||
| type T struct { | |||||
| A string | |||||
| B struct { | |||||
| RenamedC int `yaml:"c"` | |||||
| D []int `yaml:",flow"` | |||||
| } | |||||
| } | |||||
| func main() { | |||||
| t := T{} | |||||
| err := yaml.Unmarshal([]byte(data), &t) | |||||
| if err != nil { | |||||
| log.Fatalf("error: %v", err) | |||||
| } | |||||
| fmt.Printf("--- t:\n%v\n\n", t) | |||||
| d, err := yaml.Marshal(&t) | |||||
| if err != nil { | |||||
| log.Fatalf("error: %v", err) | |||||
| } | |||||
| fmt.Printf("--- t dump:\n%s\n\n", string(d)) | |||||
| m := make(map[interface{}]interface{}) | |||||
| err = yaml.Unmarshal([]byte(data), &m) | |||||
| if err != nil { | |||||
| log.Fatalf("error: %v", err) | |||||
| } | |||||
| fmt.Printf("--- m:\n%v\n\n", m) | |||||
| d, err = yaml.Marshal(&m) | |||||
| if err != nil { | |||||
| log.Fatalf("error: %v", err) | |||||
| } | |||||
| fmt.Printf("--- m dump:\n%s\n\n", string(d)) | |||||
| } | |||||
| ``` | |||||
| This example will generate the following output: | |||||
| ``` | |||||
| --- t: | |||||
| {Easy! {2 [3 4]}} | |||||
| --- t dump: | |||||
| a: Easy! | |||||
| b: | |||||
| c: 2 | |||||
| d: [3, 4] | |||||
| --- m: | |||||
| map[a:Easy! b:map[c:2 d:[3 4]]] | |||||
| --- m dump: | |||||
| a: Easy! | |||||
| b: | |||||
| c: 2 | |||||
| d: | |||||
| - 3 | |||||
| - 4 | |||||
| ``` | |||||
| @@ -0,0 +1,746 @@ | |||||
| // | |||||
| // Copyright (c) 2011-2019 Canonical Ltd | |||||
| // Copyright (c) 2006-2010 Kirill Simonov | |||||
| // | |||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy of | |||||
| // this software and associated documentation files (the "Software"), to deal in | |||||
| // the Software without restriction, including without limitation the rights to | |||||
| // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |||||
| // of the Software, and to permit persons to whom the Software is furnished to do | |||||
| // so, subject to the following conditions: | |||||
| // | |||||
| // The above copyright notice and this permission notice shall be included in all | |||||
| // copies or substantial portions of the Software. | |||||
| // | |||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| // SOFTWARE. | |||||
| package yaml | |||||
| import ( | |||||
| "io" | |||||
| ) | |||||
| func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { | |||||
| //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) | |||||
| // Check if we can move the queue at the beginning of the buffer. | |||||
| if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { | |||||
| if parser.tokens_head != len(parser.tokens) { | |||||
| copy(parser.tokens, parser.tokens[parser.tokens_head:]) | |||||
| } | |||||
| parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] | |||||
| parser.tokens_head = 0 | |||||
| } | |||||
| parser.tokens = append(parser.tokens, *token) | |||||
| if pos < 0 { | |||||
| return | |||||
| } | |||||
| copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) | |||||
| parser.tokens[parser.tokens_head+pos] = *token | |||||
| } | |||||
| // Create a new parser object. | |||||
| func yaml_parser_initialize(parser *yaml_parser_t) bool { | |||||
| *parser = yaml_parser_t{ | |||||
| raw_buffer: make([]byte, 0, input_raw_buffer_size), | |||||
| buffer: make([]byte, 0, input_buffer_size), | |||||
| } | |||||
| return true | |||||
| } | |||||
| // Destroy a parser object. | |||||
| func yaml_parser_delete(parser *yaml_parser_t) { | |||||
| *parser = yaml_parser_t{} | |||||
| } | |||||
| // String read handler. | |||||
| func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { | |||||
| if parser.input_pos == len(parser.input) { | |||||
| return 0, io.EOF | |||||
| } | |||||
| n = copy(buffer, parser.input[parser.input_pos:]) | |||||
| parser.input_pos += n | |||||
| return n, nil | |||||
| } | |||||
| // Reader read handler. | |||||
| func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { | |||||
| return parser.input_reader.Read(buffer) | |||||
| } | |||||
| // Set a string input. | |||||
| func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { | |||||
| if parser.read_handler != nil { | |||||
| panic("must set the input source only once") | |||||
| } | |||||
| parser.read_handler = yaml_string_read_handler | |||||
| parser.input = input | |||||
| parser.input_pos = 0 | |||||
| } | |||||
| // Set a file input. | |||||
| func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { | |||||
| if parser.read_handler != nil { | |||||
| panic("must set the input source only once") | |||||
| } | |||||
| parser.read_handler = yaml_reader_read_handler | |||||
| parser.input_reader = r | |||||
| } | |||||
| // Set the source encoding. | |||||
| func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { | |||||
| if parser.encoding != yaml_ANY_ENCODING { | |||||
| panic("must set the encoding only once") | |||||
| } | |||||
| parser.encoding = encoding | |||||
| } | |||||
| // Create a new emitter object. | |||||
| func yaml_emitter_initialize(emitter *yaml_emitter_t) { | |||||
| *emitter = yaml_emitter_t{ | |||||
| buffer: make([]byte, output_buffer_size), | |||||
| raw_buffer: make([]byte, 0, output_raw_buffer_size), | |||||
| states: make([]yaml_emitter_state_t, 0, initial_stack_size), | |||||
| events: make([]yaml_event_t, 0, initial_queue_size), | |||||
| } | |||||
| } | |||||
| // Destroy an emitter object. | |||||
| func yaml_emitter_delete(emitter *yaml_emitter_t) { | |||||
| *emitter = yaml_emitter_t{} | |||||
| } | |||||
| // String write handler. | |||||
| func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { | |||||
| *emitter.output_buffer = append(*emitter.output_buffer, buffer...) | |||||
| return nil | |||||
| } | |||||
| // yaml_writer_write_handler uses emitter.output_writer to write the | |||||
| // emitted text. | |||||
| func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { | |||||
| _, err := emitter.output_writer.Write(buffer) | |||||
| return err | |||||
| } | |||||
| // Set a string output. | |||||
| func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { | |||||
| if emitter.write_handler != nil { | |||||
| panic("must set the output target only once") | |||||
| } | |||||
| emitter.write_handler = yaml_string_write_handler | |||||
| emitter.output_buffer = output_buffer | |||||
| } | |||||
| // Set a file output. | |||||
| func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { | |||||
| if emitter.write_handler != nil { | |||||
| panic("must set the output target only once") | |||||
| } | |||||
| emitter.write_handler = yaml_writer_write_handler | |||||
| emitter.output_writer = w | |||||
| } | |||||
| // Set the output encoding. | |||||
| func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { | |||||
| if emitter.encoding != yaml_ANY_ENCODING { | |||||
| panic("must set the output encoding only once") | |||||
| } | |||||
| emitter.encoding = encoding | |||||
| } | |||||
| // Set the canonical output style. | |||||
| func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { | |||||
| emitter.canonical = canonical | |||||
| } | |||||
| // Set the indentation increment. | |||||
| func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { | |||||
| if indent < 2 || indent > 9 { | |||||
| indent = 2 | |||||
| } | |||||
| emitter.best_indent = indent | |||||
| } | |||||
| // Set the preferred line width. | |||||
| func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { | |||||
| if width < 0 { | |||||
| width = -1 | |||||
| } | |||||
| emitter.best_width = width | |||||
| } | |||||
| // Set if unescaped non-ASCII characters are allowed. | |||||
| func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { | |||||
| emitter.unicode = unicode | |||||
| } | |||||
| // Set the preferred line break character. | |||||
| func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { | |||||
| emitter.line_break = line_break | |||||
| } | |||||
| ///* | |||||
| // * Destroy a token object. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(void) | |||||
| //yaml_token_delete(yaml_token_t *token) | |||||
| //{ | |||||
| // assert(token); // Non-NULL token object expected. | |||||
| // | |||||
| // switch (token.type) | |||||
| // { | |||||
| // case YAML_TAG_DIRECTIVE_TOKEN: | |||||
| // yaml_free(token.data.tag_directive.handle); | |||||
| // yaml_free(token.data.tag_directive.prefix); | |||||
| // break; | |||||
| // | |||||
| // case YAML_ALIAS_TOKEN: | |||||
| // yaml_free(token.data.alias.value); | |||||
| // break; | |||||
| // | |||||
| // case YAML_ANCHOR_TOKEN: | |||||
| // yaml_free(token.data.anchor.value); | |||||
| // break; | |||||
| // | |||||
| // case YAML_TAG_TOKEN: | |||||
| // yaml_free(token.data.tag.handle); | |||||
| // yaml_free(token.data.tag.suffix); | |||||
| // break; | |||||
| // | |||||
| // case YAML_SCALAR_TOKEN: | |||||
| // yaml_free(token.data.scalar.value); | |||||
| // break; | |||||
| // | |||||
| // default: | |||||
| // break; | |||||
| // } | |||||
| // | |||||
| // memset(token, 0, sizeof(yaml_token_t)); | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Check if a string is a valid UTF-8 sequence. | |||||
| // * | |||||
| // * Check 'reader.c' for more details on UTF-8 encoding. | |||||
| // */ | |||||
| // | |||||
| //static int | |||||
| //yaml_check_utf8(yaml_char_t *start, size_t length) | |||||
| //{ | |||||
| // yaml_char_t *end = start+length; | |||||
| // yaml_char_t *pointer = start; | |||||
| // | |||||
| // while (pointer < end) { | |||||
| // unsigned char octet; | |||||
| // unsigned int width; | |||||
| // unsigned int value; | |||||
| // size_t k; | |||||
| // | |||||
| // octet = pointer[0]; | |||||
| // width = (octet & 0x80) == 0x00 ? 1 : | |||||
| // (octet & 0xE0) == 0xC0 ? 2 : | |||||
| // (octet & 0xF0) == 0xE0 ? 3 : | |||||
| // (octet & 0xF8) == 0xF0 ? 4 : 0; | |||||
| // value = (octet & 0x80) == 0x00 ? octet & 0x7F : | |||||
| // (octet & 0xE0) == 0xC0 ? octet & 0x1F : | |||||
| // (octet & 0xF0) == 0xE0 ? octet & 0x0F : | |||||
| // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; | |||||
| // if (!width) return 0; | |||||
| // if (pointer+width > end) return 0; | |||||
| // for (k = 1; k < width; k ++) { | |||||
| // octet = pointer[k]; | |||||
| // if ((octet & 0xC0) != 0x80) return 0; | |||||
| // value = (value << 6) + (octet & 0x3F); | |||||
| // } | |||||
| // if (!((width == 1) || | |||||
| // (width == 2 && value >= 0x80) || | |||||
| // (width == 3 && value >= 0x800) || | |||||
| // (width == 4 && value >= 0x10000))) return 0; | |||||
| // | |||||
| // pointer += width; | |||||
| // } | |||||
| // | |||||
| // return 1; | |||||
| //} | |||||
| // | |||||
| // Create STREAM-START. | |||||
| func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_STREAM_START_EVENT, | |||||
| encoding: encoding, | |||||
| } | |||||
| } | |||||
| // Create STREAM-END. | |||||
| func yaml_stream_end_event_initialize(event *yaml_event_t) { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_STREAM_END_EVENT, | |||||
| } | |||||
| } | |||||
| // Create DOCUMENT-START. | |||||
| func yaml_document_start_event_initialize( | |||||
| event *yaml_event_t, | |||||
| version_directive *yaml_version_directive_t, | |||||
| tag_directives []yaml_tag_directive_t, | |||||
| implicit bool, | |||||
| ) { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_DOCUMENT_START_EVENT, | |||||
| version_directive: version_directive, | |||||
| tag_directives: tag_directives, | |||||
| implicit: implicit, | |||||
| } | |||||
| } | |||||
| // Create DOCUMENT-END. | |||||
| func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_DOCUMENT_END_EVENT, | |||||
| implicit: implicit, | |||||
| } | |||||
| } | |||||
| // Create ALIAS. | |||||
| func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_ALIAS_EVENT, | |||||
| anchor: anchor, | |||||
| } | |||||
| return true | |||||
| } | |||||
| // Create SCALAR. | |||||
| func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_SCALAR_EVENT, | |||||
| anchor: anchor, | |||||
| tag: tag, | |||||
| value: value, | |||||
| implicit: plain_implicit, | |||||
| quoted_implicit: quoted_implicit, | |||||
| style: yaml_style_t(style), | |||||
| } | |||||
| return true | |||||
| } | |||||
| // Create SEQUENCE-START. | |||||
| func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_SEQUENCE_START_EVENT, | |||||
| anchor: anchor, | |||||
| tag: tag, | |||||
| implicit: implicit, | |||||
| style: yaml_style_t(style), | |||||
| } | |||||
| return true | |||||
| } | |||||
| // Create SEQUENCE-END. | |||||
| func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_SEQUENCE_END_EVENT, | |||||
| } | |||||
| return true | |||||
| } | |||||
| // Create MAPPING-START. | |||||
| func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_MAPPING_START_EVENT, | |||||
| anchor: anchor, | |||||
| tag: tag, | |||||
| implicit: implicit, | |||||
| style: yaml_style_t(style), | |||||
| } | |||||
| } | |||||
| // Create MAPPING-END. | |||||
| func yaml_mapping_end_event_initialize(event *yaml_event_t) { | |||||
| *event = yaml_event_t{ | |||||
| typ: yaml_MAPPING_END_EVENT, | |||||
| } | |||||
| } | |||||
| // Destroy an event object. | |||||
| func yaml_event_delete(event *yaml_event_t) { | |||||
| *event = yaml_event_t{} | |||||
| } | |||||
| ///* | |||||
| // * Create a document object. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(int) | |||||
| //yaml_document_initialize(document *yaml_document_t, | |||||
| // version_directive *yaml_version_directive_t, | |||||
| // tag_directives_start *yaml_tag_directive_t, | |||||
| // tag_directives_end *yaml_tag_directive_t, | |||||
| // start_implicit int, end_implicit int) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // struct { | |||||
| // start *yaml_node_t | |||||
| // end *yaml_node_t | |||||
| // top *yaml_node_t | |||||
| // } nodes = { NULL, NULL, NULL } | |||||
| // version_directive_copy *yaml_version_directive_t = NULL | |||||
| // struct { | |||||
| // start *yaml_tag_directive_t | |||||
| // end *yaml_tag_directive_t | |||||
| // top *yaml_tag_directive_t | |||||
| // } tag_directives_copy = { NULL, NULL, NULL } | |||||
| // value yaml_tag_directive_t = { NULL, NULL } | |||||
| // mark yaml_mark_t = { 0, 0, 0 } | |||||
| // | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // assert((tag_directives_start && tag_directives_end) || | |||||
| // (tag_directives_start == tag_directives_end)) | |||||
| // // Valid tag directives are expected. | |||||
| // | |||||
| // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error | |||||
| // | |||||
| // if (version_directive) { | |||||
| // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) | |||||
| // if (!version_directive_copy) goto error | |||||
| // version_directive_copy.major = version_directive.major | |||||
| // version_directive_copy.minor = version_directive.minor | |||||
| // } | |||||
| // | |||||
| // if (tag_directives_start != tag_directives_end) { | |||||
| // tag_directive *yaml_tag_directive_t | |||||
| // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) | |||||
| // goto error | |||||
| // for (tag_directive = tag_directives_start | |||||
| // tag_directive != tag_directives_end; tag_directive ++) { | |||||
| // assert(tag_directive.handle) | |||||
| // assert(tag_directive.prefix) | |||||
| // if (!yaml_check_utf8(tag_directive.handle, | |||||
| // strlen((char *)tag_directive.handle))) | |||||
| // goto error | |||||
| // if (!yaml_check_utf8(tag_directive.prefix, | |||||
| // strlen((char *)tag_directive.prefix))) | |||||
| // goto error | |||||
| // value.handle = yaml_strdup(tag_directive.handle) | |||||
| // value.prefix = yaml_strdup(tag_directive.prefix) | |||||
| // if (!value.handle || !value.prefix) goto error | |||||
| // if (!PUSH(&context, tag_directives_copy, value)) | |||||
| // goto error | |||||
| // value.handle = NULL | |||||
| // value.prefix = NULL | |||||
| // } | |||||
| // } | |||||
| // | |||||
| // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, | |||||
| // tag_directives_copy.start, tag_directives_copy.top, | |||||
| // start_implicit, end_implicit, mark, mark) | |||||
| // | |||||
| // return 1 | |||||
| // | |||||
| //error: | |||||
| // STACK_DEL(&context, nodes) | |||||
| // yaml_free(version_directive_copy) | |||||
| // while (!STACK_EMPTY(&context, tag_directives_copy)) { | |||||
| // value yaml_tag_directive_t = POP(&context, tag_directives_copy) | |||||
| // yaml_free(value.handle) | |||||
| // yaml_free(value.prefix) | |||||
| // } | |||||
| // STACK_DEL(&context, tag_directives_copy) | |||||
| // yaml_free(value.handle) | |||||
| // yaml_free(value.prefix) | |||||
| // | |||||
| // return 0 | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Destroy a document object. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(void) | |||||
| //yaml_document_delete(document *yaml_document_t) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // tag_directive *yaml_tag_directive_t | |||||
| // | |||||
| // context.error = YAML_NO_ERROR // Eliminate a compiler warning. | |||||
| // | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // | |||||
| // while (!STACK_EMPTY(&context, document.nodes)) { | |||||
| // node yaml_node_t = POP(&context, document.nodes) | |||||
| // yaml_free(node.tag) | |||||
| // switch (node.type) { | |||||
| // case YAML_SCALAR_NODE: | |||||
| // yaml_free(node.data.scalar.value) | |||||
| // break | |||||
| // case YAML_SEQUENCE_NODE: | |||||
| // STACK_DEL(&context, node.data.sequence.items) | |||||
| // break | |||||
| // case YAML_MAPPING_NODE: | |||||
| // STACK_DEL(&context, node.data.mapping.pairs) | |||||
| // break | |||||
| // default: | |||||
| // assert(0) // Should not happen. | |||||
| // } | |||||
| // } | |||||
| // STACK_DEL(&context, document.nodes) | |||||
| // | |||||
| // yaml_free(document.version_directive) | |||||
| // for (tag_directive = document.tag_directives.start | |||||
| // tag_directive != document.tag_directives.end | |||||
| // tag_directive++) { | |||||
| // yaml_free(tag_directive.handle) | |||||
| // yaml_free(tag_directive.prefix) | |||||
| // } | |||||
| // yaml_free(document.tag_directives.start) | |||||
| // | |||||
| // memset(document, 0, sizeof(yaml_document_t)) | |||||
| //} | |||||
| // | |||||
| ///** | |||||
| // * Get a document node. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(yaml_node_t *) | |||||
| //yaml_document_get_node(document *yaml_document_t, index int) | |||||
| //{ | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // | |||||
| // if (index > 0 && document.nodes.start + index <= document.nodes.top) { | |||||
| // return document.nodes.start + index - 1 | |||||
| // } | |||||
| // return NULL | |||||
| //} | |||||
| // | |||||
| ///** | |||||
| // * Get the root object. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(yaml_node_t *) | |||||
| //yaml_document_get_root_node(document *yaml_document_t) | |||||
| //{ | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // | |||||
| // if (document.nodes.top != document.nodes.start) { | |||||
| // return document.nodes.start | |||||
| // } | |||||
| // return NULL | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Add a scalar node to a document. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(int) | |||||
| //yaml_document_add_scalar(document *yaml_document_t, | |||||
| // tag *yaml_char_t, value *yaml_char_t, length int, | |||||
| // style yaml_scalar_style_t) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // mark yaml_mark_t = { 0, 0, 0 } | |||||
| // tag_copy *yaml_char_t = NULL | |||||
| // value_copy *yaml_char_t = NULL | |||||
| // node yaml_node_t | |||||
| // | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // assert(value) // Non-NULL value is expected. | |||||
| // | |||||
| // if (!tag) { | |||||
| // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG | |||||
| // } | |||||
| // | |||||
| // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||||
| // tag_copy = yaml_strdup(tag) | |||||
| // if (!tag_copy) goto error | |||||
| // | |||||
| // if (length < 0) { | |||||
| // length = strlen((char *)value) | |||||
| // } | |||||
| // | |||||
| // if (!yaml_check_utf8(value, length)) goto error | |||||
| // value_copy = yaml_malloc(length+1) | |||||
| // if (!value_copy) goto error | |||||
| // memcpy(value_copy, value, length) | |||||
| // value_copy[length] = '\0' | |||||
| // | |||||
| // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) | |||||
| // if (!PUSH(&context, document.nodes, node)) goto error | |||||
| // | |||||
| // return document.nodes.top - document.nodes.start | |||||
| // | |||||
| //error: | |||||
| // yaml_free(tag_copy) | |||||
| // yaml_free(value_copy) | |||||
| // | |||||
| // return 0 | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Add a sequence node to a document. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(int) | |||||
| //yaml_document_add_sequence(document *yaml_document_t, | |||||
| // tag *yaml_char_t, style yaml_sequence_style_t) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // mark yaml_mark_t = { 0, 0, 0 } | |||||
| // tag_copy *yaml_char_t = NULL | |||||
| // struct { | |||||
| // start *yaml_node_item_t | |||||
| // end *yaml_node_item_t | |||||
| // top *yaml_node_item_t | |||||
| // } items = { NULL, NULL, NULL } | |||||
| // node yaml_node_t | |||||
| // | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // | |||||
| // if (!tag) { | |||||
| // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG | |||||
| // } | |||||
| // | |||||
| // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||||
| // tag_copy = yaml_strdup(tag) | |||||
| // if (!tag_copy) goto error | |||||
| // | |||||
| // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error | |||||
| // | |||||
| // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, | |||||
| // style, mark, mark) | |||||
| // if (!PUSH(&context, document.nodes, node)) goto error | |||||
| // | |||||
| // return document.nodes.top - document.nodes.start | |||||
| // | |||||
| //error: | |||||
| // STACK_DEL(&context, items) | |||||
| // yaml_free(tag_copy) | |||||
| // | |||||
| // return 0 | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Add a mapping node to a document. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(int) | |||||
| //yaml_document_add_mapping(document *yaml_document_t, | |||||
| // tag *yaml_char_t, style yaml_mapping_style_t) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // mark yaml_mark_t = { 0, 0, 0 } | |||||
| // tag_copy *yaml_char_t = NULL | |||||
| // struct { | |||||
| // start *yaml_node_pair_t | |||||
| // end *yaml_node_pair_t | |||||
| // top *yaml_node_pair_t | |||||
| // } pairs = { NULL, NULL, NULL } | |||||
| // node yaml_node_t | |||||
| // | |||||
| // assert(document) // Non-NULL document object is expected. | |||||
| // | |||||
| // if (!tag) { | |||||
| // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG | |||||
| // } | |||||
| // | |||||
| // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||||
| // tag_copy = yaml_strdup(tag) | |||||
| // if (!tag_copy) goto error | |||||
| // | |||||
| // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error | |||||
| // | |||||
| // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, | |||||
| // style, mark, mark) | |||||
| // if (!PUSH(&context, document.nodes, node)) goto error | |||||
| // | |||||
| // return document.nodes.top - document.nodes.start | |||||
| // | |||||
| //error: | |||||
| // STACK_DEL(&context, pairs) | |||||
| // yaml_free(tag_copy) | |||||
| // | |||||
| // return 0 | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Append an item to a sequence node. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(int) | |||||
| //yaml_document_append_sequence_item(document *yaml_document_t, | |||||
| // sequence int, item int) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // | |||||
| // assert(document) // Non-NULL document is required. | |||||
| // assert(sequence > 0 | |||||
| // && document.nodes.start + sequence <= document.nodes.top) | |||||
| // // Valid sequence id is required. | |||||
| // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) | |||||
| // // A sequence node is required. | |||||
| // assert(item > 0 && document.nodes.start + item <= document.nodes.top) | |||||
| // // Valid item id is required. | |||||
| // | |||||
| // if (!PUSH(&context, | |||||
| // document.nodes.start[sequence-1].data.sequence.items, item)) | |||||
| // return 0 | |||||
| // | |||||
| // return 1 | |||||
| //} | |||||
| // | |||||
| ///* | |||||
| // * Append a pair of a key and a value to a mapping node. | |||||
| // */ | |||||
| // | |||||
| //YAML_DECLARE(int) | |||||
| //yaml_document_append_mapping_pair(document *yaml_document_t, | |||||
| // mapping int, key int, value int) | |||||
| //{ | |||||
| // struct { | |||||
| // error yaml_error_type_t | |||||
| // } context | |||||
| // | |||||
| // pair yaml_node_pair_t | |||||
| // | |||||
| // assert(document) // Non-NULL document is required. | |||||
| // assert(mapping > 0 | |||||
| // && document.nodes.start + mapping <= document.nodes.top) | |||||
| // // Valid mapping id is required. | |||||
| // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) | |||||
| // // A mapping node is required. | |||||
| // assert(key > 0 && document.nodes.start + key <= document.nodes.top) | |||||
| // // Valid key id is required. | |||||
| // assert(value > 0 && document.nodes.start + value <= document.nodes.top) | |||||
| // // Valid value id is required. | |||||
| // | |||||
| // pair.key = key | |||||
| // pair.value = value | |||||
| // | |||||
| // if (!PUSH(&context, | |||||
| // document.nodes.start[mapping-1].data.mapping.pairs, pair)) | |||||
| // return 0 | |||||
| // | |||||
| // return 1 | |||||
| //} | |||||
| // | |||||
| // | |||||
| @@ -0,0 +1,931 @@ | |||||
| // | |||||
| // Copyright (c) 2011-2019 Canonical Ltd | |||||
| // | |||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| // you may not use this file except in compliance with the License. | |||||
| // You may obtain a copy of the License at | |||||
| // | |||||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||||
| // | |||||
| // Unless required by applicable law or agreed to in writing, software | |||||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| // See the License for the specific language governing permissions and | |||||
| // limitations under the License. | |||||
| package yaml | |||||
| import ( | |||||
| "encoding" | |||||
| "encoding/base64" | |||||
| "fmt" | |||||
| "io" | |||||
| "math" | |||||
| "reflect" | |||||
| "strconv" | |||||
| "time" | |||||
| ) | |||||
| // ---------------------------------------------------------------------------- | |||||
| // Parser, produces a node tree out of a libyaml event stream. | |||||
| type parser struct { | |||||
| parser yaml_parser_t | |||||
| event yaml_event_t | |||||
| doc *Node | |||||
| anchors map[string]*Node | |||||
| doneInit bool | |||||
| } | |||||
| func newParser(b []byte) *parser { | |||||
| p := parser{} | |||||
| if !yaml_parser_initialize(&p.parser) { | |||||
| panic("failed to initialize YAML emitter") | |||||
| } | |||||
| if len(b) == 0 { | |||||
| b = []byte{'\n'} | |||||
| } | |||||
| yaml_parser_set_input_string(&p.parser, b) | |||||
| return &p | |||||
| } | |||||
| func newParserFromReader(r io.Reader) *parser { | |||||
| p := parser{} | |||||
| if !yaml_parser_initialize(&p.parser) { | |||||
| panic("failed to initialize YAML emitter") | |||||
| } | |||||
| yaml_parser_set_input_reader(&p.parser, r) | |||||
| return &p | |||||
| } | |||||
| func (p *parser) init() { | |||||
| if p.doneInit { | |||||
| return | |||||
| } | |||||
| p.anchors = make(map[string]*Node) | |||||
| p.expect(yaml_STREAM_START_EVENT) | |||||
| p.doneInit = true | |||||
| } | |||||
| func (p *parser) destroy() { | |||||
| if p.event.typ != yaml_NO_EVENT { | |||||
| yaml_event_delete(&p.event) | |||||
| } | |||||
| yaml_parser_delete(&p.parser) | |||||
| } | |||||
| // expect consumes an event from the event stream and | |||||
| // checks that it's of the expected type. | |||||
| func (p *parser) expect(e yaml_event_type_t) { | |||||
| if p.event.typ == yaml_NO_EVENT { | |||||
| if !yaml_parser_parse(&p.parser, &p.event) { | |||||
| p.fail() | |||||
| } | |||||
| } | |||||
| if p.event.typ == yaml_STREAM_END_EVENT { | |||||
| failf("attempted to go past the end of stream; corrupted value?") | |||||
| } | |||||
| if p.event.typ != e { | |||||
| p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) | |||||
| p.fail() | |||||
| } | |||||
| yaml_event_delete(&p.event) | |||||
| p.event.typ = yaml_NO_EVENT | |||||
| } | |||||
| // peek peeks at the next event in the event stream, | |||||
| // puts the results into p.event and returns the event type. | |||||
| func (p *parser) peek() yaml_event_type_t { | |||||
| if p.event.typ != yaml_NO_EVENT { | |||||
| return p.event.typ | |||||
| } | |||||
| if !yaml_parser_parse(&p.parser, &p.event) { | |||||
| p.fail() | |||||
| } | |||||
| return p.event.typ | |||||
| } | |||||
| func (p *parser) fail() { | |||||
| var where string | |||||
| var line int | |||||
| if p.parser.problem_mark.line != 0 { | |||||
| line = p.parser.problem_mark.line | |||||
| // Scanner errors don't iterate line before returning error | |||||
| if p.parser.error == yaml_SCANNER_ERROR { | |||||
| line++ | |||||
| } | |||||
| } else if p.parser.context_mark.line != 0 { | |||||
| line = p.parser.context_mark.line | |||||
| } | |||||
| if line != 0 { | |||||
| where = "line " + strconv.Itoa(line) + ": " | |||||
| } | |||||
| var msg string | |||||
| if len(p.parser.problem) > 0 { | |||||
| msg = p.parser.problem | |||||
| } else { | |||||
| msg = "unknown problem parsing YAML content" | |||||
| } | |||||
| failf("%s%s", where, msg) | |||||
| } | |||||
| func (p *parser) anchor(n *Node, anchor []byte) { | |||||
| if anchor != nil { | |||||
| n.Anchor = string(anchor) | |||||
| p.anchors[n.Anchor] = n | |||||
| } | |||||
| } | |||||
| func (p *parser) parse() *Node { | |||||
| p.init() | |||||
| switch p.peek() { | |||||
| case yaml_SCALAR_EVENT: | |||||
| return p.scalar() | |||||
| case yaml_ALIAS_EVENT: | |||||
| return p.alias() | |||||
| case yaml_MAPPING_START_EVENT: | |||||
| return p.mapping() | |||||
| case yaml_SEQUENCE_START_EVENT: | |||||
| return p.sequence() | |||||
| case yaml_DOCUMENT_START_EVENT: | |||||
| return p.document() | |||||
| case yaml_STREAM_END_EVENT: | |||||
| // Happens when attempting to decode an empty buffer. | |||||
| return nil | |||||
| case yaml_TAIL_COMMENT_EVENT: | |||||
| panic("internal error: unexpected tail comment event (please report)") | |||||
| default: | |||||
| panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) | |||||
| } | |||||
| } | |||||
| func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { | |||||
| var style Style | |||||
| if tag != "" && tag != "!" { | |||||
| tag = shortTag(tag) | |||||
| style = TaggedStyle | |||||
| } else if defaultTag != "" { | |||||
| tag = defaultTag | |||||
| } else if kind == ScalarNode { | |||||
| tag, _ = resolve("", value) | |||||
| } | |||||
| return &Node{ | |||||
| Kind: kind, | |||||
| Tag: tag, | |||||
| Value: value, | |||||
| Style: style, | |||||
| Line: p.event.start_mark.line + 1, | |||||
| Column: p.event.start_mark.column + 1, | |||||
| HeadComment: string(p.event.head_comment), | |||||
| LineComment: string(p.event.line_comment), | |||||
| FootComment: string(p.event.foot_comment), | |||||
| } | |||||
| } | |||||
| func (p *parser) parseChild(parent *Node) *Node { | |||||
| child := p.parse() | |||||
| parent.Content = append(parent.Content, child) | |||||
| return child | |||||
| } | |||||
| func (p *parser) document() *Node { | |||||
| n := p.node(DocumentNode, "", "", "") | |||||
| p.doc = n | |||||
| p.expect(yaml_DOCUMENT_START_EVENT) | |||||
| p.parseChild(n) | |||||
| if p.peek() == yaml_DOCUMENT_END_EVENT { | |||||
| n.FootComment = string(p.event.foot_comment) | |||||
| } | |||||
| p.expect(yaml_DOCUMENT_END_EVENT) | |||||
| return n | |||||
| } | |||||
| func (p *parser) alias() *Node { | |||||
| n := p.node(AliasNode, "", "", string(p.event.anchor)) | |||||
| n.Alias = p.anchors[n.Value] | |||||
| if n.Alias == nil { | |||||
| failf("unknown anchor '%s' referenced", n.Value) | |||||
| } | |||||
| p.expect(yaml_ALIAS_EVENT) | |||||
| return n | |||||
| } | |||||
| func (p *parser) scalar() *Node { | |||||
| var parsedStyle = p.event.scalar_style() | |||||
| var nodeStyle Style | |||||
| switch { | |||||
| case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: | |||||
| nodeStyle = DoubleQuotedStyle | |||||
| case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: | |||||
| nodeStyle = SingleQuotedStyle | |||||
| case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: | |||||
| nodeStyle = LiteralStyle | |||||
| case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: | |||||
| nodeStyle = FoldedStyle | |||||
| } | |||||
| var nodeValue = string(p.event.value) | |||||
| var nodeTag = string(p.event.tag) | |||||
| var defaultTag string | |||||
| if nodeStyle == 0 { | |||||
| if nodeValue == "<<" { | |||||
| defaultTag = mergeTag | |||||
| } | |||||
| } else { | |||||
| defaultTag = strTag | |||||
| } | |||||
| n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) | |||||
| n.Style |= nodeStyle | |||||
| p.anchor(n, p.event.anchor) | |||||
| p.expect(yaml_SCALAR_EVENT) | |||||
| return n | |||||
| } | |||||
| func (p *parser) sequence() *Node { | |||||
| n := p.node(SequenceNode, seqTag, string(p.event.tag), "") | |||||
| if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { | |||||
| n.Style |= FlowStyle | |||||
| } | |||||
| p.anchor(n, p.event.anchor) | |||||
| p.expect(yaml_SEQUENCE_START_EVENT) | |||||
| for p.peek() != yaml_SEQUENCE_END_EVENT { | |||||
| p.parseChild(n) | |||||
| } | |||||
| n.LineComment = string(p.event.line_comment) | |||||
| n.FootComment = string(p.event.foot_comment) | |||||
| p.expect(yaml_SEQUENCE_END_EVENT) | |||||
| return n | |||||
| } | |||||
| func (p *parser) mapping() *Node { | |||||
| n := p.node(MappingNode, mapTag, string(p.event.tag), "") | |||||
| block := true | |||||
| if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { | |||||
| block = false | |||||
| n.Style |= FlowStyle | |||||
| } | |||||
| p.anchor(n, p.event.anchor) | |||||
| p.expect(yaml_MAPPING_START_EVENT) | |||||
| for p.peek() != yaml_MAPPING_END_EVENT { | |||||
| k := p.parseChild(n) | |||||
| if block && k.FootComment != "" { | |||||
| // Must be a foot comment for the prior value when being dedented. | |||||
| if len(n.Content) > 2 { | |||||
| n.Content[len(n.Content)-3].FootComment = k.FootComment | |||||
| k.FootComment = "" | |||||
| } | |||||
| } | |||||
| v := p.parseChild(n) | |||||
| if k.FootComment == "" && v.FootComment != "" { | |||||
| k.FootComment = v.FootComment | |||||
| v.FootComment = "" | |||||
| } | |||||
| if p.peek() == yaml_TAIL_COMMENT_EVENT { | |||||
| if k.FootComment == "" { | |||||
| k.FootComment = string(p.event.foot_comment) | |||||
| } | |||||
| p.expect(yaml_TAIL_COMMENT_EVENT) | |||||
| } | |||||
| } | |||||
| n.LineComment = string(p.event.line_comment) | |||||
| n.FootComment = string(p.event.foot_comment) | |||||
| if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { | |||||
| n.Content[len(n.Content)-2].FootComment = n.FootComment | |||||
| n.FootComment = "" | |||||
| } | |||||
| p.expect(yaml_MAPPING_END_EVENT) | |||||
| return n | |||||
| } | |||||
| // ---------------------------------------------------------------------------- | |||||
| // Decoder, unmarshals a node into a provided value. | |||||
| type decoder struct { | |||||
| doc *Node | |||||
| aliases map[*Node]bool | |||||
| terrors []string | |||||
| stringMapType reflect.Type | |||||
| generalMapType reflect.Type | |||||
| knownFields bool | |||||
| uniqueKeys bool | |||||
| decodeCount int | |||||
| aliasCount int | |||||
| aliasDepth int | |||||
| } | |||||
| var ( | |||||
| nodeType = reflect.TypeOf(Node{}) | |||||
| durationType = reflect.TypeOf(time.Duration(0)) | |||||
| stringMapType = reflect.TypeOf(map[string]interface{}{}) | |||||
| generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) | |||||
| ifaceType = generalMapType.Elem() | |||||
| timeType = reflect.TypeOf(time.Time{}) | |||||
| ptrTimeType = reflect.TypeOf(&time.Time{}) | |||||
| ) | |||||
| func newDecoder() *decoder { | |||||
| d := &decoder{ | |||||
| stringMapType: stringMapType, | |||||
| generalMapType: generalMapType, | |||||
| uniqueKeys: true, | |||||
| } | |||||
| d.aliases = make(map[*Node]bool) | |||||
| return d | |||||
| } | |||||
| func (d *decoder) terror(n *Node, tag string, out reflect.Value) { | |||||
| if n.Tag != "" { | |||||
| tag = n.Tag | |||||
| } | |||||
| value := n.Value | |||||
| if tag != seqTag && tag != mapTag { | |||||
| if len(value) > 10 { | |||||
| value = " `" + value[:7] + "...`" | |||||
| } else { | |||||
| value = " `" + value + "`" | |||||
| } | |||||
| } | |||||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) | |||||
| } | |||||
| func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { | |||||
| err := u.UnmarshalYAML(n) | |||||
| if e, ok := err.(*TypeError); ok { | |||||
| d.terrors = append(d.terrors, e.Errors...) | |||||
| return false | |||||
| } | |||||
| if err != nil { | |||||
| fail(err) | |||||
| } | |||||
| return true | |||||
| } | |||||
| func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { | |||||
| terrlen := len(d.terrors) | |||||
| err := u.UnmarshalYAML(func(v interface{}) (err error) { | |||||
| defer handleErr(&err) | |||||
| d.unmarshal(n, reflect.ValueOf(v)) | |||||
| if len(d.terrors) > terrlen { | |||||
| issues := d.terrors[terrlen:] | |||||
| d.terrors = d.terrors[:terrlen] | |||||
| return &TypeError{issues} | |||||
| } | |||||
| return nil | |||||
| }) | |||||
| if e, ok := err.(*TypeError); ok { | |||||
| d.terrors = append(d.terrors, e.Errors...) | |||||
| return false | |||||
| } | |||||
| if err != nil { | |||||
| fail(err) | |||||
| } | |||||
| return true | |||||
| } | |||||
| // d.prepare initializes and dereferences pointers and calls UnmarshalYAML | |||||
| // if a value is found to implement it. | |||||
| // It returns the initialized and dereferenced out value, whether | |||||
| // unmarshalling was already done by UnmarshalYAML, and if so whether | |||||
| // its types unmarshalled appropriately. | |||||
| // | |||||
| // If n holds a null value, prepare returns before doing anything. | |||||
| func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { | |||||
| if n.ShortTag() == nullTag { | |||||
| return out, false, false | |||||
| } | |||||
| again := true | |||||
| for again { | |||||
| again = false | |||||
| if out.Kind() == reflect.Ptr { | |||||
| if out.IsNil() { | |||||
| out.Set(reflect.New(out.Type().Elem())) | |||||
| } | |||||
| out = out.Elem() | |||||
| again = true | |||||
| } | |||||
| if out.CanAddr() { | |||||
| outi := out.Addr().Interface() | |||||
| if u, ok := outi.(Unmarshaler); ok { | |||||
| good = d.callUnmarshaler(n, u) | |||||
| return out, true, good | |||||
| } | |||||
| if u, ok := outi.(obsoleteUnmarshaler); ok { | |||||
| good = d.callObsoleteUnmarshaler(n, u) | |||||
| return out, true, good | |||||
| } | |||||
| } | |||||
| } | |||||
| return out, false, false | |||||
| } | |||||
| func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { | |||||
| if n.ShortTag() == nullTag { | |||||
| return reflect.Value{} | |||||
| } | |||||
| for _, num := range index { | |||||
| for { | |||||
| if v.Kind() == reflect.Ptr { | |||||
| if v.IsNil() { | |||||
| v.Set(reflect.New(v.Type().Elem())) | |||||
| } | |||||
| v = v.Elem() | |||||
| continue | |||||
| } | |||||
| break | |||||
| } | |||||
| v = v.Field(num) | |||||
| } | |||||
| return v | |||||
| } | |||||
| const ( | |||||
| // 400,000 decode operations is ~500kb of dense object declarations, or | |||||
| // ~5kb of dense object declarations with 10000% alias expansion | |||||
| alias_ratio_range_low = 400000 | |||||
| // 4,000,000 decode operations is ~5MB of dense object declarations, or | |||||
| // ~4.5MB of dense object declarations with 10% alias expansion | |||||
| alias_ratio_range_high = 4000000 | |||||
| // alias_ratio_range is the range over which we scale allowed alias ratios | |||||
| alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) | |||||
| ) | |||||
| func allowedAliasRatio(decodeCount int) float64 { | |||||
| switch { | |||||
| case decodeCount <= alias_ratio_range_low: | |||||
| // allow 99% to come from alias expansion for small-to-medium documents | |||||
| return 0.99 | |||||
| case decodeCount >= alias_ratio_range_high: | |||||
| // allow 10% to come from alias expansion for very large documents | |||||
| return 0.10 | |||||
| default: | |||||
| // scale smoothly from 99% down to 10% over the range. | |||||
| // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. | |||||
| // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). | |||||
| return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) | |||||
| } | |||||
| } | |||||
| func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { | |||||
| d.decodeCount++ | |||||
| if d.aliasDepth > 0 { | |||||
| d.aliasCount++ | |||||
| } | |||||
| if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { | |||||
| failf("document contains excessive aliasing") | |||||
| } | |||||
| if out.Type() == nodeType { | |||||
| out.Set(reflect.ValueOf(n).Elem()) | |||||
| return true | |||||
| } | |||||
| switch n.Kind { | |||||
| case DocumentNode: | |||||
| return d.document(n, out) | |||||
| case AliasNode: | |||||
| return d.alias(n, out) | |||||
| } | |||||
| out, unmarshaled, good := d.prepare(n, out) | |||||
| if unmarshaled { | |||||
| return good | |||||
| } | |||||
| switch n.Kind { | |||||
| case ScalarNode: | |||||
| good = d.scalar(n, out) | |||||
| case MappingNode: | |||||
| good = d.mapping(n, out) | |||||
| case SequenceNode: | |||||
| good = d.sequence(n, out) | |||||
| default: | |||||
| panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind))) | |||||
| } | |||||
| return good | |||||
| } | |||||
| func (d *decoder) document(n *Node, out reflect.Value) (good bool) { | |||||
| if len(n.Content) == 1 { | |||||
| d.doc = n | |||||
| d.unmarshal(n.Content[0], out) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { | |||||
| if d.aliases[n] { | |||||
| // TODO this could actually be allowed in some circumstances. | |||||
| failf("anchor '%s' value contains itself", n.Value) | |||||
| } | |||||
| d.aliases[n] = true | |||||
| d.aliasDepth++ | |||||
| good = d.unmarshal(n.Alias, out) | |||||
| d.aliasDepth-- | |||||
| delete(d.aliases, n) | |||||
| return good | |||||
| } | |||||
| var zeroValue reflect.Value | |||||
| func resetMap(out reflect.Value) { | |||||
| for _, k := range out.MapKeys() { | |||||
| out.SetMapIndex(k, zeroValue) | |||||
| } | |||||
| } | |||||
| func (d *decoder) scalar(n *Node, out reflect.Value) bool { | |||||
| var tag string | |||||
| var resolved interface{} | |||||
| if n.indicatedString() { | |||||
| tag = strTag | |||||
| resolved = n.Value | |||||
| } else { | |||||
| tag, resolved = resolve(n.Tag, n.Value) | |||||
| if tag == binaryTag { | |||||
| data, err := base64.StdEncoding.DecodeString(resolved.(string)) | |||||
| if err != nil { | |||||
| failf("!!binary value contains invalid base64 data") | |||||
| } | |||||
| resolved = string(data) | |||||
| } | |||||
| } | |||||
| if resolved == nil { | |||||
| if out.CanAddr() { | |||||
| switch out.Kind() { | |||||
| case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | |||||
| out.Set(reflect.Zero(out.Type())) | |||||
| return true | |||||
| } | |||||
| } | |||||
| return false | |||||
| } | |||||
| if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | |||||
| // We've resolved to exactly the type we want, so use that. | |||||
| out.Set(resolvedv) | |||||
| return true | |||||
| } | |||||
| // Perhaps we can use the value as a TextUnmarshaler to | |||||
| // set its value. | |||||
| if out.CanAddr() { | |||||
| u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) | |||||
| if ok { | |||||
| var text []byte | |||||
| if tag == binaryTag { | |||||
| text = []byte(resolved.(string)) | |||||
| } else { | |||||
| // We let any value be unmarshaled into TextUnmarshaler. | |||||
| // That might be more lax than we'd like, but the | |||||
| // TextUnmarshaler itself should bowl out any dubious values. | |||||
| text = []byte(n.Value) | |||||
| } | |||||
| err := u.UnmarshalText(text) | |||||
| if err != nil { | |||||
| fail(err) | |||||
| } | |||||
| return true | |||||
| } | |||||
| } | |||||
| switch out.Kind() { | |||||
| case reflect.String: | |||||
| if tag == binaryTag { | |||||
| out.SetString(resolved.(string)) | |||||
| return true | |||||
| } | |||||
| out.SetString(n.Value) | |||||
| return true | |||||
| case reflect.Interface: | |||||
| out.Set(reflect.ValueOf(resolved)) | |||||
| return true | |||||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
| // This used to work in v2, but it's very unfriendly. | |||||
| isDuration := out.Type() == durationType | |||||
| switch resolved := resolved.(type) { | |||||
| case int: | |||||
| if !isDuration && !out.OverflowInt(int64(resolved)) { | |||||
| out.SetInt(int64(resolved)) | |||||
| return true | |||||
| } | |||||
| case int64: | |||||
| if !isDuration && !out.OverflowInt(resolved) { | |||||
| out.SetInt(resolved) | |||||
| return true | |||||
| } | |||||
| case uint64: | |||||
| if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | |||||
| out.SetInt(int64(resolved)) | |||||
| return true | |||||
| } | |||||
| case float64: | |||||
| if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | |||||
| out.SetInt(int64(resolved)) | |||||
| return true | |||||
| } | |||||
| case string: | |||||
| if out.Type() == durationType { | |||||
| d, err := time.ParseDuration(resolved) | |||||
| if err == nil { | |||||
| out.SetInt(int64(d)) | |||||
| return true | |||||
| } | |||||
| } | |||||
| } | |||||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
| switch resolved := resolved.(type) { | |||||
| case int: | |||||
| if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | |||||
| out.SetUint(uint64(resolved)) | |||||
| return true | |||||
| } | |||||
| case int64: | |||||
| if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | |||||
| out.SetUint(uint64(resolved)) | |||||
| return true | |||||
| } | |||||
| case uint64: | |||||
| if !out.OverflowUint(uint64(resolved)) { | |||||
| out.SetUint(uint64(resolved)) | |||||
| return true | |||||
| } | |||||
| case float64: | |||||
| if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { | |||||
| out.SetUint(uint64(resolved)) | |||||
| return true | |||||
| } | |||||
| } | |||||
| case reflect.Bool: | |||||
| switch resolved := resolved.(type) { | |||||
| case bool: | |||||
| out.SetBool(resolved) | |||||
| return true | |||||
| case string: | |||||
| // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). | |||||
| // It only works if explicitly attempting to unmarshal into a typed bool value. | |||||
| switch resolved { | |||||
| case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": | |||||
| out.SetBool(true) | |||||
| return true | |||||
| case "n", "N", "no", "No", "NO", "off", "Off", "OFF": | |||||
| out.SetBool(false) | |||||
| return true | |||||
| } | |||||
| } | |||||
| case reflect.Float32, reflect.Float64: | |||||
| switch resolved := resolved.(type) { | |||||
| case int: | |||||
| out.SetFloat(float64(resolved)) | |||||
| return true | |||||
| case int64: | |||||
| out.SetFloat(float64(resolved)) | |||||
| return true | |||||
| case uint64: | |||||
| out.SetFloat(float64(resolved)) | |||||
| return true | |||||
| case float64: | |||||
| out.SetFloat(resolved) | |||||
| return true | |||||
| } | |||||
| case reflect.Struct: | |||||
| if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | |||||
| out.Set(resolvedv) | |||||
| return true | |||||
| } | |||||
| case reflect.Ptr: | |||||
| panic("yaml internal error: please report the issue") | |||||
| } | |||||
| d.terror(n, tag, out) | |||||
| return false | |||||
| } | |||||
| func settableValueOf(i interface{}) reflect.Value { | |||||
| v := reflect.ValueOf(i) | |||||
| sv := reflect.New(v.Type()).Elem() | |||||
| sv.Set(v) | |||||
| return sv | |||||
| } | |||||
| func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { | |||||
| l := len(n.Content) | |||||
| var iface reflect.Value | |||||
| switch out.Kind() { | |||||
| case reflect.Slice: | |||||
| out.Set(reflect.MakeSlice(out.Type(), l, l)) | |||||
| case reflect.Array: | |||||
| if l != out.Len() { | |||||
| failf("invalid array: want %d elements but got %d", out.Len(), l) | |||||
| } | |||||
| case reflect.Interface: | |||||
| // No type hints. Will have to use a generic sequence. | |||||
| iface = out | |||||
| out = settableValueOf(make([]interface{}, l)) | |||||
| default: | |||||
| d.terror(n, seqTag, out) | |||||
| return false | |||||
| } | |||||
| et := out.Type().Elem() | |||||
| j := 0 | |||||
| for i := 0; i < l; i++ { | |||||
| e := reflect.New(et).Elem() | |||||
| if ok := d.unmarshal(n.Content[i], e); ok { | |||||
| out.Index(j).Set(e) | |||||
| j++ | |||||
| } | |||||
| } | |||||
| if out.Kind() != reflect.Array { | |||||
| out.Set(out.Slice(0, j)) | |||||
| } | |||||
| if iface.IsValid() { | |||||
| iface.Set(out) | |||||
| } | |||||
| return true | |||||
| } | |||||
| func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { | |||||
| l := len(n.Content) | |||||
| if d.uniqueKeys { | |||||
| nerrs := len(d.terrors) | |||||
| for i := 0; i < l; i += 2 { | |||||
| ni := n.Content[i] | |||||
| for j := i + 2; j < l; j += 2 { | |||||
| nj := n.Content[j] | |||||
| if ni.Kind == nj.Kind && ni.Value == nj.Value { | |||||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) | |||||
| } | |||||
| } | |||||
| } | |||||
| if len(d.terrors) > nerrs { | |||||
| return false | |||||
| } | |||||
| } | |||||
| switch out.Kind() { | |||||
| case reflect.Struct: | |||||
| return d.mappingStruct(n, out) | |||||
| case reflect.Map: | |||||
| // okay | |||||
| case reflect.Interface: | |||||
| iface := out | |||||
| if isStringMap(n) { | |||||
| out = reflect.MakeMap(d.stringMapType) | |||||
| } else { | |||||
| out = reflect.MakeMap(d.generalMapType) | |||||
| } | |||||
| iface.Set(out) | |||||
| default: | |||||
| d.terror(n, mapTag, out) | |||||
| return false | |||||
| } | |||||
| outt := out.Type() | |||||
| kt := outt.Key() | |||||
| et := outt.Elem() | |||||
| stringMapType := d.stringMapType | |||||
| generalMapType := d.generalMapType | |||||
| if outt.Elem() == ifaceType { | |||||
| if outt.Key().Kind() == reflect.String { | |||||
| d.stringMapType = outt | |||||
| } else if outt.Key() == ifaceType { | |||||
| d.generalMapType = outt | |||||
| } | |||||
| } | |||||
| if out.IsNil() { | |||||
| out.Set(reflect.MakeMap(outt)) | |||||
| } | |||||
| for i := 0; i < l; i += 2 { | |||||
| if isMerge(n.Content[i]) { | |||||
| d.merge(n.Content[i+1], out) | |||||
| continue | |||||
| } | |||||
| k := reflect.New(kt).Elem() | |||||
| if d.unmarshal(n.Content[i], k) { | |||||
| kkind := k.Kind() | |||||
| if kkind == reflect.Interface { | |||||
| kkind = k.Elem().Kind() | |||||
| } | |||||
| if kkind == reflect.Map || kkind == reflect.Slice { | |||||
| failf("invalid map key: %#v", k.Interface()) | |||||
| } | |||||
| e := reflect.New(et).Elem() | |||||
| if d.unmarshal(n.Content[i+1], e) { | |||||
| out.SetMapIndex(k, e) | |||||
| } | |||||
| } | |||||
| } | |||||
| d.stringMapType = stringMapType | |||||
| d.generalMapType = generalMapType | |||||
| return true | |||||
| } | |||||
| func isStringMap(n *Node) bool { | |||||
| if n.Kind != MappingNode { | |||||
| return false | |||||
| } | |||||
| l := len(n.Content) | |||||
| for i := 0; i < l; i += 2 { | |||||
| if n.Content[i].ShortTag() != strTag { | |||||
| return false | |||||
| } | |||||
| } | |||||
| return true | |||||
| } | |||||
| func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { | |||||
| sinfo, err := getStructInfo(out.Type()) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| var inlineMap reflect.Value | |||||
| var elemType reflect.Type | |||||
| if sinfo.InlineMap != -1 { | |||||
| inlineMap = out.Field(sinfo.InlineMap) | |||||
| inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) | |||||
| elemType = inlineMap.Type().Elem() | |||||
| } | |||||
| for _, index := range sinfo.InlineUnmarshalers { | |||||
| field := d.fieldByIndex(n, out, index) | |||||
| d.prepare(n, field) | |||||
| } | |||||
| var doneFields []bool | |||||
| if d.uniqueKeys { | |||||
| doneFields = make([]bool, len(sinfo.FieldsList)) | |||||
| } | |||||
| name := settableValueOf("") | |||||
| l := len(n.Content) | |||||
| for i := 0; i < l; i += 2 { | |||||
| ni := n.Content[i] | |||||
| if isMerge(ni) { | |||||
| d.merge(n.Content[i+1], out) | |||||
| continue | |||||
| } | |||||
| if !d.unmarshal(ni, name) { | |||||
| continue | |||||
| } | |||||
| if info, ok := sinfo.FieldsMap[name.String()]; ok { | |||||
| if d.uniqueKeys { | |||||
| if doneFields[info.Id] { | |||||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) | |||||
| continue | |||||
| } | |||||
| doneFields[info.Id] = true | |||||
| } | |||||
| var field reflect.Value | |||||
| if info.Inline == nil { | |||||
| field = out.Field(info.Num) | |||||
| } else { | |||||
| field = d.fieldByIndex(n, out, info.Inline) | |||||
| } | |||||
| d.unmarshal(n.Content[i+1], field) | |||||
| } else if sinfo.InlineMap != -1 { | |||||
| if inlineMap.IsNil() { | |||||
| inlineMap.Set(reflect.MakeMap(inlineMap.Type())) | |||||
| } | |||||
| value := reflect.New(elemType).Elem() | |||||
| d.unmarshal(n.Content[i+1], value) | |||||
| inlineMap.SetMapIndex(name, value) | |||||
| } else if d.knownFields { | |||||
| d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) | |||||
| } | |||||
| } | |||||
| return true | |||||
| } | |||||
| func failWantMap() { | |||||
| failf("map merge requires map or sequence of maps as the value") | |||||
| } | |||||
| func (d *decoder) merge(n *Node, out reflect.Value) { | |||||
| switch n.Kind { | |||||
| case MappingNode: | |||||
| d.unmarshal(n, out) | |||||
| case AliasNode: | |||||
| if n.Alias != nil && n.Alias.Kind != MappingNode { | |||||
| failWantMap() | |||||
| } | |||||
| d.unmarshal(n, out) | |||||
| case SequenceNode: | |||||
| // Step backwards as earlier nodes take precedence. | |||||
| for i := len(n.Content) - 1; i >= 0; i-- { | |||||
| ni := n.Content[i] | |||||
| if ni.Kind == AliasNode { | |||||
| if ni.Alias != nil && ni.Alias.Kind != MappingNode { | |||||
| failWantMap() | |||||
| } | |||||
| } else if ni.Kind != MappingNode { | |||||
| failWantMap() | |||||
| } | |||||
| d.unmarshal(ni, out) | |||||
| } | |||||
| default: | |||||
| failWantMap() | |||||
| } | |||||
| } | |||||
| func isMerge(n *Node) bool { | |||||
| return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) | |||||
| } | |||||
| @@ -0,0 +1,561 @@ | |||||
| // | |||||
| // Copyright (c) 2011-2019 Canonical Ltd | |||||
| // | |||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| // you may not use this file except in compliance with the License. | |||||
| // You may obtain a copy of the License at | |||||
| // | |||||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||||
| // | |||||
| // Unless required by applicable law or agreed to in writing, software | |||||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| // See the License for the specific language governing permissions and | |||||
| // limitations under the License. | |||||
| package yaml | |||||
| import ( | |||||
| "encoding" | |||||
| "fmt" | |||||
| "io" | |||||
| "reflect" | |||||
| "regexp" | |||||
| "sort" | |||||
| "strconv" | |||||
| "strings" | |||||
| "time" | |||||
| "unicode/utf8" | |||||
| ) | |||||
| type encoder struct { | |||||
| emitter yaml_emitter_t | |||||
| event yaml_event_t | |||||
| out []byte | |||||
| flow bool | |||||
| indent int | |||||
| doneInit bool | |||||
| } | |||||
| func newEncoder() *encoder { | |||||
| e := &encoder{} | |||||
| yaml_emitter_initialize(&e.emitter) | |||||
| yaml_emitter_set_output_string(&e.emitter, &e.out) | |||||
| yaml_emitter_set_unicode(&e.emitter, true) | |||||
| return e | |||||
| } | |||||
| func newEncoderWithWriter(w io.Writer) *encoder { | |||||
| e := &encoder{} | |||||
| yaml_emitter_initialize(&e.emitter) | |||||
| yaml_emitter_set_output_writer(&e.emitter, w) | |||||
| yaml_emitter_set_unicode(&e.emitter, true) | |||||
| return e | |||||
| } | |||||
| func (e *encoder) init() { | |||||
| if e.doneInit { | |||||
| return | |||||
| } | |||||
| if e.indent == 0 { | |||||
| e.indent = 4 | |||||
| } | |||||
| e.emitter.best_indent = e.indent | |||||
| yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) | |||||
| e.emit() | |||||
| e.doneInit = true | |||||
| } | |||||
| func (e *encoder) finish() { | |||||
| e.emitter.open_ended = false | |||||
| yaml_stream_end_event_initialize(&e.event) | |||||
| e.emit() | |||||
| } | |||||
| func (e *encoder) destroy() { | |||||
| yaml_emitter_delete(&e.emitter) | |||||
| } | |||||
| func (e *encoder) emit() { | |||||
| // This will internally delete the e.event value. | |||||
| e.must(yaml_emitter_emit(&e.emitter, &e.event)) | |||||
| } | |||||
| func (e *encoder) must(ok bool) { | |||||
| if !ok { | |||||
| msg := e.emitter.problem | |||||
| if msg == "" { | |||||
| msg = "unknown problem generating YAML content" | |||||
| } | |||||
| failf("%s", msg) | |||||
| } | |||||
| } | |||||
| func (e *encoder) marshalDoc(tag string, in reflect.Value) { | |||||
| e.init() | |||||
| var node *Node | |||||
| if in.IsValid() { | |||||
| node, _ = in.Interface().(*Node) | |||||
| } | |||||
| if node != nil && node.Kind == DocumentNode { | |||||
| e.nodev(in) | |||||
| } else { | |||||
| yaml_document_start_event_initialize(&e.event, nil, nil, true) | |||||
| e.emit() | |||||
| e.marshal(tag, in) | |||||
| yaml_document_end_event_initialize(&e.event, true) | |||||
| e.emit() | |||||
| } | |||||
| } | |||||
| func (e *encoder) marshal(tag string, in reflect.Value) { | |||||
| tag = shortTag(tag) | |||||
| if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { | |||||
| e.nilv() | |||||
| return | |||||
| } | |||||
| iface := in.Interface() | |||||
| switch value := iface.(type) { | |||||
| case *Node: | |||||
| e.nodev(in) | |||||
| return | |||||
| case time.Time: | |||||
| e.timev(tag, in) | |||||
| return | |||||
| case *time.Time: | |||||
| e.timev(tag, in.Elem()) | |||||
| return | |||||
| case time.Duration: | |||||
| e.stringv(tag, reflect.ValueOf(value.String())) | |||||
| return | |||||
| case Marshaler: | |||||
| v, err := value.MarshalYAML() | |||||
| if err != nil { | |||||
| fail(err) | |||||
| } | |||||
| if v == nil { | |||||
| e.nilv() | |||||
| return | |||||
| } | |||||
| e.marshal(tag, reflect.ValueOf(v)) | |||||
| return | |||||
| case encoding.TextMarshaler: | |||||
| text, err := value.MarshalText() | |||||
| if err != nil { | |||||
| fail(err) | |||||
| } | |||||
| in = reflect.ValueOf(string(text)) | |||||
| case nil: | |||||
| e.nilv() | |||||
| return | |||||
| } | |||||
| switch in.Kind() { | |||||
| case reflect.Interface: | |||||
| e.marshal(tag, in.Elem()) | |||||
| case reflect.Map: | |||||
| e.mapv(tag, in) | |||||
| case reflect.Ptr: | |||||
| e.marshal(tag, in.Elem()) | |||||
| case reflect.Struct: | |||||
| e.structv(tag, in) | |||||
| case reflect.Slice, reflect.Array: | |||||
| e.slicev(tag, in) | |||||
| case reflect.String: | |||||
| e.stringv(tag, in) | |||||
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
| e.intv(tag, in) | |||||
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
| e.uintv(tag, in) | |||||
| case reflect.Float32, reflect.Float64: | |||||
| e.floatv(tag, in) | |||||
| case reflect.Bool: | |||||
| e.boolv(tag, in) | |||||
| default: | |||||
| panic("cannot marshal type: " + in.Type().String()) | |||||
| } | |||||
| } | |||||
| func (e *encoder) mapv(tag string, in reflect.Value) { | |||||
| e.mappingv(tag, func() { | |||||
| keys := keyList(in.MapKeys()) | |||||
| sort.Sort(keys) | |||||
| for _, k := range keys { | |||||
| e.marshal("", k) | |||||
| e.marshal("", in.MapIndex(k)) | |||||
| } | |||||
| }) | |||||
| } | |||||
| func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { | |||||
| for _, num := range index { | |||||
| for { | |||||
| if v.Kind() == reflect.Ptr { | |||||
| if v.IsNil() { | |||||
| return reflect.Value{} | |||||
| } | |||||
| v = v.Elem() | |||||
| continue | |||||
| } | |||||
| break | |||||
| } | |||||
| v = v.Field(num) | |||||
| } | |||||
| return v | |||||
| } | |||||
| func (e *encoder) structv(tag string, in reflect.Value) { | |||||
| sinfo, err := getStructInfo(in.Type()) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| e.mappingv(tag, func() { | |||||
| for _, info := range sinfo.FieldsList { | |||||
| var value reflect.Value | |||||
| if info.Inline == nil { | |||||
| value = in.Field(info.Num) | |||||
| } else { | |||||
| value = e.fieldByIndex(in, info.Inline) | |||||
| if !value.IsValid() { | |||||
| continue | |||||
| } | |||||
| } | |||||
| if info.OmitEmpty && isZero(value) { | |||||
| continue | |||||
| } | |||||
| e.marshal("", reflect.ValueOf(info.Key)) | |||||
| e.flow = info.Flow | |||||
| e.marshal("", value) | |||||
| } | |||||
| if sinfo.InlineMap >= 0 { | |||||
| m := in.Field(sinfo.InlineMap) | |||||
| if m.Len() > 0 { | |||||
| e.flow = false | |||||
| keys := keyList(m.MapKeys()) | |||||
| sort.Sort(keys) | |||||
| for _, k := range keys { | |||||
| if _, found := sinfo.FieldsMap[k.String()]; found { | |||||
| panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) | |||||
| } | |||||
| e.marshal("", k) | |||||
| e.flow = false | |||||
| e.marshal("", m.MapIndex(k)) | |||||
| } | |||||
| } | |||||
| } | |||||
| }) | |||||
| } | |||||
| func (e *encoder) mappingv(tag string, f func()) { | |||||
| implicit := tag == "" | |||||
| style := yaml_BLOCK_MAPPING_STYLE | |||||
| if e.flow { | |||||
| e.flow = false | |||||
| style = yaml_FLOW_MAPPING_STYLE | |||||
| } | |||||
| yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) | |||||
| e.emit() | |||||
| f() | |||||
| yaml_mapping_end_event_initialize(&e.event) | |||||
| e.emit() | |||||
| } | |||||
| func (e *encoder) slicev(tag string, in reflect.Value) { | |||||
| implicit := tag == "" | |||||
| style := yaml_BLOCK_SEQUENCE_STYLE | |||||
| if e.flow { | |||||
| e.flow = false | |||||
| style = yaml_FLOW_SEQUENCE_STYLE | |||||
| } | |||||
| e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) | |||||
| e.emit() | |||||
| n := in.Len() | |||||
| for i := 0; i < n; i++ { | |||||
| e.marshal("", in.Index(i)) | |||||
| } | |||||
| e.must(yaml_sequence_end_event_initialize(&e.event)) | |||||
| e.emit() | |||||
| } | |||||
| // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. | |||||
| // | |||||
| // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported | |||||
| // in YAML 1.2 and by this package, but these should be marshalled quoted for | |||||
| // the time being for compatibility with other parsers. | |||||
| func isBase60Float(s string) (result bool) { | |||||
| // Fast path. | |||||
| if s == "" { | |||||
| return false | |||||
| } | |||||
| c := s[0] | |||||
| if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { | |||||
| return false | |||||
| } | |||||
| // Do the full match. | |||||
| return base60float.MatchString(s) | |||||
| } | |||||
| // From http://yaml.org/type/float.html, except the regular expression there | |||||
| // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. | |||||
| var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) | |||||
| // isOldBool returns whether s is bool notation as defined in YAML 1.1. | |||||
| // | |||||
| // We continue to force strings that YAML 1.1 would interpret as booleans to be | |||||
| // rendered as quotes strings so that the marshalled output valid for YAML 1.1 | |||||
| // parsing. | |||||
| func isOldBool(s string) (result bool) { | |||||
| switch s { | |||||
| case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", | |||||
| "n", "N", "no", "No", "NO", "off", "Off", "OFF": | |||||
| return true | |||||
| default: | |||||
| return false | |||||
| } | |||||
| } | |||||
| func (e *encoder) stringv(tag string, in reflect.Value) { | |||||
| var style yaml_scalar_style_t | |||||
| s := in.String() | |||||
| canUsePlain := true | |||||
| switch { | |||||
| case !utf8.ValidString(s): | |||||
| if tag == binaryTag { | |||||
| failf("explicitly tagged !!binary data must be base64-encoded") | |||||
| } | |||||
| if tag != "" { | |||||
| failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |||||
| } | |||||
| // It can't be encoded directly as YAML so use a binary tag | |||||
| // and encode it as base64. | |||||
| tag = binaryTag | |||||
| s = encodeBase64(s) | |||||
| case tag == "": | |||||
| // Check to see if it would resolve to a specific | |||||
| // tag when encoded unquoted. If it doesn't, | |||||
| // there's no need to quote it. | |||||
| rtag, _ := resolve("", s) | |||||
| canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) | |||||
| } | |||||
| // Note: it's possible for user code to emit invalid YAML | |||||
| // if they explicitly specify a tag and a string containing | |||||
| // text that's incompatible with that tag. | |||||
| switch { | |||||
| case strings.Contains(s, "\n"): | |||||
| if e.flow { | |||||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
| } else { | |||||
| style = yaml_LITERAL_SCALAR_STYLE | |||||
| } | |||||
| case canUsePlain: | |||||
| style = yaml_PLAIN_SCALAR_STYLE | |||||
| default: | |||||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
| } | |||||
| e.emitScalar(s, "", tag, style, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) boolv(tag string, in reflect.Value) { | |||||
| var s string | |||||
| if in.Bool() { | |||||
| s = "true" | |||||
| } else { | |||||
| s = "false" | |||||
| } | |||||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) intv(tag string, in reflect.Value) { | |||||
| s := strconv.FormatInt(in.Int(), 10) | |||||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) uintv(tag string, in reflect.Value) { | |||||
| s := strconv.FormatUint(in.Uint(), 10) | |||||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) timev(tag string, in reflect.Value) { | |||||
| t := in.Interface().(time.Time) | |||||
| s := t.Format(time.RFC3339Nano) | |||||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) floatv(tag string, in reflect.Value) { | |||||
| // Issue #352: When formatting, use the precision of the underlying value | |||||
| precision := 64 | |||||
| if in.Kind() == reflect.Float32 { | |||||
| precision = 32 | |||||
| } | |||||
| s := strconv.FormatFloat(in.Float(), 'g', -1, precision) | |||||
| switch s { | |||||
| case "+Inf": | |||||
| s = ".inf" | |||||
| case "-Inf": | |||||
| s = "-.inf" | |||||
| case "NaN": | |||||
| s = ".nan" | |||||
| } | |||||
| e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) nilv() { | |||||
| e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
| } | |||||
| func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { | |||||
| // TODO Kill this function. Replace all initialize calls by their underlining Go literals. | |||||
| implicit := tag == "" | |||||
| if !implicit { | |||||
| tag = longTag(tag) | |||||
| } | |||||
| e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) | |||||
| e.event.head_comment = head | |||||
| e.event.line_comment = line | |||||
| e.event.foot_comment = foot | |||||
| e.event.tail_comment = tail | |||||
| e.emit() | |||||
| } | |||||
| func (e *encoder) nodev(in reflect.Value) { | |||||
| e.node(in.Interface().(*Node), "") | |||||
| } | |||||
| func (e *encoder) node(node *Node, tail string) { | |||||
| // If the tag was not explicitly requested, and dropping it won't change the | |||||
| // implicit tag of the value, don't include it in the presentation. | |||||
| var tag = node.Tag | |||||
| var stag = shortTag(tag) | |||||
| var rtag string | |||||
| var forceQuoting bool | |||||
| if tag != "" && node.Style&TaggedStyle == 0 { | |||||
| if node.Kind == ScalarNode { | |||||
| if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { | |||||
| tag = "" | |||||
| } else { | |||||
| rtag, _ = resolve("", node.Value) | |||||
| if rtag == stag { | |||||
| tag = "" | |||||
| } else if stag == strTag { | |||||
| tag = "" | |||||
| forceQuoting = true | |||||
| } | |||||
| } | |||||
| } else { | |||||
| switch node.Kind { | |||||
| case MappingNode: | |||||
| rtag = mapTag | |||||
| case SequenceNode: | |||||
| rtag = seqTag | |||||
| } | |||||
| if rtag == stag { | |||||
| tag = "" | |||||
| } | |||||
| } | |||||
| } | |||||
| switch node.Kind { | |||||
| case DocumentNode: | |||||
| yaml_document_start_event_initialize(&e.event, nil, nil, true) | |||||
| e.event.head_comment = []byte(node.HeadComment) | |||||
| e.emit() | |||||
| for _, node := range node.Content { | |||||
| e.node(node, "") | |||||
| } | |||||
| yaml_document_end_event_initialize(&e.event, true) | |||||
| e.event.foot_comment = []byte(node.FootComment) | |||||
| e.emit() | |||||
| case SequenceNode: | |||||
| style := yaml_BLOCK_SEQUENCE_STYLE | |||||
| if node.Style&FlowStyle != 0 { | |||||
| style = yaml_FLOW_SEQUENCE_STYLE | |||||
| } | |||||
| e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)) | |||||
| e.event.head_comment = []byte(node.HeadComment) | |||||
| e.emit() | |||||
| for _, node := range node.Content { | |||||
| e.node(node, "") | |||||
| } | |||||
| e.must(yaml_sequence_end_event_initialize(&e.event)) | |||||
| e.event.line_comment = []byte(node.LineComment) | |||||
| e.event.foot_comment = []byte(node.FootComment) | |||||
| e.emit() | |||||
| case MappingNode: | |||||
| style := yaml_BLOCK_MAPPING_STYLE | |||||
| if node.Style&FlowStyle != 0 { | |||||
| style = yaml_FLOW_MAPPING_STYLE | |||||
| } | |||||
| yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style) | |||||
| e.event.tail_comment = []byte(tail) | |||||
| e.event.head_comment = []byte(node.HeadComment) | |||||
| e.emit() | |||||
| // The tail logic below moves the foot comment of prior keys to the following key, | |||||
| // since the value for each key may be a nested structure and the foot needs to be | |||||
| // processed only the entirety of the value is streamed. The last tail is processed | |||||
| // with the mapping end event. | |||||
| var tail string | |||||
| for i := 0; i+1 < len(node.Content); i += 2 { | |||||
| k := node.Content[i] | |||||
| foot := k.FootComment | |||||
| if foot != "" { | |||||
| kopy := *k | |||||
| kopy.FootComment = "" | |||||
| k = &kopy | |||||
| } | |||||
| e.node(k, tail) | |||||
| tail = foot | |||||
| v := node.Content[i+1] | |||||
| e.node(v, "") | |||||
| } | |||||
| yaml_mapping_end_event_initialize(&e.event) | |||||
| e.event.tail_comment = []byte(tail) | |||||
| e.event.line_comment = []byte(node.LineComment) | |||||
| e.event.foot_comment = []byte(node.FootComment) | |||||
| e.emit() | |||||
| case AliasNode: | |||||
| yaml_alias_event_initialize(&e.event, []byte(node.Value)) | |||||
| e.event.head_comment = []byte(node.HeadComment) | |||||
| e.event.line_comment = []byte(node.LineComment) | |||||
| e.event.foot_comment = []byte(node.FootComment) | |||||
| e.emit() | |||||
| case ScalarNode: | |||||
| value := node.Value | |||||
| if !utf8.ValidString(value) { | |||||
| if tag == binaryTag { | |||||
| failf("explicitly tagged !!binary data must be base64-encoded") | |||||
| } | |||||
| if tag != "" { | |||||
| failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |||||
| } | |||||
| // It can't be encoded directly as YAML so use a binary tag | |||||
| // and encode it as base64. | |||||
| tag = binaryTag | |||||
| value = encodeBase64(value) | |||||
| } | |||||
| style := yaml_PLAIN_SCALAR_STYLE | |||||
| switch { | |||||
| case node.Style&DoubleQuotedStyle != 0: | |||||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
| case node.Style&SingleQuotedStyle != 0: | |||||
| style = yaml_SINGLE_QUOTED_SCALAR_STYLE | |||||
| case node.Style&LiteralStyle != 0: | |||||
| style = yaml_LITERAL_SCALAR_STYLE | |||||
| case node.Style&FoldedStyle != 0: | |||||
| style = yaml_FOLDED_SCALAR_STYLE | |||||
| case strings.Contains(value, "\n"): | |||||
| style = yaml_LITERAL_SCALAR_STYLE | |||||
| case forceQuoting: | |||||
| style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
| } | |||||
| e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,5 @@ | |||||
| module "gopkg.in/yaml.v3" | |||||
| require ( | |||||
| "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 | |||||
| ) | |||||